POST /exchange. The operation to perform is determined entirely by the action.type field inside the JSON body, not by the URL. This action-based routing means your HTTP client only ever needs to know one write URL; you change the payload to change the operation.
Endpoint
Envelope Structure
EveryPOST /exchange request body is a JSON object with the following top-level fields:
Fields
object
required
action (required) — The operation payload. Must contain a type string that identifies the action (e.g. "order", "cancel", "registerAccount"). All other fields are action-specific.string
required
action.type (required) — Identifies which action to perform. It also selects the EIP-712 signing path — funds/permission actions use a typed struct, everything else uses the Agent path. See Authentication.Common values: order, cancel, cancelByCloid, modify, registerAccount, updateLeverage, updateIsolatedMargin.object
required
signature (required) — EIP-712 ECDSA signature (secp256k1) over the action. The server recovers the signer’s Ethereum address from this signature to authorize the operation — there are no API keys.string
required
signature.r (required) — The r component of the ECDSA signature. Must be a 0x-prefixed lowercase hex string representing a 32-byte value (66 characters total).string
required
signature.s (required) — The s component of the ECDSA signature. Same format as r.integer
required
signature.v (required) — The recovery parameter. Must be 27 or 28 (Ethereum convention: 27 + recovery_bit).integer
required
nonce (required) — An int64 Unix millisecond timestamp. Must be unique per signer. Used as replay protection — the server rejects any previously seen nonce from the same address. While a request is still being processed, a second request from the same address with the same nonce returns 400 DUPLICATE_NONCE. See Nonce.string
vaultAddress — Optional. Reserved for vault operations, which are not currently enabled; the server ignores this field. Delegated agent trading does not use it: an API wallet signs with its own key and the server routes to the master account from the recovered signer, so you do not send vaultAddress or any master identifier.boolean
sync — Optional. Selects the response form for the order and cancel family only: order, cancel, cancelByCloid, cancelAll, and modify. true returns HTTP 200 with the action’s result after it has been executed; false returns HTTP 202 accepted immediately. Must be a JSON boolean. Sits at the top level of the envelope and is not part of the signed message. Sending it with any other action returns 400 SYNC_NOT_APPLICABLE. See Synchronous and asynchronous responses.string
signatureChainId — Optional. The domain.chainId your client used when signing a typed-path action, as a 0x-prefixed hex string. Defaults to the environment’s chain ID — 0x2627 (9767) on Devnet, 0x263c (9788) on Mainnet. Sits at the top level of the envelope and is not part of the signed message. See Authentication.string
inviteCode — Conditional. A 6-character alphanumeric invitation code required by registerAccount. Include this field at the top level of the envelope — not inside action. This field is not included in the signed message.Synchronous and asynchronous responses
The order and cancel family —order, cancel, cancelByCloid, cancelAll, and modify — supports two response forms, selected per request by the top-level sync field. Both forms submit the same transaction and produce the same matching result; they differ only in when and how the HTTP response is returned.
When
sync is omitted, the server default applies, which is currently the asynchronous 202 form. An omitted sync is not the same as false: to rely on one form, set sync explicitly.
sync is not part of the signature, so the same signed action can be submitted in either form without changing any signing code.
The 200 body differs by action:
Do not write a generic parser that reads
statuses[] from every 200; a cancelAll response has none.
Constraints:
syncapplies only to the five actions above. Every other write action already returns its result synchronously and has no asynchronous form; sendingsyncwith any of them —trueorfalse— returns400 SYNC_NOT_APPLICABLE.syncmust be a JSON boolean. A string such as"true"or a number such as1is rejected with400.- Under heavy load, a
sync: truerequest can return503 DOWNSTREAM_UNAVAILABLE. Submit withsync: falseand read the result from WebSocket instead. - A
504 DOWNSTREAM_TIMEOUTon async: truerequest means the server did not receive the result in time — not that the request failed. The transaction may already have executed. Confirm the final state throughorderUpdatesor a query before submitting anything new. See Error Codes. - Do not submit the same
nonceconcurrently. While a request is in flight, a second request with the samenoncefrom the same signing address returns400 DUPLICATE_NONCE; re-sending that envelope cannot succeed.
Action-Based Routing
Unlike a traditional REST API where
POST /orders creates an order and DELETE /orders/:id cancels one, UpsideMAX routes every write operation through POST /exchange. Routing is determined by action.type in the body. This keeps the transport layer simple and makes it easy to batch operations in a single request structure.action.type values to their operations:
Example Request
The following example places a limit GTC buy order (rawp / s values — scale per the contract’s configs):
