Answer
How a brand or a person asks us to export or delete their data, what we promise, and how our team carries it out on production.
Today
Deletion
- The account menu has Delete my data for anyone signed in with a real address. It drafts an email from the person's own mail app to hello@joinsquareup.com with the brand and their email. brew/public/live.js:341-347
- The sheet says we delete within 7 days and reply when it is done. It lists the sign in data, saved cards, feed settings and jam messages, and for an admin who asks, the whole workspace: the read, Plays, jams and connected keys. brew/public/live.js:348-351
- The privacy page asks for an email from the work address naming the store, and says we delete the read, Plays, jams and connected keys and reply when done. It gives no time. brew/legal-pages.js:33-34
- Since 25 Oct the sheet is an honest request handled by hand, not a dummy. brew/DUMMY.md row 9
- Start again in the account menu only clears what this browser kept. It deletes nothing on our side. brew/public/app.js:2955
Shopify
- Uninstalling stops every read and removes our access to the store. brew/legal-pages.js:26
shop/redact, which Shopify sends 48 hours after an uninstall, drops the store's token and numbers, even on a protected brand, unless the store installed again since. brew/shopify-app.js:31, 204-216customers/data_requestandcustomers/redacthave nothing to send or erase, because we keep no record of any one customer. brew/shopify-app.js:18-19, 218
Export
- No export of a brand's data exists. What a team can download today is the Brand Book as a page, Markdown, JSON, llms.txt, a kit zip and a PDF, plus Plays and briefs as PDFs. brew/brand-book-routes.js:93
Our tools
db/delete-brand.mjsdeletes all of one brand's data for a data request, on a site that keeps its data in Postgres. It is a dry run until--yes, and prints exactly what would go. brew/db/delete-brand.mjs:1-4- It removes every row under
brands/<slug>/with our stored tokens and connections, the workspaces, archived copies in_deleted/, build progress, jobs, bus messages, counters, access log rows, every bucket version of its files, and made or research media no other row points at. brew/db/delete-brand.mjs:5-7; brew/db/brand-wipe.js:5-6 - It keeps one
data_deletionsrow: counts by folder, when, who, the grants, and what backups still hold. Never contents or member names. brew/db/migrations/010_data_deletions.sql:1-3; brew/db/brand-wipe.js:132-133, 187-195 - It never calls a platform. It prints the grants the brand must revoke itself, and where. brew/db/brand-wipe.js:7, 74-84, 240
- It refuses the five design partners unless
--partner-override --confirm-slug <slug>, and always refuses a brand inPROTECTED_CONNECTIONS, Arata by default. brew/db/brand-wipe.js:24-32; brew/connectors/protected.js:3 - What still holds a deleted brand for a while: point in time logs for 7 days, 14 daily backups, the nightly export, and the bucket's 7 day soft delete. brew/db/brand-wipe.js:236-238; brew/docs/production.md:108-110
- Staging runs it as the
brew-opsjob. Production needs abrew-prod-opsjob made once from the promoted image, and thedata_deletionstable arrives with the next promote. Neither exists on production today. brew/docs/production.md:83-106; deploy state 26 Oct tools/reset-brand.shis bucket era: withSTORAGE=pg, the default, it prints where to go and exits 3. brew/tools/reset-brand.sh:11-19db/reset-jams.mjsclears jams only, never connections. brew/db/reset-jams.mjs:36, 53; memory brew-jam-reset- The five partners are never wiped unless they ask. Every Arata connection (Meta, Shopify and the rest) is protected and is never disconnected, including through the 27 Oct domain swap (Param, 26 Oct). memory brew-live-stores, never-disconnect-arata-meta
- Disconnecting inside our product does not revoke the grant on Shopify, Meta, Google or Slack. memory brew-wipe-data
Tomorrow
| Version | Tag | Item |
|---|---|---|
| v1 | Changes | The deletion job is built on origin/main and works on staging. Production gets it with the next promote and a one time brew-prod-ops job. It keeps no archive, only the audit row. |
| v1 | New | A deletion for one person only: their sign in, member rows, mail settings and jam messages, leaving the brand. |
| v1 | Changes | The privacy page states the same 7 days the sheet promises. |
| v1 | New | An export on request: a zip of the read, Plays, Brand Book and briefs, sent within 7 days. Proposed. |
| v2 | New | Self serve export and deletion in the account menu, for admins. |
How we do it
A request arrives at hello@joinsquareup.com, usually with the subject
Delete my dataand the brand slug.Check it came from the person's own address. For a whole workspace, check they are the brand's admin.
Reply the same day that we have it, from a named person.
For one person: remove them from the brand's team in Team, ask an engineer to remove their mail settings and sessions, and leave the brand.
For a whole brand, dry run first and read every line. On staging:
gcloud run jobs execute brew-ops --region=asia-south1 --args=db/delete-brand.mjs,--slug,<slug> --wait gcloud run jobs execute brew-ops --region=asia-south1 --args=db/delete-brand.mjs,--slug,<slug>,--rev-n,<n>,--yes,--request,<ref>,--operator,<name> --waitOn production, once promoted and the
brew-prod-opsjob is made asdocs/production.mdsays, the same withbrew-prod-opsand without--rev-n. Never touch a partner who did not ask.Copy the grants the run prints and what backups still hold into the reply. A
data_deletionsrow withdone_atempty did not finish: run it again.Tell them to revoke our access on Shopify, Meta, Google, Slack, Klaviyo or the reviews app themselves, as the run printed.
Reply when it is done, within 7 days of the request.
Numbers
| Metric | Definition | Where measured | State |
|---|---|---|---|
| Requests | Deletion and export requests per month | hello@ inbox, label data request | Proposed |
| Days to done | Days from request to our done reply | hello@ inbox | Proposed |
| Within promise | Share done within 7 days | hello@ inbox | Proposed |
Open
- When do we make the
brew-prod-opsjob? Proposal: right after the next promote, with one dry run on a test brand on staging first. - The tool keeps no archive, only the audit row and the backups for up to 14 days. Do we say that on the privacy page? Proposal: yes, one line with the 14 days.
- What does a shared brief link show after its brand is deleted? Proposal: a plain page that says the brief was removed.
- Do we offer export in v1? Proposal: on request only, as a zip within 7 days.