Skip to main content
A mobile app uses exactly the same Storefront API as a web frontend. The only difference is that it doesn’t run on the store’s domain, so identify the store with the publishable key on every request.

Client setup

Customer login & tokens

Login (POST /auth/login) returns an access_token (30 minutes) and a refresh_token (7 days). Send the access token as Authorization: Bearer on account-area calls and renew it with POST /auth/refresh. The refresh rotates: every refresh returns a new refresh token and supersedes the one you presented. Rules for the app:
  • Store the new pair on every refresh, in the system’s secure storage: Keychain on iOS, a value encrypted with an Android Keystore key on Android, flutter_secure_storage in Flutter. Write both tokens before using the new access token.
  • One refresh at a time: requests that get a 401 together wait for the same refresh instead of each starting their own.
  • Reuse: if the app is killed between the response and the save, on restart it presents the old token. Within 60 seconds the API returns the same current token; later, for a customer account, it still returns the session’s current token. No forced logout, as long as you store what you receive.
  • End of session: a 401 from /auth/refresh (auth.session_revoked, auth.refresh_token_reused, auth.refresh_token_invalid) or a 403 auth.tenant_unavailable ends the session: clear the tokens and show the login screen.
  • No network or 5xx: don’t clear anything, try again later.
Flutter

Logout

  • Sign out: if you registered the device for notifications, first call DELETE /devices/{device_id} with the access token (a device linked to the account can only be removed by its owner). Then POST /auth/logout with {"refresh_token": "..."} in the body closes this device’s session on the server. Finally clear the tokens.
  • Sign out everywhere: POST /auth/logout?all=true with the access token (or the refresh token in the body). From then on every token issued before answers 401 auth.session_revoked.

Two-factor authentication (2FA)

If the customer has enabled 2FA, login takes two steps:
  1. POST /auth/login answers 401 auth.totp_required: show the code field.
  2. Repeat the same call with totp_code. The same field accepts a recovery code xxxxx-xxxxx: offer a “Lost your phone? Use a recovery code” link. Each code is valid only once (401 auth.totp_invalid if reused).
If the app lets customers enable 2FA:
  • POST /auth/2fa/setup with the current password returns secret and qr_uri; on mobile, also offer a button that opens qr_uri in the authenticator app.
  • POST /auth/2fa/verify with code and current_password enables 2FA and answers with new tokens (the other sessions are closed: store them) and with 10 recovery_codes. Show them only once, with “Copy” and “Share”, and ask the customer to confirm they saved them.
  • GET /auth/me exposes recovery_codes_remaining: when it gets close to 0, offer to regenerate them with POST /auth/2fa/recovery-codes (the current password is required).
  • POST /auth/2fa/disable accepts the app code or a recovery code and also returns new tokens to store.
POST /users/me/change-password doesn’t close sessions unless the app sends the X-Session-Rotation: 1 header: in that case the response carries new tokens, to store right away in place of the old ones.

Push notifications

Register the customer’s device token for transactional notifications (order shipped, etc.) through the Storefront API’s device-push endpoints — see the API Reference.
Images and payments work the same as on the web: image URLs must be prefixed with the origin, and the payment flow returned by POST /checkout must be completed with the gateway’s native SDK (Stripe/PayPal/…) when required. Prices and totals arrive as decimal strings: see Build a storefront.