Skip to main content

Customer API

Use the Customer API to let customers see and manage their subscriptions in an app you build, such as your own iOS or Android app. Customers sign in with their Shopify customer account (email and one-time code), so they don't need a new password. Zubs runs the sign-in with Shopify's Customer Account API and gives your app a customer access token. With that token, your app reaches that customer's own subscriptions and nothing else.


Before You Start​

  • Your store uses Shopify's new customer accounts.
  • You've created a Client ID in Shopify's Headless app and sent it to Zubs. See Generate a Client ID.
  • We've confirmed that access is set up for your store.

Sign Customers In​

Zubs uses OAuth 2.0 with PKCE. All sign-in endpoints take and return JSON under https://hub.zubs.app/headless-auth.

  1. Start. POST /headless-auth/start with { "shop": "your-store.myshopify.com" }. The response contains authUrl, verifier, nonce, state and redirectUri. Keep verifier and nonce for step 3.
  2. Let the customer sign in. Open authUrl in the system browser (ASWebAuthenticationSession on iOS, Custom Tabs on Android). Shopify redirects to redirectUri, which has the form shop.{shopId}.app://callback, with a code. Register this URL scheme in your app.
  3. Exchange the code. POST /headless-auth/exchange with code, verifier, nonce and shop. The response contains accessToken, refreshToken, customerId and expiresAt.

Store both tokens in the device's secure storage (Keychain on iOS, Keystore on Android) and never log them.

Stay Signed In and Sign Out​

  • Refresh before expiresAt: POST /headless-auth/refresh with shop, customerId and refreshToken. Shopify may rotate the refresh token. When the response contains a new refreshToken, replace the stored one, or the next refresh fails.
  • Sign out: DELETE /headless-auth/revoke with shop, customerId and refreshToken, then clear the stored tokens.
  • Browse before sign-in: POST /headless-auth/storefront-token with { "shop": "…" } returns a Shopify Storefront API token for public catalogue data.

Sign-In Errors​

StatusCodeMeaning
400MISSING_SHOP, MISSING_PARAMETERSA required field is missing
400INVALID_NONCEThe nonce doesn't match the sign-in that was started
401CLIENT_ID_NOT_CONFIGUREDZubs doesn't have a Client ID for this store yet
401SHOP_NOT_INSTALLEDZubs isn't installed on this store
401INVALID_REFRESH_TOKENThe refresh token is wrong or was replaced; sign the customer in again
404TOKEN_NOT_FOUNDNo sign-in is stored for this customer

Read and Manage Subscriptions​

Send GraphQL requests with the customer's access token:

POST https://hub.zubs.app/api/storefront/unstable/graphql.json

curl -X POST https://hub.zubs.app/api/storefront/unstable/graphql.json \
-H "Authorization: Bearer shcat_customer_access_token" \
-H "Content-Type: application/json" \
-d '{
"query": "query Subs($id: ID!) { customer(id: $id) { subscriptionContracts(first: 20, reverse: true) { edges { node { id status nextBillingDate } } } } }",
"variables": { "id": "gid://shopify/Customer/7234567890" }
}'

/exchange returns customerId as a number. Use it as gid://shopify/Customer/<id> in GraphQL.

For a subscription's detail screen, subscriptionContractDetails returns the contract, its lines, delivery details and billing cycles in one request.

The operations available here are summarized in What the APIs Cover. Each request is checked against the signed-in customer:

  • A request for another customer's subscription is rejected.
  • Operations and fields that aren't available to customers are rejected with Field "…" is not available to customer accounts.
  • Introspection isn't available on this endpoint. Use the explorer or the SDL file instead.

An expired or invalid token returns HTTP 401 with the code UNAUTHENTICATED. Refresh the token, and sign the customer in again if that fails.


Open the Customer Portal in Your App​

The API can change subscription content, but to let customers change products, quantities or their delivery interval themselves, we recommend opening the Zubs customer portal in a web view. It already handles your stock and pricing. It's part of the customer's Shopify account. Because sign-in ran through the system browser, the customer is usually still signed in. Otherwise Shopify asks for their email and code.

  • All subscriptions: https://{YOUR_SHOPIFY_STORE_URL}/account/pages/80d48e84-1b93-4dc9-b82b-9a555222a52c
  • One subscription: add /subscriptions/{subscriptionId}, using the numeric contract ID.

Enable JavaScript, cookies and DOM storage in the web view.