Skip to content
KOTOVADOCS
Launch App

Orders

View as Markdown

An order is created from a quote and is placed at a source that quote named: its own source or, when that source cannot take the order, one of its fallback sources. Your user then sends the deposit to an address that belongs to the order’s source, and you read the order to follow it until it is final.

POST /orders creates the order at the source of a quote and answers 201 with the order. When that source cannot take the order, it is placed at the next best source of the quote instead (see Next best source). It needs your API key and has a weight of 50. The body carries the quote_id and where the funds go; the pair, the amount and the sources all come from the quote. Before you call it, your user must have seen Kotova X as the provider and the notices of the quote’s source and fallback_sources: see Display requirements.

Send an Idempotency-Key header with every request, and give the request a generous timeout: the order is created at the source while you wait, so the answer can take several seconds, and up to 90 seconds when other sources are tried.

A sandbox is not available yet. Every order created through the API, including one created from a console on this page, is a real order at a source.

Terminal window
curl https://api.kotova.io/x/v1/orders \
-H "Authorization: Bearer $KOTOVA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3f1c9a52-7d0e-4b8a-9c61-2e5f4a7b8d90" \
-d '{
"quote_id": "q_5mJc2Vq9XkT3nR8bYw1LdP",
"to_address": "REPLACE_WITH_RECIPIENT_TRX_ADDRESS",
"refund_address": "REPLACE_WITH_REFUND_BTC_ADDRESS",
"partner_order_id": "shop-order-1042",
"lang": "en"
}'

The body is JSON (Content-Type: application/json). The endpoint takes no query parameters.

Field Required Rules
quote_id Yes The quote_id of a quote from POST /quote, used before its expires_at. Single use.
to_address Yes Where your user receives the funds, on the quote’s to network.
to_memo No The memo or destination tag of the recipient. Only on networks that support one (the quote’s requirements.to_memo is optional).
refund_address Recommended; required when the quote says so Where a refund goes, on the quote’s from network. Required when the quote’s requirements.refund_address is required, and recommended for every order: where the source takes it with the order, it goes to the source then, so a refund needs no extra step, and the quote’s fallback sources that need one stay eligible. See Refund addresses.
refund_memo No The memo or destination tag of the refund address, when that address needs one. Needs a refund_address.
partner_order_id No Your own reference for the order: 1 to 64 characters of A–Z, a–z, 0–9, ., _, : and -. It is returned on the order, and you can filter your order list by it. It does not have to be unique.
lang No The language of the source notice in the response: en (the default), de, it, es, fr or ru. Any other value falls back to en with an unsupported_lang warning.
retry_next_best_source No true (the default): when the quote’s source cannot take the order, it is placed at the next of the quote’s fallback_sources that can. false: only the quote’s source is tried. A JSON boolean: omit the field for the default. null or any other value is a 400 invalid_request with details.field: retry_next_best_source and details.reason: invalid_type. See Next best source.

The quote fixes the pair, the amount, the rate type and the source filters. Sending any of from, to, amount, amount_type, rate_type, exclude_sources, freeze_protection, can_provide_refund_address or end_user_ip is a 400 invalid_request with details.reason: not_allowed_with_quote_id (or, without a quote_id, a 400 saying that quote_id is required). Any other field the API does not know is a 400 with details.reason: unknown_field.

An order that was created answers 201, with a Location header (/x/v1/orders/X-7KQ2ZD) and the order object. GET /orders/{id} returns the same object. Shortened here: provider and source carry the full blocks described below.

{
"id": "X-7KQ2ZD",
"status": "waiting_for_deposit",
"is_final": false,
"status_detail": null,
"rate_type": "variable",
"created_at": "2026-11-10T10:15:39Z",
"updated_at": "2026-11-10T10:15:39Z",
"expires_at": "2026-11-10T11:15:39Z",
"completed_at": null,
"from": { "currency": "BTC", "network": "btc", "amount": "0.05", "amount_received": null },
"to": {
"currency": "USDT",
"network": "trx",
"amount": "3389.8",
"amount_sent": null,
"address": "REPLACE_WITH_RECIPIENT_TRX_ADDRESS",
"memo": null
},
"deposit": {
"address": "bc1q9h7garjv2gx0s4kd3mzt6v8wcl5ul3xzq0p2ne",
"memo": null,
"memo_label": null,
"amount": "0.05"
},
"refund": { "address": "REPLACE_WITH_REFUND_BTC_ADDRESS", "memo": null, "forwarded": true },
"transactions": { "deposit": null, "payout": null, "refund": null },
"order_url": "https://app.kotova.io/x/X-7KQ2ZD",
"estimated_duration_seconds": 1500,
"quote_id": "q_5mJc2Vq9XkT3nR8bYw1LdP",
"partner_order_id": "shop-order-1042",
"provider": { "name": "Kotova X", "website_url": "https://app.kotova.io" },
"source": {
"slug": "sideshift",
"name": "SideShift",
"order_id": "7f3c1e9a2b5d4c8e",
"tracking_url": "https://sideshift.ai/orders/7f3c1e9a2b5d4c8e"
},
"warnings": []
}

The order object, field by field. Amounts follow the rounding rules of quotes: what your user sends is rounded up, what they receive is rounded down. Ignore fields you do not know: new ones can be added at any time.

Field What it is
id The public order id: X- followed by 6 characters of A–Z and 0–9. Quote it to support.
status One of nine statuses. See Order statuses.
is_final true once nothing will change any more. Read the order until it is true.
status_detail Why an order needs attention: set for action_required, and for refunding when the reason is known. Otherwise null. See Status detail.
rate_type fixed or variable.
created_at, updated_at When the order was created and when it last changed.
expires_at The deposit deadline, the one the order page counts down. null when unknown. See Expiry and late deposits.
completed_at Set only once the status is completed.
from.currency, from.network What your user sends.
from.amount The order amount: binding for a fixed rate, indicative for a variable one.
from.amount_received The deposit as the source saw it, once there is one.
to.currency, to.network What your user receives.
to.amount The amount to receive: binding for a fixed rate, indicative for a variable one.
to.amount_sent The payout as sent, once there is one.
to.address, to.memo Where the payout goes, as you sent them.
deposit.address Where your user sends the deposit: an address of the source.
deposit.amount How much to send.
deposit.memo When set, your user must send it with the deposit. Show it prominently.
deposit.memo_label What to call that memo, such as Memo or Destination Tag. Set together with deposit.memo.
refund.address, refund.memo Where a refund goes, on the from network. null when there is none, and after the source refused the address (status_detail.reason: refund_address_rejected).
refund.forwarded true when the refund address went to the source with the order. false when there is none, or when it is kept with the order only. See Refund addresses.
transactions.deposit null until the deposit is seen. Then hash, explorer_url, confirmations and required_confirmations: how many confirmations the source needs before it treats the deposit as final, null when unknown.
transactions.payout null until the payout is sent. Then hash, explorer_url and confirmations.
transactions.refund The same for a refund.
order_url The order page for your user; optional to show. See The order page.
estimated_duration_seconds A typical time from deposit to payout, or null when there is no estimate.
quote_id The quote the order was created from.
partner_order_id Your reference, as you sent it, or null.
provider Kotova X, the provider of the service: name, icon, icon_png and website_url.
source The source that executes the order: slug, name, kind, icon, terms_url and notice, as in a quote, plus order_id (the source’s own id for the order, for support with the source) and tracking_url (the source’s tracking page for the order, null when there is none Kotova can link to). After a fallback, it is not the quote’s source.
warnings Non-fatal notes, usually empty. source_fallback when the order was placed at one of the quote’s fallback sources, and refund_address_on_file when the refund address is kept with the order only: in the 201 and its Idempotency-Key replays, not from GET /orders/{id}.

In a transaction, explorer_url is null when no block explorer is known for the network, and confirmations is null when the count is unknown.

A source can be unable to take an order at the moment it is created: it is unavailable, it refuses the order, or it cannot create it right now. By default (retry_next_best_source omitted or true), the order is then placed at the first of the quote’s fallback_sources that can take it, in their order, and the answer is that order alone.

  • What is tried. The quote’s source first, then the quote’s fallback_sources in order: at most three sources, all named in the quote. A fallback source whose requirements.refund_address is required is skipped when the order has no refund_address. No further source is asked once the request has run for 40 seconds, and a fallback source is skipped earlier when its create could not finish in time; both are deadline in details.sources_tried.
  • What is never retried elsewhere. An address or memo a source refuses (422 invalid_address with details.reason: rejected_by_source), also when the source says so only in its message (it names the field, or says the address or memo is invalid, missing or similar), and an unknown outcome (502 source_error with details.reason: outcome_unknown) end the request at that source: the address is the problem, or the order may exist there. A 422 invalid_address from one of the quote’s fallback sources carries details.sources_tried, as does a 500 internal_error from one of them; a 502 outcome_unknown carries it whenever retry_next_best_source is on.
  • What changes. The order’s source names the source that created it, and its amounts are that source’s: they can differ from the quote’s. Show your user the order’s source and amounts.
  • How you know. The 201 carries a source_fallback warning, with the quote’s source in quoted_source and why it did not take the order in reason: disabled, paused or busy (it was not available), or rejected, unavailable or rate_limited (it was asked and created nothing). An Idempotency-Key replay of that 201 carries it again. GET /orders/{id} does not: there, compare the order’s source.slug with the quote’s.
  • What it costs. The request has a weight of 50 once, however many sources are tried.
"warnings": [
{
"code": "source_fallback",
"message": "The quote's source (sideshift) could not take the order, so it was placed with the next best source of the quote.",
"quoted_source": "sideshift",
"reason": "unavailable"
}
]

Because the order can be placed at a fallback source, your user must see the notices of the quote’s fallback_sources before confirming, as well as the notice of its source, by the same rules. The fallback notices may follow the quote’s notice, including in an expandable section placed directly next to it. If you cannot show them, send "retry_next_best_source": false: only the quote’s source is tried, and when it cannot take the order the request fails as described in When no source creates the order. See Display requirements.

A request that times out, or whose connection drops, may still have created the order. With an Idempotency-Key you can simply send the same request again: you get the order the first request created, never a second one.

  • The key is 8 to 64 characters of A–Z, a–z, 0–9, _, -, : and .. A UUID fits. A malformed key is a 400 invalid_request with details.field: Idempotency-Key.
  • Make a new key for every new order, and store it with your record of the attempt. Keys are per account, and are kept for 24 hours after the order is created.
  • Same key, same body, after the order was created: 201 with the order as it is now, not as it was when created, with the Location header and Idempotent-Replayed: true, and with the source_fallback and refund_address_on_file warnings when the first answer had them. The bodies are compared as JSON, so field order and whitespace do not matter. A replay costs 1 and does not count against the order caps.
  • Same key, different body: 409 idempotency_conflict.
  • Same key while the first request is still running: 409 idempotency_in_progress, with Retry-After: 2. Wait, then send it again.
  • The first request failed and nothing was created: the key is released, and a retry with it runs as a new request.
  • The source may have created the order without confirming it (502 source_error with details.reason: outcome_unknown): the key stays blocked for 24 hours. A retry with it answers 502 source_error with details.reason: idempotency_outcome_unknown instead of risking a second order at the source. Write to api@kotova.io with the first request’s X-Request-Id.

When you send a key and it cannot be checked, the request is refused with a 503 service_unavailable rather than run without the protection you asked for.

Even without a key, a quote creates at most one order: a second POST /orders with the same quote_id answers 409 quote_already_used, with the order’s id in details.order_id once the order exists. The key is what gets you the order itself back.

  • to_address is checked against the address format of the to network. An address that does not fit is a 422 invalid_address with details.reason: format. A network without a format rule accepts any address, and the source checks it when the order is created. To check an address before your user confirms, use POST /addresses/validate.
  • to_memo: when your user’s receiving address needs a memo or destination tag (an account at a centralised service, for example), send it here. The API cannot tell whether an address needs one. A memo on a network without memos is a 422 memo_not_supported.
  • refund_address is an address on the from network, the one the deposit comes from, and is checked against that network’s format. When the quote’s requirements.refund_address is required, an order without one is a 422 refund_address_required, with the network and the source in details. Otherwise it is optional, and we recommend sending it: see Refund addresses.
  • refund_memo: when your user’s refund address needs a memo or destination tag, send it here. It needs a refund_address (otherwise a 400 with details.reason: requires_refund_address) and a from network that supports memos.
  • Addresses are trimmed and may be up to 4,096 characters, with no spaces or control characters inside. Memos may be up to 255 characters, with no control characters. An empty memo counts as none.
  • A source can refuse an address or a memo when the order is created. That is a 422 invalid_address with details.reason: rejected_by_source, and details.field names what it refused: to_address, to_memo, refund_address or refund_memo. When the source did not say which, details.field is the best guess and details.fields lists every candidate: check them all. Nothing was created, and the order is not tried at another source, whether or not retry_next_best_source is on.

We recommend sending a refund_address with every order, and a refund_memo when that address needs one. The refund address is never required unless the quote’s requirements.refund_address is required. Where the source takes a refund address with the order, it goes to the source then, so a refund needs no extra step; and a fallback source that needs one is skipped without it, so sending one keeps more of the quote’s fallback_sources eligible. With a refund address on file, an order that needs attention can also be refunded automatically when nobody decides in time (see auto_resolution in Status detail).

What the refund address is used for depends on the order’s source. The quote says it for each of its sources in source.refund_address_forwarding, and GET /sources for every source:

refund_address_forwarding What happens to the refund address On the order
at_creation It goes to the source with the order, and the source checks it then. A refusal is a 422 invalid_address with details.field: refund_address or refund_memo, and nothing is created. refund.forwarded: true
when_needed The source takes none when the order is created. It is kept with the order and given to the source only if the order needs a refund. If the source refuses it then, the order shows status_detail.reason: refund_address_rejected. Today: FixedFloat (ffio). refund.forwarded: false, and the warning refund_address_on_file
none The source never takes one and handles refunds itself. It is kept with the order for support only. Today: CCE Cash (ccecash). refund.forwarded: false, and the warning refund_address_on_file

Every other source takes the refund address at_creation today. New values may be added: read the field rather than a list of sources, and tolerate values you do not know.

refund_address_on_file comes with the 201 and its Idempotency-Key replays, with the order’s source in source and its refund_address_forwarding in intake. GET /orders/{id} does not carry it: there, read refund.forwarded.

"warnings": [
{
"code": "refund_address_on_file",
"message": "This source does not take a refund address when the order is created: it is kept with the order and given to the source only if the order needs a refund.",
"source": "ffio",
"intake": "when_needed"
}
]
  1. The body. A malformed body is a 400 invalid_request.
  2. The Idempotency-Key. A replay, the two 409s and a blocked key (see above) are answered here.
  3. Your account’s order caps. See Weight and order caps.
  4. The quote. 404 quote_not_found for an unknown quote, one that is gone, or one of another account; 409 quote_already_used; 410 quote_expired. A usable quote is then claimed for this request.
  5. The addresses and memos, as described above.
  6. The sources. The quote’s source and then, with retry_next_best_source, its fallback sources, in order (see Next best source). A source that cannot take an order right now is passed over. When none of them can, the answer is a 503 source_unavailable with details.requote: true and Retry-After: 30. Request a new quote.
  7. A source creates the order. Its answer decides the response, or, when it created nothing, the next source is tried.

Every answer from steps 1 to 6 comes before a source is contacted: nothing was created at any source, and the request costs 1.

Once a source has been asked, its answer is typed so that you know what exists. When no source took the order, the answer is that of the last source asked, and details.source names it (the quote’s source when none was asked).

Answer What it means What to do
422 invalid_address, details.reason: rejected_by_source The source refused what details.field names: to_address, to_memo, refund_address or refund_memo (with details.fields when the source did not say which). Nothing was created. No other source is tried, with or without retry_next_best_source. Ask your user for another address or memo. The quote is released and can be used again until it expires.
502 source_error, details.retryable: true The source could not create the order right now. Nothing was created. Request a new quote and try again.
502 source_error, details.retryable: false The source refused this order. Nothing was created. Request a new quote.
502 source_error, details.reason: outcome_unknown The source did not confirm the order, and it may exist there. The quote and the Idempotency-Key stay used. No other source is tried. Do not retry on your own. Write to api@kotova.io with the X-Request-Id.
503 source_unavailable, details.requote: true No source could take an order right now. Nothing was created. Request a new quote.
500 internal_error with details.order_id The order was created but could not be read back. Read it with GET /orders/{id}, or send the request again with the same Idempotency-Key.

After any other 500, send the same request again with the same Idempotency-Key. It cannot create a second order: if one was created, the retry answers it, or a 409 that points to it.

With retry_next_best_source (the default), a 502 source_error and a 503 source_unavailable also carry details.sources_tried: the quote’s source and its fallback sources, in the order they were tried, each with the reason it did not take the order. So do a 422 invalid_address and a 500 internal_error when one of the quote’s fallback sources gave them. Here the quote’s source and the second fallback source failed, and the first fallback source was not available:

{
"error": {
"code": "source_error",
"message": "The source could not create the order right now; nothing was created. Request a new quote and retry.",
"request_id": "req_8fK2mQ9xLp3vTz7nWc1bYd4h",
"details": {
"source": "changelly",
"retryable": true,
"sources_tried": [
{ "source": "sideshift", "reason": "unavailable" },
{ "source": "changenow", "reason": "busy" },
{ "source": "changelly", "reason": "unavailable" }
]
},
"doc_url": "https://docs.kotova.io/x/api/errors#source_error"
}
}

The reasons are listed in Sources tried. Unless the last entry is outcome_unknown, nothing was created at any of these sources, and the quote and the Idempotency-Key are released.

POST /orders has a weight of 50 against your account’s budget. It costs only 1 when it is a replay, and when it is answered before any source was contacted (steps 1 to 6 above). Once a source has been contacted, the full 50 counts, once, whether or not the order was created and however many sources were tried. A request refused by the rate limit (429 rate_limited) costs nothing. Weights change only with at least 30 days’ notice.

Orders are also capped per account, across all of its keys:

  • orders still waiting for a deposit (waiting_for_deposit): 20 × limit / 250;
  • orders created in the last 24 hours: 100 × limit / 250.

limit is your account’s rate limit (250 by default, so 20 and 100). Each cap is rounded down and is at least 1. Over either cap, the answer is a 429 order_cap_reached with details.cap (open_unfunded or created_24h), details.limit, details.current and a Retry-After header: 60 seconds for open_unfunded, and for created_24h the time until the oldest order of the last 24 hours drops out of the window. See Rate limits.

The console below fills its request body with example addresses. Replace to_address and refund_address with addresses you control before you send it: every order it creates is real, and a refund goes to the refund_address you sent.

Try itPOST/ordersAPI key · weight 50
Connection

Your secret key stays in this page and is sent only to the base URL above. “Remember in this tab” keeps it in this tab’s session storage until the tab is closed; it is never stored anywhere else. If you are unsure, delete the key on your account page after testing and create a new one.

Parameters

Recommended: 8-64 characters of A-Z a-z 0-9 _ - : . (a UUID fits). A retry with the same key and the same body answers the same order (201, `Idempotent-Replayed: true`) instead of creating a second one; the same key with a different body is 409 `idempotency_conflict`. Keys are kept 24 hours, per account. While the first request with a key is still running, a retry with it is 409 `idempotency_in_progress`: repeat it after `Retry-After`. Once that request has contacted a source it may run up to about 100 s after it arrived. If it was cut off there before its outcome was recorded, the source may hold the order: from then on the key answers 502 `source_error` with `reason: idempotency_outcome_unknown` (retryable false) for 24 hours. Do not place the order again under a new key; contact api@kotova.io with the request id. Use a new key for every new order.

This is the production API. The order is placed at the source the quote named, and someone must send the deposit or it expires. Until it expires, an unfunded order counts toward your account’s order caps. Tick the box again for every order.

deposit.address is an address of the source the order was placed at, the one in the order’s source block. Your user sends deposit.amount of from.currency on from.network to it, and the source pays out to to.address. The funds go directly to the executing source: Kotova never holds them.

  • Show the address, the amount and the network together, and make clear that the deposit must be sent on from.network.
  • When deposit.memo is set, your user must send it with the deposit. Show it prominently, under the name in deposit.memo_label.
  • Show the deadline in expires_at.
  • source.order_id is the source’s own reference for the order, and source.tracking_url its tracking page, when there is one.

GET /orders/{id} returns one order of your account: any order created with any of its keys, current or revoked. It has a weight of 1. The id is not case-sensitive. A malformed id is a 400 invalid_request (details.field: id), and an unknown order, one created in the Kotova X app, or one of another account is a 404 order_not_found. The optional lang query parameter sets the language of the source notice.

An order’s state lives at its source, so a read may first ask the source for the latest state:

  • only while the order can still change;
  • at most once every 10 seconds per order while it is in progress, and once a minute while it is only being followed up (a refund under way, an expired order that a late deposit could still revive, a payout whose transaction is not recorded yet);
  • the interval is shared by everything that reads the order, the order page and Kotova’s own scheduled checks included, so reading more often does not refresh it more often;
  • the read waits up to about 6 seconds for the source. If the source takes longer, the read returns the stored state and a later read sees the result;
  • a read never fails because a refresh did.

The response header X-Kotova-Status-Refreshed is true when the order was refreshed for this read, and false when it was served as stored.

Terminal window
curl -i https://api.kotova.io/x/v1/orders/X-7KQ2ZD \
-H "Authorization: Bearer $KOTOVA_API_KEY"

A completed order, shortened:

{
"id": "X-7KQ2ZD",
"status": "completed",
"is_final": true,
"status_detail": null,
"completed_at": "2026-11-10T10:41:12Z",
"from": { "currency": "BTC", "network": "btc", "amount": "0.05", "amount_received": "0.05" },
"to": {
"currency": "USDT",
"network": "trx",
"amount": "3389.8",
"amount_sent": "3390.15",
"address": "REPLACE_WITH_RECIPIENT_TRX_ADDRESS",
"memo": null
},
"transactions": {
"deposit": {
"hash": "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"explorer_url": "https://mempool.space/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
"confirmations": 3,
"required_confirmations": 2
},
"payout": {
"hash": "c3a2d6e9b1f04e5a8d7c6b5a49382716f5e4d3c2b1a09f8e7d6c5b4a39281706",
"explorer_url": "https://tronscan.org/#/transaction/c3a2d6e9b1f04e5a8d7c6b5a49382716f5e4d3c2b1a09f8e7d6c5b4a39281706",
"confirmations": null
},
"refund": null
}
}
Try itGET/orders/{id}API key · weight 1
Connection

Your secret key stays in this page and is sent only to the base URL above. “Remember in this tab” keeps it in this tab’s session storage until the tab is closed; it is never stored anywhere else. If you are unsure, delete the key on your account page after testing and create a new one.

Parameters

The order id (`X-XXXXXX`, case-insensitive).

Language of the source notice (en default).

GET /orders lists your account’s API orders, from any of its keys, current or revoked, newest first. It has a weight of 1. Each entry is a summary; read one order with GET /orders/{id} for the full object. Orders created in the Kotova X app are not listed.

The list never asks the sources: each order is shown as last stored. To follow one order, read it.

Query parameter Rules
status Comma-separated statuses, such as waiting_for_deposit,confirming_deposit. An unknown status is a 400 invalid_request with details.reason: invalid_value and the valid statuses in details.allowed.
partner_order_id Only orders with this partner_order_id.
created_after Only orders created at or after this time: ISO-8601 with a time zone, such as 2026-11-01T00:00:00Z.
created_before Only orders created before this time (exclusive).
limit Page size, 1 to 100. Default 20.
cursor The next_cursor of the previous page. Keep the other parameters the same while you page.

An unknown or repeated query parameter is a 400 invalid_request (details.reason: unknown_parameter or duplicate_parameter).

The answer is data, has_more and next_cursor. next_cursor is null on the last page. Each entry has id, status, is_final, rate_type, created_at, updated_at, from and to (each with currency, network and amount), source (its slug), partner_order_id and order_url.

{
"data": [
{
"id": "X-7KQ2ZD",
"status": "completed",
"is_final": true,
"rate_type": "variable",
"created_at": "2026-11-10T10:15:39Z",
"updated_at": "2026-11-10T10:41:12Z",
"from": { "currency": "BTC", "network": "btc", "amount": "0.05" },
"to": { "currency": "USDT", "network": "trx", "amount": "3389.8" },
"source": { "slug": "sideshift" },
"partner_order_id": "shop-order-1042",
"order_url": "https://app.kotova.io/x/X-7KQ2ZD"
}
],
"has_more": true,
"next_cursor": "eyJjIjoiMjAyNi0xMS0xMFQxMDoxNTozOS4xMjM0NTZaIiwiaSI6IlgtN0tRMlpEIn0"
}
Terminal window
curl -G https://api.kotova.io/x/v1/orders \
-H "Authorization: Bearer $KOTOVA_API_KEY" \
--data-urlencode "status=action_required" \
--data-urlencode "limit=100"
Try itGET/ordersAPI key · weight 1
Connection

Your secret key stays in this page and is sent only to the base URL above. “Remember in this tab” keeps it in this tab’s session storage until the tab is closed; it is never stored anywhere else. If you are unsure, delete the key on your account page after testing and create a new one.

Parameters

Comma-separated order statuses: waiting_for_deposit, confirming_deposit, exchanging, sending, completed, action_required, refunding, refunded, expired.

Only orders with this partner_order_id.

Created at or after (ISO-8601 with a zone).

Created before (exclusive).

Page size, 1-100.

`next_cursor` of the previous page.

Webhooks are not available yet, so you follow an order by reading it.

  • While your user is waiting, read the order every 15 to 30 seconds until is_final is true. Reading more often does not refresh it more often, and every read costs 1.
  • Stop when is_final is true, not when the status looks final: an expired order can still change for a while.
  • On a 429 or a 503, wait for the number of seconds in Retry-After.
  • To find orders you lost track of, list them with a status filter, then read each one.

Kotova also checks orders on its own schedule, so most orders reach their final state even when nobody reads them.

expires_at is the deposit deadline: the source’s own deadline, capped at 60 minutes after creation at most sources. Some sources can extend their deadline, so the status, not the clock, says whether the order is still open.

An order without a deposit becomes expired. If your user sent nothing, nothing is owed: request a new quote for a new order. A deposit that arrives late can still revive an expired order. Kotova keeps checking with the source for a period counted from the order’s creation, usually 24 hours and shorter at some sources for some rate types, and is_final stays false until that period is over. Whether a late deposit is honoured is the source’s decision, and it can also make the order action_required with the reason late_deposit. See The deposit window and Late deposits.

An order whose source reports a problem, such as a deposit of the wrong amount, has the status action_required, and its status_detail says why, since when, and what happens if nobody acts.

  • When status_detail.resolve_url is set, your user can choose there, on the order page, between the options the source offers. Endpoints for these choices, and for giving a new refund address, are not available yet in the API.
  • auto_resolution and auto_resolution_at say what happens, and when, if nobody chooses first.
  • With the reason payout_on_hold, nobody can choose anything: write to api@kotova.io with the order id.

Every reason, and what to show your user, is on Order statuses. How sources handle these cases is described in Emergency states.

order_url is https://app.kotova.io/x/ followed by the order id. Your user opens it by entering the address the order pays out to (to.address). It shows the order and its status and, where the source allows it, the choices for an order that needs attention.

Giving your user order_url is optional; to keep them in your app, show the order from GET /orders/{id} and send them to status_detail.resolve_url only when an order needs a decision.

The order page of an API order has no box for e-mail updates and cannot be saved to a Kotova account. It tells your user that the app they used keeps them updated, so telling your users about status changes is up to you. E-mail notifications to the end users of API orders are not available yet.

When you write to api@kotova.io about an order, include its id and the X-Request-Id of the response in question.