The two modes
There are two ways to authenticate against the API, and which one you use depends on where your code runs:Production — Shopify session token
For real integrations. Your caller obtains a session token from Shopify App Bridge and sends it on every request. Customei verifies it against Shopify’s public keys and resolves the shop and member from the token claims.
Local dev — standalone mode
For local development and scripts. Set two environment variables on your own machine and Customei skips the Shopify JWT step entirely, pretending every request comes from the Owner of a specific shop.
Production: Shopify session token
Get a token
The session token comes from Shopify App Bridge inside your embedded admin app:Send it
Put the token in anAuthorization: Bearer header:
accountId— the top-level owner of all resources.shopID— the specific shop you’re operating on.currentMember— the person whose permissions apply to this request.
From outside the embedded admin
If you need to call the API from outside an embedded-admin page (for example, from a standalone web app or a server-side cron), you have two options:- Generate a session token server-side using your Shopify app credentials and the target shop’s install. This is Shopify’s officially sanctioned path — it’s more involved but gives you a real, verifiable token.
- Store a long-lived token in your backend, obtained once from an admin session. This works but requires carefully rotating the token if the install is reauthorized.
Local dev: standalone mode
When you’re developing locally, the Shopify session-token dance is often more ceremony than you need. Customei supports standalone mode — a dev-only bypass that treats every request as coming from a specific shop’s Owner.Enable it
Set two environment variables on the server you’re running Customei against:/api/graphql without an Authorization header resolve to the Owner of your-shop.myshopify.com. No JWT is required.
Use it
In standalone mode you can call the API with plaincurl:
When not to use standalone mode
- In production. It’s gated by
NODE_ENV, but belt and suspenders: don’t bake it into anything customer-facing. - When testing permission boundaries. Standalone mode always assumes the Owner role — you can’t exercise Staff-level permission checks from it. For permission testing, switch to a real session token from a Staff user.
What the context looks like
Every authenticated request gets a GraphQL context with:accountId— the account that owns the resources you’ll see.shopID— the shop being operated on.currentMember— the acting member, with their role and permission overrides.prisma— a database client wired up for the request.loaders— DataLoader instances for efficient relation loading.
accountId almost everywhere — because resources belong to the account, not to a user or a shop. Use shopID only for shop-scoped operations like reading orders or publishing products.
Common auth failures
| Symptom | Likely cause |
|---|---|
401 Unauthorized with no message | Missing or empty Authorization header in production. |
401 Unauthorized with “Invalid signature” | Token was signed by the wrong app; double-check your Shopify app credentials. |
401 Unauthorized with “Expired” | Session token older than 60s — fetch a fresh one. |
403 Forbidden on a mutation | Authentication worked, but your member doesn’t have the required permission. |
| Works in dev, fails in prod | Standalone mode is dev-only; production needs a real session token. |
Next
- GraphQL quickstart — run your first query.
- Errors and rate limits — handling failures cleanly.
- Roles and permissions — what each permission gates.