Skip to navigation

Authentication

URL — No public environment serves this socket yet.

Credentials

ticket

Sent as query parameter ticket

The browser’s credential: a short-lived, single-use ticket from trading-gateway’s POST /v1/ws-tickets, on the handshake URL. One of the two schemes, never both.

bearer

Sent as header Authorization: Bearer … (JWT)

Every other client’s credential: the access token as Authorization: Bearer … on the handshake, stating an expiry. One of the two schemes, never both.

Create a WebSocket ticket

REST API POST /v1/ws-tickets

Exchanges your bearer token for a ticket that opens the trading WebSocket once: pass it as ?ticket= on the WebSocket URL before expiresAtNs. It carries your identity and never outlives your token; never cache or log it. No Idempotency-Key: every call mints a new ticket. The guide.

Request parameters

ParameterTypeRequiredDescription
streamstringYesThe WebSocket stream the ticket opens. One of trading.

Response parameters

ParameterTypeRequiredDescription
ticketstringYesOpaque and URL-safe as served: put it in the WebSocket URL as ?ticket= unchanged, and never parse it. It opens one session, once.
expiresAtNsstringYesWhen the ticket stops opening sessions, in epoch nanoseconds: the ticket’s lifetime, or your token’s expiry if that comes first. Open the WebSocket before then, or create another ticket.
streamstringYesThe stream the ticket opens. One of trading.

Refusals

StatusDescription
400Refused at the door — nothing reached the platform and the key was not journaled: a missing or malformed Idempotency-Key or If-Match, malformed JSON, a body userId, a body restating the path or If-Match differently, a money string off its pattern (an exponent, a plus sign, a bare point, surrounding whitespace) or past its maxLength, or one no value here can state (over 18 fractional digits, or past a 64-bit integer at its own scale), or any other value outside the schema.
401No bearer token, or one that does not verify.
403Authenticated, but not admitted. FORBIDDEN_PRINCIPAL: a token with no org_id, a non-ASCII org_id or sub, a disabled user, or a local binding with no user. AWAITING_ADMISSION: your user is not yet admitted — every route but GET /me answers this until an administrator admits you. ORG_NOT_ACTIVE: your organisation is suspended, pending activation, retired or unregistered. The record owners’ 403s (CAPABILITY_DENIED, SELF_APPROVAL, NOT_PROPOSER, …) are refusals on the platform’s stream: they carry the position header, and a retried key replays them with Idempotent-Replay.
429Shed at once: INBOX_FULL or IN_FLIGHT_FULL (this member’s bounds; nothing queued, the key not journaled), the intake’s or the ticket route’s RATE_LIMITED, or the orders owner’s RATE_CEILING (the organisation’s per-minute ceiling — a refusal on the stream, replayed like any, so its retryable is false and the order goes again under a fresh key). Retry-After says when.
503This member cannot mint tickets now: NOT_PRIMED — it is still catching up; retry shortly — or TICKETS_UNAVAILABLE — it holds no ticket signing key, so a retry here changes nothing: a browser cannot open the trading WebSocket through this member until it serves tickets, and any other client sends its bearer token on the WebSocket handshake instead.

The session is open and authenticated

The first frame of every session, sent before the client says anything — unless access was revoked between the handshake and this frame, when the first frame is sessionClosing (PRINCIPAL_REVOKED, 4003, not retryable) instead. The session was authenticated on its handshake, so there is no auth to ack, and this carries what the ack carries — the grant, never the grantee. Subscribe straight away. To renew the credential before expiresAtNs, re-auth over the open socket with op: auth, for the same principal; its answer is authAck.

ParameterTypeRequiredDescription
eventstringYessessionOpened
msgTypestringYessessionOpened
dataobjectYesWhat this session may do and until when — the grant, never the grantee: no identifier rides it. Read at admission and fixed for the session.
> capabilitiesarray of stringsYesScope strings in choice order — the same vocabulary as the HTTP lane’s GET /me, published here as an enum rather than promised in prose. Shape your affordances from these, never from a token claim.
> expiresAtNsstringYesWhen this session’s credential lapses and the session is closed. “0” for a roster binding, which never expires (local and test only).

Re-authenticate the session

Op auth

The in-band re-auth: a session is authenticated on its handshake (a ticket or a bearer token), and this op renews its credential over the open socket — answer the sessionExpiring hint with it, or schedule it off expiresAtNs. It is never the way in: from 1.4.0 a handshake without a credential is refused, so no session ever owes it. The answer carries the GRANT, not the grantee — what this session may do and until when, with no identifier in it, because the client presented the token and already knows who it is.

A session may re-auth with a fresh credential for the SAME principal, which renews its lease. A credential naming a different principal is refused FORBIDDEN_PRINCIPAL and the session keeps the one it was opened with: the identity is immutable for the life of the socket.

Request parameters

ParameterTypeRequiredDescription
opstringYesauth
reqIdstringYesEchoed byte-exact on the answer. Printable ASCII, escape-free; anything else answers MALFORMED.
paramsobjectYes
> tokenstringYesThe bearer token, verbatim.

Response parameters

The grant, never the grantee. capabilities are the register row’s bits — not token claims, so they reflect what an administrator has granted rather than what was true when the token was issued — read at admission and fixed for the session. An administrator clearing one does not change an open session’s grant: re-auth over the socket to pick up the new set. expiresAtNs is when the session closes; schedule a re-auth off it rather than decoding the token.

ParameterTypeRequiredDescription
opstringYesauth
reqIdstringYesEchoed byte-exact on the answer. Printable ASCII, escape-free; anything else answers MALFORMED.
successbooleanYestrue
sessionobjectYesWhat this session may do and until when — the grant, never the grantee: no identifier rides it. Read at admission and fixed for the session.
> capabilitiesarray of stringsYesScope strings in choice order — the same vocabulary as the HTTP lane’s GET /me, published here as an enum rather than promised in prose. Shape your affordances from these, never from a token claim.
> expiresAtNsstringYesWhen this session’s credential lapses and the session is closed. “0” for a roster binding, which never expires (local and test only).

Refusal parameters

ParameterTypeRequiredDescription
opstringYesThe op being answered, or “error” when it could not be read.
reqIdstring, nullableYes
successbooleanYesfalse
codestringYesA closed enum, but tolerate an unknown value: codes are appended as the surface grows, and retryable tells you what to do whatever the code says. The door’s codes — INVALID_*, UNKNOWN_INSTRUMENT, UNKNOWN_ACCOUNT, UNKNOWN_ORDER, NOT_SERVING, OWNER_ABSENT, BACKPRESSURE — arrived in 1.1.0 beside the ops that write, and are the whole of what that version added here. A client generated against 1.0.0 meets them the first time it sends a write. UNAUTHENTICATED is not emitted from 1.4.0: every session is authenticated on its handshake, so no op can arrive before authentication. It stays in the set, which is append-only. OWNER_ABSENT is reserved and not emitted by this version: when the orders owner is not established, a write refuses NOT_SERVING today. It is published so that a client’s handling of it is written once rather than added later, but do not expect to observe it before a release says otherwise. One of NOT_PRIMED, UNAUTHENTICATED, SESSION_EXPIRED, RATE_LIMITED, MALFORMED, UNKNOWN_OP, INVALID_TOKEN, FORBIDDEN_PRINCIPAL, ORG_NOT_ACTIVE, AWAITING_ADMISSION, FORBIDDEN_CAPABILITY, UNKNOWN_TOPIC, TOPIC_NOT_ACTIVE, TOO_MANY_TOPICS, INVALID_FIELD, INVALID_PRICE, INVALID_QTY, INVALID_CLIENT_ORDER_ID, UNKNOWN_INSTRUMENT, UNKNOWN_ACCOUNT, UNKNOWN_ORDER, NOT_SERVING, OWNER_ABSENT, BACKPRESSURE.
messagestringYesDisplay text. May change between versions; never parse it.
retryablebooleanYes
failedTopicIndexintegerNoPresent on a topic refusal: the zero-based index into params.topics AS SENT of the entry that caused it.

This session’s credential lapses soon

Sent once per credential, one lead time before the lapse. Re-auth over the open socket with a fresh credential for the same principal and keep your subscriptions; renewing re-arms the hint. It exists because a client that schedules its own refresh may simply not be running when its timer fires — a throttled background tab — where an inbound frame is something the browser will deliver.

ParameterTypeRequiredDescription
eventstringYessessionExpiring
msgTypestringYessessionExpiring
dataobjectYes
> expiresAtNsstringYes
> inMsintegerYes