Authorize a user (OAuth)
Use the authorization code flow with PKCE when your app acts on behalf of an Vantr merchant — for example, a third-party app that an account owner connects to their store. The merchant signs in on Vantr, approves the scopes you request, and your app receives a tenant-bound access token. You never see the merchant’s password, and each merchant authorizes you separately.If you are calling your own Vantr account from a backend you control, you do not need this flow. Use client credentials instead — it is a single request with no browser redirect.
Which flow do I need?
How the flow works
The merchant signs in and approves
Vantr shows a consent screen listing exactly the scopes you asked for.
You exchange the code for tokens
Server-side, at
/oauth/token, proving you started the request with your PKCE verifier.Before you start
Register a developer application
Create an app in the developer portal. Add every redirect URI you will use under Redirect URIs — the callback must match one of them exactly (scheme, host, port, path, and trailing slash). Enable the
authorization_code grant type and select the scopes your app may request.Decide: confidential or public client
A confidential client runs entirely on a server and can keep a
client_secret. A public client (single-page app, mobile, desktop, CLI) cannot. PKCE is required either way; the difference is only how you authenticate at the token endpoint (see Step 4).Step 1 — Generate a PKCE verifier and challenge
For each authorization attempt, generate a fresh random code verifier (43–128 characters fromA–Z a–z 0–9 - . _ ~) and derive its challenge as BASE64URL(SHA256(verifier)). Store the verifier in the user’s session — you will need it in Step 4.
Only
S256 is supported. A plain challenge or a verifier shorter than 43 characters is rejected with invalid_grant.Step 2 — Send the merchant to the authorize URL
Redirect the user’s browser tohttps://api.vantr.ai/oauth/authorize with the query parameters below. Vantr forwards the merchant to its consent screen (they sign in if needed).
string
required
Must be
code.string
required
Your application’s client ID.
string
required
Must exactly match a redirect URI registered on your application.
string
required
Space-separated scopes, e.g.
products.read categories.read. Request only what you need — see Scopes.string
required
The PKCE challenge from Step 1.
string
required
Must be
S256.string
Strongly recommended. An opaque value you generate and verify on return to prevent CSRF. Vantr echoes it back unchanged.
The merchant approving the consent screen must have permission for the scopes you request. If their role cannot grant a scope, the request is denied — request the narrowest scope set that does the job.
Step 3 — Handle the redirect back
After the merchant approves, Vantr redirects the browser to yourredirect_uri with a one-time code and your state. Always verify state against the value you stored, then continue server-side.
Approved
Denied
Node.js callback
Step 4 — Exchange the code for tokens
POST to https://api.vantr.ai/oauth/token with Content-Type: application/x-www-form-urlencoded.
string
required
authorization_code.string
required
The code from Step 3.
string
required
The same
redirect_uri you used in Step 2.string
required
The PKCE verifier from Step 1 that matches the challenge you sent.
- Confidential client
- Public client
Send your credentials with HTTP Basic auth (
client_id as username, client_secret as password).cURL
Node.js
Response
string
required
Opaque bearer token for v2 requests. Lives 1 hour by default.
string
Use it to mint a new access token without sending the merchant through consent again. Lives 90 days. Omitted if the application has refresh tokens disabled.
string
required
The scopes actually granted. May be narrower than you requested.
Step 5 — Call the v2 API
Send the access token as a Bearer credential. The token already identifies the merchant’s tenant — you never pass a tenant ID yourself.cURL
Node.js
A
200 here confirms the full chain works: PKCE, consent, tenant binding, and scope enforcement.Step 6 — Refresh the access token
When the access token expires (after ~1 hour), exchange the refresh token for a new one — same endpoint,grant_type=refresh_token. Use the same client authentication as Step 4.
cURL
scope to narrow the new token, but never to widen it beyond the original grant.
Token lifetimes
Revoke and inspect
Revoke
When a merchant disconnects your app, call
/oauth/revoke so their tokens stop working immediately rather than waiting for expiry.Troubleshooting
invalid_grant — PKCE verification failed
invalid_grant — PKCE verification failed
The
code_verifier you sent in Step 4 does not hash to the code_challenge you sent in Step 2. Make sure you store the verifier per attempt and that both sides use base64url without padding.invalid_grant — redirect_uri does not match
invalid_grant — redirect_uri does not match
The
redirect_uri in the token request must be byte-for-byte identical to the one in the authorize request, and both must be registered on the application. Watch for trailing slashes and http vs https.invalid_grant — code already used or expired
invalid_grant — code already used or expired
Authorization codes are single-use and expire after 5 minutes. Do not retry an exchange with the same code; restart from Step 2.
invalid_client — 401 at the token endpoint
invalid_client — 401 at the token endpoint
A confidential client must use HTTP Basic auth, and the secret must not have been rotated. A public client must send
client_id in the body and must be registered as public.access_denied on the callback
access_denied on the callback
The merchant declined, or their role cannot grant a scope you requested. Request fewer or narrower scopes, or have an account owner complete the connection.
403 from a v2 endpoint
403 from a v2 endpoint
The token is valid but its granted scopes do not include one the endpoint requires. Check the scope on the endpoint’s API reference page and re-authorize with it included.
slow_down / 429 from the token endpoint
slow_down / 429 from the token endpoint
OAuth token calls are rate limited to 60 per minute per credential. Back off using the
Retry-After header. v2 endpoints allow 240 per minute.Security checklist
One verifier per attempt
Generate a fresh
code_verifier and state for every authorization and bind them to the user’s session.Verify state
Reject the callback if
state does not match what you stored.Exchange server-side
Never expose the
client_secret or run the token exchange in the browser for a confidential client.Encrypt refresh tokens
Store refresh tokens encrypted, per tenant, and rotate the stored value on every refresh.