Skip to main content
Orchestra issues two kinds of API keys. Server keys stay secret. Client keys are public.
Server keyClient key
Prefixfn_fnp_
SecrecySecret, hashed at restPublic, visible anytime in the dashboard
CapabilitiesEverythingQuote, submit, onramp, address creation
Dashboard accessYesNo
Webhooks, affiliates, transaction historyYesNo
Embedding in open-source SDKs / browser codeNoYes
Use client keys when the key will ship inside something your users can inspect — a mobile app, a web bundle, an open-source SDK. Use server keys for anything running on a backend you control.

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:
ScopeEndpoint
orders:quotePOST /v1/orchestration/quote
orders:submitPOST /v1/orchestration/submit
orders:onrampPOST /v1/orchestration/onramp
orders:readGET /v1/orchestration/status?id=...
orders:sseGET /v1/sse/operations/:id
accumulation:createPOST /v1/accumulation-addresses
liquidation:createPOST /v1/liquidation-addresses
Any request outside this allowlist returns 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 the Origin header is enforced.
ModeOrigin check
serverNot checked. Use from backends that don’t send Origin.
browserOrigin header required, must match the key’s allowed origins.
bothIf Origin is present it must match; if absent, the request is allowed.
Allowed origins are a per-key list configured at creation time. Leave the list empty to accept any origin (useful when you can’t enumerate SDK consumers up front). Global CORS (CORS_ALLOWED_ORIGINS) still applies in every case.

Read-tokens

Client keys are shared across many end users. If user A calls submit, 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.
# Submit under a client key — response contains a readToken
{
  "orderId": "op_...",
  "readToken": "eyJ...ABC"
}

# Read the order using the token — pass via X-Read-Token header OR ?readToken=
GET /v1/orchestration/status?id=op_... HTTP/1.1
Authorization: Bearer fnp_...
X-Read-Token: eyJ...ABC
The token is bound to (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:
GET /v1/sse/operations/op_...?token=fnp_...&readToken=eyJ... HTTP/1.1

Rate limits

Client keys are rate-limited at two layers. Server keys are not limited by these buckets.
RoutePer-key/minPer-(key, IP)/min
orders:quote60060
orders:submit12010
orders:onramp12010
accumulation:create605
liquidation:create605
The per-(key, IP) layer stops a single abusive user from exhausting the partner’s aggregate budget. The per-key layer is the DoS ceiling. Both return 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 clearing disabled_at in the database.
  • Revoke (DELETE /v1/partner/dashboard/api-keys/:id) — permanent. Sets revoked_at; in-flight operations continue, but no new requests succeed.
Disable first when rotating; revoke only when a key will never be used again.

End-to-end example

# 1. Quote
curl -sS -X POST "https://orchestration.flashnet.xyz/v1/orchestration/quote" \
  -H "Authorization: Bearer fnp_..." \
  -H "X-Idempotency-Key: quote:$(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceChain": "base",
    "sourceAsset": "USDC",
    "destinationChain": "spark",
    "destinationAsset": "BTC",
    "amount": "100000000",
    "recipientAddress": "spark1...",
    "slippageBps": 50
  }'

# 2. Submit — response includes readToken
curl -sS -X POST "https://orchestration.flashnet.xyz/v1/orchestration/submit" \
  -H "Authorization: Bearer fnp_..." \
  -H "X-Idempotency-Key: submit:$(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "quoteId": "quote_...", "depositTransactionId": "0x..." }'
# -> { "orderId": "op_...", "readToken": "eyJ..." }

# 3. Poll status — read-token required (pass as ?readToken= or X-Read-Token header)
curl -sS "https://orchestration.flashnet.xyz/v1/orchestration/status?id=op_..." \
  -H "Authorization: Bearer fnp_..." \
  -H "X-Read-Token: eyJ..."
The rest of the Orchestra flow (deposit handling, webhook delivery, repricing) is identical to the server-key flow documented in Quickstart.