shop= takes a function for exactly this,
and it is called on every request. The app is one Dev Dashboard app for every
store: Register a Shopify app.
Ask for an expiring offline token. Shopify requires public apps to use one
for the GraphQL Admin API, which is what the pack calls, by
January 1, 2027.
It lasts an hour and comes with a refresh token that lasts 90 days, and both
are replaced on every refresh. exchange_params={"expiring": "1"} on the server
is what asks for it.
The server
shopify_server.py
scope_separator is the comma Shopify documents. dead_grant_errors is
how Shopify says a grant is gone: Refresh behaviour.
revocation uninstalls the app: Disconnecting.
Scopes
Each tool declares the access scopes it needs, from the “Requires … access scope” line on every object and mutation it touches in the Admin GraphQL reference. “Any scope that writes a resource also grants read access to it”, so each reader also accepts the write scope:shopify_scopes.py
SHOPIFY_SCOPES is the list to type under Access
when you register the app.
Two classes of resource sit behind their own scopes, and no tool asks for them.
Orders older than 60 days need read_all_orders beside read_orders, and Shopify
approves that scope per app; without it the order tools see the last 60 days, with
no error for the rest. Fulfillment orders at fulfillment-service, third-party or
marketplace locations need their own *_fulfillment_orders scopes; the tools ask
for the merchant-managed ones, which is where a store’s own app works. An app
that needs either derives the tools with scopes=[...].
A field the token’s scopes do not reach comes back as 200 with an ACCESS_DENIED
error naming them (“Required access: read_orders access scope.”), and raises
InsufficientScope with those scopes.
A public app’s access to
protected customer data
is approved in the Partner Dashboard rather than granted by a scope: customer data
at all, and a customer’s name, address, phone and email on top. Until it is, a
field the app is not approved for comes back null in a 200, beside an errors
entry (“This app is not approved to access the Customer object”), which the
pack’s envelope raises as an APIError. A custom app has both levels without
review, unless a merchant created it in the admin, where level 2 depends on the
plan.
Connecting a merchant
shopify_users.py
X-Shopify-Access-Token, wherever it came from.
Refresh behaviour
Shopify refuses a refresh token it no longer honours, the grant’s after the app was uninstalled or one it never issued, withinvalid_request (“This
request requires an active refresh_token”) in a 401, measured, where RFC 6749
says invalid_grant. dead_grant_errors declares it, so once a merchant
uninstalls your app their tool calls stop asking Shopify again and the error’s
reauthorize is true. As with Linear, the same code would answer a missing
parameter, which a refresh from Charter never has.
A non-expiring token
A store that holds a non-expiring offline token, issued before expiring tokens or to a custom app, keeps working asStaticTokenProvider(token), exchanged
with expect_refresh_token=False. Pairing a per-call store with a single
access_token raises: one store’s token would go to every other store.
Disconnecting
TheSubjectProvider the pack is configured with builds the merchant’s client
from your store and revokes the grant, in one call:
shopify_disconnect.py
DELETE /admin/api_permissions/current.json, so that is the declaration. The
merchant can install it again from your link.
Measured, the access token was refused at once and the refresh token with it.
Uninstalling ends every grant your app holds on that store, not only this one:
the app’s client credentials grant on the store answered app_not_installed
until the app was installed again. A store already uninstalled answers 401,
which counts as gone, and revoke() returns False.
Where the rest is
- Register a Shopify app — the Dev Dashboard app, the scopes, the redirect URL
- Getting the grant — the route, the session,
state, PKCE - Connect your users’ accounts — every pack’s providers, side by side
- Packs — the Shopify tools