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.xyz

  • Protocol: WSS

  • Description: Production WebSocket order entry server

Staging Environment

  • URL: wss://ws-exec-staging.reya.xyz

  • Protocol: WSS

  • Description: Staging WebSocket order entry server for pre-production testing

Test Environment

  • URL: wss://ws-exec-testnet.reya.xyz

  • Protocol: 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. (pong is server-only — see Heartbeats.)

  • id (string, required): Client-chosen correlation identifier. Must be unique across in-flight requests on the connection — see In-Flight id Uniqueness 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): true for success, false for failure.

  • payload (object, required when ok = true, forbidden when ok = false): Operation-specific success body, byte-identical to the REST 200 response body.

  • error (object, required when ok = false, forbidden when ok = 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

Purpose: 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 always 2.

  • symbol (string): Trading symbol (e.g. WETHRUSD, ETHRUSDPERP).

  • accountId (integer, required): Reya account ID placing the order.

  • isBuy (boolean, required): true for a buy, false for 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), or SL (stop-loss).

  • timeInForce (string): IOC or GTC. Required for LIMIT orders.

  • triggerPx (string): Trigger price. Required for TP / SL orders.

  • 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-issued orderId.

Data Type — CreateOrderResponse payload
  • status (string, required): One of OPEN, 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's clientOrderId.

cancelOrder

Purpose: 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 either orderId or clientOrderId.

  • clientOrderId (integer): Client-provided order ID to cancel. Provide either orderId or clientOrderId.

  • 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): Always CANCELLED on success.

  • orderId (string, required): The cancelled order ID.

  • clientOrderId (integer, optional): Echoes the request's clientOrderId.

cancelAll

Purpose: 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

Purpose: 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)

Code
When emitted
Client action

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)

These codes appear only in the top-level error envelope, never inside a per-operation {ok: false} response.

Code
When emitted
Client action

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 (with timeInForce = IOC or GTC).

  • 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:

  1. 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.

  2. Verify in-flight requests via REST before resubmitting. Closing the WebSocket has zero persistent side effects on the order book itself — an in-flight createOrder whose response cannot be delivered still settles on-chain (same as a REST timeout). For any request whose response was not received before disconnect, check GET /v2/wallet/{address}/openOrders and GET /v2/wallet/{address}/perpExecutions to 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

Each 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)

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:

Concern
REST
WebSocket Order Entry

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

Heartbeat

n/a (stateless)

Protocol-level, automatic — see Heartbeats

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:

  1. Spot LIMIT GTC createOrder (resting in the book)

  2. Spot cancelOrder (cancels the order from step 1)

  3. Spot cancelAll (opens N orders then mass-cancels them)

  4. 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