Appearance
Customer Account Support
Plan requirement: Customer account channel access requires the Pro plan or above.
Customer account extensions are treated as their own ingress channel.
Auth model (v1)
- Exchange: The extension calls
POST /api/customer-account/auth/exchange. SaveLayer validates the request withauthenticate.public.customerAccount(Shopify session token). Shop is taken fromsessionToken.dest, customer GID fromsessionToken.sub— not from the request body. - The response includes
data.authorization(Bearer <jwt>),expiresInSeconds(300),channel,shop, andcustomerGid, wrapped withcors(...)for extension requests. - Subsequent calls: Use
Authorization: Bearer <SaveLayer JWT>on routes such asPOST /api/customer-account/saveandGET /api/customer-account/list. Identity is enforced from the JWT, not from payload fields.
Invalid extension sessions map to INVALID_EXTENSION_SESSION (401).
CORS and preflight
- Exchange (
POST /api/customer-account/auth/exchange): success and error responses are wrapped with Shopify’scors(...)helper so browser extensions get appropriateAccess-Control-*headers. - Other routes (
save,list, etc.):OPTIONSrequests are handled through the same customer-account authenticate entrypoint (preflight). Operation responses use the standard SaveLayer JSON envelope once the SaveLayer JWT is sent on the request.
See /docs/authorization on the marketing site for the full direct API auth narrative shared with headless.
Shared behavior
Customer account requests still use the same SaveLayer contracts, JWT mint/verify path, and service layer as headless. Only the exchange proof (session token vs Customer Account API token) and transport (browser extension + CORS) differ.
Configuration
Use the same DIRECT_API_JWT_SECRET as headless: apps/pages/.dev.vars locally (and root .env for the Vite overlay), plus the DIRECT_API_JWT_SECRET secret in each GitHub Environment (Staging and Production) that deploys the Pages app. Details: Headless guide — Configuration.
The customer-account extension API host is app-managed at build time via SAVELAYER_CUSTOMER_ACCOUNT_API_BASE (not merchant-managed settings). This value should point to your canonical app URL plus /api/customer-account, for example:
- local:
https://<shopify-cli-tunnel-host>/api/customer-account - staging:
https://<staging-app-host>/api/customer-account - production:
https://<production-app-host>/api/customer-account
storefront_domain remains an optional extension setting used only for product link rendering. Leave it blank for default behavior; set it only when product links should target a custom storefront domain instead of the default shop domain.