Getting started

Authentication

Century authenticates every non-public call with a short-lived bearer token that belongs to a real user account. There are no long-lived API keys and no service-to-service credentials.

Bearer tokens

Send the access token on every authenticated request:

Authorization: Bearer <access_token>
Content-Type: application/json
  • Tokens are JWTs issued by Century's auth service and expire in roughly one hour.
  • Refresh with the refresh token before expiry; a 401 means refresh and retry exactly once.
  • Tokens are bound to the user, not to an application — the caller can only see and move that user's money.

Authorization layers

Passing authentication is not enough. Each call can additionally require:

LayerApplies toFailure
Row-level securityEvery database read and writeEmpty result or 403 — never another user's data
Transaction PINAll money movement: purchases, transfers, withdrawals400 INVALID_PIN, temporary lock after repeats
KYC tierTier 1 for transactions, Tier 2 for transfers and virtual accounts403 KYC_REQUIRED
Two-factorAccounts with mandatory 2FA enrolmentChallenge before a session is issued
Permissions are never client-side
Every permission is resolved server-side through a security-definer check. A client claiming extra privileges changes nothing.

Single active session

Century enforces one active session per account. Signing in on a new device invalidates the previous session, so a stored token can stop working before it expires. Treat 401 as "re-authenticate", not as a bug.

Login methods

  • Email + password
  • Email one-time code (rate limited by a server-side cooldown)
  • 6-digit login PIN with @username or email
  • Passkey / biometric (WebAuthn)
  • Google sign-in

What to never do

Do not embed passwords, service-role keys or provider secrets in frontend code, mobile bundles, or public repositories. Do not proxy another user's token. Do not attempt to call undocumented internal operations — the server re-checks permissions and will reject you.