Signed actions
Every state change — orders, cancels, withdrawals, transfers, triggers, session
management — is an Action: a protobuf message, signed, submitted to a single
endpoint. If you use the TypeScript SDK this is
handled for you; this page is what you need to implement it elsewhere.
The schema is served by the exchange
curl -sO https://api-mainnet.n1.xyz/schema.proto
Generate bindings from that file rather than from a vendored copy, so your client
matches the deployment's version. Check the version you are talking to with
GET /info and the Changelog.
Submitting
POST /action
Content-Type: application/octet-stream
The body is the length-delimited protobuf encoding of the Action, immediately
followed by the signature over those encoded bytes:
body = size_delimited(Action) || signature
The response is a length-delimited Receipt. A receipt whose kind is err
carries an Error enum value — an HTTP 200 with an error receipt is a rejected
action, not a success, so decode the receipt before treating a submission as
executed.
The encoded action, including the signature, must be 1024 bytes or less. Batch with care: an atomic action containing many subactions can exceed the limit.
Required fields
Every Action carries:
| Field | Value |
|---|---|
currentTimestamp | Server time from GET /timestamp, not local clock time |
nonce | Monotonic per signer; replayed or stale nonces are rejected |
kind | The specific action (place order, cancel, withdraw, …) |
Recover state after a restart with GET /event/last-acked-nonce for your signer
and GET /action/last-executed-id. Using the client clock instead of
/timestamp is the most common cause of otherwise-valid actions being rejected.
Signing
Actions are signed either by the wallet key or by a session key authorized by
that wallet. Sessions exist so a trading process never holds the wallet key —
see Accounts and sessions.
Session-signed actions carry the sessionId inside the action kind.
Session creation supports two signature framings, hex and
solanaTransaction, the latter for wallets that will only sign a Solana
transaction rather than an arbitrary message.
Units
All prices, sizes, and amounts in actions are scaled integers, using the
market's priceDecimals / sizeDecimals or the token's decimals from
GET /info. Quote notional amounts scale by
priceDecimals + sizeDecimals. u128 values are carried as hi / lo pairs.
Sending a human-readable decimal where an integer is expected is silently a very
different order.
Checklist
- Fetch
/infoonce; cache market and token IDs and decimals. - Fetch
/timestampbefore signing. - Track your nonce.
- Encode length-delimited, sign, append the signature.
POST /actionasapplication/octet-stream.- Decode the
Receipt, and check forerr.