Skip to content

Relay WebSocket API

Use WebSocket when a trading session will submit many orders and should keep one persistent connection to the relay.

wss://ws.relay.testnet.kuru.io/relay/ws

The WebSocket API supports the same onboarding, trading, and trigger methods as REST. Read the REST API overview, wallet onboarding, and trading methods first. This page explains only the transport differences.

Connect

First complete the REST authentication flow with the secondary trading EOA. Send the resulting JWT in the WebSocket upgrade request:

GET /relay/ws HTTP/1.1
Host: ws.relay.testnet.kuru.io
Upgrade: websocket
Connection: Upgrade
Authorization: Bearer <JWT bound to the trading wallet>

The JWT binds the connection to one secondary trading EOA. Every message on that connection acts for that wallet, so WebSocket messages do not contain a wallet field.

Authentication challenges and token exchanges are available only on https://api.relay.testnet.kuru.io; do not send them to the WebSocket hostname.

Open a separate connection for another wallet. After reconnecting, treat the new socket as a new session and recover current wallet and transaction state from the chain.

Send a method

Replace REST's requestId and wallet fields with a connection-local id:

{
  "id": "order-42",
  "method": "wallet.execute_batch",
  "payload": {
    "header": {
      "accountId": "123",
      "market": "0x2222222222222222222222222222222222222222",
      "authNonce": "1",
      "nonce": "1720000000123",
      "deadline": "1720000030",
      "clientOrderId": "0x0000000000000000000000000000000000000000000000000000000000000001",
      "builder": "0x0000000000000000000000000000000000000000",
      "builderFeePps": "0"
    },
    "orders": [],
    "cancelSlotIdxs": ["3"],
    "expectedOrderIds": ["31"],
    "signature": "0x..."
  },
  "authorization7702": null
}

id is an opaque string of at most 128 bytes. It must be unique within the connection. The relay returns exactly one response for each accepted ID and keeps a bounded cache of recent responses so that a repeated ID can receive the same response.

Receive the result

{
  "id": "order-42",
  "status": "BROADCAST",
  "txHash": "0x...",
  "sponsorAddress": "0x...",
  "sponsorNonce": "42",
  "transactionType": "DYNAMIC_FEE",
  "retryable": false
}

As with REST, BROADCAST does not prove execution. Track txHash until its receipt is available and verify the action-specific event or state change.

Failure responses contain the original id, stable error code, human-readable message, and retryable flag. The WebSocket failure envelope currently omits REST's retryAfterMs and candidate transaction fields. If the result is ambiguous and no transaction hash is available, reconcile wallet nonce and contract state before resubmitting.

Message ordering

Several messages may be in flight, but wallet intent nonces accepted on one connection must increase. The relay rejects a nonce equal to or below the connection's last broadcast-accepted wallet nonce with NON_INCREASING_SESSION_NONCE.

This WebSocket rule applies only to the KuruTradingWallet intent nonce. It does not compare:

  • the AccountCore authorization nonce;
  • the EIP-7702 authority nonce;
  • the relay sponsor transaction nonce; or
  • the WebSocket message id.

The connection-local check is not canonical blockchain state. It is not shared with REST, another socket, another relay instance, or a direct chain transaction.

Reconnect behavior

Close code Meaning Client action
4001 Session rate, queue, in-flight, or memory limit reached. Reconcile outstanding messages, back off, and reconnect.
4002 Relay instance is shutting down or draining. Reconcile outstanding messages and reconnect to a ready instance.
4003 The session's pinned sponsor lane is unavailable. Reconcile outstanding messages and reconnect for a new lane.

A closed connection or missing response does not prove that a transaction was rejected.

Use the AsyncAPI 3.1 specification for the exact message schemas.