{
 "id": "18.1",
 "title": "Shopify app",
 "department": "Integrations and ecosystem",
 "department_slug": "integrations-and-ecosystem",
 "direction": null,
 "branded": false,
 "status": "draft",
 "owner": "Param",
 "last_verified": "2026-10-26",
 "holds": "The public app, its listing, review status, install flow, scopes",
 "urls": {
  "html": "https://handbook.joinsquareup.com/integrations-and-ecosystem/shopify-app",
  "markdown": "https://handbook.joinsquareup.com/integrations-and-ecosystem/shopify-app.md",
  "json": "https://handbook.joinsquareup.com/integrations-and-ecosystem/shopify-app.json"
 },
 "other_directions": [],
 "tldr": {
  "headline": "The Brew Shopify app awaits review; brands use their own app meanwhile",
  "points": [
   {
    "icon": "clock",
    "text": "Submitted to the App Store on 21 Oct; no answer is recorded since."
   },
   {
    "icon": "lock",
    "text": "Read only scopes, with no customer names, emails, phones or addresses."
   },
   {
    "icon": "link",
    "text": "Today every brand connects through its own Dev Dashboard app, keys pasted."
   },
   {
    "icon": "rocket",
    "text": "After approval one deploy turns on one-click install from the listing."
   },
   {
    "icon": "warn",
    "text": "Whether the live listing matches the new Plays wording is not recorded."
   }
  ],
  "stat": {
   "value": "21 Oct",
   "label": "submitted for review"
  },
  "decide": "What did Shopify answer, and do we go fully visible at once?",
  "mood": "waiting"
 },
 "sources": [
  "brew origin/main d6154b3: docs/shopify-app-review.md:1-149; docs/shopify.app.toml:1-39; shopify-app.js:1-31; connectors/shopify.js:1-16, 84; docs/connectors-approvals.md:13, 30-35; docs/production.md:125-127; tools/prod-env.sh:9; legal-pages.js:26",
  "memory brew-shopify-app-review, brew-shopify-connector-qa, brew-real-connectors, never-disconnect-arata-meta, brew-live-stores",
  "handbook content/engineering/connectors.md, read for consistency"
 ],
 "open_questions": [
  "**What did Shopify answer?** No outcome is recorded after 21 Oct. Proposal: Param checks hello@ and the Partner Dashboard today and notes the state here.",
  "**The repo draft now says `Brew: Daily Marketing Plays`; the listing was submitted as `Brew: Daily Marketing Moves`.** Proposal: change the live listing to the Plays wording only after approval, so the review is not reset, then rename to the picked name.",
  "**Fully visible at once, or limited to the link?** Proposal: stay limited to the direct link until the five design partners have installed it and production carries the app.",
  "**Webhook version 2026-10 against Admin API 2026-04.** Proposal: keep both, and bump the Admin API to the newer version in one tested change after approval.",
  "**Ask for `read_all_orders`?** Proposal: no; 60 days of orders is enough for every Play today."
 ],
 "parts": {
  "Answer": "Brew has one public Shopify app, \"Brew\", submitted for App Store review on 21 October 2026 and still waiting; until Shopify approves it, brands connect through their own Dev Dashboard app, and after approval one deploy turns on the one-click install from the listing.",
  "Today": "### The app\n\n| Fact | Value | Source |\n|---|---|---|\n| App | A new public app \"Brew\" in the SquareUp partner org, app 437593374721, client `ddb97e17…` | [src: brew/docs/shopify-app-review.md:12] [src: memory brew-shopify-app-review] |\n| Partner org and dev dashboard org | Partner org 4986332, dev dashboard org 222601853 | [src: memory brew-shopify-app-review] |\n| Old app | `squareup-prod` still serves the old shop.joinsquareup.com and is never repointed | [src: brew/docs/shopify-app-review.md:12] [src: memory brew-shopify-app-review] |\n| Active version | `brew-2`, pushed with the Shopify CLI from `~/Documents/ChatGPT/_shopify-brew-app` | [src: brew/docs/shopify-app-review.md:13-14] |\n| Keys | `FEED_SHOPIFY_CLIENT_ID` and `FEED_SHOPIFY_CLIENT_SECRET`, version 2 in Secret Manager, live | [src: brew/docs/shopify-app-review.md:15] |\n| App URL | `https://brew.joinsquareup.com/shopify`, embedded | [src: brew/docs/shopify.app.toml:9-10] |\n| Scopes | `read_products,read_inventory,read_orders,read_customers`; `read_locations` dropped; no `read_all_orders` | [src: brew/docs/shopify.app.toml:15] [src: brew/docs/shopify-app-review.md:38] |\n| Protected customer data | Level 1 only: order totals, dates, statuses, line items and each customer's order count; no names, emails, phones or addresses | [src: brew/docs/shopify-app-review.md:93-98] |\n| Webhooks | `app/uninstalled` plus `customers/data_request`, `customers/redact`, `shop/redact`, all to `/shopify/webhooks`, HMAC checked | [src: brew/docs/shopify.app.toml:24-33] [src: brew/docs/shopify-app-review.md:39] |\n| Admin API | GraphQL only, version `2026-04` | [src: brew/connectors/shopify.js:10] [src: brew/shopify-app.js:27] |\n| Billing | Free during the preview, so no Billing API | [src: brew/docs/shopify-app-review.md:40] |\n\nThe toml sets the webhook API version to `2026-10` while the code reads the Admin API at `2026-04`; both are allowed by Shopify, but they differ [src: brew/docs/shopify.app.toml:25] [src: brew/connectors/shopify.js:10].\n\n### Review status\n\n| Step | State | Source |\n|---|---|---|\n| Automated checks | All passed | [src: brew/docs/shopify-app-review.md:17] |\n| Distribution | Public chosen; listing visibility limited to the direct link until someone presses Make fully visible | [src: brew/docs/shopify-app-review.md:6, 18] |\n| Data request form | Level 1, app functionality and analytics, 9 of 9 answers | [src: brew/docs/shopify-app-review.md:18-19] |\n| Listing | English draft filled, with icon, three screenshots and a feature image in `docs/shopify-listing/` | [src: brew/docs/shopify-app-review.md:19-21] |\n| Listing copy in the repo | Since 26 Oct the draft names the app `Brew: Daily Marketing Plays` and says plays and we throughout. Whether the live listing in the Partner Dashboard was changed to match is not recorded | [src: brew/docs/shopify-app-review.md:107-115] [src: brew commit ca2bc7b] |\n| App name in `docs/shopify.app.toml` | Still `Brew`, left as is | [src: brew/docs/shopify.app.toml:8] |\n| Screencast | 1 minute 55, captioned, at `brew.joinsquareup.com/gen/brew-shopify-review-20261021.mp4` | [src: brew/docs/shopify-app-review.md:7] |\n| Submitted | 21 Oct 2026, about 23:15 IST; issues arrive by email to hello@joinsquareup.com | [src: brew/docs/shopify-app-review.md:3-5] |\n| Outcome | No approval or rejection is recorded in any source after 21 Oct | [src: memory brew-shopify-app-review] |\n\n### How a store connects today\n\n| Route | Who uses it | Source |\n|---|---|---|\n| The brand's own Dev Dashboard app, client id and secret pasted | Every brand until approval; the only route to order numbers until protected data is approved | [src: brew/docs/connectors-approvals.md:13] [src: brew/connectors/shopify.js:1-5] |\n| `own-oauth`: Brew keeps the pasted keys, the store approves once, `/shopify/own/callback` saves an offline token | An app that sits in another Shopify organization, such as Arata's | [src: brew/connectors/shopify.js:84] [src: memory brew-shopify-connector-qa] |\n| A legacy `shpat_` token | A brand that already has one | [src: brew/docs/connectors-approvals.md:13] |\n| One-click install from the listing | Off: `SHOPIFY_OAUTH=0` on staging and production | [src: brew/tools/prod-env.sh:9] [src: brew/docs/shopify-app-review.md:8] |\n\n### What the app does inside the admin\n\n- `/shopify` is the app home; App Bridge session tokens are traded for the shop's token, so there is no cookie and no separate sign in [src: brew/shopify-app.js:1-5].\n- The store's primary domain picks the brand; the installer of an unclaimed brand becomes owner, the store owner admin, other staff members [src: brew/shopify-app.js:6-7] [src: memory brew-shopify-app-review].\n- A store Brew has not read waits for the merchant to tap Read my store, because a read is paid [src: brew/shopify-app.js:8].\n- A store behind the password page, which is every development store and so every reviewer, is read from its Admin API; the checkout walk, page speed, AI answers and own Google rank say they wait until the store opens [src: brew/docs/shopify-app-review.md:54-68].\n- Google, Meta and Slack sign in through a signed two minute hop in a new tab, because they refuse frames [src: brew/docs/shopify-app-review.md:51-52].\n- Uninstall and shop/redact drop the token and the store's numbers; they disconnect only `app:'brew'` connections, so a brand's own app is untouched [src: brew/shopify-app.js:18-19] [src: memory brew-shopify-connector-qa].\n\n### Where it runs\n\nThe app's URLs point at staging, brew.joinsquareup.com; moving it to production is listed as not done and waits for the review [src: brew/docs/shopify.app.toml:9, 19-22] [src: brew/docs/production.md:125-127].\n\n### Protected\n\nArata's Shopify connection, through its own app, is never disconnected [src: memory never-disconnect-arata-meta] [src: memory brew-shopify-connector-qa]. The full connector rules are in [Connectors](/engineering/connectors).",
  "Tomorrow": "| Item | Version | Tag |\n|---|---|---|\n| Read only scopes, the four above | v1 | Stays |\n| One-click install from the listing, `SHOPIFY_OAUTH=1` with `SHOPIFY_INSTALL_URL` | v1 | Changes after approval |\n| App URLs, redirects and webhooks moved to feed.joinsquareup.com | v1 | Changes after approval |\n| Listing visibility set to fully visible | v1 | Changes, Param's call |\n| Listing copy says plays, done in the repo draft | v1 | Stays |\n| Listing and app name follow the chosen name | v1 | Changes |\n| Built for Shopify badge: 50 net paid installs, 5 reviews, Polaris design, admin speed | v2 | New |\n| Write scopes for any action on the store | v3 | New, only behind a person's yes per action |\n\nRows from v2 on are proposals for Param, not decisions.",
  "How we do it": "1. Watch hello@joinsquareup.com for Shopify's reviewer and answer the same day [src: brew/docs/shopify-app-review.md:5].\n2. On approval, deploy staging with the flags, then check that Connect Shopify says Install on Shopify:\n\n```bash\nSHOPIFY_OAUTH=1 SHOPIFY_INSTALL_URL=<the listing link> ./deploy.sh\n```\n\n3. Move the app to production: new app version with `https://feed.joinsquareup.com/shopify`, both callbacks and the webhook address on feed, released with the Shopify CLI from `brew/docs`:\n\n```bash\nshopify app config link\nshopify app deploy\n```\n\n4. Set `SHOPIFY_OAUTH=1` and the install URL in `tools/prod-env.sh`, then release with `tools/promote.sh` from a clean origin/main checkout.\n5. Keep `access_scopes` in the toml in step with `connectors/shopify.js` and `shopify-app.js` [src: brew/docs/shopify.app.toml:5].\n6. Audit what Brew shows against Shopify, read only: `node tools/shopify-audit.mjs <slug>` [src: memory brew-shopify-connector-qa].\n7. Test installs and uninstalls on the SquareUp DevStore only; never on a design partner [src: memory brew-live-stores].",
  "Numbers": "| Metric | Definition | Where measured |\n|---|---|---|\n| Review days | Days from submission on 21 Oct to Shopify's answer | This document, Proposed |\n| Installs | Stores with a live `app:'brew'` Shopify connection | `connections` on production, Proposed |\n| Uninstalls | `app/uninstalled` webhooks received | Server log `/shopify/webhooks`, Proposed |\n| Net paid installs | Installs minus uninstalls on a paid plan, the Built for Shopify bar of 50 | Partner Dashboard, Proposed for v2 |",
  "Open": "1. **What did Shopify answer?** No outcome is recorded after 21 Oct. Proposal: Param checks hello@ and the Partner Dashboard today and notes the state here.\n2. **The repo draft now says `Brew: Daily Marketing Plays`; the listing was submitted as `Brew: Daily Marketing Moves`.** Proposal: change the live listing to the Plays wording only after approval, so the review is not reset, then rename to the picked name.\n3. **Fully visible at once, or limited to the link?** Proposal: stay limited to the direct link until the five design partners have installed it and production carries the app.\n4. **Webhook version 2026-10 against Admin API 2026-04.** Proposal: keep both, and bump the Admin API to the newer version in one tested change after approval.\n5. **Ask for `read_all_orders`?** Proposal: no; 60 days of orders is enough for every Play today."
 }
}