Authentication

SorenPay uses workspace-scoped API keys prefixed apk_. Every authenticated request carries one in the Authorization: Bearer … header.

Header format

curl https://sorenpay.com/api/treasury \
-H "authorization: Bearer apk_<your key>"

Token lifecycle

ActionWhere
CreateIn a sandbox workspace: Dashboard → Developers → Create key (a fixed set of scopes, see the quickstart). With the API: POST /api/auth/keys with a key that has write:api_keys and every scope it hands out
RevealOnce on creation. Store immediately.
ListGET /api/auth/keys
RotateCreate a new key → switch traffic → revoke the old one
RevokeDELETE /api/auth/keys?id=… with a key that holds write:api_keys and every scope of the key it revokes (also revokes every key that key created). Revoking from the dashboard isn't available yet
One-time reveal

The full token is shown only at creation. If you lose it, you must create a new key and rotate.

Scopes

Every key carries an explicit list of scopes, and every endpoint checks the one it needs (403 missing_scope:<scope> otherwise). * can no longer be issued (keys minted before that still work until revoked — rotate them), and a key with no scopes can do nothing. A write: scope does not imply the matching read: scope: a key that sends payouts and then reads their status needs both write:payouts and read:treasury. Scope each key to its exact role — an agent's key typically needs read:agents + write:agents, plus read:cards if it inspects its cards.

ScopeWhat it grants
read:treasuryBalances, ledger, recipients, payouts, exchange rates, USDC ramp orders
write:treasuryTest deposits; multi-currency accounts and conversions
write:counterpartiesSave and remove payout recipients
write:payoutsSend payouts, firm exchange-rate quotes, release or cancel held wires
write:rampsUSDC quotes, orders, withdrawals and wallets (an old * key isn't enough)
read:cardsList cards + lookup by id, list cardholders (DOB / SSN last-4 are never returned), physical cards
write:cardsMint, update limits, freeze + terminate cards; register cardholders
read:agentsList agents + view intent log
write:agentsRegister agents + submit intents
read:checkoutView checkout sessions
write:checkoutCreate checkout sessions
read:api_keysList this workspace's keys
write:api_keysCreate/revoke other keys
read:webhooks / write:webhooksReserved for outbound webhook endpoints (no endpoint uses them yet)

Rate limits

Each key can make 100 requests a minute. All keys in a workspace share 600 a minute, and each IP address gets 1,200 a minute across the whole API. Go over and you get HTTP 429 with a Retry-After header (in seconds) and x-ratelimit-reset-ms. Wait that long, then retry.

If we briefly can't check limits, requests that change something get HTTP 503 rate_limit_unavailable with Retry-After. Nothing was changed, so retry with the same Idempotency-Key.

For higher limits, contact us.

Idempotency

Routes marked idempotent in the API reference take an Idempotency-Key header (payouts, recipients and quotes require it). Use a UUIDv4:

curl -X POST https://sorenpay.com/api/cards \
-H "authorization: Bearer apk_<your key>" \
-H "idempotency-key: $(uuidgen)" \
-H "content-type: application/json" \
-d '{...}'

Replays within 24h return the cached response. Conflicting bodies on the same key return HTTP 422 idempotency_key_mismatch so you can detect bugs.

Business verification (KYB)

Live mode isn't switched on yet. When it is, money-movement routes (payouts, international payments, USDC, multi-currency accounts, cards, cardholders, physical cards and agents) need the workspace's business verification to be approved; otherwise they answer 403 with {"error": "kyb_required", "message": "...", "kybStatus": "..."}. Sandbox workspaces don't need it. Payouts, deposits and the USDC ramp work only in sandbox today.

Webhook signing (inbound)

Every webhook we receive from a partner is checked before we act on it (a signature, or a shared secret for partners that don't sign), and repeats of the same event are ignored.

Webhook signing (outbound)

Each endpoint has its own signing secret. Every delivery carries sorenpay-signature: t=<unix>,v1=<hmac>, where v1 is the hex HMAC-SHA256 of <t>.<raw body>. See Notifications.