WebSocket Order Entry API Reference
Overview
The Reya DEX WebSocket Order Entry API v2 is a request/response surface for placing and cancelling orders over a persistent WebSocket connection. It carries the same operations and payload bodies as the REST /v2 endpoints (POST /v2/createOrder, POST /v2/cancelOrder, POST /v2/cancelAll), with lower per-operation overhead and id-correlated responses on the same channel.
Payload bodies are reused verbatim from REST. Request and response envelopes are id-correlated; the server replies on the same connection with a frame carrying the same id the client sent.
This surface is order-entry only. For real-time market data, position updates, and fill streaming, see the WebSocket Info API Reference. The recommended Market Maker integration runs both connections in parallel: this surface for order entry, the streaming surface for read-side fanout.
Server Endpoints
Production Environment
URL:
wss://ws-exec.reya.xyzProtocol: WSS
Description: Production WebSocket order entry server
Staging Environment
URL:
wss://ws-exec-staging.reya.xyzProtocol: WSS
Description: Staging WebSocket order entry server for pre-production testing
Test Environment
URL:
wss://ws-exec-testnet.reya.xyzProtocol: WSS
Description: Test WebSocket order entry server (cronos)
Connection & Auth Model
The connection itself is anonymous — no handshake, no login, no API key. There is no concept of a session-bound wallet identity.
Authentication is per-frame: every order-bearing request carries an EIP-712 signature in its payload (signature, nonce, signerWallet, expiresAfter). The server validates the signature against the order contents on every request — identical to the REST /v2/createOrder etc. body shape. See Signatures and Nonces for the signing model; both transports use the same scheme and the same Python SDK helpers.
A consequence of per-frame authentication is that a single WebSocket connection can carry orders signed by multiple different signerWallet values — useful for market makers operating multiple subaccounts on one connection.
Rate limits are keyed off the signing wallet, not the connection. Sending createOrder / cancelOrder / cancelAll over WebSocket Order Entry counts toward the same per-wallet bucket as sending the same operation over REST — choosing the transport doesn't change the limits. See Rate Limits for the full picture (default limits, tiers, open-order caps, and recommended client patterns).
Message Structure
All WebSocket messages follow a standardized envelope structure with a type discriminator and a client-chosen id for correlation.
Request Envelope (Client → Server)
Components
type (string, required): One of
createOrder,cancelOrder,cancelAll,ping. (pongis server-only — see Heartbeats.)id (string, required): Client-chosen correlation identifier. Must be unique across in-flight requests on the connection — see In-Flight
idUniqueness below.payload (object, required for
createOrder/cancelOrder/cancelAll): Operation-specific request body, byte-identical to the corresponding REST endpoint's request body.
Response Envelope (Server → Client)
or, on failure:
Components
type (string, required): Echoes the request
type.id (string, required): Echoes the request
id.ok (boolean, required):
truefor success,falsefor failure.payload (object, required when
ok = true, forbidden whenok = false): Operation-specific success body, byte-identical to the REST200response body.error (object, required when
ok = false, forbidden whenok = true): See Error Catalog for the shape and possible codes.
Top-Level Error Envelope (Server → Client)
The server emits a top-level error envelope when it cannot parse a request at all (malformed JSON, unknown type, in-flight id collision, internal failure). Connection stays open. Operation-specific errors instead come back as { ok: false, error } on the corresponding response envelope correlated by id.
id is present when the offending request carried one (e.g. for
DUPLICATE_REQUEST_ID); absent for frame-level errors (e.g. unparseable JSON) where the server has no id to echo.
Heartbeats
The heartbeat / connection-liveness mechanism is documented in detail on its own page — see Heartbeats. Short version: protocol-level pings handle liveness automatically, no application-level code is required on the client.
Operations Reference
createOrder
createOrderPurpose: Place a spot LIMIT GTC order, a perp IOC order, or a perp GTC/SL/TP conditional order. Identical body and semantics to REST POST /v2/createOrder.
Request Envelope (spot LIMIT GTC):
Request Envelope (perp IOC):
Success Response:
Error Response:
Data Type — CreateOrderRequest payload
exchangeId(integer, required): Reya exchange identifier. Currently always2.symbol(string): Trading symbol (e.g.WETHRUSD,ETHRUSDPERP).accountId(integer, required): Reya account ID placing the order.isBuy(boolean, required):truefor a buy,falsefor a sell.limitPx(string, required): Limit price as a decimal string.qty(string): Order quantity as a decimal string.orderType(string, required):LIMIT,TP(take-profit), orSL(stop-loss).timeInForce(string):IOCorGTC. Required forLIMITorders.triggerPx(string): Trigger price. Required forTP/SLorders.reduceOnly(boolean): Whether the order is reduce-only. Required for perp IOC orders.signature(string, required): EIP-712 signature over the order. See Signatures and Nonces.nonce(string, required): Order nonce.signerWallet(string, required): Address that produced the signature.expiresAfter(integer): Expiration timestamp in seconds since epoch. Required for perp IOC orders and all spot orders.clientOrderId(integer, optional): Echoed back in the response; useful for client-side correlation independent of the server-issuedorderId.
Data Type — CreateOrderResponse payload
status(string, required): One ofOPEN,FILLED,CANCELLED,REJECTED.execQty(string, optional): Executed quantity in this order update.cumQty(string, optional): Total executed quantity across all fills where the order is active.orderId(string, optional): Server-issued order ID. Present for all order types except perp IOC (which fills/voids on-chain in the same call and has no resting state).clientOrderId(integer, optional): Echoes the request'sclientOrderId.
cancelOrder
cancelOrderPurpose: Cancel a previously placed order by orderId (or clientOrderId for spot). Identical body and semantics to REST POST /v2/cancelOrder.
Request Envelope:
Success Response:
Data Type — CancelOrderRequest payload
orderId(string): Internal matching engine order ID to cancel. Provide eitherorderIdorclientOrderId.clientOrderId(integer): Client-provided order ID to cancel. Provide eitherorderIdorclientOrderId.accountId(integer): Account ID that owns the order. Required for spot markets.symbol(string): Market symbol for the order. Required for spot market orders.signature(string, required): EIP-712 signature over the cancellation.nonce(string): Cancel nonce. Required for spot.expiresAfter(integer): Expiration timestamp. Required for spot.
Data Type — CancelOrderResponse payload
status(string, required): AlwaysCANCELLEDon success.orderId(string, required): The cancelled order ID.clientOrderId(integer, optional): Echoes the request'sclientOrderId.
cancelAll
cancelAllPurpose: Mass-cancel all open orders for an account on a given (spot) market, or across all markets if symbol is omitted. Identical body and semantics to REST POST /v2/cancelAll. Perp mass-cancel is not supported and returns the same not-supported error REST returns.
Request Envelope:
Success Response:
Data Type — MassCancelRequest payload
accountId(integer, required): Account ID to cancel orders for.symbol(string, optional): Symbol to cancel orders for. If omitted, cancels all orders for the account across all markets.signature(string, required): EIP-712 signature.nonce(string, required): Mass-cancel nonce.expiresAfter(integer, required): Expiration timestamp.
Data Type — MassCancelResponse payload
cancelledCount(integer, required): Number of orders that were cancelled.
ping / pong
ping / pongPurpose: Optional client-initiated application-level liveness/RTT probe. The client sends {type:"ping", id?}; the server replies with {type:"pong", id?} echoing the optional id. See Heartbeats for the full description — when to use it, when not to, and how it relates to the protocol-level liveness mechanism that keeps the connection alive automatically.
Error Catalog
Every error envelope (both per-operation {ok: false, error} and top-level error) carries a RequestError-shaped object:
The error field is one of the codes below. Per-operation responses ({ok: false, error} on createOrder / cancelOrder / cancelAll) use codes from the Trade Handler group, shared 1:1 with REST. Top-level error envelopes use codes from the Framing Layer group exclusively; these only make sense for a streamed envelope protocol and never appear in REST responses.
Trade Handler Codes (shared with REST)
SYMBOL_NOT_FOUND
The symbol in the payload doesn't resolve to a known market.
Refresh market definitions via REST GET /v2/marketDefinitions.
NO_ACCOUNTS_FOUND
The accountId doesn't exist or isn't owned by the signer.
Verify account configuration.
NO_PRICES_FOUND_FOR_SYMBOL
Stork oracle has no recent price for the symbol — usually transient at startup.
Retry after a short backoff.
INPUT_VALIDATION_ERROR
Generic body-validation failure (missing field, wrong type, illegal value).
Fix the request body; consult the human-readable message.
CREATE_ORDER_OTHER_ERROR
Generic createOrder failure not covered by a more specific code (includes on-chain reverts surfaced through the relayer).
Read message for the underlying reason; for perp IOC, also check that the limit price can be filled within the on-chain price-limit check.
CANCEL_ORDER_OTHER_ERROR
Generic cancelOrder failure not covered by a more specific code.
Read message.
ORDER_DEADLINE_PASSED_ERROR
expiresAfter is in the past.
Re-sign with a fresh deadline.
ORDER_DEADLINE_TOO_HIGH_ERROR
expiresAfter is too far in the future (anti-abuse cap).
Use a shorter deadline (typically <= 24h for spot GTC, <= 60s for IOC).
INVALID_NONCE_ERROR
Nonce is not strictly monotonic for this signer, or was already used.
Re-sign with a fresh monotonic nonce.
UNAVAILABLE_MATCHING_ENGINE_ERROR
The matching engine is unavailable (transient).
Retry after a short backoff.
UNAUTHORIZED_SIGNATURE_ERROR
The recovered signer is not authorized to act on the accountId.
Verify the signer wallet is in the account's permissioned-addresses list on-chain.
NUMERIC_OVERFLOW_ERROR
A numeric field exceeds the allowed uint64 / int256 range.
Fix the request body.
Framing Layer Codes (top-level error envelope only)
error envelope only)These codes appear only in the top-level error envelope, never inside a per-operation {ok: false} response.
MALFORMED_JSON
The frame body could not be parsed as JSON, or required envelope fields (type, id) are missing. Connection stays open.
Fix the client serializer.
UNKNOWN_TYPE
The frame's type field is not one of the accepted values. Connection stays open.
Verify the request type.
DUPLICATE_REQUEST_ID
The frame's id is already in-flight on this connection — i.e., the client sent a new request with an id whose response hasn't yet been emitted. Connection stays open; the new request is rejected, the original is unaffected.
Use a fresh id for each request (UUIDs work).
INTERNAL
The server hit an internal failure handling the frame. Connection stays open.
Retry; if the problem persists, contact support with the id.
Data Types & Schemas
Enumeration Types
OrderType
LIMIT— Limit order (withtimeInForce=IOCorGTC).TP— Take-profit conditional order (perp).SL— Stop-loss conditional order (perp).
TimeInForce
IOC— Immediate or Cancel. Required for perp IOC orders.GTC— Good Till Cancelled. Used for spot LIMIT orders.
OrderStatus
OPEN— Order is resting in the book.FILLED— Order is fully filled.CANCELLED— Order is cancelled.REJECTED— Order was rejected by the matching engine.
For the complete enumeration of RequestErrorCode (per-operation errors) and WsExecErrorCode (top-level errors), see the Error Catalog above.
Connection Management
Reconnection Pattern
Reconnect with the usual exponential-backoff-with-jitter pattern any robust WebSocket client should use. The Reya-specific bits are:
No subscription state to replay. There is no session-bound identity to restore — the next request authenticates itself via its EIP-712 signature just like the first one did.
Verify in-flight requests via REST before resubmitting. Closing the WebSocket has zero persistent side effects on the order book itself — an in-flight
createOrderwhose response cannot be delivered still settles on-chain (same as a REST timeout). For any request whose response was not received before disconnect, checkGET /v2/wallet/{address}/openOrdersandGET /v2/wallet/{address}/perpExecutionsto confirm outcome before retrying — otherwise you risk a duplicate order. See Idempotency (clientOrderId) for safe retry correlation.
For the meaning of WS close codes you'll see on onclose (1000, 1001, 1006, etc.), see What Happens When the Server Closes the Connection.
Graceful Shutdown
The server's drain-and-close behavior on rolling deploys (10s drain, /ready returns 503, idle connections closed first, 1001 SERVER_SHUTTING_DOWN on close) is shared with the Info WebSocket and documented in Server-Side Graceful Shutdown.
In-Flight id Uniqueness
id UniquenessEach id must be unique across in-flight requests on the connection. Once the server has emitted the corresponding response envelope, the id is free to reuse. Sending a fresh request with the same id as an in-flight one triggers a top-level DUPLICATE_REQUEST_ID error envelope; the original in-flight request is unaffected.
In practice, clients should generate a fresh id for every request (e.g. UUIDv4 or a monotonic counter prefixed with a session token).
Idempotency (clientOrderId)
clientOrderId)For createOrder, the optional clientOrderId field is echoed back unchanged in the response and in any subsequent order-update events on the Info WebSocket. Use it for client-side correlation independent of the server-issued orderId — particularly useful when the response envelope is lost mid-flight and the client must reconcile state from Info-WebSocket updates after reconnect.
clientOrderId does not provide server-side deduplication: two requests with the same clientOrderId will be treated as two separate orders.
Signatures and Nonces
All createOrder, cancelOrder, and cancelAll payloads carry an EIP-712 signature over the order contents. The shape of the signed message is identical to the REST /v2 endpoints — see Signatures and Nonces for the canonical reference. The Python SDK provides the helpers sign_raw_order, sign_cancel_order_spot, and sign_mass_cancel to produce these signatures correctly.
The WebSocket transport does not add or change any signing requirement. Frames are signed by your wallet (or a permissioned trading key) at the payload level; the WebSocket connection itself is unauthenticated.
Differences vs REST
The WebSocket Order Entry surface is functionally equivalent to the corresponding REST endpoints, with transport-level differences:
Request body
CreateOrderRequest / CancelOrderRequest / MassCancelRequest
Same — embedded as payload
Response body
CreateOrderResponse / CancelOrderResponse / MassCancelResponse
Same — embedded as payload on ok: true
Success / failure
HTTP 200 vs 400 / 500
ok: true vs ok: false inside the frame
Error body
RequestError
Same — embedded as error on ok: false
Correlation
HTTP request/response pairing
Client-supplied id field
Auth
EIP-712 signature in body
Same EIP-712 signature in same body
Idempotency
clientOrderId echoed
Same
Connection
Per-request
Persistent, multiplexed
When to use which:
REST — One-off requests, low frequency, simpler client integration, no need to maintain a long-lived connection.
WebSocket Order Entry — High-frequency order submission, lower per-request overhead (no TLS handshake per request), latency-sensitive integrations. Recommended for Market Makers running together with the WebSocket Info API.
Python SDK Example
A worked end-to-end example is included in the Reya Python SDK at examples/ws_exec/mvp.py. It demonstrates:
Spot LIMIT GTC
createOrder(resting in the book)Spot
cancelOrder(cancels the order from step 1)Spot
cancelAll(opens N orders then mass-cancels them)Perp IOC
createOrder(fills at the current mark with a loose limit)
Run with:
See the script's accompanying README for prerequisites (.env setup, funded test accounts on cronos).
Last updated