Shopify authorization
Verify an app launch and authorize a shop; catalog sync and storefront sizing remain separate.
Configure the standalone app
Set the app launch URL to https://metr.so/console and register https://metr.so/api/console/shopify/callback as the exact redirect. The API requests read_products,read_inventory. App credentials and the stable token-encryption key belong in server-side Secret Manager configuration, never in themes or browser JavaScript.
Verify the launch
A launch without code and state is not completed authorization. The website backend sends the original signed query in a JSON body to the gateway-only endpoint below. Go verifies HMAC, shop syntax, unique query parameters and freshness. The response contains only shop; it saves no connection and creates no OAuth state.
POST /v1/console/shopify/launch{
"query": "<original Shopify-signed launch query without a leading ?>"
}Retain the signed launch privately for at most ten minutes, require seller sign-in and explicit Metr store selection, and reverify before starting OAuth. An unsigned shop query is never proof of authorization.
Start and complete authorization
The website sends launch_query in the authenticated store-specific start body, or an empty object for the ordinary flow. The backend scopes the selected store to the merchant and binds fresh single-use state to its store-domain snapshot, seller session and the verified Shopify shop. It does not silently rewrite the saved storefront domain.
POST /v1/console/stores/{store_id}/shopify/start{
"launch_query": "<original Shopify-signed launch query>"
}The callback requires Shopify HMAC, a fresh timestamp, the originating session, unexpired single-use state and an exact shop match. Only a successful token exchange with the required scopes saves AES-GCM encrypted tokens. An app launch alone cannot save a connection.
GET /v1/console/shopify/callbackFailures and limits
- shop_mismatch: the verified callback shop differs from the shop in this session's state. Open the app from Shopify and start a new authorization for its verified shop.
- invalid_callback: callback signature, timestamp or required fields failed validation.
- invalid_state: state expired, was consumed, is missing or belongs to another tenant/session.
- invalid_launch: the launch signature, fields or freshness could not be verified. Open the app again from Shopify.
- store_changed: the saved store domain changed during authorization.
- shopify_unavailable: token exchange failed; start a fresh authorization.
Authorization does not import a catalog or install a sizing widget. Automatic token refresh is not implemented; expired tokens require reauthorization. Never log launch/callback queries, codes, state, credentials or tokens.