Authentication
Every call carries a bearer token in the Authorization header. There are two kinds: API keys for integrations, and session tokens (JWTs) for the three kinds of people who sign in. The API also needs to know which store a storefront call is for.
API keys — for integrations
An API key is the right credential for anything that is not a person: an ERP job, a supplier feeding data, a reporting script.
- A staff member creates the key under Settings → API keys (or
POST /admin/api-keys) with a name, a set of scopes and an optional expiry. Scopes are permission strings such asorder.view,invoice.vieworimport.ingest, and a key can never carry a scope its creator lacks. - The secret starts with
tgk_and is shown once. Store it in a secrets manager. - Send it as a bearer token:
curl https://api.example.com/admin/orders \
-H "Authorization: Bearer tgk_…"
A key acts with admin audience for the organization that created it, limited to its scopes. Last used is recorded on every call. Rotate issues a new secret and invalidates the old one immediately; Revoke stops the key for good.
Give a supplier's key only import.ingest; give a reporting job only the *.view scopes it reads. A key with import.ingest can discover import targets and templates but can do nothing else.
Session tokens — for people
The apps sign in with email and password and receive a JWT bound to one audience. Tokens are not interchangeable: a buyer token on an admin route is a 403 with "wrong token audience".
| Audience | Who | Sign in | Lifetime |
|---|---|---|---|
admin | Seller-side staff | POST /admin/auth/login | JWT_TTL, default 24 h |
storefront | Buyers (users of a customer company) | POST /storefront/auth/login | 30 days, sliding — the storefront calls POST /storefront/auth/refresh on boot |
vendor | Marketplace vendor users | POST /vendor/auth/login | JWT_TTL, default 24 h |
curl -X POST https://api.example.com/admin/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"…"}'
# → {"token":"<JWT>"}
If an email exists in more than one organization on the platform, the login answers "this email exists in multiple organizations; pass org_id"; retry with "org_id": <number> in the body. Login routes are rate-limited (10 attempts per minute per address by default) and answer 429 with "too many requests, slow down".
Staff tokens embed the resolved permission set; every admin route checks a permission (order.view, order.manage, …) and hides itself from callers who lack it. Buyer tokens carry the customer company; buyer routes authorize on ownership. Single sign-on (OIDC, SAML) issues the same tokens — see the admin guide's SSO providers.
Which store is this call for?
One API serves every tenant. Staff and vendor tokens carry their organization, so admin and vendor routes need nothing more. Storefront routes are bound to a website by host: the API reads X-Tenant-Host, then X-Forwarded-Host, then the request Host, and matches it against the website's domain or a verified custom domain. There is no fallback — an unknown host is refused.
So when you call storefront endpoints from a server rather than a browser on the store's own domain, name the store:
curl https://api.example.com/storefront/products \
-H "X-Tenant-Host: shop.acme-industrial.com"
Guest browsing (catalog, search, product pages) needs no token; carts, checkout, quotes, orders and account pages need a storefront token.
Capability URLs
A few assets are served by signed, time-limited URLs instead of a bearer token, so a browser or an email link can open them directly: invoice PDFs and media renditions. The signature covers the path and expiry; a guessed or expired URL is rejected. Treat these URLs as secrets while they are valid.
Errors
| Status | Meaning |
|---|---|
401 | Missing, malformed, expired or revoked token |
403 | Wrong audience, or a permission or scope the token lacks |
404 | Also returned for records that exist but belong to another organization or account |
409 | Duplicate — the same payment reference, PO number or idempotency key again |
422 | Valid request that the business rules refuse (insufficient stock, over budget, unknown SKU) |
429 | Rate limited |
Every error body is { "code": "...", "message": "..." }.
Related topics
- Webhooks
- Partner data import
- AI agents and MCP — OAuth 2.1 tokens for agents acting for a buyer
- Admin guide — API keys
- API reference