Документация API

Добавьте нужные адреса в любой из 23 сетей и получайте уведомление о каждой входящей транзакции: подписанный webhook, событие API, сообщение в Telegram или email, публикацию в Discord либо Slack.

Аутентификация

В каждом запросе передавайте API-ключ в заголовке Authorization. Доступны два ключа: ck_test_… (sandbox — бесплатная имитация транзакций) и ck_live_… (production).

формат запроса
$ curl https://api.cryptanio.com/v1/watches \
  -H "Authorization: Bearer ck_live_4t9G…R1SC"
Храните ключи на сервере. Ключ даёт полный доступ к аккаунту — не помещайте его в мобильное приложение или браузерный код. Для ротации создайте второй ключ, переведите на него трафик и только затем отзовите первый. Отзыв действует сразу, поэтому такая последовательность не заблокирует ваш доступ.

Быстрый старт

Одного запроса достаточно, чтобы начать наблюдение. Следующая транзакция по адресу вызовет первое уведомление — обычно через несколько секунд после попадания в блок.

создание watch
$ curl -X POST https://api.cryptanio.com/v1/watches \
  -H "Authorization: Bearer ck_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "chain":   "ethereum",
    "address": "0x7c3A1d4E8b2f6C9a0D5e8F1b4C7a2E5d8B1f9eF2",
    "asset":   "USDC",
    "notify":  ["webhook", "telegram"],
    "meta":    { "invoice_id": "inv_8817" }
  }'
ответ · 201 создано
{
  "id":      "wch_01J5WQ4T9GZ3MH8B",
  "status":  "active",
  "chain":   "ethereum",
  "address": "0x7c3A…9eF2",
  "asset":   "USDC"
}

Настройте получателей webhook, Telegram, email, Discord и Slack в кабинете либо создайте webhook через API получателей событий, а затем выберите каналы для каждого watch в поле notify. Сначала проверьте интеграцию в sandbox: watches, созданные с ключом ck_test_, можно запустить вручную запросом POST /v1/simulate.

API наблюдений

Объект watch — это один адрес и один актив в одной сети. Именно такие объекты учитывает тариф. Все методы API расположены по адресу https://api.cryptanio.com/v1.

Метод APIНазначение
POST/v1/watchesСоздаёт watch. Возвращает 201 с объектом watch.
GET/v1/watchesВозвращает список watches с фильтрами по chain, status, meta.invoice_id.
GET/v1/watches/:idВозвращает один watch и его последние платежи.
DELETE/v1/watches/:idОстанавливает наблюдение. Транзакции в обработке ещё некоторое время будут приходить в течение защитного периода.
GET/v1/paymentsИщет обнаруженные платежи по watch, адресу, идентификатору транзакции или периоду времени.
GET/v1/chainsВозвращает сети текущего развёртывания, их правила подтверждения и поддержку токенов. Ключ не требуется.
GET/v1/eventsПовторно получает доставленные события за период до 30 дней — резервный способ восстановления после недоступности endpoint.

Поля запроса

ПолеТипОписание
chainstringОдно из значений bitcoin, ethereum, solana, tron, ton, bsc, base, xrp, dash. Обязательное поле. Актуальный список возвращает GET /v1/chains.
addressstringАдрес получения. Для Solana передавайте кошелёк: связанные token accounts вычисляются и отслеживаются автоматически.
assetstring"native" (по умолчанию), поддерживаемый символ, например USDC, USDT или DAI, либо идентификатор актива для конкретной сети. Актуальные правила активов возвращает GET /v1/chains; сеть без поддержки токенов принимает только native.
notifystring[]Любое из значений webhook, telegram, email, discord, slack. По умолчанию: ["webhook"].
watch_outgoingbooleanТакже уведомлять, когда средства уходят с этого адреса. Исходящие переводы приходят отдельными типами событий — debit.*, но не payment.* — поэтому существующая обработка входящих платежей не меняется. Не каждая сеть поддерживает исходящие операции: неподдерживаемая сеть отклонит создание watch с явной ошибкой, а не выполнит запрос частично.
expires_atstringНеобязательное время в формате RFC 3339. Подходит для инвойсов: watch отключится автоматически.
metaobjectДо 10 собственных пар ключ-значение, которые возвращаются в каждом уведомлении.

Новый адрес может получить статус pending. По умолчанию новый адрес в аккаунте, где уже есть адреса, активируется не сразу, а поле active_at показывает время включения. Защитное окно даёт владельцу возможность заметить адрес, который он не добавлял: уведомление отправляется во все подключённые каналы сразу после добавления, а удаление ожидающего адреса ничего не стоит. Первый адрес и все sandbox-адреса активируются сразу; задержку можно изменить или отключить в кабинете.

Webhooks

Каждое изменение состояния отправляется одним HTTP POST на ваш endpoint. Доставка работает по принципу at-least-once: повторы возможны и ожидаемы. Удаляйте дубликаты по (chain, tx_id, index, address, asset) или просто по event_id.

ЗаголовокЗначение
X-Event-IdУникальный идентификатор события (ULID). Также присутствует в теле как event_id.
X-Event-TypeОдин из двенадцати типов ниже. Маршрутизируйте по этому заголовку, а не по полю внутри тела.
X-TimestampUnix-время в секундах на момент подписи; входит в подписываемые данные.
X-SignatureHMAC-SHA256 в hex-формате для строки <timestamp>.<body>.
X-AttemptНомер попытки доставки, начиная с 1.

Типы событий

Три семейства намеренно разделены. Маршрутизация идёт по типу, поэтому вывод средств или изменение multisig нельзя случайно принять за входящий платёж из-за непрочитанного поля.

ТипЧто означает
payment.pendingСредства поступили, и сеть увидела транзакцию. Финальности ещё нет.
payment.confirmedСредства поступили, транзакция подтверждена. По этому событию можно выдавать товар.
payment.rolled_backРеорганизация отменила входящую транзакцию. Платёж считается несостоявшимся.
debit.pendingСредства уходят с адреса. Только для watches с параметром watch_outgoing.
debit.confirmedСредства ушли, транзакция подтверждена.
debit.rolled_backРеорганизация отменила исходящую транзакцию.
contract.approvalДля вашего адреса выдано разрешение на расходование.
contract.owner_addedВ отслеживаемом multisig появился новый владелец.
contract.owner_removedИз multisig удалён владелец.
contract.threshold_changedИзменилось необходимое число подписей.
contract.permissions_changedЗаменён встроенный в сеть набор разрешений аккаунта, включая порог подписей.
asset.unexpectedПоступил неожиданный актив. Средства действительно перемещены, но ничего не зачислено.

Две формы тела, а не двенадцать. Семейства payment.* и debit.* содержат объект payment с суммой. Семейство contract.* и событие asset.unexpected содержат объект contract вместо него — без суммы и глубины подтверждения, потому что эти события ничего не переводят.

Тело события

payment.confirmed · application/json
{
  "event_id":    "01J5WQ4T9GZ3MH8B2E7KD6R1SC",
  "event_type":  "payment.confirmed",
  "chain":       "ethereum",
  "occurred_at": "2026-08-16T13:52:01Z",
  "watch_id":    "wch_01J5WQ4T9GZ3MH8B",
  "payment": {
    "tx_id":         "0x9b41f2…c77d",
    "index":         2,
    "height":        20731442,
    "address":       "0x7c3A…9eF2",
    "asset":         "USDC",
    "contract":      "0xA0b8…eB48",
    "amount_raw":    "250000000",
    "decimals":      6,
    "amount":        "250",
    "confirmations": 12,
    "meta":          { "invoice_id": "inv_8817" },
    "fiat": {
      "currency": "USD",
      "value":    250.00,
      "rate":     1,
      "rate_at":  "2026-08-16T13:00:00Z"
    }
  }
}
debit.confirmed · исходящий перевод
{
  "event_id":    "01J5WQ8N2PB4KC7XR3TE9MDA5F",
  "event_type":  "debit.confirmed",
  "chain":       "bitcoin",
  "occurred_at": "2026-08-16T14:07:44Z",
  "watch_id":    "wch_01J5WQ4T9GZ3MH8B",
  "payment": {
    "tx_id":         "c1d89fd9…fc8f",
    "index":         0,
    "height":        964207,
    "address":       "bc1qltw…uxjh",
    "asset":         null,
    "amount_raw":    "435000",
    "decimals":      8,
    "direction":     "out",
    "confirmations": 3
  }
}
contract.owner_added · без суммы
{
  "event_id":    "01J5WQ9X6RT2VN5HB8KQ4WZE7M",
  "event_type":  "contract.owner_added",
  "chain":       "ethereum",
  "occurred_at": "2026-08-16T14:19:02Z",
  "contract": {
    "tx_id":     "0x4ae1…b902",
    "log_index": 4,
    "height":    20731509,
    "address":   "0x7c3A…9eF2",
    "kind":      "owner_added",
    "detail":    { "owner": "0x91Bd…4c7A" }
  }
}
  • direction принимает значение "in" или "out" и всегда присутствует у платежа. Направление также следует из типа события; используйте удобный вариант, но не определяйте его по знаку суммы — суммы никогда не бывают отрицательными.
  • Каждое значение в detail передаётся строкой. Allowance имеет тип uint256, который не помещается в JSON number.
  • match_key содержит назначение, переданное самой сетью: комментарий TON или reference Solana Pay. Поле отсутствует в сетях и платежах без такого значения — то есть в большинстве случаев.
  • mode принимает значение "live" или "test": какое окружение отправило событие. Значение находится в подписанном теле, а не только в заголовке, поэтому тестовый запрос нельзя выдать production-endpoint без этой пометки.
  • amount_raw — строка в минимальных единицах актива. JSON numbers теряют точность после 2⁵³ и не вмещают Ethereum uint256 — используйте целочисленный тип произвольной точности. amount — удобная десятичная строка для отображения, но не для вычислений.
  • Ваш объект meta возвращается внутри payment, а не рядом. Всё, что передано при создании watch — invoice_id, номер заказа или идентификатор клиента — приходит в payment.meta, поэтому одного чтения достаточно для получения платежа и его назначения.
  • index разделяет несколько зачислений в одной транзакции: индекс выхода Bitcoin, индекс лога Ethereum, ноль в Solana.
  • meta повторяет данные, прикреплённые к watch, и позволяет маршрутизировать события без запроса к базе данных.
  • fiat показывает стоимость платежа на момент поступления, использованный курс и время его фиксации — выгрузка сегодня и через месяц даст одинаковое число. Поле отсутствует, если курс недоступен или токен не удалось определить: для сверки отсутствие значения лучше догадки. Токены определяются по контракту через ценовой источник, поэтому жёстко заданный список не требуется.

Повторные попытки

  • Тайм-аут одной попытки: 10 s. Любой ответ не 2xx или отсутствие ответа запускает повторную попытку.
  • Экспоненциальная задержка от 2 s до 15 минут, в течение 24 часов.
  • Всё же пропустили событие? GET /v1/events повторно отдаёт события за последние 30 дней.

Проверка подписей

Подпись — это HMAC-SHA256 в hex-формате для строки <timestamp>.<raw body> с секретом webhook в качестве ключа (кабинет → Webhooks). Сравнивайте за постоянное время и отклоняйте устаревшие timestamp, чтобы закрыть повторное воспроизведение.

node.js
const crypto = require("node:crypto");

function verify(secret, timestamp, rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected), Buffer.from(signature));
}

// reject if |now − timestamp| > 300 s, then verify
go
func verify(secret, ts string, body []byte, sig string) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(ts))
    mac.Write([]byte("."))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(sig))
}
Почему timestamp входит в MAC. Подпись только тела позволила бы бесконечно повторять перехваченный запрос. Когда timestamp включён в подпись, отклонение старых значений закрывает окно повтора, а изменить время без нарушения подписи невозможно.

Поток событий

GET /v1/stream — поток Server-Sent Events с теми же данными, которые получает webhook. Он подходит там, куда webhook не доберётся: ноутбук за NAT, локальная разработка или браузерная панель реального времени.

терминал
$ curl -N https://api.cryptanio.com/v1/stream \
  -H "Authorization: Bearer ck_live_…"

: connected

retry: 5000

event: payment
data: {"event_id":"01J5WQ…","event_type":"payment.confirmed", … }

: ping
  • Каждое событие использует event: payment, а data: содержит тот же JSON, который был бы отправлен webhook: одинаковые поля и форма позволяют использовать один парсер.
  • Строки, начинающиеся с двоеточия, — комментарии и встроенный keep-alive SSE. Одна приходит при подключении, затем каждые двадцать секунд, чтобы отличить тихий аккаунт от разорванного соединения.
  • Это не архив событий. Приходят только события, возникшие во время подключения. Пропущенные данные доступны через GET /v1/events за последние 30 дней.
  • Переподключайтесь свободно — параметр retry: 5000 указывает стандартному EventSource вернуться через пять секунд; браузерные реализации делают это автоматически.

API получателей событий

Подписывайте URL на события аккаунта без открытия кабинета — например, при создании tenant или для staging-окружения, которое должно получать те же платежи, что production.

Метод APIНазначение
POST /v1/endpointsПодписывает URL с протоколом https://. Возвращает endpoint и его secret — секрет показывается только один раз.
GET /v1/endpointsВозвращает подписки аккаунта. Секреты повторно не показываются.
DELETE /v1/endpoints/:idОстанавливает доставку на endpoint.
GET /v1/healthПроверяет доступность и возвращает текущую версию. Ключ не требуется.
$ curl -X POST … /v1/endpoints
{
  "url":   "https://example.com/hooks/cryptanio",
  "label": "staging"
}

→ 201

{
  "id":         "ep_01J5WQ4T9GZ3MH8B",
  "url":        "https://example.com/hooks/cryptanio",
  "label":      "staging",
  "secret":     "0460765e9292…544ce",
  "source":     "api",
  "created_at": "2026-08-16T13:52:01Z"
}
  • Каждый endpoint подписывает события собственным секретом, который возвращается один раз при создании. Проверяйте подпись как описано выше; для ротации создайте новый endpoint и удалите старый.
  • Только https. Событие содержит адрес будущего платежа, поэтому передавать его открытым текстом нельзя.
  • До двадцати на аккаунт. Это не тарифный лимит: превращение одного платежа в сотню доставок расходует исходящий трафик и ресурсы чужого endpoint, а обычной интеграции достаточно нескольких адресатов.

API инвойсов

Запросите конкретную сумму и получите событие при её поступлении. Инвойс берёт адрес из вашей адресной книги, удерживает его на время заказа и сопоставляет точную поступившую сумму — не нужно угадывать, какой платёж относится к какой корзине.

Это модуль приёма платежей. Каждый аккаунт получает десять инвойсов в месяц; платный тариф увеличивает лимит. Адреса берутся из вашей адресной книги — API не создаёт место получения средств. Изменение платёжного адреса требует пароля и уведомления во всех каналах и выполняется в кабинете.

Метод APIНазначение
POST/v1/invoicesСоздаёт инвойс. Возвращает 201 с адресом и точной суммой для отображения.
GET/v1/invoicesВозвращает список с фильтрами по project, chain, status.
GET/v1/invoices/:idВозвращает текущее состояние одного инвойса.
POST/v1/invoices/:id/cancelОтменяет инвойс и освобождает адрес. Запись остаётся доступной, чтобы отменённый заказ можно было найти.

Поля запроса

ПолеТипОписание
chainstringОбязательное поле. Список совпадает с GET /v1/chains.
amountstringОбязательная десятичная строка в единицах актива — "12.50", не центы и не float. JSON number потеряет младшие разряды токена с 18 знаками после запятой.
assetstringСимвол актива. По умолчанию используется нативная монета сети.
referencestringВаш идентификатор заказа. Возвращается в каждом событии, поэтому отдельное сопоставление не нужно.
projectstringПроект или направление бизнеса. По умолчанию используется первый проект; пул адресов не смешивается между проектами.
addressstringЗакрепляет один из ваших адресов вместо выбора из пула. Несколько открытых заказов смогут использовать его одновременно и различаться по сумме.
tolerancestringДопустимая недоплата, которая всё ещё считается оплатой, например из-за удержанной у отправителя сетевой комиссии. Пустое значение требует точного совпадения.
expires_innumberМинуты. Инвойс удерживает адрес на весь срок, поэтому значение ограничено сверху.
$ curl -X POST … /v1/invoices
{
  "chain":      "bitcoin",
  "amount":     "0.0125",
  "reference":  "order-4471",
  "expires_in": 30
}

→ 201

{
  "id":         "chg_01J5WQ4T9GZ3MH8B",
  "status":     "pending",
  "reference":  "order-4471",
  "chain":      "bitcoin",
  "asset":      "BTC",
  "address":    "bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq",
  "asked":      "0.0125",
  "amount":     "0.01250002",
  "amount_raw": "1250002",
  "decimals":   8,
  "sandbox":    false,
  "expires_at": "2026-08-16T14:22:01Z",
  "created_at": "2026-08-16T13:52:01Z"
}
  • Показывайте amount, а сверяйте по amount_raw. Если другой открытый заказ на том же адресе уже ждёт такую же круглую сумму, мы добавим несколько минимальных единиц, чтобы различить платежи. asked — согласованная с покупателем сумма; amount — сумма, которая должна фактически поступить.
  • Инвойс закрывается теми же событиями, что и остальные процессы. Webhook срабатывает на платёж, а инвойс одновременно переходит в paid. Отдельного потока событий для подписки нет.
  • Два состояния требуют решения человека. underpaid и late устанавливают needs_decision и ожидают: Cryptanio не решает за вас, считать ли оплатой недостающую сумму или перевод после срока. Обработайте их в кабинете.
  • Тестовый ключ создаёт только sandbox-инвойсы на sandbox-адресах. Интеграционные тесты могут работать весь день, не резервируя адреса реальных заказов: освобождённый адрес ждёт 48 часов перед возвратом в пул, и активные тесты иначе быстро исчерпали бы его.

Жизненный цикл платежа

Каждый платёж проходит явную машину состояний. Каждый переход создаёт одно уведомление, а настроенные каналы получают отдельные доставки.

СостояниеЗначениеКогда приходит
pending Транзакция находится в блоке, который ещё может быть отменён. Через несколько секунд после включения в блок — покажите прогресс на странице оплаты.
confirmed Глубина или финальность достигла уровня, который сеть считает необратимым. Для каждой сети действует своё правило: финальность протокола в Ethereum, BNB Chain и Base; finalized slot в Solana; solidified block в TRON; ChainLock в Dash; validated ledger в XRP; глубина 3 в Bitcoin. По этому событию можно выдавать товар.
rolled_back Блок покинул цепочку из-за реорганизации либо транзакция завершилась ошибкой. Только при наличии подтверждения, никогда по отсутствию данных. Событие редкое, но именно оно делает остальные два статуса надёжными.

Платёж, отменённый реорганизацией и позже снова включённый в блок, создаёт новую последовательность pending → confirmed с новой ревизией — журнал событий остаётся полным и упорядоченным.

Лимиты и оплата

Тарифы мониторинга различаются только объёмом: все сети, каналы и весь API доступны на каждом уровне, включая бесплатный объём. Отличается только уровень поддержки. Полные цены — на странице мониторинга.

ТарифАдресов под наблюдениемУведомлений в месяцЦена
Бесплатно51,000$0
Start5010,000$9 / месяц
Pro500100,000$29 / месяц
Scale5,0001,000,000$99 / месяц
  • Watch = один адрес + один актив. Учитываются активные watches; удалённые или истёкшие сразу освобождают место.
  • Уведомление = одно изменение состояния. Отправка в несколько каналов и повторная доставка одного события не увеличивают использование.
  • Превысили лимит? Когда тарифицируемый перерасход выключен, как по умолчанию, уведомления продолжают приходить ещё 48 часов, а мы сообщаем по email: без неожиданных списаний и молча потерянных событий. Если вы сами включите перерасход, оплата ограничится заданным месячным пределом.
  • Лимит API: 10 запросов/с на бесплатном уровне, 50 запросов/с на платных тарифах. 429 с заголовком Retry-After при превышении.

Остались вопросы?

Напишите на hello@cryptanio.com — обычно инженер отвечает в течение нескольких часов.