Z Zise Developers 简体中文

Two execution paths: execute immediately, or lock a quote before executing. Settlement is atomic, with no intermediate state.

Internal Conversions


GET  /v1/exchange/pairs     ← 开了哪些方向
POST /v1/exchange/quotes    ← 报价(可选锁价)
POST /v1/exchange/orders    ← 成交

Configured by direction, not by currency pair

USDT → USD and USD → USDT are two independent records. Their fees need not be symmetric, and only one direction may be enabled. A direction absent from the list is not enabled.

⚠ Do not model this as a single currency pair with a direction flag. That would give you only one fee configuration,

whereas we can configure the two directions differently.


Choose an execution path to match your interface

A. Immediate execution without a locked quote

The quote is for display. When the order is placed, the amounts are recalculated using the price at execution time.


POST /v1/exchange/quotes   { from_asset, to_asset, from_amount }
POST /v1/exchange/orders   { from_asset, to_asset, from_amount }

This suits a single-screen interaction where the user enters an amount and confirms immediately.

⚠ Without a price lock, quote_ttl_sec is indicative only, not a commitment.

The response does not contain a quote_id field. The field is absent, rather than null,

so you can distinguish “no lock requested” from “lock requested but not provided”.

B. Execution with a locked quote

Lock the quote when the user needs a confirmation screen, such as “Exchange 1000 USDT for 999.5 USD?”:


POST /v1/exchange/quotes   { …, "lock": true }
   → { quote_id: "exc_…", quote_expires_at: "…", quote_ttl_sec: 120 }

POST /v1/exchange/orders   { "quote_id": "exc_…" }

The lock lasts quote_ttl_sec seconds, configured per pair with a database-enforced range of 30–600 seconds. Execution after that deadline is always rejected; the order is never silently executed at a new price.

⚠⚠ The quote_id is also the order ID. After execution, the id returned by POST /v1/exchange/orders

is identical character for character, and GET /v1/exchange/orders/{id} accepts it too.

This is useful when a request times out on the locked-quote path: if you already have the ID,

query it directly to find out whether the order completed.

⚠ Locking a quote creates a real order row in the quoted state. Do not lock every quote just for display:

doing so fills your order list with quoted rows that are never executed.

A locked quote also gives you a free option for N seconds, executable only when the price suits you.

We monitor for abuse.


Idempotency: the two endpoints have different requirements

EndpointIdempotency keyReason
POST /v1/exchange/quotesOptionalQuoting itself does not require idempotency. Providing a key deduplicates the request: a retry after timeout returns the same locked quote instead of creating two quotes when only one will be used.
POST /v1/exchange/ordersRequired; reuse the same key for retriesThis endpoint moves funds.

Execution is atomic

The conversion is complete when order creation returns. There is no “processing” state, and therefore no cancellation, execution retry or refund workflow.

⚠ A network timeout from POST /v1/exchange/orders means the outcome is unknown, not that execution failed.

Retry with the same idempotency key, or query quote_id directly on the locked-quote path.

Treating an unknown outcome as a failure can cause the same conversion to execute twice.


Which asset pays the fee?

The quote includes fee_amount and fee_asset. The latter identifies the asset from which the fee is charged.

⚠ Do not calculate the fee yourself as amount × rate. Charging on the source side and charging on the destination side

produce different results, and users will reconcile against the amounts you display.

Use fee_amount / fee_asset directly.

Amounts are fixed-point strings. The precision on each side comes from ledger_scale_from / ledger_scale_to. The two assets often have different precision, so do not reuse one scale for both.

Related endpoints