Order sessions and recovery
Binding an order's outcome to a session so it survives a dropped connection, and the three ways to recover one.
An order can outlive the connection that placed it. The connection can drop between the moment we accept the order and the moment its outcome is known, and nothing you do on your side changes what has already gone to market.
autoSubscribe selects how the outcome reaches you, per order.
Without it, the order is bound to the connection: you get exactly one frame,
the terminal result, and if the connection drops before it arrives the outcome
is gone from that socket. With it, the order is bound to your session: you
get an immediate PENDING acknowledgement, the terminal arrives as a separate
message, and it is redelivered on every reconnect until you acknowledge it.
A session is named by the sessionId query parameter on the WebSocket
handshake:
wss://<host>/zm/ws/ws-client?token=<one-shot token>&sessionId=<your id>
Reconnecting under the same sessionId is what makes recovery work. Omitting it
is legal — the gateway mints one — but a minted id is new on every connect, so
nothing is ever redelivered to it. If you want recovery, choose your own
sessionId and reuse it.
sessionId must be 1 to 64 characters and must not contain /, whitespace or
control characters. A handshake carrying an invalid one is refused: you receive
a single error frame and the socket closes. Connecting again with the same
sessionId closes any earlier connection of yours still holding it, so exactly
one socket is the delivery target.
Recovering an outcome
Three routes, in the order you would reach for them:
- Reconnect under the same
sessionId. Every subscribed order whose terminal has been recorded is pushed again. - Read one order by
clientRequestId. - List your orders, optionally narrowed to one session.
Records are held for 24 hours, and that window is not extended by reading them. Beyond it, an order that executed is in your trades.
Redelivery is at-least-once — deduplicate on transactionId. A subscribed
order is re-pushed on every reconnect until you unsubscribe from it, not just
the first one after the drop. Sending orderUnsubscribe once you have the
terminal is how you stop it.
Order request
Sent as messageType: "order".
| Field | Type | Required | Notes |
|---|---|---|---|
messageType | string | Required | Always order on this message. It is how the gateway picks the message type, and a request without it is rejected |
instrument | string | Required | The pair, as TRADED.SETTLEMENT. Source declares a default, but it is a placeholder and is not a usable order |
quoteId | string | Required | The quote you are accepting, from the price stream. It must be yours, and it expires five minutes after issue |
quoteIdx | integer | Not read on an order | |
apiKey | string | Taken from the authenticated connection. Anything you send is discarded | |
accountGrpUuid | string | Conditional | The account group to trade for. Required when your credential covers more than one |
side | OrderSide | BUY or SELL. A trade collecting from a registered sender must be SELL | |
price | string | Not read on an order; the price is the quote's | |
quantity | string | How much to trade. Must not exceed the quantity on the quote. Left empty, the quote's own quantity is used | |
currency | string | Which side of the pair quantity is expressed in. Must be one of the two currencies in instrument | |
quantityAmountType | QuantityAmountType | Whether quantity is the traded or the settlement amount | |
transactionId | string | Assigned by the gateway. Anything you send is discarded | |
clientRequestId | string | Your own reference, echoed on every response and the key you look the order up by later. Without one you cannot read the order back individually. Maximum 64 characters | |
appropriateLevel | decimal | Not read on an order | |
startTimestamp | integer | Not read on an order | |
lastTimestamp | integer | Not read on an order | |
userSpread | decimal | Not read on an order | |
holidaySpread | decimal | Not read on an order | |
treasurySpread | decimal | Not read on an order | |
totalSpread | decimal | Not read on an order | |
tenor | string | Settlement tenor, matching the quote | |
settleDate | string | Not read on an order; the settlement date comes from the quote | |
signedIntent | SignedIntent | Tamper-evident signature over the order parameters. See signed order intent | |
paymentReason | string | Conditional | Required for a third-party transfer, and must be one of the reasons configured for you |
_beneficiaryDetails | BeneficiaryDetails | Third-party delivery destination, set by the gateway from the quote's own beneficiary. Anything you send is discarded — the destination is fixed when you subscribe. Note the leading underscore — that is the field name on the wire | |
_senderDetails | SenderDetails | Third-party collection source, set by the gateway from the quote's own sender. Anything you send is discarded. Note the leading underscore | |
idempotencyKey | string | Guards against a retry placing a second order. Maximum 64 characters, and it may not contain whitespace or : | |
autoSubscribe | boolean | Send true to bind the outcome to your session and make it recoverable. Left false, the outcome is delivered once on this connection and not retained for redelivery | |
sessionId | string | Ignored. The session comes from the connection; a different value here is overridden, and an invalid one rejects the order | |
chanId | string | Set by the gateway to your connection identifier. Anything you send is discarded | |
authUserUUID | string | Set by the gateway from your credential. Anything you send is discarded |
SignedIntent
The optional signature over an order's parameters.
| Field | Type | Required | Notes |
|---|---|---|---|
payload | string | Required | Base64 of a JSON object carrying quoteId, side, quantity and timestamp |
signature | string | Required | Base64 RS256 signature over payload, made with the private key whose public half you registered |
BeneficiaryDetails
Where a third-party delivery is sent. Echoed as _beneficiaryDetails on the
order — the leading underscore is the field name on the wire. It comes from the
quote, not from the order, so there is nothing here for you to send. Chosen on
the price channel.
| Field | Type | Notes |
|---|---|---|
beneficiaryId | string | The registered beneficiary to deliver to |
networkId | string | The delivery network resolved for the beneficiary |
payoutCurrency | string | The currency the beneficiary is paid in |
SenderDetails
Where a third-party collection comes from. Echoed as _senderDetails on the
order, from the quote rather than from anything you send. Chosen on the
price channel, and a collection must be a
SELL.
| Field | Type | Notes |
|---|---|---|
senderId | string | The registered sender to collect from |
networkId | string | The collection network |
payinCurrency | string | The currency the sender pays in. Must be the traded currency of the instrument |
OrderSide
| Value | Meaning |
|---|---|
BUY | Buy the traded currency of the instrument |
SELL | Sell the traded currency. A trade collecting from a registered sender must be a sell |
QuantityAmountType
Which side of the pair quantity is measured in. Sent as the name, not a number.
| Value | Meaning |
|---|---|
TRADED | quantity is an amount of the traded currency |
SETTLEMENT | quantity is an amount of the settlement currency |
Order response
Sent as messageType: "order". A subscribed order produces two of these: the
PENDING acknowledgement, then the terminal.
| Field | Type | Notes |
|---|---|---|
messageType | string | Always order |
message | string | Human-readable detail accompanying code |
code | string | Set when something went wrong; see trade error codes |
instrument | string | The pair traded |
side | OrderSide | BUY or SELL |
price | string | The executed price |
quantity | string | The executed quantity |
clientRequestId | string | Your own reference, echoed back |
orderStatus | string | The outcome so far. PENDING on the acknowledgement of a subscribed order; afterwards a terminal value. Treat it as an open set of strings rather than a fixed list — it carries both settlement outcomes and finer-grained lifecycle values |
quoteId | string | The quote the order was placed against |
transactionId | string | The gateway's identifier for the order, and the key to deduplicate redeliveries on |
tenor | string | Settlement tenor |
settleDate | string | Settlement date |
subscriptionStatus | SubscriptionStatus | Present only on a subscribed order, and absent from the message entirely otherwise |
sessionId | string | The session the outcome is bound to. Present only on a subscribed order, and absent otherwise — this is where you learn the id when the gateway minted one for you |
chanId | string | The connection the message was delivered on |
timestamp | integer | When the gateway produced this message, in milliseconds. Not the time of any order event |
Unsubscribe request
Sent as messageType: "orderUnsubscribe". Acknowledges a terminal you have
received and stops it being redelivered.
| Field | Type | Required | Notes |
|---|---|---|---|
messageType | string | Required | Always orderUnsubscribe |
transactionId | string | Required | The order to stop redelivering, from the response you received |
sessionId | string | Ignored. The order is found by transactionId within your own records | |
chanId | string | Set by the gateway. Anything you send is discarded | |
authUserUUID | string | Set by the gateway from your credential. Anything you send is discarded |
Unsubscribe response
| Field | Type | Notes |
|---|---|---|
messageType | string | Always orderUnsubscribe |
success | boolean | Whether redelivery has stopped. Unsubscribing an order that was already unsubscribed succeeds |
message | string | Human-readable detail. An order still in flight is refused here — unsubscribing is only allowed once a terminal result has been delivered, so that you cannot detach yourself from an outcome you have not seen |
transactionId | string | The order the response is about, echoed back |
code | string | Set when the request was refused |
chanId | string | The connection the message was delivered on |
timestamp | integer | When the gateway produced this message, in milliseconds |
SubscriptionStatus
Whether the outcome is still being redelivered to you. Independent of the
order's own outcome — an order can be PENDING and NOT_SUBSCRIBED at once.
| Value | Meaning |
|---|---|
NOT_SUBSCRIBED | The order was placed without autoSubscribe. Its outcome is not retained for redelivery, and this value never appears on a WebSocket message — you see it only when reading the order back over REST |
SUBSCRIBED | The outcome is bound to your session and will be redelivered on every reconnect until you unsubscribe or the 24-hour window passes |
UNSUBSCRIBED | You have acknowledged the outcome and redelivery has stopped |