Надёжность и лимиты¶
На этой странице — рантайм-контракт API: как он ведёт себя при повторах, частичных сбоях и под нагрузкой. Прочитайте один раз при интеграции; дефолты подобраны так, чтобы клиент работал безопасно при сетевых проблемах.
Idempotency-Key¶
Каждый запрос, перемещающий средства, требует заголовок Idempotency-Key:
POST /transactions/withdrawPOST /transactions/transfer
Формат: до 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) не имеет собственного
лимита.
При превышении:
Тело ответа пустое — клиент должен корректно обрабатывать 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 |
Да | Сбой кастоди-бэкенда. Повторите с задержкой. |
См. Обработка ошибок — полный справочник.