Skip to main content

Authentication

Every developer request authenticates with an API key. The key identifies your tenant, carries a set of scopes, and pins the environment (sb_test_ → TEST, sb_live_ → LIVE).

note

This page covers the developer key used by the SDK and CLI — the authentication for the public API platform. Legacy first-party surfaces (Privy bearer, app keys, partner keys) are internal and not part of the developer API.

Sending the key

Present the key in either accepted header:

# Preferred header
curl https://api.sandbox.safebank.ai/v1/wallets \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY"

# Bearer form (also accepted)
curl https://api.sandbox.safebank.ai/v1/wallets \
-H "Authorization: Bearer $SAFEBANK_API_KEY"

How authorization works

Each /v1 route runs two checks server-side:

  1. PrincipalGuard resolves the caller to a principal. If the request presents an sb_(test|live)_… key (in either header), it authenticates as an api_key principal holding exactly its stored scopes. Otherwise it falls back to a Privy session (user principal — the dashboard), which implicitly holds every scope for its tenant.
  2. ScopesGuard enforces the route's @RequiredScopes(...). An api_key passes when its scopes include the required scope (or the root * scope); a user session always passes.

A key missing the required scope gets 403 with code INSUFFICIENT_SCOPE (see Errors).

Scopes

Grant a key the least privilege it needs. The full taxonomy:

GroupScopes
Walletswallets:read, wallets:write, wallets:fund
Paymentspayments:read, payments:create, payments:execute
Cardscards:read, cards:issue, cards:manage
Policiespolicies:read, policies:write, policies:evaluate
Agentsagents:read, agents:manage
Treasurytreasury:read, treasury:write
Invoicesinvoices:read, invoices:write, invoices:release
Keyskeys:manage
Root* (all scopes — avoid for shared keys)

A few concrete mappings from the REST surface:

  • Creating a wallet needs wallets:write; reading needs wallets:read.
  • Funding a wallet from the faucet needs wallets:fund.
  • Creating a payment needs payments:create; executing needs payments:execute; reading/listing needs payments:read.
  • Minting or rotating keys needs keys:manage.

Creating and rotating keys

You need a key (with keys:manage) or a dashboard session to mint more keys. The secret is returned once — store it immediately.

curl -X POST https://api.sandbox.safebank.ai/v1/keys \
-H "X-SafeBank-Api-Key: $SAFEBANK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CI sandbox",
"environment": "test",
"scopes": ["wallets:write", "wallets:fund", "payments:create"]
}'

Response (CreatedApiKey):

{
"id": "c1a2b3d4-…",
"key": "sb_test_Ab12Cd34Ef56_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"pubId": "Ab12Cd34Ef56",
"environment": "TEST",
"scopes": ["wallets:write", "wallets:fund", "payments:create"],
"expiresAt": null,
"createdAt": "2026-08-09T12:00:00.000Z"
}
Agent keys

Keys created for agents cannot hold the elevated scopes agents:manage, keys:manage, or policies:write — an agent must not mint keys, manage other agents, or author policy. This is enforced at key-creation time.

Handling secrets

  • Never commit keys or embed them in client-side code. Keep them in a secret manager or env var (SAFEBANK_API_KEY).
  • Use separate keys per service/environment so you can rotate or revoke one without downtime.
  • Rotate on a schedule and on any suspected exposure — sb.keys.rotate(id) issues a new secret and invalidates the old one.