Authentication
Two-credential token flow — apiKey + secretKey → Bearer access token.
Context and key considerations
Authentication is a two-credential flow. You exchange your apiKey + secretKey pair for a short-lived Bearer access token, then send that token on every API call. Requiring both halves of the pair means a single leak (e.g. a stray log line, a shared screen) doesn't hand an attacker a working credential.
Your credentials
Each merchant gets two pairs — one for sandbox, one for production:
| Key kind | Prefix | Allowed usage |
|---|---|---|
| Secret · production | sk_live_… | Server-side only. Full access. |
| Secret · sandbox | sk_test_… | Server-side only. Sandbox cascade. |
| Publishable · production | pk_live_… | Browser/native client. Hosted checkout only. |
| Publishable · sandbox | pk_test_… | Browser/native client. Sandbox checkout only. |
secretKey grants full API access. Store it in a secret manager — never commit it or expose it in client-side code. If a key is exposed, rotate the pair from your dashboard.Exchange for a token
Send both keys to POST /api/v1/auth/token. Both must come from the same environment (test pair or live pair) — pairing them across environments returns 401.
curl -X POST https://sandbox.key2pay.ai/api/v1/auth/token \
-H "Content-Type: application/json" \
-d '{
"apiKey": "pk_test_…",
"secretKey": "sk_test_…"
}'{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp…",
"tokenType": "Bearer",
"expiresIn": 3600,
"environment": "sandbox",
"merchant": { "id": "MCH-001", "name": "Acme Inc" }
}Use the token
Send the accessToken as a Bearer on every other request:
curl https://sandbox.key2pay.ai/api/v1/ping \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp…"Token lifetime + refresh
Access tokens are valid for 1 hour. POST /auth/token returns a long-lived refreshToken alongside the access token — exchange it at POST /auth/refresh for a fresh access token (and a rotated refresh token) without sending your apiKey + secretKey across the wire again.
curl -X POST https://sandbox.key2pay.ai/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp…" }'{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp…",
"tokenType": "Bearer",
"expiresIn": 3600,
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp…",
"refreshExpiresIn": 2592000,
"environment": "sandbox"
}Refresh tokens are valid for 30 days. Each /auth/refresh call returns a new access and refresh token pair, but renewal does not revoke tokens issued earlier; each remains valid until its own expiry. Rotating the shop's API keys increments their credential version (kv) and immediately revokes every access and refresh token minted from the previous key version. Refresh tokens cannot be used to call business endpoints (you'll get 401 invalid_api_key); they only redeem at /auth/refresh.
Legacy raw-key auth
For backward compatibility you can still pass a raw sk_test_… / sk_live_… directly in the Authorization header — the API will accept it as the Bearer. New integrations should prefer the token flow above to avoid sending the long-lived secret on every request. Treat key rotation as an immediate revocation of all outstanding sessions minted from that key version.