Reference exposure and launch availability
GET /api-docs/openapi.json is the public integrator projection generated at
docs/api/integrator/openapi.json, byte-for-byte. Launch annotations appear only
after the export owner publishes them; the runtime does not patch the artifact.
It never serves the full platform API as a fallback.
GET /api-docs/full/openapi.json requires a current platform-admin session with verified MFA, and is not available to an API key or a workspace
administrator without that platform grant. Its response is private, no-store.
Operations that are not launched are left out of the projection; they return when
they launch. Leaving one out does not grant or revoke runtime execution authority. UI
and SDK generation continues to consume committed full-spec files, not these HTTP routes.
What we promise
After launch, a change to the integrator API is additive. We may:- add an operation, an optional request field, a query parameter or a header;
- add a field to a response object;
- add a value to a response enum or a tagged union, or a new error code;
- add a status code to an operation, as long as it uses the error body below;
- make a request rule looser, for example by accepting a value that used to be refused.
- remove or rename an operation, field, value, header or code;
- change a field’s type or meaning, or a code’s HTTP status;
- make an optional request field required, or a request rule stricter;
- stop sending a response field we documented as always present.
sizeLimits.amountSide
(payExact/receiveExact) became sizeLimits.fixedAmount (sellAmount/buyAmount),
and the reasons receiveExactUnsupported, payChainInvalid and receiveChainInvalid
became buyAmountUnsupported, sellChainInvalid and buyChainInvalid.
What your client should do
- Ignore fields you don’t know. Response objects can gain fields at any time, so don’t validate them as closed.
- Treat unknown enum values as “other”. Every response enum and tagged union in the
spec is marked
x-extensible-enum. Handle the values you know, and give anything else a neutral fallback. For example, a quote source with an unknownstatusis a source you can’t use. Anextaction with an unknownkindis a step you can’t take automatically. - Handle unknown error codes by status. Act on the codes you know, and treat any other
code by its HTTP status class: retry a
429or503afterRetry-After, fix the request on any other4xx, and retry a5xxwith backoff. - Send only documented request fields. Request bodies stay strict: an unknown field
answers
400 INVALID_REQUESTnaming it, so a typo never passes silently. - Discover values instead of hard-coding them. Assets, chains and venues come from
GET /api/v1/assetsand from quote responses. A venue or asset can be added without any change to your code.
Order identity and minimum receive
Both order kinds carry one top-levelorderId, accepted by GET /api/v1/orders/{id}.
An Advanced order retains order.orderId; a Basic order retains operation.operationId.
The ordered by-quote recovery answer and order.updated.data carry that same canonical id.
These nested resource identifiers are not aliases for a second order resource.
Read each quote card’s minReceiveRequired boolean before ordering. When true, supply
minReceive; omission returns 400 MIN_RECEIVE_REQUIRED with details.field: "minReceive".
The flag describes existing admission, not a new restriction. A false flag does not grant
permission to order: readiness, expiry and the card’s next still apply.
The error body
Every refusal of an integrator operation, at every status, is one JSON object:erroris the stable code. Match on it, never onmessage.messageis for people and may change.requestIdequals theX-Request-Idresponse header. Quote it when you contact support.next, when present, is the way forward.details, when present, carries the code’s detail. Its keys are camelCase and it can gain keys. On429and some503answers,details.retryAfteris an ISO 8601 duration, and theRetry-Afterheader carries the same wait in seconds.
Retries and idempotency
POST /api/v1/orders takes an Idempotency-Key header, and API keys must send one. A
retry with the same key and the same body returns the first answer and never places a
second order. A different body under the same key answers 409 IDEMPOTENCY_CONFLICT.
Refusals are not stored, so after one, fix the request and send it again. The OpenAPI
description of the header has the full rules.
Cancellation
POST /orders/{id}/cancel accepts the GET order identifier and returns that same
envelope with status: "cancelled" and terminal: true. It is naturally idempotent
by order id: a repeated cancellation does not change the resource. Sessions need
PlaceOrder; keys need orders_write and Idempotency-Key. Only undispatched Basic
orders and deposit-first pay-ins still awaiting funds are eligible. A durable
claim or any progressed funding/delivery step refuses with 409
ORDER_NOT_CANCELLABLE; details.status is the current public lifecycle status.
Advanced orders are not cancellable through this endpoint. No cancellation calls
reach a venue, and any deposit already credited remains the workspace’s balance.
Order lifecycle
POST /api/v1/orders and GET /api/v1/orders/{id} return the same envelope:
{kind, status, terminal, statusUrl, quoteId?, requestId?, order|operation}.
kind is advancedTrade (nested order) or basicTrade (nested operation).
For Basic trades, {id} is the operation id. statusUrl is always
/api/v1/orders/{id}, including quote-order recovery and webhook data.
A workspace reader with ViewOrders or PlaceOrder can read a placed order;
API keys need orders_read. Unknown, foreign and unreadable orders share
404 ORDER_NOT_FOUND; a key missing the read scope receives 403 INSUFFICIENT_SCOPE.
States may be skipped; act on terminal, not on a particular sequence.
The public status is an open, camelCase enum; nested native status fields
retain their own vocabulary.
Terminality comes from the order’s durable product facts. Advanced escrow,
resource-lock and deferred orders can still owe a claim after native
settled;
route delivery and atomic venue orders need no separate claim. A failed fill
that can recover remains nonterminal. Basic orders require final execution
and final settlement/delivery, or no such legs. Review and unknown outcomes
never mean terminal.
For an uncertain or review-required order, next names the supported step:
wait carries an ISO 8601 retryAfter for another read; contact carries the
order reference for support. Neither authorizes another order or a funds movement.
Terminal orders carry no next. Unknown future action kinds are not actions a
client may take automatically.
Webhooks
A workspace webhook receives a signedPOST for each event it subscribes to.
order.updated is sent on every status change of an order placed in that workspace.
- Body.
{id, type, apiVersion, createdAt, data}. Fororder.updated,datais{orderId, kind, status, terminal, statusUrl}; read the order atstatusUrlfor its full state. If durable lifecycle facts are temporarily unavailable,terminalis omitted (never guessed) andstatusnames the observed event. Newtypevalues and new fields can appear without notice, so ignore what you don’t know. A breaking change to a body ships under a newapiVersion. - Signature. Standard Webhooks headers:
webhook-id,webhook-timestamp(Unix seconds), andwebhook-signature: v1,<base64>. Each signature is HMAC-SHA256 over"<webhook-id>.<webhook-timestamp>.<raw request body>", keyed with the base64-decoded suffix of the shown-oncewhsec_<base64>secret. Verify the raw body with a Standard Webhooks library and reject timestamps more than five minutes from now before processing the event. Rotation dual-signs with current and previous keys for 24 hours: space-separatedv1,<base64>values, either of which may verify. No legacy headers are sent. Existing sealed key bytes are unchanged. For an older hex secret, use"whsec_" + Buffer.from(oldSecret, "hex").toString("base64")in the reference library. - Delivery. At least once: a delivery that does not get a
2xxis retried after 5 s, 30 s, 10 min, 1 h, 6 h and 24 h. Deduplicate onwebhook-id, and on the body’sidfor the event itself. The delivery log and the test call are in the webhook endpoints.
Deprecation
When something has to go after launch:- The changelog announces it, with a removal date at least 6 months away. A security fix may need a shorter window, and the notice says why.
- Every affected response carries a
Deprecationheader (RFC 9745), aSunsetheader with the removal date (RFC 8594), and aLink: <…>; rel="deprecation"header pointing to the notice. The spec marks the operation or fielddeprecated: true. - The replacement ships before the notice, so you can move at any point in the window.
- On the sunset date, the old shape is removed.