Skip to main content
Every call (except register and login) requires a credential in the Authorization header:
X-API-Key: <token> is accepted as an alternative header.

Credential types

Obtained from POST /v1/auth/register or POST /v1/auth/login, valid for 24 hours. Meant for apps where users sign in. Along with the access_token you receive a refresh token to renew the session without asking for the password again — see session renewal.
Company accounts can have multiple members with roles — see company members below.
If the account’s policy requires OTP on login, the response carries otp_required: true with a pending_token instead of the session: the second step completes at POST /v1/auth/login/otp with the code received over SMS/WhatsApp. Full flow in security and 2FA.
You can also offer sign up and sign in with Google, Apple, Microsoft or Facebook (passwordless) — see social login.

Session renewal (refresh tokens)

The access_token lasts 24 hours; the refresh_token (rt_…) lets you get a fresh pair without re-login for 30 days, renewed on every use up to a maximum of 90 days from the original login. It is single-use: every exchange returns a new refresh_token and invalidates the previous one (rotation).
Security rules your front end must know:
  • Strict rotation: on exchange, the previous access token of that device is revoked immediately (only one live access token per chain). Always replace BOTH tokens with the ones in the response.
  • Reuse = theft: if an already-exchanged refresh token is presented again, the device’s entire chain is revoked (tokens and sessions) and a refresh_token_reuse event is recorded in GET /v1/me/security/events. The user must sign in again.
  • Dies with the session: signing out (DELETE /v1/me/sessions/{id}), POST /v1/me/sessions/revoke-all or a password change/reset also invalidate the associated refresh tokens.
  • Every rejection returns 401 invalid_refresh_token — on that error, send the user to the login screen.
  • Store the refresh token in secure storage (Keychain/Keystore on mobile; on web, prefer memory + re-login or an httpOnly cookie from your backend). pk_ API keys do not use refresh: they never expire.
When the access token is about to expire (use expires_at) or upon a 401 on a normal call. Avoid refreshing in parallel from several places: if two exchanges of the same token race, one wins and the other gets a 401 without penalty — but exchanging a token that was ALREADY rotated revokes the chain.
The chain’s absolute cap is 90 days from the original login: even refreshing daily, once the limit is reached the refresh returns 401 and the user must authenticate again (password, passkey or social login).
Not applicable: pk_ API keys never expire and have no session. Refresh is only for JWT sessions of human users.

Access level

Your credential (session JWT or API key) operates your own account: balances, payouts, payins, transfers, crypto, KYC/KYB and your own webhooks. If an endpoint responds 403 account_required or 403 org_admin_required, that operation belongs to a different credential level — contact the CBPay team.

Your account profile

PATCH /v1/me accepts display_name, tax_id, phone and country (send only the ones that change). email, status and kyc_status are not self-managed: the administrator resolves them.

Company members

Company accounts can have multiple users with their own login and different permission levels:
On a person account, POST /v1/members responds 403 company_only.

Best practices

  • Store API keys in a secrets manager; never in code or in the browser.
  • Use one key per environment/service (descriptive label) so you can rotate without downtime.
1

Zero-downtime rotation

Issue the new key with POST /v1/api-keys (new label).
2

Deploy

Update your service to use the new key.
3

Retire the old one

Ask the CBPay team to revoke the previous key once traffic migrated.
  • JWT sessions are for front-ends; automated processes should always use API keys.
Last modified on July 13, 2026