Skip to main content
Каждый запрос к /v1/public/* подписывается на вашей стороне ключом api_secret. Платформа проверяет подпись на сервере — сам api_secret никогда не передаётся по сети после момента выдачи. Базовый URL индивидуален для вашего инстанса, например https://wallet.your-exchange.com.
В этих примерах используются единые значения во всех разделах документации: базовый URL https://wallet.your-exchange.com, X-Api-Id = pk_live_a1b2c3d4, order_id в формате INV-2026-000042. Подставьте свои реальные значения.

Обязательные заголовки

Эти заголовки должны присутствовать в каждом запросе.
X-Api-Id
string
required
Публичный идентификатор ключа. Выдаётся в админ-кабинете при создании API-ключа. Не является секретом — это лишь указатель, по какому ключу проверять подпись.
X-Timestamp
string
required
Unix-время в секундах на момент формирования запроса. Сервер принимает значение только в пределах ±300 секунд от своего времени. Часть подписываемого сообщения.
X-Signature
string
required
HMAC-SHA256 в нижнем регистре hex от сообщения {X-Timestamp}.{raw_body} с ключом api_secret. Подробности — ниже.
X-Idempotency-Key
string
Опционально, только для write-операций (создание депозита, выплаты и т. п.). UUID, который гарантирует, что безопасный повтор того же запроса не создаст дубликат. См. Идемпотентность.
Подписывается сырое тело запроса — ровно те байты, которые уходят на сервер. Сначала сериализуйте JSON в строку, подпишите эту строку и отправьте её же в теле. Любое расхождение (лишний пробел, другой порядок ключей, повторная сериализация) сломает подпись и вернёт INVALID_SIGNATURE.

Как формируется подпись

Сообщение для HMAC — это конкатенация трёх частей: значение X-Timestamp, символ-разделитель точка . и сырое тело запроса.
message   = X-Timestamp + "." + raw_body
signature = HMAC_SHA256_hex( api_secret, message )
Для запросов без тела (GET, HEAD, DELETE) сырое тело — это пустая строка "" (именно пустая строка, а не {}). Поэтому сообщение оканчивается на точку:
message = "<timestamp>."
1

Соберите тело

Сериализуйте JSON в строку — это и есть raw_body. Для запросов без тела используйте пустую строку.
2

Возьмите timestamp

Текущее Unix-время в секундах (не миллисекундах). Поместите его в X-Timestamp.
3

Постройте сообщение

Соедините: message = timestamp + "." + raw_body.
4

Подпишите

signature = HMAC_SHA256_hex(api_secret, message) — результат в нижнем регистре hex.
5

Отправьте

Заголовки X-Api-Id, X-Timestamp, X-Signature (и тело, если оно есть). Тело отправляйте байт-в-байт тем же, что подписали.

Полный разбор на конкретных числах

Возьмём заведомо известные входные данные и проверим, что у вас получается тот же результат. Эти значения детерминированы — повторите расчёт у себя и сверьте подпись.
ПараметрЗначение
api_secrets3cr3t_ApiSecret_ExampleOnly_DoNotUse
X-Timestamp1735680000
raw_body{"assetCode":"USDT_TRC20","orderId":"INV-2026-000042","expectedAmount":"100.50"}
Сообщение для подписи:
1735680000.{"assetCode":"USDT_TRC20","orderId":"INV-2026-000042","expectedAmount":"100.50"}
Результат HMAC-SHA256 в hex:
58d7e5946e50f93551ac9d48728b5d66308c5328241044a0e7ef03528f0bc6ed
Если ваша реализация выдаёт ровно 58d7e5946e50f93551ac9d48728b5d66308c5328241044a0e7ef03528f0bc6ed — алгоритм подписи у вас собран правильно.
Для пустого тела (GET) с тем же секретом и тем же X-Timestamp = 1735680000 сообщение — 1735680000., а подпись:
6f52d4e629b5daa5789bdc218214e5f4d80586cde1100e5a2211294d6b0d2317

Готовый запрос

Так выглядит подписанный POST /v1/public/deposits с данными из примера выше.
curl -X POST https://wallet.your-exchange.com/v1/public/deposits \
  -H "X-Api-Id: pk_live_a1b2c3d4" \
  -H "X-Timestamp: 1735680000" \
  -H "X-Signature: 58d7e5946e50f93551ac9d48728b5d66308c5328241044a0e7ef03528f0bc6ed" \
  -H "Content-Type: application/json" \
  -d '{"assetCode":"USDT_TRC20","orderId":"INV-2026-000042","expectedAmount":"100.50"}'
Для запросов без тела (например GET /v1/public/deposits/by-order-id/{order_id}) передайте в функцию подписи пустую строку — тогда сообщение будет "<timestamp>.", а тело запроса не отправляется.

Формат ответа

Все ответы возвращаются в едином envelope.
Успех
{ "ok": true, "data": { "uuid": "…", "status": "check" } }
Ошибка
{ "ok": false, "error": { "code": "INVALID_SIGNATURE", "message": "X-Signature mismatch" } }
Суммы во всех полях — строки (например "100.50"), чтобы исключить потерю точности на плавающей запятой. Полный справочник кодов — на странице Ошибки.

IP-whitelist

Помимо подписи платформа проверяет IP-адрес вызывающего. Если для сайта задан белый список, запросы принимаются только с перечисленных адресов; иначе — 403 IP_NOT_WHITELISTED.
1

Узнайте исходящий IP

Это адрес, с которого ваш бэкенд CMS обращается к API (а не IP браузера клиента).
2

Добавьте его в админ-кабинете

Белый список IP сайта управляется оператором в админ-панели платформы.
3

Учитывайте смену IP

При переезде сервера, смене провайдера или добавлении балансировщика обновите список — иначе запросы начнут отклоняться с IP_NOT_WHITELISTED.
Если белый список для сайта пуст, проверка IP не применяется. Для боевого окружения мы рекомендуем всегда настраивать whitelist.

Ротация ключей

api_secret показывается один раз — в момент создания ключа. Сохраните его сразу в безопасное место (секрет-менеджер). Восстановить его позже нельзя; если секрет утерян или скомпрометирован — выпустите новый ключ через ротацию.
1

Выпустите новый ключ

В админ-кабинете создайте новый credential или выполните ротацию существующего. Вы получите новый X-Api-Id и новый api_secret (последний — единожды).
2

Переключите интеграцию

Обновите X-Api-Id и api_secret в своей CMS. Старый ключ при ротации помечается как rotated и продолжает работать ограниченное время (grace period) — это позволяет выкатить смену без простоя.
3

Отзовите старый ключ

После того как весь трафик идёт на новый ключ, отзовите старый. Отозванный ключ сразу перестаёт проходить аутентификацию.
Никогда не передавайте api_secret в теле запроса, в URL, в логах или клиентском коде. По сети должна уходить только подпись X-Signature. Платформа не принимает секрет в открытом виде.

Возможные ошибки аутентификации

HTTPКодКогда возникаетЧто проверить
401UNAUTHORIZEDНет X-Api-Id, X-Signature или X-Timestamp; ключ не найден, неактивен, истёк; сайт не активен; X-Timestamp не числоВсе три обязательных заголовка на месте; ключ действующий; сайт активен
401INVALID_SIGNATUREПодпись не совпалаПодписаны те же байты, что отправлены; верный секрет; сообщение timestamp + "." + raw_body
401TIMESTAMP_SKEWX-Timestamp вне диапазона ±300 секундСинхронизируйте часы сервера по NTP; шлите время в секундах, не миллисекундах
403IP_NOT_WHITELISTEDIP вызывающего не в белом списке сайтаДобавьте исходящий IP бэкенда в whitelist
429RATE_LIMITEDПревышен лимит запросов для сайтаСнизьте частоту; учитывайте заголовок Retry-After. См. Лимиты

Частые вопросы

В 9 случаях из 10 причина — расхождение тела. Подписывайте и отправляйте одну и ту же строку. Не сериализуйте JSON дважды, не позволяйте HTTP-клиенту повторно кодировать тело, не добавляйте отступы. Сравните: сообщение для HMAC — это X-Timestamp, затем точка, затем байт-в-байт тело запроса.
Время сервера расходится с временем платформы более чем на 300 секунд. Включите синхронизацию по NTP. Также убедитесь, что отправляете Unix-время в секундах: Math.floor(Date.now() / 1000) в JS, int(time.time()) в Python.
Тело пустое, поэтому raw_body = "", а сообщение — "<timestamp>." (timestamp и точка). Заголовки X-Api-Id, X-Timestamp, X-Signature всё равно обязательны.
Нет. Достаточно X-Api-Id плюс подпись. api_secret не передаётся по сети ни в каком виде — он используется только локально для вычисления X-Signature.
Имена HTTP-заголовков нечувствительны к регистру. А вот сама подпись — hex в нижнем регистре, и сообщение для HMAC чувствительно к каждому байту тела.