Token Exchange
OAuth
Token Exchange
Exchange an authorization code or refresh token for an access token
POST
Token Exchange
Documentation Index
Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
Use this file to discover all available pages before exploring further.
Token Exchange
Exchanges either a one-time authorizationcode (from
GET /apps/oauth/authorize) or a long-lived refresh_token for a fresh
access/refresh token pair. Two grant types are supported:
authorization_code— first-time install flow.refresh_token— silent token rotation after the 24h access token expires.
Request
Body Parameters
Grant type: authorization_code
Must be
authorization_code.Your app’s public client identifier.
Your app’s hashed client secret. Verified against the stored bcrypt
hash on the
App row.The one-time authorization code from
GET /apps/oauth/authorize. Single-use, expires in 10 minutes.The server-generated state token from the authorize call. Must match
what was bound to the code in Redis or the request fails with
Invalid state parameter.Required when the authorize call sent a
code_challenge. Length 43-128
chars. For S256 flows, the server SHA-256 hashes this and compares
(constant-time) against the stored challenge.Grant type: refresh_token
Must be
refresh_token.The current refresh token. Must not be expired, blacklisted, or
already rotated.
Response
200 on success.
success or error.Example Response
Token Lifetimes
| Token | TTL | Notes |
|---|---|---|
| Access token | 24 hours (expires_in: 86400) | Used as Authorization: Bearer on /api/v1/*. |
| Refresh token | 30 days | Resets on every successful rotation. |
| Authorization code | 10 minutes | Single-use; deleted after exchange. |
401 Token has been revoked.
Install/Re-install Limits
Whengrant_type=authorization_code would create a brand new active
installation (or re-activate a previously disabled one), the server
checks per-shop function caps before issuing tokens. If the app ships
functions and the merchant already has too many active installs of apps
with the same function types, the endpoint returns:
Error Codes
| HTTP | Error message | When |
|---|---|---|
400 | Unsupported grant_type | grant_type is not authorization_code or refresh_token. |
400 | Invalid or expired authorization code | code not in Redis (consumed or > 10 minutes old). |
400 | Invalid state parameter | state doesn’t match the value bound to the code. |
400 | State validation failed | state not in Redis or client_id doesn’t match. |
400 | code_verifier is required for this authorization code | Authorize call sent PKCE challenge but token call omitted verifier. |
400 | code_verifier must be 43-128 characters | PKCE verifier length out of range. |
400 | code_verifier does not match the code_challenge | PKCE verification failed (constant-time compare). |
401 | Invalid client credentials | client_id/client_secret doesn’t match. |
401 | Invalid refresh token | No installation has this refresh token. |
401 | Refresh token has expired. Please re-authenticate. | > 30 days since last rotation. |
401 | Token has been revoked | Refresh token was rotated or revoked. |
409 | Per-shop function limit exceeded for <type>: max <N> | Function caps prevent a new active install. |
429 | (throttler) | More than 10 requests/minute from this IP. |
Refresh Loop Pattern
Security Notes
- Always send this request server-side. Never expose
client_secretin browser code. - For browser-based public clients, use PKCE (no
client_secret). - Successful exchange consumes both the code and its state — both are deleted from Redis atomically.
- The HMAC on
client_secretuses bcrypt; brute-force is throttled by the IP rate limit (10/min) and by bcrypt’s per-hash cost.