Branch your client on these stable values. The exchange reports failures two ways: non-2xx responses carry application/problem+json with a stable code (branch on code, not detail), and signed writes return 200 OK with a RequestAck body whose status can be a rejection. The Error Model covers the response shape and why a 200 is not proof of acceptance.
HTTP status codes
These statuses span every endpoint.
| Status | Meaning to the caller | Retry? |
|---|
200 OK | Processed. May still be an application-level rejection — read the body. | No (resubmit only if the body reports a retryable rejection) |
204 No Content | Success, no body (e.g. device-key revoke, pairing write). | No |
400 Bad Request | Malformed payload, invalid request type, bad query parameters, or a request_id outside the skew window. | No — fix the request first |
401 Unauthorized | Missing credential, or signature/session verification failed. | No — fix credentials/signature |
404 Not Found | Target does not exist or is not reachable by your credential. | No |
409 Conflict | The resource state blocks the operation (e.g. pairing already completed, portfolio close/reopen/name conflict). | No |
413 Payload Too Large | Encoded request exceeded the size cap. See Rate Limits. | No — shrink the request |
429 Too Many Requests | Account exceeded its rate limit. | Yes — back off and retry |
500 Internal Server Error | The exchange hit an internal error. | Maybe — retry with backoff |
503 Service Unavailable | The exchange is briefly unavailable (at capacity, or the request was dropped before processing). | Yes — back off and retry |
504 Timeout | The exchange did not confirm before the deadline. The write may or may not have been applied. | Yes — reuse the same request_id |
On 500, 503, or 504 you may not know whether a write was applied. Retry with the same request_id (it is the idempotency key). A re-applied request returns duplicate_request_id instead of executing twice.
ProblemDetails codes
These code values appear in the application/problem+json body on non-2xx responses.
code | Status | Trigger |
|---|
invalid_signature | 401 | Signature verification failed, or the credential could not be resolved. |
malformed_payload | 400 | Submitted payload is not well-formed. |
invalid_request_type | 400 | Payload does not match the endpoint’s expected request type. |
unsupported_request_type | 400 | Request type is not currently supported. |
unsupported_content_type | 415 | Content-Type is neither application/json nor application/octet-stream. |
request_timestamp_skew | 400 | The request_id’s embedded v7 timestamp is not current (too old or too far ahead). |
invalid_portfolio_name | 400 | Portfolio name fails length/charset/reserved checks. |
duplicate_portfolio_name | 409 | Portfolio name already in use. |
portfolio_close_conflict | 409 | Portfolio cannot be closed (already closed, non-zero balance, open positions/orders). |
portfolio_reopen_conflict | 409 | Portfolio cannot be reopened (already active, or unavailable). |
pairing_already_completed | 409 | The device pairing has already been completed. |
request_too_large | 413 | Encoded request payload exceeded the size cap. |
rate_limited | 429 | Account exceeded its rate limit. |
at_capacity | 503 | The exchange is at capacity. Retry. |
request_dropped | 503 | The request was dropped before processing. Retry. |
service_unavailable | 503 | The exchange is briefly unavailable. Retry. |
retry_required | 503 | The request needs to be retried. |
ack_failed | 500 | The exchange did not return a confirmation. |
ack_timeout | 504 | The exchange did not confirm before the deadline. |
request_timestamp_skew is the most common avoidable rejection. Generate a fresh UUIDv7 request_id at send time and keep your clock in sync.
RequestAck status values
Signed-write endpoints return 200 OK with a RequestAck body. status reports the outcome; processed_at_ns is the exchange processing time (nanoseconds since the Unix epoch).
{ "status": "request_completed", "processed_at_ns": 1719158400000000000 }
Accepted
status | Meaning |
|---|
request_completed | Request applied. |
duplicate_request_id | This request_id was already processed (idempotent replay). Safe — do not resubmit with a new id. |
duplicate_request_id is the success signal for a safe retry. After a 504 or a network error on a write, resubmit with the same request_id: if the original was applied you get duplicate_request_id, otherwise it is processed once.
Transient (retry)
The request was not processed. Retry with the same request_id.
status | Meaning |
|---|
request_dropped | The exchange was busy and dropped the request before processing. |
retry_required | The request needs to be retried. |
Order rejections
status | Trigger |
|---|
order_rejected_invalid | Order failed validation. |
order_rejected_expired | Order expiry already passed. |
order_rejected_internal | Internal failure handling the order. |
order_rejected_invalid_market | Market reference is invalid. |
rejected_invalid_portfolio_id | Portfolio id does not resolve. |
rejected_invalid_price | Price failed validation. |
rejected_invalid_size | Size failed validation. |
rejected_size_zero | Size is zero. |
rejected_size_not_equal | Size mismatch on an amend/replace. |
rejected_order_not_found | Target order does not exist. |
rejected_invalid_order_flags | Order flags combination is invalid. |
rejected_invalid_uuid | A referenced UUID is invalid. |
rejected_max_open_orders_exceeded | Account/portfolio is at its open-order cap. |
rejected_post_only_would_cross_book | A post-only order would cross the book (it would take, not make). |
invalid_reduce_only_order | Reduce-only order does not reduce a position. |
Market state
status | Trigger |
|---|
market_closed | Market is closed. |
rejected_market_not_found | Market does not exist. |
rejected_market_halted | Market is halted. |
rejected_market_reduce_only | Market accepts reduce-only orders only. |
rejected_market_post_only | Market accepts post-only orders only. |
rejected_market_cancel_only | Market accepts cancels only. |
rejected_mark_price_zero | Mark price is zero; order cannot be priced. |
Margin and equity
status | Trigger |
|---|
insufficient_margin_to_place | Not enough margin to place the order. |
insufficient_margin_to_trade | Not enough margin to execute the trade. |
rejected_insufficient_free_equity | Not enough free equity. |
rejected_f_o_k_not_fully_filled_insufficient_margin | Fill-or-kill could not fully fill due to margin. |
rejected_f_o_k_not_fully_filled_low_liquidity | Fill-or-kill could not fully fill due to liquidity. |
rejected_slippage_exceeds_maximum | Slippage exceeded the allowed maximum. |
Auth, account, and rate
status | Trigger |
|---|
rejected_unauthorized | Signer is not authorized for the action. |
rejected_account_suspended | Account is suspended. |
rejected_rate_limited | Rejected at the exchange for rate reasons. |
self_trade_prevention | Order would trade against your own resting order; STP blocked it. |
rejected_self_transfer | Transfer source and destination are the same. |
Conditional orders
These statuses are reserved and will not occur today: conditional order types (stop / take-profit) are not yet available over the REST API, and self-trade-prevention flags are accepted but not yet enforced.
status | Trigger |
|---|
rejected_max_conditional_orders_exceeded | At the conditional-order cap. |
rejected_trigger_already_satisfied | Trigger condition is already met. |
rejected_conditional_order_expired | Conditional order already expired. |
rejected_invalid_trigger | Trigger parameters are invalid. |
conditional_order_on_books | Conditional order is resting. |
conditional_order_not_found | Conditional order does not exist. |
Master keys
status | Trigger |
|---|
master_key_added | Master key added. |
master_key_removed | Master key removed. |
master_key_rejected_invalid | Master key is invalid. |
master_key_rejected_unauthorized | Signer not authorized to manage this key. |
master_key_rejected_self_removal | A key cannot remove itself. |
master_key_rejected_last_key | Cannot remove the last remaining admin key. |
Sessions
status | Trigger |
|---|
session_created | Session created. |
session_revoked | Session revoked. |
session_rejected | Session request rejected (e.g. unknown master key, invalid scope). |
session_rejected_max_sessions | At the session cap. |
Account creation
status | Trigger |
|---|
account_created_with_session | Account and initial session created. |
account_created_session_failed | Account created, but session registration failed (partial success). |
creation_rejected | Account creation rejected. |
Account, master-key, and session endpoints may instead return their own response object with a success boolean (and, for account creation, session_created). Read the body there too — see Error Model.