API Documentation
0xSwap Partner API
Backend integration reference for currencies, live quotes, automatic order creation, status lookup, client IP forwarding, aliases, and legacy route compatibility.
Base URL
https://zeroxswap.com
HTTPS only
JSON request bodies
Backend-only credentials
Overview
How integrations should work
01
Call from your backend
Partner credentials must never be exposed in browser code.
02
Pass clientIp
Send the real end-user IP when your server proxies order creation.
03
Store order data
Persist orderNumber, token, client_reference, and your idempotency key immediately after create-order succeeds.
Quick start
curl -X POST https://zeroxswap.com/api/partner/price \
-H "Content-Type: application/json" \
-H "X-API-Public-Key: $OXSWAP_PUBLIC_KEY" \
-H "X-API-Secret-Key: $OXSWAP_SECRET_KEY" \
-d '{
"fromCcy": "BTC",
"toCcy": "USDTTRC",
"direction": "from",
"amount": 0.015
}'Runtime contract
Order lifecycle and polling
01
Create the order
Call create-order from your backend and return the deposit address to the user.
02
Persist identifiers
Store orderNumber, token, client_reference, local idempotency key, local order id, and initial status before showing success.
03
Poll until terminal
Check order status after creation and continue polling while it is NEW, PENDING, EXCHANGE, or WITHDRAW.
04
Fulfill only DONE
Trigger idempotent fulfillment only for the exact DONE status. Route every unknown status to manual review.
Required polling pattern
const activeStatuses = new Set([
"NEW",
"PENDING",
"EXCHANGE",
"WITHDRAW"
]);
async function poll0xSwapOrder(orderNumber) {
let delayMs = 10000;
while (true) {
const response = await fetch(
`https://zeroxswap.com/api/partner/order/${orderNumber}`,
{ headers: partnerAuthHeaders }
);
const payload = await response.json();
const status = payload.data?.status;
await saveStatus(orderNumber, status, payload.data);
if (status === "DONE") {
await fulfillOnce(orderNumber, payload.data);
return payload.data;
}
if (status === "EXPIRED" || status === "REFUND") {
return payload.data;
}
if (!activeStatuses.has(status)) {
await queueForManualReview(orderNumber, status, payload.data);
return payload.data;
}
await sleep(delayMs);
delayMs = 30000;
}
}Do not stop at NEW
The create response is only the initial state. A single immediate status check can still return NEW because the user has not paid yet or the provider has not finished.
Keep polling
NEW, PENDING, EXCHANGE, WITHDRAW
Fulfill exactly once
DONE only
Stop without fulfillment
EXPIRED, REFUND
Manual review
COMPLETE, COMPLETED, or any unknown status
Preflight
Integration checklist
Credentials are used only from server-side code.
Real end-user IP is sent as clientIp for server-to-server creates.
Every create uses a unique X-Idempotency-Key; a lost response is retried with the exact same key and body.
orderNumber, token, client_reference, and the local idempotency key are stored before success is shown.
Status polling continues until a terminal status, not just once after create.
DONE fulfillment is idempotent and safe to retry.
EXPIRED, REFUND, COMPLETE, COMPLETED, and unknown statuses never trigger automatic fulfillment.
Network and 5xx errors retry without marking the local order failed.
Partner secrets, API keys, and raw signatures are never written to logs.
Support requests include orderNumber and local order id when available.
Security
Authentication
| Route family | Headers | Notes |
|---|---|---|
| Partner | X-API-Public-Key, X-API-Secret-Key | Recommended for new integrations. Keep both headers on your backend only. |
| Legacy v1 | X-API-KEY, X-API-SIGN | Signature is HMAC-SHA256 over the raw JSON body. |
Partner order ownership
An authenticated Partner API key can read only orders created under that same key. The provider performs this ownership check before any upstream status synchronization. A foreign order number and an unknown order number are deliberately indistinguishable and return the same response.
Foreign or missing order
HTTP/1.1 404 Not Found
{
"code": 3,
"error": "Order not found"
}Context
Client IP and timezone forwarding
For server-to-server create requests, include clientIp. Without it, the order will show the IP of your backend server.
Fallback extraction order is cf-connecting-ip, x-real-ip, then the first x-forwarded-for value.
Optional client time fields are clientLocalTime, clientTimezone, and clientUtcOffset.
/api/partner/cciesCurrencies
Returns enabled currencies in the same priority order used by the public exchange selector.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| none | - | - | No request body. |
Response fields
| Field | Type | Description |
|---|---|---|
| code | string | Currency code accepted by the API. |
| coin | string | Base asset symbol. |
| network | string | Network name, for example BTC, ETH, TRC20. |
| recv / send | boolean | Whether the asset can be deposited or paid out. |
| tag | string | null | Required memo/tag field name when the network needs one. |
| priority | number | Sorting priority used by the public selector. |
/api/partner/priceLive quote
Returns pair limits and an estimated route amount for direction = from or direction = to.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| fromCcy | string | yes | Asset the user sends. Alias-compatible. |
| toCcy | string | yes | Asset the user receives. Alias-compatible. |
| direction | "from" | "to" | yes | Use from for send amount, to for receive target. |
| amount | number | yes | Positive amount in the selected direction. |
Response fields
| Field | Type | Description |
|---|---|---|
| from / to | object | Resolved amounts, coin, network, limits, and rate. |
| errors | array | Provider or validation hints. Empty on success. |
| mode | string | Current route mode. |
Request example
{
"fromCcy": "BTC",
"toCcy": "ETH",
"direction": "from",
"amount": 0.01
}Response example
{
"code": 0,
"data": {
"from": {
"code": "BTC",
"coin": "BTC",
"network": "BTC",
"amount": "0.01",
"min": "0.0000127",
"max": "1000000",
"rate": 35.84
},
"to": {
"code": "ETH",
"coin": "ETH",
"network": "ETH",
"amount": "0.3584",
"min": "0.0022",
"max": "1000000",
"rate": 35.84
},
"errors": [],
"mode": "auto"
}
}/api/partner/create-orderCreate order
Creates an automatic provider-backed order and returns a deposit address plus tracking token.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| fromCcy | string | yes | Asset the user sends. Alias-compatible. |
| toCcy | string | yes | Asset the user receives. Alias-compatible. |
| direction | "from" | "to" | yes | Direction used for amount. |
| amount | number | yes | Positive amount. |
| toAddress | string | yes | Destination payout address. |
| toTag | string | null | conditional | Required when the selected payout currency has a tag field. |
| string | null | optional | End-user email for order context when available. | |
| clientIp | string | recommended | Real end-user IP for server-to-server integrations. |
| clientLocalTime | string | optional | Client local timestamp for support context. |
| clientTimezone | string | optional | IANA timezone, for example Europe/Berlin. |
| clientUtcOffset | string | optional | UTC offset, for example UTC +02:00. |
| X-Idempotency-Key | header string | strongly required | Required for Bulk PSN. Case-sensitive, 8-200 printable non-space ASCII characters; never reuse for another order. |
Response fields
| Field | Type | Description |
|---|---|---|
| orderNumber | string | Public order id. Automatic orders use C-prefixed ids. |
| token | string | Secret tracking token. Store it server-side. |
| client_reference | string | Stable provider reference returned for a reserved idempotent request and repeated by order status. |
| from.address | string | Deposit address for the user. |
| timeLeft | number | Remaining deposit window in seconds. |
Request example
curl -X POST https://zeroxswap.com/api/partner/create-order \
-H "Content-Type: application/json" \
-H "X-API-Public-Key: $OXSWAP_PUBLIC_KEY" \
-H "X-API-Secret-Key: $OXSWAP_SECRET_KEY" \
-H "X-Idempotency-Key: bulk-order-2026-0001" \
-d '{
"fromCcy": "BTC",
"toCcy": "USDTTRC",
"direction": "from",
"amount": 0.015,
"toAddress": "TRON_DESTINATION_ADDRESS",
"toTag": null,
"email": "buyer@example.com",
"clientIp": "203.0.113.14",
"clientLocalTime": "2026-05-20 00:33:25",
"clientTimezone": "Europe/Berlin",
"clientUtcOffset": "UTC +02:00"
}'Response example
{
"code": 0,
"data": {
"orderNumber": "C4M2Q9",
"token": "secure-order-token",
"status": "NEW",
"client_reference": "idem_11111111111111111111111111111111",
"from": {
"code": "BTC",
"amount": "0.015",
"address": "bc1q...",
"tag": null,
"txId": null
},
"to": {
"code": "USDTTRC",
"amount": "1543.42",
"address": "TRON_DESTINATION_ADDRESS",
"tag": null,
"txId": null
},
"timeLeft": 1200,
"timeExpiration": 1770000000
}
}/api/partner/order/:orderNumberOrder status
Returns the latest status only for an order owned by the authenticated API key. Foreign and unknown order numbers return the same 404 response.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| orderNumber | path string | yes | Public order number returned by create-order. |
Response fields
| Field | Type | Description |
|---|---|---|
| status | string | Current order status. |
| client_reference | string | omitted | Stable reference for orders created with X-Idempotency-Key; omitted for legacy orders. |
| from.txId | string | null | Deposit transaction hash when known. |
| to.txId | string | null | Payout transaction hash when known. |
| timeExpiration | number | Unix timestamp for order expiration. |
Response example
{
"code": 0,
"data": {
"orderNumber": "C4M2Q9",
"status": "EXCHANGE",
"client_reference": "idem_11111111111111111111111111111111",
"from": {
"code": "BTC",
"amount": "0.015",
"address": "bc1q...",
"txId": "deposit_tx_hash",
"confirmations": 2
},
"to": {
"code": "USDTTRC",
"amount": "1543.42",
"address": "TRON_DESTINATION_ADDRESS",
"txId": null
},
"timeLeft": 840,
"timeExpiration": 1770000000,
"createdAt": "2026-05-20T00:33:25.000Z"
}
}Create safety
Create-order idempotency
Required integration rule for Bulk PSN
Send one unique, case-sensitive X-Idempotency-Key for every logical create. If the response is lost, retry with the same authenticated API key, the exact same header value, and the exact same request fields. Never generate a new key merely because a request timed out.
| Header | Returned when | Contract |
|---|---|---|
| X-Idempotency-Reference | After reservation | Stable provider reference in idem_<32 lowercase hex> format. It is also returned as client_reference in JSON and, when an order was created, in later order-status responses. |
| X-Idempotent-Replay | Stored replay only | Literal true when the response is a replay. The original HTTP status and JSON body are returned unchanged. |
| Retry-After | Processing 409 only | Value is 2 seconds. Retry the exact same request and key; do not create a replacement request. |
| error_type | HTTP status | Required client action |
|---|---|---|
| IDEMPOTENCY_KEY_REUSED | 409 | The same scoped key has a different canonical request. Do not retry it with this body; investigate key reuse or use a new key only for a genuinely new logical order. |
| IDEMPOTENCY_REQUEST_IN_PROGRESS | 409 | The matching request is unresolved. Wait at least Retry-After seconds, then retry the exact same key and body. Do not start another order. |
Key format and scope
8-200 printable ASCII characters from ! through ~, with no spaces. Values are case-sensitive and scoped by authenticated apiKeyId plus key. The raw key is not persisted; the provider stores its SHA-256 digest.
Canonical request
The request hash covers every accepted create field, including submitted and resolved currency codes, direction, normalized numeric amount, destination address/tag, email, and client IP/time/timezone fields.
Reservation boundary
Malformed JSON, missing required fields, an invalid idempotency-key format, and a non-finite or non-positive amount are rejected before reservation. They have no client_reference and are not stored for replay. Once reserved, any completed validation, business, success, or server outcome is stored and replayed.
Same key and same request
No duplicate order is created. A completed request replays the stored original status and body, including stored business or server errors. An in-flight request waits briefly and replays if it completes.
Same key and different request
Returns deterministic HTTP 409 with code 4 and the original client_reference. Changing any canonical request field requires a new key for a genuinely new logical order.
Still processing
If the first execution is still unresolved after the wait window, the API returns HTTP 409, code 4, Retry-After: 2, and the stable client_reference. Keep retrying the same key/body or reconcile manually.
Legacy requests without the header
Remain accepted for backward compatibility but have legacy create behavior: each call may create a new order. Bulk PSN must not use this mode. Treat idempotency keys as permanently reserved and never reuse them.
Stored success replay
HTTP/1.1 200 OK
X-Idempotency-Reference: idem_11111111111111111111111111111111
X-Idempotent-Replay: true
{
"code": 0,
"data": {
"orderNumber": "C4M2Q9",
"token": "secure-order-token",
"status": "NEW",
"client_reference": "idem_11111111111111111111111111111111",
"from": {
"code": "BTC",
"amount": "0.015",
"address": "bc1q...",
"tag": null,
"txId": null
},
"to": {
"code": "USDTTRC",
"amount": "1543.42",
"address": "TRON_DESTINATION_ADDRESS",
"tag": null,
"txId": null
},
"timeLeft": 1200,
"timeExpiration": 1770000000
}
}Different request conflict
HTTP/1.1 409 Conflict
X-Idempotency-Reference: idem_11111111111111111111111111111111
{
"code": 4,
"error_type": "IDEMPOTENCY_KEY_REUSED",
"error": "Idempotency key was already used with a different create-order request",
"client_reference": "idem_11111111111111111111111111111111"
}Still processing
HTTP/1.1 409 Conflict
X-Idempotency-Reference: idem_11111111111111111111111111111111
Retry-After: 2
{
"code": 4,
"error_type": "IDEMPOTENCY_REQUEST_IN_PROGRESS",
"error": "Idempotency request is still processing; retry the same request with the same key",
"client_reference": "idem_11111111111111111111111111111111"
}Runtime
Errors, statuses, and aliases
Error codes
| Code | Meaning |
|---|---|
| 0 | Success. Read the data field. |
| 1 | Validation, disabled currency, min amount, invalid address, or provider business-rule error. |
| 2 | Authentication failed. Check API credentials and headers. |
| 3 | Order was not found. |
| 4 | Idempotency conflict or a matching create request is still processing. Read the HTTP 409 body and headers. |
| 5 | Unexpected server or upstream provider error. Retry safely or contact support with request context. |
Order statuses
| Status | Meaning |
|---|---|
| NEW | Order created and waiting for deposit. |
| PENDING | Deposit detected or confirming. |
| EXCHANGE | Swap is being processed. |
| WITHDRAW | Payout transaction is being sent. |
| DONE | Order completed successfully. |
| EXPIRED | Deposit window expired or the order cannot continue. |
| REFUND | Refund handling is required or already in progress. |
Alias compatibility
| Accepted input | Internal code |
|---|---|
| USDTTRC | USDTTRC20 |
| USDTARBITRUM | USDTARB |
| USDTAVAX | USDTAVAXC |
Business error
{
"code": 1,
"error": "Minimum exchange amount: 0.0000127 BTC"
}Compatibility
Legacy v1 routes
Keep these routes only for existing integrations that already rely on signed v1 requests. New partners should use /api/partner/*.
/api/v1/cciesLegacy currency list with X-API-KEY / X-API-SIGN auth.
/api/v1/priceLegacy quote endpoint. Envelope: code / msg / data.
/api/v1/createLegacy create endpoint. Supports clientIp body override.
/api/v1/orderLegacy order lookup by id and token in request body.
Need access or integration review?
Use support for API credentials, rollout questions, or contract verification.