---
id: "18.1"
title: "Shopify app"
dept: integrations-and-ecosystem
status: draft
owner: Param
branded: false
last_verified: "2026-10-26"
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"
---

# Shopify app

## 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

| Fact | Value | Source |
|---|---|---|
| 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] |
| Partner org and dev dashboard org | Partner org 4986332, dev dashboard org 222601853 | [src: memory brew-shopify-app-review] |
| 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] |
| Active version | `brew-2`, pushed with the Shopify CLI from `~/Documents/ChatGPT/_shopify-brew-app` | [src: brew/docs/shopify-app-review.md:13-14] |
| Keys | `FEED_SHOPIFY_CLIENT_ID` and `FEED_SHOPIFY_CLIENT_SECRET`, version 2 in Secret Manager, live | [src: brew/docs/shopify-app-review.md:15] |
| App URL | `https://brew.joinsquareup.com/shopify`, embedded | [src: brew/docs/shopify.app.toml:9-10] |
| 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] |
| 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] |
| 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] |
| Admin API | GraphQL only, version `2026-04` | [src: brew/connectors/shopify.js:10] [src: brew/shopify-app.js:27] |
| Billing | Free during the preview, so no Billing API | [src: brew/docs/shopify-app-review.md:40] |

The 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].

### Review status

| Step | State | Source |
|---|---|---|
| Automated checks | All passed | [src: brew/docs/shopify-app-review.md:17] |
| 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] |
| Data request form | Level 1, app functionality and analytics, 9 of 9 answers | [src: brew/docs/shopify-app-review.md:18-19] |
| 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] |
| 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] |
| App name in `docs/shopify.app.toml` | Still `Brew`, left as is | [src: brew/docs/shopify.app.toml:8] |
| Screencast | 1 minute 55, captioned, at `brew.joinsquareup.com/gen/brew-shopify-review-20261021.mp4` | [src: brew/docs/shopify-app-review.md:7] |
| 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] |
| Outcome | No approval or rejection is recorded in any source after 21 Oct | [src: memory brew-shopify-app-review] |

### How a store connects today

| Route | Who uses it | Source |
|---|---|---|
| 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] |
| `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] |
| A legacy `shpat_` token | A brand that already has one | [src: brew/docs/connectors-approvals.md:13] |
| 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] |

### What the app does inside the admin

- `/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].
- 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].
- 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].
- 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].
- 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].
- 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].

### Where it runs

The 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].

### Protected

Arata'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 |
|---|---|---|
| Read only scopes, the four above | v1 | Stays |
| One-click install from the listing, `SHOPIFY_OAUTH=1` with `SHOPIFY_INSTALL_URL` | v1 | Changes after approval |
| App URLs, redirects and webhooks moved to feed.joinsquareup.com | v1 | Changes after approval |
| Listing visibility set to fully visible | v1 | Changes, Param's call |
| Listing copy says plays, done in the repo draft | v1 | Stays |
| Listing and app name follow the chosen name | v1 | Changes |
| Built for Shopify badge: 50 net paid installs, 5 reviews, Polaris design, admin speed | v2 | New |
| Write scopes for any action on the store | v3 | New, only behind a person's yes per action |

Rows 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].
2. On approval, deploy staging with the flags, then check that Connect Shopify says Install on Shopify:

```bash
SHOPIFY_OAUTH=1 SHOPIFY_INSTALL_URL=<the listing link> ./deploy.sh
```

3. 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`:

```bash
shopify app config link
shopify app deploy
```

4. 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.
5. Keep `access_scopes` in the toml in step with `connectors/shopify.js` and `shopify-app.js` [src: brew/docs/shopify.app.toml:5].
6. Audit what Brew shows against Shopify, read only: `node tools/shopify-audit.mjs <slug>` [src: memory brew-shopify-connector-qa].
7. Test installs and uninstalls on the SquareUp DevStore only; never on a design partner [src: memory brew-live-stores].

## Numbers

| Metric | Definition | Where measured |
|---|---|---|
| Review days | Days from submission on 21 Oct to Shopify's answer | This document, Proposed |
| Installs | Stores with a live `app:'brew'` Shopify connection | `connections` on production, Proposed |
| Uninstalls | `app/uninstalled` webhooks received | Server log `/shopify/webhooks`, Proposed |
| 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.
2. **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.
3. **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.
4. **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.
5. **Ask for `read_all_orders`?** Proposal: no; 60 days of orders is enough for every Play today.
