| Server key | Client key | |
|---|---|---|
| Prefix | fn_ | fnp_ |
| Secrecy | Secret, hashed at rest | Public, visible anytime in the dashboard |
| Capabilities | Everything | Quote, submit, onramp, address creation |
| Dashboard access | Yes | No |
| Webhooks, affiliates, transaction history | Yes | No |
| Embedding in open-source SDKs / browser code | No | Yes |
When to pick which
If you own the environment the key runs in (your servers, a VPC, a private container), use a server key. If end users can extract the key by opening devtools, decompiling an APK, or reading a git repo, use a client key.Getting a key
Every new partner is provisioned with both keys automatically. Find them in the dashboard under API Keys. Client keys can be revealed at any time (they’re public). Server keys are shown once at creation and hashed afterward. Additional keys of either type can be created from the same page.Scopes
Client keys are scope-gated. Only these actions are permitted:| Scope | Endpoint |
|---|---|
orders:quote | POST /v1/orchestration/quote |
orders:submit | POST /v1/orchestration/submit |
orders:onramp | POST /v1/orchestration/onramp |
orders:read | GET /v1/orchestration/status?id=... |
orders:sse | GET /v1/sse/operations/:id |
accumulation:create | POST /v1/accumulation-addresses |
liquidation:create | POST /v1/liquidation-addresses |
403 forbidden with "This route is not available for client keys". There is no opt-in: client keys cannot read transaction history, manage webhooks, or touch affiliate data, regardless of what scopes are assigned.
Modes
When you create a client key you choose a mode. The mode determines how theOrigin header is enforced.
| Mode | Origin check |
|---|---|
server | Not checked. Use from backends that don’t send Origin. |
browser | Origin header required, must match the key’s allowed origins. |
both | If Origin is present it must match; if absent, the request is allowed. |
CORS_ALLOWED_ORIGINS) still applies in every case.
Read-tokens
Client keys are shared across many end users. If user A callssubmit, user B must not be able to read user A’s order by guessing the ID. Orchestra enforces this with short-lived HMAC read-tokens.
(partnerId, apiKeyId, orderId, expiry). Tokens for other orders, other keys, or expired tokens return 403 read_token_required or 403 invalid_read_token.
Server keys don’t need a read-token — they can read any order under the partner.
SSE subscriptions (/v1/sse/operations/:id) follow the same rule. Because EventSource can’t set headers, pass the read-token as a query param:
Rate limits
Client keys are rate-limited at two layers. Server keys are not limited by these buckets.| Route | Per-key/min | Per-(key, IP)/min |
|---|---|---|
orders:quote | 600 | 60 |
orders:submit | 120 | 10 |
orders:onramp | 120 | 10 |
accumulation:create | 60 | 5 |
liquidation:create | 60 | 5 |
429 rate_limited with Retry-After when tripped.
Privileged fields are stripped
Request bodies passed with a client key have privileged fields silently removed before validation. This includes anything that would let a caller redirect fees, override tiers, or inject admin flags. The request continues as if those fields were never sent; no error surfaces so the schema stays opaque.Revocation and disable
Client keys support two lifecycle actions:- Disable (
POST /v1/partner/dashboard/api-keys/:id/disable) — the key starts failing auth immediately, but in-flight operations continue to completion. Reversible: an operator can re-enable the key via the dashboard or by clearingdisabled_atin the database. - Revoke (
DELETE /v1/partner/dashboard/api-keys/:id) — permanent. Setsrevoked_at; in-flight operations continue, but no new requests succeed.