Подключите обмен к своему продукту: получите курс, создайте заявку и отслеживайте исполнение. Здесь — методы API, примеры и порядок интеграции.
Создайте ключ в кабинете и передавайте его в заголовке каждого запроса.
https://co-re.ioКлюч идёт в заголовке Authorization как Bearer-токен и показывается один раз. Первый ключ можно создать в кабинете, дополнительные и замену — через поддержку CORE, даже после отзыва. Отозвать ключ можно самостоятельно. Тестовой среды нет: ключ боевой с первой минуты.
curl https://co-re.io/api/v1/quotes \
-H "Authorization: Bearer <api_key>" \
-H "Content-Type: application/json" \
-d '{"send":{"asset":"BTC","network":"BTC"},
"receive":{"asset":"USDT","network":"TRON"},
"amount":"0.05"}'Суммы передаются десятичными строками, например "0.0213585". Не преобразуйте их в числа с плавающей точкой: это приводит к потере точности.
Указывайте код актива и сеть. USDT в TRON и USDT в Ethereum требуют разных адресов и не взаимозаменяемы.
Используйте code для обработки ошибки и message для сообщения пользователю.
{ "error": { "code": "pair_unavailable", "message": "..." } }Время в UTC, формат ISO 8601.
Что CORE умеет обменивать прямо сейчас: код, сеть, в какую сторону работает, нужен ли memo и точность. Для сопоставления со своим списком монет: адрес контракта, chainId для EVM, как сеть называет memo, ссылка на транзакцию в эксплорере ({txHash} заменить на хеш) и иконка. contractAddress указан только там, где мы сверили его с сетью; null значит нативную монету или адрес, которого у нас нет. Не опрашивает исполнителей, поэтому дёшев и кэшируется. У сетей с memo sendEnabled = true, только если исполнитель принимает memo для адреса возврата (refundMemo в POST /deals).
{
"currencies": [
{
"asset": "USDT",
"network": "ETH",
"name": "Tether USD",
"networkName": "Ethereum",
"sendEnabled": true,
"receiveEnabled": true,
"memoRequired": false,
"decimals": 6,
"memoName": null,
"contractAddress": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"chainId": 1,
"explorerTxUrl": "https://etherscan.io/tx/{txHash}",
"iconUrl": "https://co-re.io/coins/usdt.svg"
},
{
"asset": "XRP",
"network": "XRP",
"name": "XRP",
"networkName": "XRP Ledger",
"sendEnabled": true,
"receiveEnabled": true,
"memoRequired": true,
"decimals": 6,
"memoName": "Destination tag",
"contractAddress": null,
"chainId": null,
"explorerTxUrl": "https://xrpscan.com/tx/{txHash}",
"iconUrl": "https://co-re.io/coins/xrp.svg"
}
],
"count": 2
}Все направления, которые CORE может котировать прямо сейчас: актив и сеть с обеих сторон, плюс сохранённый минимум. Исполнителей не опрашивает: список строится из того же справочника, по которому идёт котировка, и хранится в памяти 5 минут, поэтому вызов дешёвый. Свой лимит: 30 вызовов в минуту на ключ. Ответ несёт ETag и Cache-Control: пришлите If-None-Match, и без изменений придёт 304 без тела. Фильтр ?send=АКТИВ:СЕТЬ (например, BTC:BTC) оставляет направления из одного актива. min — наименьший минимум, который назвал хоть один исполнитель, в активе отправки; null, если его не измеряли последние 12 часов. max пока всегда null: живые границы даёт /limits. Котировка остаётся главной: пара из списка может не пройти, если исполнитель отказал. Одно направление — около 110 байт, а без фильтра в ответе все направления сразу, так что просите сжатие (Accept-Encoding: gzip) или берите фильтр. Сеть с memo бывает стороной отправки, только если направление обслуживает исполнитель, который принимает memo для адреса возврата (так же, как sendEnabled в /currencies). Если адрес возврата принадлежит бирже или кастодиальному сервису, для такого направления передавайте refundMemo в POST /deals: без memo возврат теряется.
{
"pairs": [
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" },
"min": "0.0007",
"max": null
},
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "XRP", "network": "XRP" },
"min": null,
"max": null
}
],
"count": 2,
"updatedAt": "2026-09-30T09:14:44.000Z"
}Минимум и максимум по паре, обе цифры в активе отправки. Считается опросом провайдеров двумя заведомо негодными суммами, поэтому стоит два обращения к провайдерам; полный ответ по паре хранится 5 минут. Кэшируйте на пару, не дёргайте на каждое нажатие клавиши; минимум всех пар сразу есть в GET /pairs. null означает «ни один провайдер не назвал» — не ноль и не «без ограничений».
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" }
}{
"limits": {
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" },
"min": "0.0007",
"max": null
}
}Тот же вопрос, который задаст создание обмена, и тем же модулем — проверив адрес заранее, можно быть уверенным, что создание из-за него не откажет. Заодно отвечает про memo: выплата в XRP без тега невозвратна, и узнать это до отправки формы дороже, чем после. Проверка структурная: форма, алфавит, длина. Не контрольная сумма и не «этим адресом кто-то владеет».
{
"asset": "XRP",
"network": "XRP",
"address": "rN7n7otQDd6FczFgLdSqtcsAUxDkw6fzRH"
}{
"valid": true,
"networkKnown": true,
"memoRequired": true,
"checked": "structure"
}Вознаграждение в USD. pending - вознаграждение по завершённым обменам, доход по которым ещё не подтверждён; к выводу оно недоступно. После подтверждения сумма переходит в available. available учитывает удержания, резервы и суммы к восстановлению. held означает проверку источника. Минимум вывода берите из balance.minPayout. monthVolume - объём, засчитанный в вашу долю за календарный месяц (UTC), в USD; с ним растёт доля. toNextLevel - сколько засчитанного объёма в этом месяце осталось до повышения доли, null на максимальном уровне. Оба поля null, когда показать нечего; денежные поля от этого не зависят.
{
"balance": {
"currency": "USD",
"available": "0",
"pending": "0",
"held": "0",
"reserved": "0",
"paid": "0",
"minPayout": "100",
"monthVolume": "3120.45",
"toNextLevel": "11879.55"
}
}Лучший живой курс на пару и сумму среди активных провайдеров. Ничего не создаёт и не двигает. Исполнитель назван там, где этого требуют его собственные условия, иначе помечен как нераскрытый. Только плавающий курс и сумма отправки, как в /deals: rateType и amountSide можно не передавать, другое значение даст unsupported_option. min и max — сохранённые границы пары, те же, что в GET /pairs, в активе отправки; null — «здесь не известно», не ноль и не «без ограничений». Если сеть отправки требует memo, курс сравнивается только среди исполнителей, которые принимают memo для адреса возврата: туда же POST /deals отправит обмен.
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" },
"amount": "0.05"
}{
"quote": {
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3212.44" },
"rateType": "float",
"expiresAt": "2026-08-24T09:14:44Z",
"executor": { "disclosed": false, "executorTermsAccepted": true },
"coreMarkup": "0",
"partnerMarkup": "0",
"comparedExecutors": 3,
"min": "0.0007",
"max": null
}
}Ответ /quotes несёт executor: по умолчанию только факт, что исполнитель есть и его условия приняты. Имя, disclosure и passport (кто держит депозит, режимы курса, условия и наша история с этим провайдером) приходят там, где условия исполнителя требуют назвать его. comparedExecutors по умолчанию число: сколько исполнителей ответило на запрос. Показывать ли это пользователю, решаете вы. Имена исполнителей включаются на ключе, если вы действительно показываете сравнение.
Завершённые обмены по вашему ключу могут приносить вознаграждение. Если исполнитель удерживает наценку на ордере, вознаграждением за этот обмен будет эта наценка. Сумма ждёт подтверждения дохода, затем её можно вывести. Начисления и выплаты видны в кабинете партнёра и в /api/v1/balance. partnerMarkup всегда равен строке «0» и заработок не показывает.
До 20 котировок одним вызовом, для витрин с курсами по многим парам. Каждый элемент считается ровно как POST /quotes и отвечает тем же телом: quote или error, плюс status — HTTP-статус, который дал бы одиночный запрос. Ответы идут в порядке элементов, index — номер элемента. Плохой элемент не ломает пакет: его ошибка приходит в его ответе. Элементы считаются параллельно; на весь пакет не больше 8 секунд, опоздавшие элементы получают quote_timeout. Стоит 1 + ceil(n/5) запросов чтения: 20 элементов — 5. У ключа идёт не больше одного пакета за раз: второй, пока первый не закончен, получает 429 с Retry-After. Ошибки в списке ниже — ошибки всего запроса.
{
"items": [
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" },
"amount": "0.05"
},
{
"send": { "asset": "ETH", "network": "ETH" },
"receive": { "asset": "USDT", "network": "TRON" },
"amount": "0.0001"
}
]
}{
"results": [
{
"index": 0,
"status": 200,
"quote": {
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3212.44" },
"rateType": "float",
"expiresAt": "2026-08-24T09:14:44Z",
"executor": { "disclosed": false, "executorTermsAccepted": true },
"coreMarkup": "0",
"partnerMarkup": "0",
"comparedExecutors": 3,
"min": "0.0007",
"max": null
}
},
{
"index": 1,
"status": 422,
"error": {
"code": "amount_below_minimum",
"message": "The amount is below the minimum for this pair. The minimum is 0.004 ETH/ETH."
}
}
],
"count": 2
}Ваши сделки, новые сверху. Параметры: limit (по умолчанию 50, максимум 100) и cursor из nextCursor предыдущей страницы. partnerMarkup — наценка партнёра, записанная в котировке этой сделки, как есть; у сделок с отключённой наценкой это «0», и по нему нельзя определять доход. commissionUsd — проведённое начисление в USD. null означает, что начисления ещё нет, в том числе у завершённого обмена без подтверждения дохода. Это не нулевой заработок. Доступную к выводу сумму запрашивайте через /balance.
{
"deals": [
{
"id": "7f1c...",
"status": "COMPLETED",
"stage": "COMPLETED",
"createdAt": "2026-08-24T09:12:44Z",
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3180.31", "final": true },
"partnerMarkup": "0",
"commissionUsd": null
}
],
"nextCursor": "MjAyNi0wOC0yNFQwOToxMjo0NFp8N2YxYw"
}Выбирает исполнителя, пере-котирует у него и возвращает адрес депозита, на который платит ваш клиент. Курс из /quotes не принимается: он всегда считается заново. Только плавающий курс и сумма отправки: rateType и amountSide можно не передавать, другое значение даст unsupported_option. memo — строка; целое число (тег XRP) тоже принимается. refundMemo — memo адреса возврата, по тем же правилам; если сеть отправки требует memo или refundMemo передан, обмен уйдёт только к исполнителю, который принимает memo для возврата. Если адрес возврата принадлежит бирже или кастодиальному сервису, возврат без memo теряется: в этом случае всегда передавайте refundMemo. Повтор с тем же Idempotency-Key отвечает записанной сделкой без новой котировки, поэтому он не зависит от того, доступна ли пара сейчас. status и stage в ответе те же, что вернёт GET /deals/:id: у нового обмена это ACTIVE и AWAITING_DEPOSIT.
{
"send": { "asset": "BTC", "network": "BTC" },
"receive": { "asset": "USDT", "network": "TRON" },
"amount": "0.05",
"payoutAddress": "TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"refundAddress": "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
"refundMemo": null,
"memo": null,
"campaignId": null
}{
"deal": {
"id": "7f1c...",
"status": "ACTIVE",
"stage": "AWAITING_DEPOSIT",
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3212.44" },
"deposit": {
"address": "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
"tag": null,
"amount": "0.05",
"deadline": "2026-07-27T10:15:00Z"
},
"trackUrl": "https://<site>/track/7f1c..."
}
}Если результат создания неизвестен, API возвращает 202: status ACTIVE, stage UNDER_REVIEW, адреса депозита нет. Этот ответ не означает, что обмен создан. Не запрашивайте депозит у клиента: заявку проверит оператор.
Текущее состояние вашей сделки, восстановленное из журнала событий. status - статус сделки, stage - шаг обмена (таблицы ниже). Чтение не пассивное: пока сделка идёт, перед ответом CORE спрашивает у исполнителя свежий статус и продвигает сделку, поэтому GET может вернуть статус новее, чем был до запроса (не больше шести раз в минуту на сделку). Пока идёт ожидание депозита, ответ повторяет реквизиты депозита.
{
"deal": {
"id": "7f1c...",
"status": "ACTIVE",
"stage": "AWAITING_DEPOSIT",
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3212.44", "final": false },
"deposit": {
"address": "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
"tag": null,
"amount": "0.05",
"deadline": "2026-08-24T09:42:44Z"
},
"trackUrl": "https://<site>/track/7f1c..."
}
}trackUrl в ответе это публичная страница сделки. Её можно отдать клиенту: аккаунт не нужен, там видно статус, хеши и форма обращения.
status - статус сделки: идёт она или чем закончилась. Создание, GET, список и вебхуки отдают его одинаково.
| DRAFT | Заведена, ордер у провайдера ещё не подтверждён. |
| ACTIVE | Идёт: ждём депозит, подтверждения, обмен или выплату. |
| COMPLETED | Выплата отправлена клиенту. · конечный |
| FAILED | Обмен не состоялся и возврат не проводился. · конечный |
| REFUNDED | Провайдер вернул монеты на адрес возврата. · конечный |
| EXPIRED | Депозит не пришёл в срок. · конечный |
stage - шаг внутри сделки, его и показывайте клиенту. Приходит рядом со status везде, где есть сделка.
| AWAITING_DEPOSIT | Ждём депозит. Реквизиты в deposit. |
| CONFIRMING | Депозит виден, сеть его подтверждает. |
| EXCHANGING | Депозит подтверждён, идёт обмен. |
| SENDING | Выплата отправляется клиенту. |
| COMPLETED | Выплата отправлена. · конечный |
| FAILED | Обмен не состоялся. · конечный |
| REFUNDED | Депозит вернули на адрес возврата. · конечный |
| EXPIRED | Депозит не пришёл в срок. · конечный |
| ACTION_REQUIRED | Обмен остановлен из-за проблемы с депозитом или ордером, например недоплаты. Её решает поддержка вместе с клиентом; отправьте клиента на trackUrl. |
| UNDER_REVIEW | Ордер у исполнителя не подтверждён, адреса депозита нет. Ничего не отправляйте: заявку проверяет оператор. |
Смена статуса и шага приходит вебхуком, если он настроен: deal.status_changed и deal.stage_changed. Источник истины - GET /api/v1/deals/:id: без вебхука опрашивайте его раз в 15-30 секунд и останавливайтесь на конечном статусе.
Вебхук задаётся на API-ключ в кабинете: один https-адрес на порту 443 на ключ. При сохранении показывается секрет подписи, один раз. Там же тестовое событие, новый секрет и последние доставки с кодом ответа. Получить ключ
deal.status_changed Сделка, созданная этим ключом, перешла в другой статус. previousStatus и status описывают этот переход. deal - тот же объект, что возвращает GET /api/v1/deals/:id, со stage, на момент первой попытки доставки. Шаги внутри ACTIVE приходят событием deal.stage_changed.
{
"id": "0b6c3a1e-5d2f-4c8a-9e61-2f4b7d9c1a30",
"type": "deal.status_changed",
"createdAt": "2026-08-24T09:40:02.113Z",
"data": {
"previousStatus": "ACTIVE",
"status": "COMPLETED",
"deal": {
"id": "7f1c...",
"status": "COMPLETED",
"stage": "COMPLETED",
"trackUrl": "https://<site>/track/7f1c...",
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3180.31", "final": true }
}
}
}deal.stage_changed У сделки, созданной этим ключом, сменился stage: депозит виден, идёт обмен, выплата отправляется и так далее. previousStage и stage описывают этот шаг. Одна смена - одно событие; переходы, которые stage не меняют, событий не дают. deal - тот же объект, что возвращает GET /api/v1/deals/:id, на момент первой попытки доставки. Конечный шаг приходит и этим событием, и deal.status_changed. Две смены шага в одной транзакции могут прийти в любом порядке: опирайтесь на previousStage или сверяйтесь с GET.
{
"id": "4d2a7c90-1b3e-4f5a-8c6d-7e8f9a0b1c2d",
"type": "deal.stage_changed",
"createdAt": "2026-08-24T09:21:37.540Z",
"data": {
"previousStage": "AWAITING_DEPOSIT",
"stage": "CONFIRMING",
"deal": {
"id": "7f1c...",
"status": "ACTIVE",
"stage": "CONFIRMING",
"trackUrl": "https://<site>/track/7f1c...",
"send": { "asset": "BTC", "network": "BTC", "amount": "0.05" },
"receive": { "asset": "USDT", "network": "TRON", "amount": "3212.44", "final": false }
}
}
}reward.available Начисление по вашему аккаунту стало доступно к выводу. Только сумма в USD: ставки в событии нет. Приходит на каждый вебхук аккаунта. Сумма в примере условная.
{
"id": "5e0a9d44-7b1c-4f3e-8a26-c91d0f6b2e77",
"type": "reward.available",
"createdAt": "2026-08-31T00:01:10.502Z",
"data": {
"reward": { "id": "c2d4e6f8-1a3b-4c5d-8e7f-9a0b1c2d3e4f", "amount": "1.25", "currency": "USD" }
}
}webhook.test Отправляется кнопкой в кабинете. Подписано так же, как настоящие события.
{
"id": "9a7e2c11-3f64-4d0b-b5a8-0e2c6f1d4b93",
"type": "webhook.test",
"createdAt": "2026-08-24T09:12:44.000Z",
"data": {}
}Каждый запрос несёт заголовок X-Core-Signature: t=<unix-секунды>,v1=<hex>. v1 - HMAC-SHA256 от строки «t.тело» с вашим секретом. Проверяйте подпись по сырому телу запроса до разбора JSON и отклоняйте t старше 300 секунд.
const crypto = require("node:crypto");
// rawBody: the request body exactly as received, before any JSON parsing.
function verifyCoreWebhook(rawBody, header, secret, toleranceSeconds = 300) {
if (typeof header !== "string" || header === "") return false;
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const timestamp = Number(parts.t);
if (!Number.isInteger(timestamp) || !/^[0-9a-f]{64}$/.test(parts.v1 ?? "")) return false;
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false;
const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest();
return crypto.timingSafeEqual(Buffer.from(parts.v1, "hex"), expected);
}import hashlib, hmac, re, time
# raw_body: the request body bytes exactly as received, before any JSON parsing.
def verify_core_webhook(raw_body: bytes, header, secret: str, tolerance: int = 300) -> bool:
if not isinstance(header, str) or not header:
return False
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t, v1 = parts.get("t", ""), parts.get("v1", "")
if not re.fullmatch(r"[0-9]{1,12}", t) or not re.fullmatch(r"[0-9a-f]{64}", v1):
return False
if abs(time.time() - int(t)) > tolerance:
return False
expected = hmac.new(secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)<?php
// $rawBody = file_get_contents('php://input');
// $header = $_SERVER['HTTP_X_CORE_SIGNATURE'] ?? '';
function verifyCoreWebhook(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
$parts = [];
foreach (explode(',', $header) as $pair) {
[$key, $value] = array_pad(explode('=', $pair, 2), 2, '');
$parts[$key] = $value;
}
$t = $parts['t'] ?? '';
$v1 = $parts['v1'] ?? '';
if (!preg_match('/^[0-9]{1,12}$/', $t) || !preg_match('/^[0-9a-f]{64}$/', $v1)) {
return false;
}
if (abs(time() - (int) $t) > $tolerance) {
return false;
}
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $v1);
}Доставлено - это любой ответ 2xx за 5 секунд. Редиректы не выполняются и считаются ошибкой. Повторы через: 30 с, 2 мин, 10 мин, 30 мин, 1 ч, 3 ч, 6 ч, 12 ч, после этого доставка прекращается. Одно событие может прийти больше одного раза и не по порядку: храните id обработанных событий (он же заголовок X-Core-Event-Id, свой у каждого вебхука и не меняется при повторах) и сверяйте статус через GET. Адрес должен быть публичным: во внутренние сети и на адреса метаданных запросы не уходят.
Заголовок Idempotency-Key обязателен при создании обмена. Без него API возвращает 400. Повтор с тем же ключом возвращает исходную сделку, не создавая новую и не запрашивая курс заново: ответ не зависит от того, доступна ли пара сейчас.
curl https://co-re.io/api/v1/deals \
-H "Authorization: Bearer <api_key>" \
-H "Idempotency-Key: 6b1f2c58-0a1e-4b3f-9d21-8c0f4a7e5b12" \
-H "Content-Type: application/json" \
-d '{"send":{"asset":"BTC","network":"BTC"},
"receive":{"asset":"USDT","network":"TRON"},
"amount":"0.05",
"payoutAddress":"TJRabPrwbZy45sbavfcjinPJC18kjpRTv8",
"refundAddress":"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"}'Ключ генерируется на попытку обмена, а не на HTTP-запрос. Повторяйте с тем же ключом, пока не получите окончательный ответ. Формат: от 8 до 128 символов, латинские буквы, цифры и . _ : - , первый символ буква или цифра; UUID подходит.
| HTTP | code | |
|---|---|---|
| 400 | invalid_request | Тело не JSON; нет обязательного поля (send, receive, amount, payoutAddress, refundAddress, а для /validate-address — asset, network, address); memo или refundMemo не строка и не целое неотрицательное число, refundMemo длиннее 256 символов; refundMemo на сети отправки без memo; Idempotency-Key не по формату (8–128 символов: латинские буквы, цифры и . _ : -, первый символ буква или цифра); limit или cursor в GET /deals не подходят; send в GET /pairs не вида АКТИВ:СЕТЬ; items в /quotes/bulk не массив от 1 до 20 элементов. В /quotes/bulk ошибка одного элемента приходит в его ответе, а не во всём запросе. |
| 400 | invalid_amount | amount не строка положительного десятичного числа. |
| 400 | idempotency_key_required | На POST /deals нет заголовка Idempotency-Key. Без него повтор создал бы второй обмен. |
| 401 | unauthorized | Нет заголовка Authorization, либо ключ неверен или отозван. |
| 403 | ip_not_allowed | У ключа задан список разрешённых IP, и запрос пришёл с другого адреса. Список меняется в кабинете, в разделе API. |
| 404 | not_found | Сделки с таким id нет среди созданных вашим ключом. Чужая сделка тоже даёт 404: существование не раскрывается. Тот же код на путь /api/v1/..., которого нет. |
| 405 | method_not_allowed | Метод не подходит к эндпоинту, например DELETE /api/v1/quotes. Заголовок Allow перечисляет допустимые. |
| 409 | idempotency_key_conflict | Тот же Idempotency-Key на другой обмен: другая пара, сумма или адрес, в том числе другой refundAddress или refundMemo. Ключ генерируется на попытку обмена и повторяется только для ретраев этой же попытки. С миграции 085 это НЕ срабатывает из-за того, что сравнение при повторе выбрало другого исполнителя. |
| 503 | pricing_unavailable | Не удалось проверить настройки комиссии. Обмен не создаётся с неподтверждёнными настройками. Повторите запрос позже; для создания сохраните тот же Idempotency-Key. |
| 503 | service_unavailable | Не удалось прочитать наши собственные данные: ключ API на любом запросе, реестр активов на /validate-address, реестр активов, кампанию или уже созданный ордер на POST /deals. Ничего нового не создано, и в лимит неудачных попыток с IP это не входит. Повторите позже; для создания сохраните тот же Idempotency-Key. |
| 503 | quote_timeout | Только в ответе элемента /quotes/bulk: исполнители не ответили по этому элементу за 8 секунд. Остальные элементы от этого не страдают. Повторите этот элемент. |
| 503 | quote_unavailable | Не удалось обновить котировку перед созданием обмена. Провайдеру не отправляли запрос на создание. Повторите с тем же Idempotency-Key. |
| 503 | replay_executor_unavailable | Повтор с тем же Idempotency-Key: обмен уже создан, но исполнителя, который его ведёт, сейчас не поднять. Повторите с ТЕМ ЖЕ ключом чуть позже. Новый ключ создаст второй настоящий ордер. |
| 503 | order_reservation_failed | Обмен не удалось подтвердить. Повторите с ТЕМ ЖЕ ключом: повтор никогда не создаёт второй ордер. Если ордер у провайдера всё же был создан, повтор вернёт его или обмен уйдёт на проверку оператору. |
| 422 | executor_refused_request | Исполнитель проверил заявку и отклонил её: неверный адрес, сумма вне диапазона, актив не поддерживается или котировка истекла. Ордера не существует, депозит не нужен. Исправьте заявку и отправьте с НОВЫМ Idempotency-Key. Раньше это возвращалось как 202 «на проверке, не отправляйте депозит», хотя ждать было нечего. |
| 422 | unsupported_option | В /quotes или /deals явно указан режим, которого нет: rateType (rate_type) не "float" или amountSide (amount_side) не "SOURCE". Фиксированный курс и точная сумма получения не поддерживаются. Раньше такие поля молча игнорировались: /deals создавал обмен по плавающему курсу, /quotes отдавал плавающий курс. |
| 422 | amount_below_minimum | Сумма ниже минимума провайдера для этой пары. Увеличьте её. |
| 422 | amount_above_maximum | Сумма выше максимума провайдера для этой пары. Уменьшите её. Раньше этот случай приходил как pair_unavailable, то есть неправдой. |
| 422 | pair_unavailable | Ни один активный провайдер не котирует эту пару сейчас, либо инструмент выключен между котировкой и созданием. |
| 422 | invalid_payout_address | payoutAddress не похож на адрес сети получения. Проверка идёт до обращения к провайдеру. |
| 422 | invalid_campaign | campaignId не является активной кампанией вашего аккаунта. Проверка идёт до обращения к провайдеру. |
| 422 | invalid_refund_address | refundAddress не похож на адрес сети отправки. |
| 422 | memo_required | Сеть получения требует memo или тег, а поле memo не передано. |
| 422 | refund_memo_unsupported | Сеть отправки опознаёт получателя по memo, или передан refundMemo, а сейчас ни один исполнитель этой пары не принимает memo для адреса возврата. Или исполнитель, выбранный для обмена, перестал принимать memo для возврата до создания ордера: ордер не создан, повторите с НОВЫМ Idempotency-Key. |
| 503 | balance_unavailable | Баланс сейчас не читается. Это ошибка, а не ноль: ложный ноль на денежном эндпоинте хуже, чем 5xx. |
| 429 | rate_limited | Ключ превысил лимит: 600 запросов в минуту на чтение (справочник, границы, проверка адреса, курсы, чтение сделок), 30 на GET /pairs или 60 на создание обменов и /balance, если для ключа не договорились о большем. Пакет /quotes/bulk стоит 1 + ceil(n/5) запросов чтения, и у ключа идёт не больше одного пакета за раз: второй, пока первый не закончен, получает 429. Или с одного IP за минуту пришло больше 120 запросов с неверным или отозванным ключом либо с адреса, которого нет в списке ключа; удачные запросы сюда не входят. Заголовок Retry-After говорит, через сколько секунд повторить. |
| 500 | internal_error | Непредвиденная ошибка на нашей стороне, она записана. Повторите тот же запрос (для POST /deals — с тем же Idempotency-Key): повтор никогда не создаёт второй обмен. |
Не поддерживаются: фиксированный курс, отмена созданного обмена, указание точной суммы получения, отдельная разбивка сетевых комиссий и тестовая среда. Активные провайдеры работают с плавающим курсом.
CORE не обслуживает клиентов из следующих стран и территорий: Северная Корея, Иран, Сирия, Куба, Мьянма, Крым, Севастополь, Донецкая область, Луганская область, США, Пуэрто-Рико, Гуам, Виргинские острова США, Американское Самоа, Северные Марианские острова, Внешние малые острова США, Великобритания, Канада, Япония. API не проверяет страну по IP запроса: запросы приходят с вашего сервера. Не создавайте обмены для пользователей из этих стран и территорий.