> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kuru.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Relay

> Gas sponsorship for Kuru account and trading actions.

Kuru Relay sponsors gas for Kuru accounts. For most methods, an authorized EOA signs an AccountCore or trading-wallet action; Relay validates the intent, constructs the expected calldata, pays the outer transaction's gas, and schedules broadcast. The opt-in `wallet.finalize_twap` operation is a permissionless contract action without a client signature.

```text theme={null}
Mainnet REST (playground default): https://relay.mainnet.kuru.io
Mainnet WebSocket: wss://relay.mainnet.kuru.io/relay/ws?account_id=<accountId>
Testnet REST: https://relay.testnet.kuru.io
Testnet WebSocket: wss://relay.testnet.kuru.io/relay/ws?account_id=<accountId>
```

Select testnet in the REST playground's server dropdown when testing there. **Mainnet Relay submissions can broadcast real transactions.** Confirm the selected host, chain ID, market, signer authorization, wallet-bound JWT, and signed payload before submitting. Switching servers never re-signs an intent or exchanges a JWT between networks. Copyable cURL templates can contain a hard-coded host and illustrative fields; selecting a different playground server does not rewrite them. Request payloads containing placeholder signatures are illustrations, not executable signed actions.

The sponsor cannot create or change a user's signed action. It only submits supported account and trading operations so the secondary trading wallet does not need to hold native gas tokens.

Authenticate the trading wallet with the two public endpoints below, then use either the REST or WebSocket method references.

See [Relay errors and outcomes](/api/relay/errors.md) for stable error codes, retry guidance, and WebSocket transaction-outcome delivery.

Read [Relay security and operating model](/api/relay/security.md) before deploying a signer.

> [!NOTE]
>
> Using the TypeScript SDK? Follow [Trading-wallet signing](/sdk/trading-wallet.md)
> and [Relay](/sdk/relay.md). Their testnet examples use Monad chain ID `10143`.

## Authentication

The two authentication endpoints are public POST APIs. They do not require a JWT.

> **[Create challenge](/api/relay/reference/create-challenge.md)**
>
> Public endpoint that creates the exact message a secondary trading EOA must
> sign.

> **[Exchange token](/api/relay/reference/exchange-token.md)**
>
> Public endpoint that verifies the signed challenge and returns a
> wallet-bound JWT.

Request a challenge for the secondary EOA, sign the exact returned UTF-8 message with Ethereum `personal_sign`, and exchange that signature for a short-lived JWT. Challenges expire after five minutes and can be consumed only once. Use the returned `expiresAt` rather than assuming the same JWT lifetime on both networks.

The JWT authenticates Relay access only. It does not grant AccountCore permission, install EIP-7702 delegation, replace method-specific signatures, or revoke on-chain rights when it expires.

## Enabled REST methods

> **[Authorize account signer](/api/relay/reference/authorize-account-signer.md)**
>
> Grant AccountCore permission and optionally install EIP-7702 delegation.

> **[Execute order batch](/api/relay/reference/execute-batch.md)**
>
> Place readable native orders, cancel slots, or perform both atomically.

> **[Execute packed replacement](/api/relay/reference/execute-replace-by-slot.md)**
>
> Submit compact 32-byte maker-slot operations.

> **[Cancel conditional order](/api/relay/reference/cancel-trigger.md)**
>
> Cancel an active trigger with a signed one-shot nonce.

## Opt-in operational methods

These slow, simulation-first methods are available only where explicitly enabled. They do not replace the fast batch/packed trading path; FOK trigger and TWAP creation only **store** instructions, with no condition monitor or slice scheduler. Read the [operational-methods overview](/api/relay/operational.md) before integration.

> [!WARNING]
>
> TP/SL and other automated triggers, as well as TWAP execution, are **not implemented** as live features. They are work in progress; storage-only methods do not provide automated execution.

> **[Cancel orders by ID](/api/relay/reference/execute-cancel-orders-by-id.md)**
>
> Cancel explicit order IDs.

> **[Cancel all in market](/api/relay/reference/execute-cancel-all.md)**
>
> Cancel one account's resting orders in one market.

> **[FOK swap](/api/relay/reference/execute-fok-swap.md)**
>
> Execute the full base quantity or revert.

> **[Store FOK trigger](/api/relay/reference/create-fok-swap-trigger.md)**
>
> Persist a conditional FOK instruction only.

> **[Store TWAP](/api/relay/reference/create-twap.md)**
>
> Persist a schedule only.

> **[Cancel TWAP](/api/relay/reference/cancel-twap.md)**
>
> Cancel a signed stored schedule.

> **[Finalize TWAP](/api/relay/reference/finalize-twap.md)**
>
> Record expiry; no slices are run.

> **[Revoke account signer](/api/relay/reference/revoke-account-signer.md)**
>
> Revoke with a separate AccountCore authorizer signature.

> **[Create subaccount](/api/relay/reference/create-subaccount.md)**
>
> Create the next child sequence by signature.

## Conditional actions — coming soon

> **[Create batch trigger](/api/relay/reference/create-batch-trigger.md)**
>
> Inspect the conditional native-order batch contract.

> **[Create packed trigger](/api/relay/reference/create-replace-trigger.md)**
>
> Inspect the conditional packed-replacement contract.

## WebSocket

For sessions that submit many actions, connect once and send the same typed methods over a wallet-bound WebSocket.

```http theme={null}
GET /relay/ws?account_id=123 HTTP/1.1
Host: relay.mainnet.kuru.io
Upgrade: websocket
Connection: Upgrade
Authorization: Bearer <JWT bound to the trading wallet>
```

The JWT binds the connection to one secondary EOA, so messages omit REST's `wallet` field and replace `requestId` with a connection-local `id`. Open a separate connection for each wallet. The required `account_id` is the **Kuru trading account ID**, not the signer address. It binds exchange-state context; it does not replace JWT authentication or on-chain trading authorization. Supply exactly one positive `uint40` in canonical decimal form (for example, `123`): no leading zero, sign, or whitespace. A missing or invalid value fails the upgrade with HTTP 400 `canonical account_id is required`.

> [!NOTE]
>
> The WebSocket reference renders typed messages and network selection, but its browser viewer cannot open Relay's authenticated connection because the upgrade requires an `Authorization` header. Use a WebSocket client that supports custom upgrade headers to send live Relay messages. The [TypeScript SDK Relay client](/sdk/relay.md) currently uses REST, not this WebSocket API. Do not put JWTs in a URL query string.

> **[Authorize account signer](/api/relay/websocket-reference/authorize-account-signer.md)**
>
> Grant AccountCore trading permission.

> **[Execute order batch](/api/relay/websocket-reference/execute-batch.md)**
>
> Place orders and cancel slots.

> **[Execute packed replacement](/api/relay/websocket-reference/execute-replace-by-slot.md)**
>
> Submit compact maker-slot operations.

> **[Cancel conditional order](/api/relay/websocket-reference/cancel-trigger.md)**
>
> Cancel an active trigger.

> **[Create batch trigger](/api/relay/websocket-reference/create-batch-trigger.md)**
>
> Defined for reference but not a supported live Relay flow.

> **[Create packed trigger](/api/relay/websocket-reference/create-replace-trigger.md)**
>
> Defined for reference but not a supported live Relay flow.

> **[Subscribe to transaction](/api/relay/websocket-reference/transaction-subscribe.md)**
>
> Recover a known transaction after reconnecting.

> **[Transaction outcome](/api/relay/websocket-reference/transaction-outcome.md)**
>
> Read the terminal result delivered for a WebSocket submission.

The nine opt-in operational methods also have WebSocket references in the sidebar. Their request payloads are the same as REST; the authenticated connection supplies `wallet` identity and each message uses a connection-local `id` instead of a REST `requestId`.

Several messages may be in flight. Each accepted `id` receives one correlated immediate response. Immediate wallet-intent nonces must increase within a connection. A `BROADCAST` response includes the server-assigned `requestId`, sponsor nonce, and transaction hash, but it is not a terminal outcome or proof that an RPC accepted the transaction. Relay automatically delivers `transaction.outcome` on that same connection with `type: "transaction.outcome"`, the original client `id`, `requestId`, and `txHash`. Terminal statuses are `CONFIRMED`, `REVERTED`, `REPLACED`, and `UNKNOWN`. **You do not need to subscribe to your own new submission or acknowledge its outcome.** Use `transaction.subscribe` mainly after reconnecting with a known `requestId` and `txHash`.

The current Relay configuration tracks at most **64 pending transaction outcomes per client WebSocket connection**. This is a configurable outstanding-outcome limit, **not** a lifetime limit of 64 submissions, a sponsor-nonce limit, or the separate in-flight-request limit. Tracking ends when a terminal outcome is processed for delivery; no client acknowledgement is needed. Immediate submission rejections do not take an outcome slot. If all slots are occupied, Relay may close the socket while registering another outcome **after** writing its `BROADCAST` response. Closing the socket does not undo the scheduled broadcast or prove the transaction failed. Bound outstanding submissions below the limit and retain each `requestId` and `txHash` for recovery.

The signer retains the latest **4,096 terminal outcomes per signer**, shared across users. This is count-based, not a time guarantee; do not assume replay survives a signer restart. On reconnect, establish a new wallet-bound session and reconcile unresolved transactions by their existing identities. If the connection fell before `requestId` and `txHash` arrived, the old WS `id` cannot recover the submission across connections. Use a unique signed batch-level `payload.header.clientOrderId` and paginated [order events](/api/data-source/reference/order-events.md) as one reconciliation signal; absence of a match does not prove failure. Keep recovery bounded and asynchronous so receipt or log queries do not block new submissions for tens of seconds. Relay does not impose a 50-second reconnect backoff; a long pause of that kind can arise in the client connector's reconciliation path. See [Relay errors and outcomes](/api/relay/errors.md#websocket-transaction-outcomes) for the full outcome and recovery limits.

## Shared request rules

Every `POST /relay` request includes a lowercase UUIDv7 `requestId`, an exact `method`, the secondary EOA in `wallet`, and a method-specific `payload`. Most methods require a signature; permissionless `wallet.finalize_twap` does not. `authorization7702` is available only to existing supported onboarding/trading methods, not the new operational routes.

All integer request fields are canonical unsigned decimal strings. Addresses and byte values use exact-width `0x` hexadecimal. Unknown and duplicate JSON fields are rejected. The request wallet must match the bearer JWT's wallet claim.

> [!WARNING]
>
> `BROADCAST` means Relay accepted and scheduled the signed transaction for
> asynchronous RPC broadcast. The RPC call may not have returned yet. It does
> not prove provider acceptance, inclusion, execution, or finality. Track the
> transaction receipt and verify the expected event or state transition.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.