Authentication
API keys, the OTP flow, and how humans and agents authenticate.
Every request to /context and /runtime authenticates with an API key:
curl https://api.superior.trade/context/venues \ -H "x-api-key: st_live_4f2a9c81d7e3b0…"
wget -qO- \ --header='x-api-key: st_live_4f2a9c81d7e3b0…' \ https://api.superior.trade/context/venues
$response = Invoke-RestMethod `
-Method GET `
-Uri "https://api.superior.trade/context/venues" `
-Headers @{ "x-api-key" = "st_live_4f2a9c81d7e3b0…" }
$response | ConvertTo-JsonKeys are prefixed st_live_. There is no test-key tier — paper mode is how you rehearse without capital.
How you get a key#
Two paths issue keys; both end at the same place.
The OTP flow (built for agents): POST /account/register emails a six-digit code, and POST /account/verify exchanges it for a session token and an api_key, returned once in the response body. The human only ever handles a short-lived code, never the key itself — so an agent can complete onboarding by asking its operator to read six digits aloud.
The dashboard flow (built for humans): sign in at account.superior.trade and mint keys from the Keys page. Same keys, same permissions.
Identity behind both flows is anchored by Privy — that's how account creation works internally. The same account uses the default managed trading wallet today; BYOK credentials are coming soon. API keys authenticate the account, not a specific custody choice.
Key handling rules#
- The key is shown once. Superior stores only a salted hash and a display prefix (
st_live_4f2a9c…). - Up to 10 active keys per account; revoke and rotate freely via
/account/keys. - Send the key only in the
x-api-keyheader — never in a URL, never in strategy code or config (both are stored and log-adjacent). - If a key leaks, revoke it immediately:
DELETE /account/keys/:idtakes effect within seconds.
Scopes#
Keys are full-access to the owning account in the initial release. Scoped keys (read-only context keys, per-deployment keys) are on the roadmap and recorded in the design log; the shape is reserved as a scopes array on the key object so adding them later isn't a breaking change.
Errors#
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | API key or authenticated Privy session is missing, invalid, or not accepted for this route |
| 429 | rate_limited | See rate limits; the response carries retry-after |