Перейти к содержанию

Надёжность и лимиты

На этой странице — рантайм-контракт API: как он ведёт себя при повторах, частичных сбоях и под нагрузкой. Прочитайте один раз при интеграции; дефолты подобраны так, чтобы клиент работал безопасно при сетевых проблемах.


Idempotency-Key

Каждый запрос, перемещающий средства, требует заголовок Idempotency-Key:

  • POST /transactions/withdraw
  • POST /transactions/transfer
Idempotency-Key: 3f7c0a1e-9b22-4f8d-bd3e-2d91a7e9f201
Content-Type: application/json

Формат: до 64 символов, [A-Za-z0-9_-]. Подходят UUID, ULID и стабильные бизнес-идентификаторы. Выбирайте ключ на одну логическую операцию, а не на каждый HTTP-запрос — тогда повтор после сетевого сбоя вернёт оригинальный ответ.

Поведение:

Тот же ключ + то же тело Тот же ключ + другое тело Новый ключ
Возвращает оригинальный ответ (кэш 24 ч). Второй вывод не отправляется. Возвращает 400 Bad Request. Почти всегда баг клиента — ключ обязан быть уникальным на запрос. Обрабатывается как новый запрос.

Кэш ключа — (tenantId, endpoint, idempotencyKey), поэтому одно и то же значение можно использовать для withdraw и transfer без коллизий. TTL — 24 часа; старые ключи освобождаются и могут быть переиспользованы.

curl -X POST $BASE_URL/transactions/withdraw \
  $HEADERS \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"sourceWalletId":"...","destinationAddress":"...","amount":"0.5"}'

Лимиты запросов

Лимиты защищают бюджет вашего API-ключа от единичного «горячего» цикла и поддерживают общую инфраструктуру здоровой для всех партнёров. API партнёра использует ровно два глобальных бакета — никаких надстроек по тенанту, скоупу или эндпоинту сверху нет.

Бакет Лимит Ключ партиционирования Поведение при превышении
Авторизованные (подписанный API-ключ) 120 запросов / мин / ключ JWT-клейм api_key_id (выставляет обработчик подписанного API-ключа) Немедленный 429; запросы не ставятся в очередь.
Анонимные (/health, опционально /swagger/*) 60 запросов / мин / IP Удалённый IP клиента (отсутствующий IP попадает в общий бакет unknown) Немедленный 429; запросы не ставятся в очередь.

Каждый бакет использует скользящее окно из 4 сегментов по 15 секунд — поэтому короткие всплески до примерно 30 запросов в любом 15-секундном интервале поглощаются и сглаживаются по всей минуте, а не упираются в лимит на 31-м запросе.

Нет лимитов на конкретные эндпоинты

Чувствительные маршруты вроде POST /transactions/withdraw не имеют отдельного лимита на партнёрском API — они делят тот же бюджет 120/мин/ключ с любым другим авторизованным вызовом. Ни один эндпоинт партнёрской поверхности (Api.Public) не имеет собственного лимита.

При превышении:

HTTP/1.1 429 Too Many Requests
Retry-After: 7

Тело ответа пустое — клиент должен корректно обрабатывать 429 без тела. Retry-After — целое число секунд, берётся из метаданных аренды лимитёра; считайте это подсказкой, а не гарантией.

Стратегия повторов на стороне клиента

при получении 429:
  1. если заголовок Retry-After есть → ждём указанное число секунд и повторяем
  2. иначе                            → ждём 1 с, затем экспоненциальная
                                         задержка с jitter (cap 30 с) на каждом
                                         следующем повторе
  3. прекращаем после N попыток (например, 6) и поднимаем событие оператору
  4. никогда не «глотайте» 429 молча — логируйте, чтобы корректно
     планировать нагрузку
  5. НЕ заводите дополнительные API-ключи, чтобы умножить бюджет — это
     обход правил, такие ключи отзываются

Если устойчивая нагрузка действительно превышает 120 запросов в минуту — сначала агрегируйте чтения (используйте список с take до максимального размера страницы вместо нескольких GET-ов), затем попросите нас поднять лимит для аккаунта. См. также Аутентификацию и строку 429 в справочнике ошибок.


Защита от повторов

Каждый подписанный запрос одноразов в пределах окна сдвига. API кэширует тройку (keyId, timestamp, signature) и отклоняет любой следующий запрос с теми же значениями: 401 Unauthorized — Request signature has already been used.

  • Окно сдвига — 30 секунд (X-Timestamp должен быть в пределах ±30 с от серверного времени).
  • Кэш живёт 60 секунд — вдвое дольше окна сдвига — поэтому захваченный на границе запрос тоже не получится переиграть.

Поэтому: при повторе клиент должен пересчитать подпись (новый timestamp + новый HMAC). Нельзя просто переотправить те же байты — будет 401.


Пагинация

Эндпоинт По умолчанию Максимум
GET /transactions take=20 take=200
GET /webhooks/deliveries pageSize=20 pageSize=200

Значение выше максимума — 400 Bad Request с указанием поля в errors. Постранично — никогда не пытайтесь забрать всё одним запросом.


Безопасность webhook-URL

URL вебхука, заданный через PUT /webhooks/outbound, должен проходить эти проверки — на этапе конфигурации и перед каждой доставкой (DNS перепроверяется заново):

  • Схема — https://. Никаких http://, file://.
  • Хост — публичный. Отклоняются loopback (localhost, 127.0.0.1), приватные сети RFC1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16), link-local (169.254.0.0/16 — включая адрес cloud-metadata), IPv6 ULA (fc00::/7) и multicast.
  • Редиректы не выполняются. 3xx от вашего эндпоинта считается ошибкой доставки.

Если URL теперь разрешается в приватный адрес (например, партнёр переехал в private network), доставка переводится в dead-letter, а не повторяется бесконечно. Смотрите через GET /webhooks/deliveries.


Семантика исходящих доставок

  • At-least-once. Сетевые ошибки и 5xx/таймауты повторяются с экспоненциальной задержкой (~10с → 30с → 60с → 5м → 15м). После 5 неуспешных попыток доставка становится dead.
  • Таймаут одной попытки — 5 секунд. Медленные обработчики считаются сбоем и повторяются.
  • Нет гарантии порядка между разными событиями — каждая доставка независима. Восстанавливайте порядок по eventType + entityId + timestamp, если он важен.
  • Подпись HMAC-SHA256 в X-Webhook-Signature; проверяйте её на каждой доставке (см. Интеграция вебхуков).

Шпаргалка по обработке ошибок

Код Повтор? Действие
400 Нет Поправьте запрос. Смотрите errors.
401 Нет Плохая подпись / часы / повтор. Пересчитайте подпись и повторите один раз.
403 Нет Нет нужного скоупа / IP не в allowlist. К администратору.
404 Нет Неверный ID или ресурс другого тенанта.
409 Нет Найдите существующий ресурс.
429 Да (back-off) Уважайте Retry-After.
500 Иногда Сбой сервера. Повторяйте редко.
502 Да Сбой кастоди-бэкенда. Повторите с задержкой.

См. Обработка ошибок — полный справочник.