Documentation Index
Fetch the complete documentation index at: https://docs.scalev.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Scalev uses OAuth 2.0 Authorization Code with PKCE.
The flow is split across two surfaces:
- the merchant-facing authorization step starts on the Scalev frontend authorize page
- the token, introspection, revocation, and installation endpoints live on the
/v3/oauth/* API surface
In practice, the flow looks like this:
- Register your application with Scalev
- Generate PKCE parameters
- Redirect the merchant’s browser to Scalev’s authorize page
- The merchant signs in and approves your app
- Your backend receives an authorization code
- Your backend exchanges the code for tokens
- Your backend uses the access token to call Scalev
/v3 APIs
- Your backend refreshes, introspects, or revokes tokens as needed
The authorize page is a frontend route, not a machine API endpoint. It is intentionally separate from the /v3 API contract.
URLs
Frontend Authorization URL
https://app.scalev.com/oauth/authorize
API Base URL
All public machine OAuth endpoints in this guide live under:
https://api.scalev.com/v3/oauth/*
Step 0: Register and Prepare Your App
Before merchants can install your app, create and configure your OAuth application in Scalev.
Important app fields include:
client_id
client_secret
redirect_uri
- optional
manage_url
- requested scopes
- optional webhook settings
- optional billing tags
- optional IP allowlist
Important rules:
redirect_uri must match exactly
client_secret must stay server-side
- if your app uses webhook events, configure the app webhook settings correctly
- if your app supports merchant launch links from Scalev, set
manage_url
- merchant installation is blocked until the app is verified
Step 1: Generate PKCE Parameters
Before redirecting the merchant, generate:
- a
code_verifier
- a
code_challenge derived from that verifier using SHA-256 and Base64 URL encoding
Code Verifier Example (JavaScript)
function generateCodeVerifier(length = 100) {
if (!Number.isInteger(length) || length < 43 || length > 128) {
throw new Error("Length must be an integer between 43 and 128.");
}
const charset =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
const randomValues = crypto.getRandomValues(new Uint8Array(length));
return Array.from(
randomValues,
(byte) => charset[byte % charset.length],
).join("");
}
Code Challenge Example (JavaScript)
function base64UrlEncode(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
async function generateCodeChallenge(codeVerifier) {
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const digest = await crypto.subtle.digest("SHA-256", data);
return base64UrlEncode(digest);
}
Store the code_verifier securely. You will need it during token exchange.
Step 2: Redirect the Merchant to Scalev
Redirect the merchant’s browser to:
https://app.scalev.com/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&state=RANDOM_STATE_STRING&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
Required query parameters:
| Parameter | Required | Description |
|---|
client_id | Yes | Your OAuth app client ID |
redirect_uri | Yes | Must exactly match the configured redirect URI |
response_type | Yes | Must be code |
state | Yes | Random CSRF-protection value generated by your app |
code_challenge | Yes | Base64 URL encoded SHA-256 hash of the code verifier |
code_challenge_method | Yes | Must be S256 |
Current behavior:
- PKCE is required
- only
S256 is accepted
- the authorize entrypoint is the frontend URL, not the
/v3 API
- if the merchant is not signed in, Scalev will take them through login and then return them to the authorize page with the same OAuth query parameters
If you want to preflight or display app metadata from your backend, call:
GET https://api.scalev.com/v3/oauth/application?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI
This returns public app metadata such as:
client_id
name
description
logo_url
homepage_url
redirect_uri
Step 3: Merchant Reviews and Approves the App
On the Scalev authorize page, the merchant can review and approve:
- the app identity
- the requested scopes
- webhook permissions, if applicable
- billing-tag approvals, if applicable
The frontend then completes the consent flow and redirects the merchant back to your configured redirect_uri.
Step 4: Receive the Authorization Code
After approval, your backend callback receives:
https://your-app.example.com/oauth/callback?code=AUTHORIZATION_CODE&state=RANDOM_STATE_STRING
Required validation:
- verify the returned
state
- exchange the code promptly
Current authorization code behavior:
- authorization-code flow only
- code TTL is 10 minutes
- each code can be exchanged only once
Step 5: Exchange the Code for Tokens
From your backend, exchange the code at:
POST https://api.scalev.com/v3/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"code": "AUTHORIZATION_CODE",
"code_verifier": "CODE_VERIFIER",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Successful response:
{
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"token_type": "Bearer",
"expires_in": 3600
}
Response fields:
| Field | Description |
|---|
access_token | Token used for authenticated /v3 API requests |
refresh_token | Token used to obtain a new access token |
token_type | Always Bearer |
expires_in | Access-token lifetime in seconds |
Step 6: Use the Access Token
Use the access token in the Authorization header:
Authorization: Bearer ACCESS_TOKEN
Your backend can then call the merchant-authorized /v3 endpoints that match the granted scopes.
Useful First Checks
Token Introspection
POST https://api.scalev.com/v3/oauth/introspect
Content-Type: application/json
{
"token": "ACCESS_TOKEN",
"token_type": "access",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Use this to check whether the token is currently active for runtime use.
Installation Status Snapshot
POST https://api.scalev.com/v3/oauth/installation/status
Content-Type: application/json
{
"token": "ACCESS_TOKEN",
"token_type": "access",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
This returns the merchant installation snapshot, including:
authorized_business_id
client_id
is_active
is_enabled
granted_scopes
webhook_status
granted_webhook_events
approved_billing_tags
manage_launch_available
updated_at
Example:
{
"authorized_business_id": 123,
"client_id": "client_abc",
"is_active": true,
"is_enabled": true,
"granted_scopes": ["order:list", "order:read"],
"webhook_status": "active",
"granted_webhook_events": ["payment.received", "shipment.status.updated"],
"approved_billing_tags": [],
"manage_launch_available": true,
"updated_at": "2026-04-01T06:10:12.345678Z"
}
Step 7: Refresh Access Tokens
When the access token expires, use the refresh token:
POST https://api.scalev.com/v3/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Successful response:
{
"access_token": "NEW_ACCESS_TOKEN",
"refresh_token": "NEW_REFRESH_TOKEN",
"token_type": "Bearer",
"expires_in": 3600
}
Important notes:
- always replace both the access token and the refresh token after a successful refresh
- refresh-token exchange is part of the
/v3/oauth/token flow
- if the installation is disabled or revoked, refresh may fail
Token Lifecycle
Current behavior:
- access token TTL: 1 hour
- refresh token TTL: 30 days
Token Revocation
To revoke a token:
POST https://api.scalev.com/v3/oauth/revoke
Content-Type: application/json
{
"token": "TOKEN_TO_REVOKE",
"token_type": "refresh",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
Current token_type values are:
Successful revocation returns:
Optional: Merchant Manage Launch Flow
If your app defines a manage_url, Scalev can launch merchants into your app with a short-lived launch_token.
Exchange that token from your backend with:
POST https://api.scalev.com/v3/oauth/installation/launch-token/exchange
Content-Type: application/json
{
"launch_token": "LAUNCH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
This returns the same installation snapshot shape as /v3/oauth/installation/status.
Important notes:
- the
launch_token is one-time and short-lived
- it is not an access token
- there is no shared manage-link secret; your backend authenticates the exchange with
client_id and client_secret
Best Practices
- Redirect merchants to the frontend authorize page, not to a backend
/v2 API endpoint.
- Generate a new PKCE verifier and challenge for every install attempt.
- Validate
state on every callback.
- Keep
client_secret, access tokens, and refresh tokens on the server only.
- Use only the scopes, webhook events, and billing tags your app actually needs.
- Treat
redirect_uri as an exact-match value.
- Use
/v3/oauth/installation/status when you need the authoritative merchant install snapshot.
- Use
/v3/oauth/introspect for runtime token diagnostics.
- Handle disabled installations separately from revoked installations.
Rate Limiting
OAuth access tokens use rate limits scoped to the OAuth application and merchant installation.
Current default:
- 10,000 requests per hour per installation
- 100 requests per 10 seconds per installation for short bursts
Rate-limit responses return 429 Too Many Requests with X-Ratelimit-Limit, X-Ratelimit-Remaining, and X-Ratelimit-Reset headers. Some endpoint-specific limits are stricter, and rate-limit responses may be plain text instead of the normal JSON error shape.
Error Handling
OAuth endpoints may return errors such as:
| Error Code | Description |
|---|
invalid_request | Missing or malformed request parameters |
invalid_client | Client authentication failed |
invalid_grant | Authorization code, refresh token, or PKCE verifier is invalid or expired |
unauthorized_client | The client is not allowed to use the requested grant type |
server_error | Unexpected server-side failure |
Typical error response shape:
{
"error": "Error message",
"error_code": "invalid_grant"
}
PKCE-related failures typically surface as:
invalid_grant when the code verifier does not match
invalid_request when required PKCE parameters are missing or malformed