Environments and Authentication
Choose the sandbox or production base URL, exchange your client credentials for a bearer token, and learn which permissions your credentials carry.
Every request to Payment Labs goes to one environment and carries a bearer token. One login serves the whole platform: the token you get from POST /login works for every API, whether you send payouts, review payment requests, run wallets or accept payins. This page shows how to pick the environment, get a token, and read the authentication errors.
Before you start
You need a client ID and a secret. Payment Labs issues them; you cannot create or rotate them through the API. Ask your Payment Labs contact for sandbox credentials first, and for production credentials when you are ready to go live.
Keep the secret on your server. Never ship it in a browser, a mobile app or a repository. When Payment Labs issues a new secret for your account, the old one stops working immediately.
Environments
Sandbox and production are separate: separate credentials, separate data, separate balances. A token from one does not work in the other.
| Sandbox | Production | |
|---|---|---|
| API base URL | https://api-sandbox.paymentlabs.io | https://api.paymentlabs.io |
Dashboard and hosted checkout page (links.checkout) | https://app-sandbox.paymentlabs.io | https://app.paymentlabs.io |
Embeddable checkout (links.embeddable) | https://checkout-sandbox.paymentlabs.io | https://checkout.paymentlabs.io |
| Payin SDK script | https://sdk.paymentlabs.io/payin/latest/paymentlabs.js | same |
Every path in this documentation is relative to the API base URL. All request and response bodies are JSON; send Content-Type: application/json on every request with a body.
What differs in sandbox:
- No real money moves. Payouts and card charges run against test processors.
- Payee identity verification is simulated and passes, so you can onboard test payees with any data.
- You control payout outcomes with keywords in
payeeMemo. See Bank Transfer Payout Test Values. - You pay checkouts with test cards. See Credit Card Payin Test Values.
- Webhook deliveries carry the header
x-environment: sandbox. In production it isproduction.
Get a token
Send your credentials to Create Access Token (POST /login) with method set to client-secret. key is your client ID. There is one login for all of Payment Labs: merchants and payors use the same operation, the same credentials and the same token.
curl -X POST https://api-sandbox.paymentlabs.io/login \
-H "Content-Type: application/json" \
-d '{
"method": "client-secret",
"key": "3f6c2a9e-8b41-4d7a-9f0e-5c2b7a1d4e88",
"secret": "<your-secret>"
}'const BASE_URL = "https://api-sandbox.paymentlabs.io";
async function getToken(clientId: string, secret: string): Promise<string> {
const response = await fetch(`${BASE_URL}/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ method: "client-secret", key: clientId, secret }),
});
if (!response.ok) {
throw new Error(`Login failed with ${response.status}: ${await response.text()}`);
}
const { token } = (await response.json()) as { token: string };
return token;
}A successful login returns 200:
{
"token": "eyJhbGciOiJSUzUxMiIsImtpZCI6IjIwMjYtMTAifQ.eyJzdWIiOiJDLTAxSkJYMlE4In0.c2lnbmF0dXJl"
}A failed login always returns 400, never 401:
code | Meaning | What to do |
|---|---|---|
MISSING_KEY | key is missing. | Send your client ID as key. |
MISSING_SECRET | secret is missing. | Send your secret. |
INVALID_KEY_OR_SECRET | The client ID and secret do not match. | Check that you use the credentials for this environment, and that the secret has not been replaced. |
INVALID_KEY | The credential is no longer linked to an active account. | Contact Payment Labs. |
INVALID_METHOD | method is not a supported value. | Send "method": "client-secret". |
If the body is missing or is not valid JSON, the response is 400 with a plain-text body (Missing request body or Invalid body), not JSON.
Use the token
Send the token in the Authorization header of every other request:
curl https://api-sandbox.paymentlabs.io/payors/me \
-H "Authorization: Bearer $TOKEN"const response = await fetch(`${BASE_URL}/payors/me`, {
headers: { Authorization: `Bearer ${token}` },
});If your credential only has a payin role, call GET /merchants/me (Get Merchant) instead: GET /payors/me needs a payout role.
- A token is valid for 3 hours from the moment it is issued. There is no refresh token: call
POST /loginagain. - Cache the token and reuse it for all requests until it is close to expiry, rather than logging in for every request.
- If a request returns
401, get a new token and retry that request once. - A token from API credentials belongs to exactly one account, so you never need the
x-payor-idheader. That header exists only for dashboard users who belong to several accounts.
A small token cache:
let cached: { token: string; expiresAt: number } | undefined;
async function authToken(): Promise<string> {
const now = Date.now();
if (cached && cached.expiresAt - now > 5 * 60 * 1000) {
return cached.token;
}
const token = await getToken(process.env.PL_CLIENT_ID!, process.env.PL_SECRET!);
cached = { token, expiresAt: now + 3 * 60 * 60 * 1000 };
return token;
}Permissions
Each credential is issued with a role for payouts, a role for payins, or both. The role decides which operations the token can call. Each operation's reference page states the role it needs when it is more than read access. Role and permission names are the ones shown on the Roles screen of your dashboard; the tables below map the permissions to roles.
Payout roles
| Role | Read: view batches, payments, payees and settings | Write: create and edit batches, payments, payees and imports; cancel payments | Payment Approval: submit payments and batches, turn auto-submit on or off | User Config: manage webhooks, set the payment availability period |
|---|---|---|---|---|
| Administrator | Yes | Yes | Yes | Yes |
| Expert User | Yes | Yes | Yes | No |
| User | Yes | Yes | No | No |
| Approver | Yes | No | Yes | No |
| Viewer | Yes | No | No | No |
Payin roles
| Role | Merchant Read: view checkouts, settlements and terminals | Merchant Write: create and charge checkouts, register and edit terminals | Merchant Approver: refund, edit subscription plans, enable and disable terminals | Merchant User Config: create and cancel plans, create, edit and delete plan offers, cancel subscriptions |
|---|---|---|---|---|
| Merchant Administrator | Yes | Yes | Yes | Yes |
| Merchant Approver | Yes | Yes | Yes | No |
| Merchant User | Yes | Yes | No | No |
| Merchant Viewer | Yes | No | No | No |
Webhooks are managed with the payout Administrator role, even if you only use payins. Ask for a credential with that role if you need to register webhooks through the API.
Wallets, wallet disputes and payment requests
Wallets, Wallet Disputes and Payment Requests need the Wallet Management or Payment Requests permission: Wallets and Wallet Disputes need Wallet Management, and Payment Requests need Payment Requests. In the dashboard the Administrator role includes both. To use them with an API credential, contact Payment Labs to enable access. Without the permission, these operations return 403 FORBIDDEN_INSUFFICIENT_TAGS.
You cannot change a credential's role or permissions through the API; ask Payment Labs.
Authentication errors
| Status | code | Meaning | What to do |
|---|---|---|---|
401 | UNAUTHORIZED_NO_TOKEN | No Authorization header. | Send Authorization: Bearer <token>. |
401 | UNAUTHORIZED_INVALID_TOKEN | The token is expired, malformed, or from another environment. | Get a new token and retry once. |
401 | UNAUTHORIZED_NO_CONTEXT, UNAUTHORIZED_NO_PAYOR | The token carries no account, or your credential has no payout or payin role. | Get a new token and retry once. If it persists, contact Payment Labs. |
401 | none, body {"message": "Unauthorized"} | Some routes reject a bad token at the gateway, without a code. | Same as UNAUTHORIZED_INVALID_TOKEN. |
403 | FORBIDDEN_INSUFFICIENT_TAGS | Your credential's role does not allow this operation. | Use a credential with the role the operation needs. Retrying does not help. |
503 | AUTH_UNAVAILABLE | Payment Labs could not verify the token in time. | Retry after a short wait. |
For every other error, see Errors.
Next steps
Updated 1 day ago
What’s Next
Next: Payouts Quickstart.
