Integrator Quote & Create Order
This guide is for Any2Any aggregator integrators. It covers these endpoints:
| # | Endpoint | Purpose |
|---|---|---|
| 1 | GET /api/v2/quote/stream | Real-time quotes returned as an SSE stream |
| 2 | POST /api/v1/integrator/orders | Create an integrator order from a selected quote |
| 3 | POST /api/v1/integrator/orders/:orderNo/request_refund | Request a refund for an order that belongs to the integrator |
Passing an integrator API key when creating an order identifies the integrator and records the commission context. It does not affect the normal user order flow.
0. Common Conventions
0.1 Base URL
https://<aggregator-api-host>
0.2 Authentication
| Endpoint | Authentication |
|---|---|
/api/v2/quote/stream | No authentication required |
/api/v1/integrator/orders | X-Integrator-Api-Key: <api_key> (recommended)or Authorization: ApiKey <api_key> |
/api/v1/integrator/orders/:orderNo/request_refund | X-Integrator-Api-Key: <api_key> |
API keys are issued by the Any2Any backend and can be managed in the integrator portal. Each key has its own
fee_rate(integrator commission rate, 0.01% to 10.00%). The order stores a snapshot of the rate attached to the API key used at creation time. For API-key orders, the user-facing fee rate is the B2B tier fee configured by the backend plus the key'sfee_rate.
0.3 Response Envelope
Non-SSE endpoints return:
{
"code": 0,
"msg": "ok",
"data": {}
}
code = 0means success. HTTP status codes:200success;400invalid parameters;401invalid API key;403integrator disabled;500/502server or upstream error.
0.4 Amounts and Rates
- Amount fields such as
amount_in,*_estimated_out_*, andmin_acceptable_out_amountare integer strings in the token's smallest unit. For example,"1000000"means 1 USDT for a 6-decimal token. slippageis a decimal percentage in(0, 1). For example,0.01means 1%.- Rate fields such as
channel_fee,fee_rate, andplatform_fee_rateare percentages. For example,0.3means 0.3%.
1. Quote: GET /api/v2/quote/stream
1.1 Overview
Quotes are returned through Server-Sent Events (SSE). The server queries upstream channels and pushes each channel quote as soon as it is available with event: channel. The stream ends with event: done.
Streaming quotes provide:
- Faster first quote because the client does not wait for the slowest channel.
- Cancelable requests by closing the connection.
- Error isolation because a single channel failure is sent as
event: errorand does not stop other channels.
1.2 Request
GET /api/v2/quote/stream?src_token_id=&dst_token_id=&amount_in=&slippage=&promo_code=&channel_id= HTTP/1.1
Accept: text/event-stream
| Query parameter | Type | Required | Description |
|---|---|---|---|
src_token_id | int | Yes | Source token ID in Any2Any |
dst_token_id | int | Yes | Destination token ID |
amount_in | string | Yes | Source token amount as a smallest-unit string |
slippage | number | Yes | Slippage in (0, 1), for example 0.01 = 1% |
promo_code | string | No | Platform promo code, if any |
channel_id | int | No | Quote only one channel. If omitted, all enabled channels are quoted |
The same path also supports
POSTwith the same fields in a JSON body, which is useful for numeric fields such aschannel_id.
1.3 Response
200 OK with Content-Type: text/event-stream; charset=utf-8. Each event follows the SSE format:
event: <event-name>
data: <json-payload>
1.3.1 Event Types
| event | When it is sent | data |
|---|---|---|
channel | When a channel quote is available | Channel quote object |
error | When one channel quote fails | { channel_id?: number, msg: string } |
done | When all quotes are finished or the server closes the stream | {} |
channel event data:
{
"channel_id": 101,
"name": "Channel A",
"symbol": "USDT",
"channel_icon": "https://.../logo.png",
"estimated_out_str": "999500000",
"estimated_out_usd": "999.500000",
"channel_fee": 0.3,
"channel_fee_original": 0.3,
"promo_applied": false,
"discount_pct": null
}
| Field | Description |
|---|---|
channel_id | Channel ID. Return this unchanged when creating the order |
name | Channel name |
symbol | Destination token symbol |
channel_icon | Channel icon URL |
estimated_out_str | Estimated received amount after fees, in the destination token's smallest unit. Use this directly as main_channel_estimated_out_amount |
estimated_out_usd | Estimated USD value for display |
channel_fee | Effective percentage fee, after promo discount if applied |
channel_fee_original | Original base percentage fee |
promo_applied | Whether a promo discount was applied |
discount_pct | Promo discount percentage, or null |
1.4 Failure Responses Before the Stream Starts
If the SSE stream has not started yet, errors are returned as normal JSON envelopes:
| HTTP | Scenario |
|---|---|
| 400 | Missing required parameters such as src_token_id/dst_token_id required |
| 400 | token not found / chain not found / channel not found |
| 400 | no available channels |
| 502 | quote service timed out / quote service unavailable |
1.5 Example
Request:
curl -N \
"https://<host>/api/v2/quote/stream?src_token_id=1&dst_token_id=2&amount_in=1000000&slippage=0.01"
Response excerpt:
event: channel
data: {"channel_id":101,"name":"Channel A","symbol":"USDT","channel_icon":"https://.../a.png","estimated_out_str":"999500000","estimated_out_usd":"999.500000","channel_fee":0.3,"channel_fee_original":0.3,"promo_applied":false,"discount_pct":null}
event: channel
data: {"channel_id":102,"name":"Channel B","symbol":"USDT","channel_icon":"https://.../b.png","estimated_out_str":"999200000","estimated_out_usd":"999.200000","channel_fee":0.3,"channel_fee_original":0.3,"promo_applied":false,"discount_pct":null}
event: error
data: {"channel_id":103,"msg":"upstream timeout"}
event: done
data: {}
1.6 Browser / Node.js Clients
EventSource
const url = new URL('https://<host>/api/v2/quote/stream');
url.searchParams.set('src_token_id', '1');
url.searchParams.set('dst_token_id', '2');
url.searchParams.set('amount_in', '1000000');
url.searchParams.set('slippage', '0.01');
const es = new EventSource(url.toString());
const channels = [];
es.addEventListener('channel', (event) => {
channels.push(JSON.parse(event.data));
});
es.addEventListener('error', (event) => {
// Channel-level errors include data; network-level errors may not.
console.warn('channel error', event.data);
});
es.addEventListener('done', () => {
es.close();
const best = channels.reduce((a, b) =>
BigInt(b.estimated_out_str) > BigInt(a.estimated_out_str) ? b : a
);
console.log('best quote', best);
});
Fetch + Streams
const res = await fetch(url, { headers: { Accept: 'text/event-stream' } });
if (!res.ok || !res.body) throw new Error(`HTTP ${res.status}`);
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
for (;;) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
let idx;
while ((idx = buffer.indexOf('\n\n')) >= 0) {
const block = buffer.slice(0, idx);
buffer = buffer.slice(idx + 2);
let event = 'message';
let data = '';
for (const line of block.split('\n')) {
if (line.startsWith('event:')) event = line.slice(6).trim();
else if (line.startsWith('data:')) data = line.slice(5).trim();
}
if (event === 'channel') handleQuote(JSON.parse(data));
if (event === 'done') return;
}
}
1.7 Notes
- The default upstream timeout is 60 seconds. Timeout errors are sent as
event: error, followed byevent: done. - Use one connection per quote request. If a quote needs to be refreshed, close the old connection first.
estimated_out_stris the net received amount after channel fees, platform fees, and promo discounts.
2. Integrator Order: POST /api/v1/integrator/orders
2.1 Overview
After selecting a quote, the integrator calls this endpoint as a server-side client to create an order. The request body is the same as the public POST /api/v1/orders endpoint. The extra X-Integrator-Api-Key header identifies the integrator and records commission information.
After the order is created, the server returns a deposit address (deposit_address) and an expiration time (expire_at). The user transfers the amount_in source token amount to deposit_address before expire_at, and the order is then executed automatically to deliver funds to dst_address.
2.2 Authentication
Send the integrator API key in one of these forms:
X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111
or:
Authorization: ApiKey aaaabbbbccccddddeeeeffff00001111
Fallback behavior: if no API key is sent, the key does not exist, the key is disabled, or the integrator is disabled, this endpoint does not return
401/403. It falls back to normal order creation without integrator binding or commission. After launch, integrators should verify thatintegrator_idis present in order details to confirm the commission flow.
2.3 Request
POST /api/v1/integrator/orders HTTP/1.1
Content-Type: application/json
X-Integrator-Api-Key: <api_key>
Request body:
{
"src_token_id": 1,
"dst_token_id": 2,
"amount_in": "1000000",
"dst_address": "0xRecipientAddress...",
"chosen_channel_id": 101,
"slippage": 0.01,
"main_channel_estimated_out_amount": "999500000",
"min_acceptable_out_amount": "989505000",
"channels": [
{
"channel_id": 101,
"estimated_out_str": "999500000",
"name": "Channel A",
"symbol": "USDT",
"channel_icon": "https://.../a.png",
"estimated_out_usd": "999.500000",
"channel_fee": 0.3
},
{
"channel_id": 102,
"estimated_out_str": "999200000",
"name": "Channel B"
}
],
"client_order_id": "your-internal-order-no-001",
"promo_code": "WELCOME"
}
2.3.1 Required Fields
| Field | Type | Description |
|---|---|---|
src_token_id | int | Source token ID, same as the quote request |
dst_token_id | int | Destination token ID |
amount_in | string | Source token amount as a smallest-unit string |
dst_address | string | User receiving address. It must match the destination chain format |
chosen_channel_id | int | Selected channel_id from the quote result |
slippage | number | Slippage, same as the quote request |
main_channel_estimated_out_amount | string | The selected channel's estimated_out_str |
min_acceptable_out_amount | string | Minimum acceptable received amount. Usually main_channel_estimated_out_amount × (1 - slippage) |
channels | array | All channel quote results received in this quote request, used for validation and audit |
channels[] object:
| Field | Type | Required | Description |
|---|---|---|---|
channel_id | int | Yes | Channel ID |
estimated_out_str | string | Yes | Estimated received amount for this channel |
name / symbol / channel_icon / estimated_out_usd / channel_fee | - | No | Optional fields copied from the quote result |
2.3.2 Optional Fields
| Field | Type | Description |
|---|---|---|
client_order_id | string | Integrator-side business order number. Unique per integrator and used for idempotency |
client_id | string | Integrator-defined identifier for logs and risk controls |
partner_app_id | int | Partner app ID, if any |
user_id | string | Integrator-side end-user ID for audit and risk controls |
invite_code | string | Invite code, if any |
promo_code | string | Platform promo code, same as the quote request |
is_use_wallet | boolean | Whether to use the integrator internal wallet flow. Defaults to false |
fee_rate | number | Only pass this if permitted by contract. Usually omit it and settle by the API key rate |
min_quote_amount | string | Reserved minimum quote amount check |
2.4 Response
Success 200 OK:
{
"code": 0,
"msg": "ok",
"data": {
"order_id": 1234567,
"order_no": "OD2026052510000001",
"deposit_address": "0xDepositAddressGeneratedByPlatform",
"expire_at": "2026-05-25T10:30:00Z",
"fee_platform": "0",
"fee_partner": "0",
"fee_integrator": "3000",
"fee_platform_original": "3000",
"platform_fee_rate": "0",
"partner_fee_rate": "0",
"integrator_fee_rate": "0.0100"
}
}
Field Reference
| Field | Type | Description |
|---|---|---|
order_id | int | Platform order ID |
order_no | string | Platform order number. Store it on the integrator side |
deposit_address | string | Deposit address. Transfer the amount_in source token amount to this address to trigger execution |
expire_at | string | ISO 8601 UTC. The deposit must be completed before this time |
fee_platform | string | Platform fee snapshot in the source token's smallest unit |
fee_partner | string | Partner fee. Always "0" for integrator orders |
fee_integrator | string | Commission amount for this integrator order |
fee_platform_original | string | Total channel fee before deducting integrator commission |
platform_fee_rate / partner_fee_rate / integrator_fee_rate | string | Fee rate snapshots |
Idempotency: if
client_order_idalready exists and matches an existing order, the response may contain onlyorder_id,order_no,deposit_address, andexpire_at. Call the order detail endpoint if full fee fields are needed.
2.5 Error Responses
Integrator order creation reuses the validation logic of POST /api/v1/orders. Common errors:
| HTTP | code | msg | Description |
|---|---|---|---|
| 400 | 400 | invalid request body | JSON parse failed |
| 400 | 400 | src_token_id/dst_token_id required | Required field missing |
| 400 | 400 | token not found / chain not found | Internal ID does not exist |
| 400 | 400 | dst_address invalid | Destination address format does not match the destination chain |
| 400 | 400 | chosen_channel_id not in channels | chosen_channel_id is not present in channels[] |
| 400 | 400 | slippage out of range | Slippage is not in (0, 1) |
| 400 | 400 | min_acceptable_out_amount too low | Below platform minimum output requirements |
| 400 | 400 | quote amount mismatch | Server-side quote check exceeds tolerance. Refresh the quote and retry |
| 400 | 400 | amount_in below minimum | Below minimum order amount |
| 400 | 400 | duplicate client_order_id | The same client_order_id is already used by a different order |
| 400 | 400 | split orders are not supported for integrator orders | Integrator orders only support one channel |
2.5.1 Single-Channel Orders
Integrator orders only support one channel:
- Do not pass
split_count, and do not setis_risktotrue. Passingsplit_count ≥ 2oris_risk=truereturnssplit orders are not supported for integrator orders. - The order executes through the single channel selected by
chosen_channel_id. fee_platform_originalis the total channel fee before commission deduction,fee_platformis the platform's actual retained amount after deducting commission, andfee_integratoris the commission amount.
2.6 Example
curl -X POST "https://<host>/api/v1/integrator/orders" \
-H "Content-Type: application/json" \
-H "X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111" \
-d '{
"src_token_id": 1,
"dst_token_id": 2,
"amount_in": "1000000",
"dst_address": "0xRecipient...",
"chosen_channel_id": 101,
"slippage": 0.01,
"main_channel_estimated_out_amount": "999500000",
"min_acceptable_out_amount": "989505000",
"channels": [
{ "channel_id": 101, "estimated_out_str": "999500000" },
{ "channel_id": 102, "estimated_out_str": "999200000" }
],
"client_order_id": "biz-order-20260525-001"
}'
3. Integrator Refund: POST /api/v1/integrator/orders/:orderNo/request_refund
3.1 Overview
When an integrator order enters a refundable state, such as deposit expired without execution, cross-chain failure, or slippage failure, the integrator can call this endpoint server-side to specify a refund address and request a refund for an order that belongs to the integrator.
This endpoint follows the public POST /api/v1/orders/:orderNo/request_refund flow with two differences:
- It requires integrator API key authentication and only allows refunds for orders that belong to that integrator.
- Refunds return principal only when the order did not succeed. Commission is settled only after a successful swap.
3.2 Authentication
This refund endpoint supports only the X-Integrator-Api-Key header. It does not support the Authorization: ApiKey form:
X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111
Unlike order creation, the refund endpoint does not fall back. Missing or invalid API keys return
401; disabled integrators return403; orders that do not belong to the integrator return403.
3.3 Request
POST /api/v1/integrator/orders/{orderNo}/request_refund HTTP/1.1
Content-Type: application/json
X-Integrator-Api-Key: <api_key>
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
orderNo | path | string | Yes | order_no returned by order creation |
refund_address | body | string | Yes | Refund receiving address. It must match the source chain format |
Request body:
{
"refund_address": "0xSourceChainRefundAddress..."
}
3.4 Refundable States
The server chooses the refund handling flow by current order state:
| Order state | Handling | Preconditions |
|---|---|---|
Depositing / CancelledNoDeposit (deposit expired) | Enter manual review (ManualReview) | Must be after expire_at and before auto_cancel_at |
RefundAddressPending / ChainFailed (On-chain Failed) / SlippageFailed (Swap Failed) | Automatically refund principal after failure, minus gas | Execution details must exist; otherwise manual confirmation is required |
Final states such as
Completed,Refunded, settledCancelledNoDeposit, andFailedNoRefundcannot be refunded again.
3.5 Response
Success 200 OK:
{
"code": 0,
"msg": "ok",
"data": "ok"
}
Idempotency: if the order already has a refund record and the
refund_addressis the same, repeated calls return success. If the newrefund_addressdiffers from the existing record, the endpoint returnsrefund_address_cannot_be_changed.
3.6 Commission After Refund
Integrator orders are single-channel orders. Commission is written to integrator_commission_ledger only after the order is settled and the swap succeeds. Therefore:
- Swap succeeds: the order is final, not refundable, and the integrator receives
fee_integrator. - Swap fails or deposit expires: no commission is written during settlement; the principal is refunded; the integrator receives no commission.
- The refund flow does not roll back or modify already settled commissions, including commissions in
WithdrawRequestedorWithdrawnstates.
3.7 Error Responses
| HTTP | msg | Description |
|---|---|---|
| 401 | unauthorized | Missing or invalid API key |
| 403 | integrator disabled | Integrator is disabled |
| 403 | order does not belong to integrator | The order does not belong to this integrator |
| 400 | order no required | Missing orderNo path parameter |
| 400 | refund_address required | Missing refund address |
| 400 | order not found | Order does not exist |
| 400 | order_already_finalized | Order is final and cannot be refunded |
| 400 | order_not_in_refundable_state | Current state does not support refunds |
| 400 | order_not_expired_yet | Deposit has not expired yet |
| 400 | order_auto_cancel_window_passed | Auto-cancel window has passed |
| 400 | refund_requires_manual_confirmation | Missing execution details; manual confirmation required |
| 400 | no_amount_to_refund | No refundable amount |
| 400 | refund_address_cannot_be_changed | Existing refund address cannot be changed |
| 400 | order_state_changed_retry | Order state changed concurrently. Retry the request |
3.8 Example
curl -X POST "https://<host>/api/v1/integrator/orders/OD2026052510000001/request_refund" \
-H "Content-Type: application/json" \
-H "X-Integrator-Api-Key: aaaabbbbccccddddeeeeffff00001111" \
-d '{
"refund_address": "0xSourceChainRefundAddress..."
}'
4. End-to-End Flow
┌──────────────────┐
│ Integrator Server │
└────────┬─────────┘
│ ① GET /api/v2/quote/stream (SSE)
│ src_token_id / dst_token_id / amount_in / slippage
▼
┌──────────────────┐
│ Any2Any API │ ──► event: channel × N
└────────┬─────────┘ event: done
│
│ ② Select the best quote (largest estimated_out_str)
│ Calculate min_acceptable_out_amount = floor(estimated_out_str * (1 - slippage))
│
│ ③ POST /api/v1/integrator/orders
│ X-Integrator-Api-Key: <api_key>
│ body: { ..., chosen_channel_id, channels[], main_channel_estimated_out_amount, min_acceptable_out_amount }
▼
┌──────────────────┐
│ Any2Any API │ ──► { order_no, deposit_address, expire_at, fee_integrator, ... }
└────────┬─────────┘
│
│ ④ Show deposit_address to the user or transfer from the integrator wallet
│
│ ⑤ User deposits before expire_at
▼
┌────────────┐
│ Settlement │ ──► Cross-chain delivery to dst_address
└────────────┘
│
│ ⑥ After the order reaches a final state, commission enters the integrator portal balance
▼
Integrator Portal → Withdrawal
5. FAQ
Q1: estimated_out_str already includes channel fees. Should I deduct fees again when creating the order?
No. estimated_out_str is the display amount after channel fees, platform fees, and promo discounts. Calculate min_acceptable_out_amount directly from it with (1 - slippage).
Q2: Must channels[] include all quote results?
It is recommended to pass every channel event object from the quote request. At minimum, include the selected chosen_channel_id. The server uses this list for channel and slippage validation.
Q3: What should I do if an API key leaks?
Delete the key in the integrator portal immediately and create a new one. Deleted keys become invalid immediately. Existing orders are not affected because they already store fee snapshots.
Q4: Do I need to quote again every time?
Yes. estimated_out_str is based on real-time upstream quotes. Order creation performs a server-side quote check, and stale quotes can return quote amount mismatch. Keep the time between quote and order creation under 10 seconds when possible.
Q5: When does an order expire?
Use the expire_at field. The deposit must be completed before that time. Otherwise the order is canceled and no commission is generated.
Q6: How is commission credited?
After the order reaches the settled final state, the fee_integrator amount enters the integrator's available balance in the integrator portal based on the source token price snapshot at order creation time.
6. Related Links
- Integrator portal, API key management, commission, and withdrawal guide: Integrator API Integration Guide
- Postman collection: the
Quotes,Orders, andIntegrator Portal (Public)folders inaggregator-api.postman_collection.json