Authentication
Every request to /api/v1/* carries a bearer token:
Authorization: Bearer pat_live_abc123...
There are two ways to get one. Pick by whose account you are acting on:
| | Use | Choose when | | --- | --- | --- | | Personal access token | Your own account | A script, an internal tool, a back-office sync — anything where you own the data. | | OAuth app | Someone else's account | You are shipping software other allthe customers install. Never ask them for a personal token. |
Personal access tokens
Create one with the scopes you need.
- Format:
pat_live_… - Shown once, at creation. Store it immediately; we keep only a hash and cannot show it again. Lost one? Revoke it and issue another.
- No expiry. It stays valid until you revoke it in the dashboard.
There is no test or sandbox variant — every token is live, and every order it places is a real order. See Testing safely.
Keep it server-side. A personal access token carries your full granted scopes with no user consent step. Anything holding it can place orders you pay for. Never ship one to a browser, a mobile app, or a public repository. If one leaks, revoke it in the dashboard — that takes effect immediately.
OAuth 2.0 for third-party apps
If your software acts on other people's allthe accounts, register an app at Apps and use the authorization code flow. The customer logs in to allthe, sees exactly which scopes you asked for, and approves. You never handle their password, and they can disconnect you at any time.
PKCE is required — code_challenge_method must be S256. This holds for confidential server-side apps too, not only public clients.
1. Send the customer to the authorize screen
GET https://developer.allthe.com/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=catalog:read%20orders:write
&state=RANDOM_OPAQUE_VALUE
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
redirect_uri must match one registered on your app exactly. state is echoed back — generate it per attempt and verify it on return, or you are open to CSRF.
2. Receive the code
https://yourapp.com/callback?code=AUTH_CODE&state=RANDOM_OPAQUE_VALUE
If the customer declines, or something is wrong, you get ?error=access_denied&state=… instead. Authorization codes expire after 10 minutes and are single-use.
3. Exchange it for tokens
curl -X POST "https://developer.allthe.com/api/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://yourapp.com/callback" \
-d "code_verifier=YOUR_ORIGINAL_VERIFIER" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 86400,
"refresh_token": "...",
"scope": "catalog:read orders:write"
}
| Credential | Lifetime | | --- | --- | | Authorization code | 10 minutes, single use | | Access token | 24 hours | | Refresh token | 60 days |
client_secret is required on every token and revoke call — there is no public-client flow, so PKCE here is defence in depth rather than a replacement for the secret. You may send the credentials as form fields, as shown, or as HTTP Basic (Authorization: Basic base64(client_id:client_secret)).
4. Refresh before it expires
curl -X POST "https://developer.allthe.com/api/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "refresh_token=YOUR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Refresh tokens rotate on every use, and reuse revokes the whole chain. Each refresh returns a new refresh token and invalidates the one you sent. If an already-rotated token is presented, we assume it was copied and revoke every token in that grant — the customer has to reconnect.
In practice this means: persist the new refresh token before you act on the response, and never refresh from two processes at once. A crash between receiving a refresh and saving it costs you the grant. (RFC 9700 §4.14.2.)
Revoking
curl -X POST "https://developer.allthe.com/api/oauth/revoke" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "token=ACCESS_OR_REFRESH_TOKEN" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
Returns 200 whether or not the token existed — by design, so it cannot be used to probe which tokens are valid. Revoke when a customer uninstalls your app.
Scopes
Scopes are resource:verb. Request only what you need: a request missing one returns 403 insufficient_scope, and the error names the scope required.
| Scope | Grants |
| ---------------- | ---------------------------------------- |
| catalog:read | Browse the base product catalogue |
| products:read | List your customer products |
| products:write | Create and update customer products |
| stock:read | Read stock levels |
| orders:read | List and fetch orders, estimate an order, read webhook subscriptions |
| orders:write | Create and cancel orders, manage webhook subscriptions |
| shipping:read | Get shipping rate quotes |
| tax:read | Get tax rate quotes |
Two worth noting: orders:read covers POST /orders/estimate, because estimating creates and charges nothing. orders:write covers webhook subscription changes, since a subscription can expose order data.
Granted scopes are fixed at issue. To widen them, issue a new personal token, or send the customer back through the authorize screen.