Skip to main content

Authentication

All customer-facing /v1/* endpoints authenticate with a bearer token:

Authorization: Bearer <prefix>.<suffix>

Two flows produce bearer tokens, and the venue treats them uniformly:

  • API keys — minted via POST /v1/credentials, intended for programmatic trading.
  • Sessions — produced by POST /v1/auth/login (email + password), used by the terminal. Sessions are credential rows with a 7-day expiry; POST /v1/auth/logout revokes the presented one.

Public market data (instruments, books, trades, candles) and the WebSocket upgrade itself require no auth.

Token format​

The token's prefix is the credential's public ID, used for lookup; the suffix is a high-entropy secret. The venue stores only a SHA-256 of the suffix.

The venue returns a bearer token exactly once, at creation:

  • POST /v1/auth/signup → session token, plus the account
  • POST /v1/credentials → data.plaintext_key

GET /v1/credentials never returns secrets. If you lose a key, mint a new credential and DELETE the old one.

Entitlements​

Each credential carries an entitlement bitmask. The default for an account's initial credential is 15 (all four bits):

BitValueEntitlementGrants
01TradeEnabledPlace and cancel orders
12WithdrawEnabledInitiate withdrawals
24DepositEnabledAllocate deposit addresses
38APITradingProgrammatic (vs UI-only) trading

Scoped agent credentials​

Since API 0.14.0 a credential can carry a scope the venue enforces on every order before it reaches the matching engine — the ring-fence for an AI agent or a bot, independent of the account's own limits. Set it at mint time on POST /v1/credentials; it is echoed as scope on every credential and, with today's usage, on GET /v1/account/limits.

FieldEffect
instrument_allowlistOrders on any other instrument fail 403 instrument_not_allowed. Cancels are never restricted.
max_order_notional_uusdcPer-order cap: 403 order_notional_cap. Limit orders count price × tick value × count; market buys count max_cost.
max_daily_notional_uusdcAccepted notional per UTC day, per credential, durable across restarts: 403 daily_notional_cap.
max_open_ordersRefuses new orders while the account has this many resting: 403 open_orders_cap.
approval_moderequire_human parks every order (202 pending_approval) for a human to approve within 15 minutes — see MCP order flow.
agentFree-form descriptor (name, model, operator, …). name is stamped on the credential's fills.
expires_in_sAbsolute expiry; an expired key fails 401.

A typical agent key: trade entitlement only (entitlement_mask: 9), a week's expiry, the two instruments it is allowed to touch, a per-order and a daily cap, and require_human until you trust it. Revoke it with DELETE /v1/credentials/{credential_id} when the run ends. POST /v1/orders/stage previews the scope's verdict (would_reject, scope) before an order is committed.

The terminal's Account page has an Agents card that mints exactly this kind of key from a form (trade + API entitlements only, require_human preselected), shows every key's scope and today's notional against its daily cap (daily on GET /v1/credentials, API 0.16.0), and carries the approval queue and an emergency stop.

Account states​

Independently of credentials, every account has a lifecycle state. Endpoints check both the credential's entitlements and the account state:

Account statePlace orderCancel orderWithdraw
pending———
active✓✓✓
trade_frozen—✓✓
withdraw_frozen✓✓—
fully_frozen—✓—
closed———

Cancellation stays allowed in frozen states so you can manage positions down; a closed account is inert.

State-related rejections use the account_not_active and account_trade_frozen error codes.

WebSocket auth​

Private channels (fill, order, account) unlock by presenting the same bearer on the upgrade request to GET /v1/ws. Public channels need no auth. Tokens never appear in URLs. See the streaming overview.