For the complete documentation index, see llms.txt. This page is also available as Markdown.

Manual REST Signing

Implement Perps order signing without an SDK. For the shorter SDK-based path, use Trading Quickstart. This tutorial covers canonical payloads, EIP-712 signatures, raw HTTP requests, and execution tracking.

Before starting, activate and fund your testnet trading account and register a trading API key. The example below submits a real testnet order; it is not a simulation.

1. Choose an environment

Environment
REST base URL
WebSocket base URL
Chain ID

Mainnet

https://mainnet-gw.sodex.dev

wss://mainnet-gw.sodex.dev

286623

Testnet

https://testnet-gw.sodex.dev

wss://testnet-gw.sodex.dev

138565

Spot REST paths start with /api/v1/spot; perpetual REST paths start with /api/v1/perps.

2. Make a public request

Public market-data endpoints do not require a signature.

curl "https://testnet-gw.sodex.dev/api/v1/perps/markets/tickers?symbol=BTC-USD" \
  -H "Accept: application/json"

Successful REST responses use a common envelope with code, timestamp, and endpoint-specific data. A code value of 0 means the request succeeded.

3. Pick an integration path

For most applications, start with an official SDK. The SDKs implement canonical request serialization, EIP-712 signing, signature prefixes, and nonce coordination.

If you need a custom client, read Authentication & Signing before implementing write requests. In particular:

  • X-API-Key contains the registered key name, not a private key or address.

  • X-API-Sign contains the EIP-712 signature; the private key is never sent.

  • X-API-Nonce must be unique for the signing address and remain inside the accepted time window.

  • Use the spot, futures, or universal signing domain required by the action.

4. Get your account ID and trading symbol

Use the master wallet address for account lookup, not the API key's public address. All commands below use the same testnet environment:

Check code == 0 before reading data.

Value you need
Where to get it
How it is used

accountID

data.aid from account state

Identifies the trading account in the signed order. This is not uid or a wallet address.

symbolID

id of the selected object in the symbols response's data array

Identifies the market in the signed order.

symbol

name from that same symbol object

Used by market queries and WebSocket subscriptions. Do not use displayName.

Available margin

data.am (cross) or data.ami (isolated) from account state

Check the margin available for the intended position mode.

Price and quantity rules

tickSize, stepSize, pricePrecision, minQuantity, maxQuantity, minNotional, maxNotional

Choose a price and quantity that satisfy the market's filters before signing.

The state endpoint returns the primary account by default. To trade a sub-account, pass its accountID in the state query and use that account ID consistently in the order and subsequent queries. An unactivated account must be activated before continuing.

See Account State, Symbol Fields, and Order Validation Rules.

5. Sign and submit a limit order

This example places one BUY / LIMIT / GTC order. GTC orders can remain open until filled or canceled. Use a testnet price and quantity you intend to trade; do not copy a production order size.

Install dependencies and save the TypeScript code below as place-order.ts:

The request uses these values:

Field
Example value
Meaning

modifier

1

Normal order

side

1

Buy

type

1

Limit

timeInForce

1

Good till canceled

positionSide

1

BOTH

price, quantity

Decimal strings

Preserve the selected market's precision; do not convert amounts to floating-point numbers.

clOrdID

Generated before submission

Your client order identifier; retain it for reconciliation.

The signed payload is {type: "newOrder", params}, but the HTTP body is only params. Keep the field order shown above. The testnet signing domain is futures with chain ID 138565; the signature is prefixed with 0x01. See Authentication & Signing for the original signing definitions.

Check the submission result

  1. Check the HTTP status and top-level code. HTTP success alone is insufficient.

  2. When the top-level code is 0, inspect each item in data. A batch can contain individual failures.

  3. Match each result by clOrdID. When its code is 0, save the returned orderID; otherwise read its error.

  4. Acceptance is not a fill. Continue with execution tracking below.

Pre-validation can return one error for the whole batch rather than one result per order. Do not assume response length always equals request length.

6. Track order status and fills

Connect to wss://testnet-gw.sodex.dev/ws/perps. Prefer subscribing before submission so that immediately filled orders are not missed. Replace YOUR_MASTER_WALLET_ADDRESS and SYMBOL_NAME_FROM_STEP_4 in each message:

Check the subscription acknowledgement's success field. Use Account Order Updates for state changes and Account Trades for executions. Follow the subscription schema when specifying a sub-account.

Order status
Interpretation

NEW

Accepted; it may still be resting on the book.

PARTIALLY_FILLED

Some quantity has executed; some remains.

FILLED

The full order quantity has executed.

CANCELED, EXPIRED

No further execution is expected, but partial fills may already exist. Check trades.

REJECTED

The order was not accepted. Inspect the error before correcting the request.

Recover state with REST

Set SYMBOL to the market name from step 4. Use the account ID from the same step:

An order missing from open orders is not necessarily filled: check history for its orderID or clOrdID, then inspect its trades. Paginate history if the order is outside the returned page. To stop an unfilled GTC order, use Cancel Multiple Orders; signing a cancellation is a separate action.

Handle errors and reconnects

Situation
What to do

HTTP timeout or connection lost during submission

Treat the outcome as unknown. Query open orders and history using the retained client order ID before submitting again. A fresh nonce does not make a retry idempotent.

An order result has a non-zero code

Read its error, check the market filters and available margin, then correct the request.

WebSocket disconnected

Reconnect, resubscribe, and reconcile open orders, history, and trades through REST. Follow the documented ping/pong rules.

Rate limited

Back off according to the applicable rate limits. Do not repeatedly resend signed orders.

Use a separate API key for each concurrent trading process so that processes do not share a nonce tracker. Never place private keys or signature headers in logs.

Next steps

Authentication & SigningRate LimitsREST APIWebSocket Streams

Last updated