Skip to content

Надёжность интеграции ​

Этот раздел описывает действующее поведение REST API, а не желаемый стандарт. Базовый путь всех примеров: /api/reseller/v1. Используйте выданный провайдером HTTPS origin. api.example в примерах является обозначением, не рабочим сервером. Значения по умолчанию приведены для текущей версии; фактические настройки вашего API-ключа и webhook имеют приоритет.

Авторизация и контекст ​

Передавайте Authorization: Bearer $RESELLER_API_TOKEN, Accept: application/json, а для JSON-тела Content-Type: application/json. Храните токен только на сервере интеграции. Не передавайте его браузеру, в query string, тикете, публичном репозитории или ИИ вместе с документацией.

Формат ключа: rsl_test_{publicKeyId}.{secret} или rsl_live_{publicKeyId}.{secret}. publicKeyId содержит 24 латинские буквы/цифры, secret содержит 64 символа base64url. Ключ личного кабинета не заменяет reseller-ключ. Среду нельзя переключить заголовком или полем запроса. Для test и live нужны разные ключи, данные и настройки подключения.

Начните с GET /context: он не требует отдельного scope и возвращает среду, доступные scopes и data.credential.rate_limit_per_minute. На остальные методы нужны scopes, указанные в справочнике; требование нескольких scopes означает логическое И. Наличие domains.read не даёт права покупать или продлевать домен. Учитываются также IP allowlist, активность ключа, срок его действия, режим API, тариф и статус реселлера. Ограниченный реселлер может выполнять безопасные HTTP-методы, но не POST, даже если POST логически используется для проверки.

Ресурсы ограничены реселлером и средой. Чужой или недоступный ресурс обычно даёт 404, а не сведения о его владельце. Передавайте UUID ровно из ответов API: не все методы одинаково обрабатывают произвольный не-UUID, возможен 500 вместо ожидаемого 422.

Контрпример: поддержка в test ​

Не считайте, что test-ключ автоматически предоставляет песочницу для всех методов. Все четыре обработчика поддержки доступны только с live-ключом, независимо от наличия tickets.read/tickets.write:

МетодПутьРезультат с корректным test-ключом и нужным scope
GET/tickets403
GET/tickets/{ticketUuid}403
POST/tickets403
POST/tickets/{ticketUuid}/messages403
json
{"error":{"code":"forbidden","message":"Support is available only to live credentials.","details":[],"request_id":"11111111-1111-4111-8111-111111111111"}}

Для записей это ограничение проверяется также перед возвратом старого HTTP replay. /balance и /ledger тоже не являются тестовыми финансовыми ресурсами: test-ключ получает 403. Для проверки поддержки используйте согласованный с провайдером live-сценарий, не переключайте финансовые тесты на live автоматически.

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

ПараметрПоведение
Базовый лимит нового ключа120 запросов за 60 секунд, может быть изменён провайдером
Настраиваемый диапазон ключа1..10000 запросов за минутное окно
Область счётчикаОдна запись API-ключа, все её методы и источники IP вместе
ОкноФиксированное окно 60 секунд с первого учитываемого запроса; не календарная минута, не скользящий час и не token bucket
СбросПо окончании окна; последующие запросы его не продлевают
УчётЗапросы после успешной проверки ключа/реселлера, до проверки Idempotency-Key и scope; ошибки валидации, scope и HTTP replay тоже расходуют квоту
ОтказыРанний отказ авторизации/реселлера не расходует этот счётчик. Собственный 429 не добавляет hit и не продлевает паузу
Ротация API-ключаМеняет секрет существующего ключа, не обещает новый счётчик

При конкурентных запросах проверка квоты и увеличение счётчика раздельны. Не используйте кратковременное превышение как разрешённую ёмкость; ограничивайте параллелизм и распределяйте запросы равномерно. Это лимит REST API, не квота реестра и не гарантия скорости выполнения доменной операции.

После прохождения ограничителя возвращаются:

http
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 119
X-Request-ID: 11111111-1111-4111-8111-111111111111

На собственной ветке исчерпания квоты ответ имеет вид:

http
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
Retry-After: 42
X-Request-ID: 11111111-1111-4111-8111-111111111111
json
{"error":{"code":"rate_limit_exceeded","message":"API rate limit exceeded.","details":{"retry_after":42},"request_id":"11111111-1111-4111-8111-111111111111"}}

В этой ветке нет X-RateLimit-Limit и X-RateLimit-Remaining. На успешном ответе нет Retry-After. API самостоятельно не формирует X-RateLimit-Reset, RateLimit, RateLimit-Policy и RateLimit-Reset. Промежуточный proxy может добавить собственные заголовки или отдельный 429. Не требуйте отсутствующие заголовки в SDK. Сохранение чужих rate-limit заголовков при обработке ошибки не означает, что API их всегда выдаёт.

На 429 приостановите всю очередь данного ключа на Retry-After секунд плюс небольшой случайный запас. Если заголовка нет, используйте ограниченный exponential backoff с jitter. При Retry-After: 0 не запускайте busy loop. Для общего HTTP-клиента поддержите также HTTP-date от proxy; собственный ограничитель API возвращает целые секунды. X-RateLimit-Remaining является снимком, а не резервированием места для следующего запроса.

Идемпотентность ​

Idempotency-Key обязателен для всех POST, PUT, PATCH и DELETE, включая /domains/check, /quotes, /domains/{domainUuid}/sync, webhook test и ротацию секрета. GET/HEAD его не требуют. Значение обрезается по краям и должно содержать 1..160 символов. Рекомендуется UUID либо ваш уникальный идентификатор логической команды без персональных данных.

Сохраните до отправки: ключ, API-ключ/среду, HTTP-метод, путь, query string и исходные байты тела. SHA-256 отпечаток учитывает метод, путь с query string в его HTTP-нормализованном представлении и точные байты тела, а не семантически равный JSON. Не меняйте порядок полей, пробелы, число 1 на 1.0, пустое тело на {} и путь /orders на /domains при повторе. Заголовки языка и X-Request-ID в отпечаток не входят.

СитуацияРезультат
Первый запрос с новым ключомОбычная обработка
Тот же ключ и отпечаток, HTTP-ответ сохранёнТот же HTTP status/JSON, Idempotency-Replayed: true
Тот же ключ, другой отпечаток409 conflict
Тот же ключ, первая попытка ещё processing409 conflict
Нет/невалидный ключ422 validation_failed, details: []
Исключение или ответ 5xxСохранённый HTTP replay не гарантируется

Обычный TTL HTTP replay: 24 часа с момента первого резервирования, настраивается провайдером и не продлевается повторами. Истёкшая запись может быть удалена при следующем обращении с этим ключом. Зависшая processing-запись не имеет отдельного короткого автоматического срока разблокировки: не обходите 409 новым ключом после таймаута. Уточните состояние у поддержки.

Доменная асинхронная операция имеет дополнительную устойчивую привязку ключа к задаче, которая не равна HTTP TTL. Однако у других методов такой защиты может не быть. Никогда не используйте старый ключ для нового бизнес-действия, в том числе спустя 24 часа. После истечения TTL сначала сверяйте ресурсы по UUID/external_id и операции, а не создавайте заказ заново.

Проверка действительности Bearer и общего доступа выполняется перед replay, но свежая проверка scope конкретного метода не гарантируется для уже сохранённого ответа. Отзыв ключа закрывает доступ; простое снятие scope не следует использовать как гарантию немедленного запрета чтения ранее сохранённых ответов. Это важно для чувствительных ответов, например секрета webhook.

HTTP replay повторяет статус и JSON, но не является побайтовым воспроизведением всех заголовков. Idempotency-Replayed отсутствует на обычном ответе, а не равен false. X-Request-ID может быть новым при старом error.request_id в сохранённом теле. При ротации/создании webhook replay может снова показать секрет: исключите эти ответы из логирования и кешей общего пользования. Срок TTL не является обещанием физического удаления секрета из хранилища в ту же секунду.

Ошибки и безопасные решения ​

API использует собственный JSON-конверт, не application/problem+json и не RFC 9457:

json
{"error":{"code":"validation_failed","message":"The name field is required.","details":{"name":["The name field is required."]},"request_id":"11111111-1111-4111-8111-111111111111"}}

Пустые details выглядят как []; объект ошибок полей содержит массивы строк. На 429 объект содержит число retry_after. details: null для этого конверта не ожидается. request_id допускает null, если контекст не назначен. Само поле error в успешном JSON ресурса операции имеет другую семантику и может быть null; не смешивайте его с HTTP ErrorEnvelope.

HTTP / codeРешение интегратора
400 / request_failed, если конверт применёнИсправить запрос/JSON; не повторять неизменённым бесконечно
401 / unauthenticatedИсправить токен, срок, отзыв; не создавать новый заказ в ответ на ошибку авторизации
403 / forbiddenПроверить scopes, IP, режим/тариф/статус реселлера и live-only ограничения
404 / resource_not_foundПроверить путь, UUID, среду и владельца; также возможно отключение API
405 / request_failed, только если конверт применёнВыбрать документированный метод; маршрутизатор часто возвращает только message
409 / conflictОтличить конфликт ключа/обработку от состояния ресурса по локальному журналу и GET; не автоматический новый ключ
422 / validation_failedПоказать ошибки полей, исправить входные данные; для изменённого запроса использовать новый ключ после сверки
429 / rate_limit_exceededОбщая пауза ключа по Retry-After, затем повтор той же команды
500 и прочие 5xx / internal_error, если конверт применёнВозможен уже выполненный побочный эффект; сверка и ограниченный повтор с прежним ключом
Timeout, разрыв соединения, ответ не JSONРезультат неизвестен, не эквивалент failed; сохранить попытку и сверять

Неподдерживаемый метод, неизвестный маршрут, неверный запрос до маршрутизации, сетевой proxy или WAF могут вернуть другой JSON, HTML или пустое тело без X-Request-ID. SDK должен сначала проверить HTTP status и Content-Type и безопасно сохранить ограниченный диагностический фрагмент. Не считать любую ошибку декодирования JSON признаком отказа бизнес-операции.

Язык выбирается из поддерживаемых ru, en, kk: в первую очередь X-Language, затем X-Locale, затем доступный контекст/Accept-Language и язык по умолчанию. Региональный суффикс явного заголовка, например ru-RU, нормализуется. Не все сообщения переведены: английская строка возможна при русском запросе. code, имена полей и структура не меняются от языка. Ветвление по message.includes(...) запрещено в интеграции: несколько разных причин сейчас используют один conflict или forbidden.

Пример неопределённой операции ​

HTTP 202 подтверждает приём, не результат реестра. После ответа сохраните UUID задачи и опрашивайте GET /operations/{operationUuid}; webhook используйте как ускоряющий сигнал, не единственное доказательство.

json
{"data":{"uuid":"66666666-6666-4666-8666-666666666666","domain_uuid":"55555555-5555-4555-8555-555555555555","action":"renew","status":"uncertain","result":[],"error":{"code":"registry_result_unknown","message":"Registry result is not confirmed. Do not repeat this operation; reconciliation is scheduled."},"created_at":"2026-10-05T09:00:00+00:00","completed_at":null,"next_check_at":"2026-10-05T09:05:00+00:00"}}

Текущий обработчик REST-операций выставляет pending, processing, uncertain, completed и failed. Первые три не являются отказом. awaiting_registry учтён защитной проверкой занятости домена, но в текущем коде не найден процесс, который его выставляет; это не этап публичного жизненного цикла REST-операции. Если получите незнакомое состояние, сохраните его и сверяйте через GET/поддержку, не приравнивайте к failed.

Не освобождайте собственный резерв клиента и не создавайте вторую команду только из-за таймаута. Завершение completed и подтверждённый failed обрабатывайте по справочнику операции. Не все неопределённые результаты можно автоматически сверить: запросите ручную проверку, сохранив UUID задачи, домена и запроса. Регистрация создаётся через заказ; после неизвестного ответа ищите заказ/услугу по external_id и её состояние, не ожидайте, что у регистрации обязательно будет тот же публичный формат задачи управления доменом.

Алгоритм клиента ​

  1. До сетевого вызова сохраните неизменяемую команду и её Idempotency-Key в своей БД.
  2. Пропустите её через общую очередь лимита API-ключа. X-Request-ID задавайте отдельно на попытку.
  3. При 2xx сохраните ответ атомарно с локальной командой; 204 не декодируйте как JSON. При 202 запланируйте проверку UUID операции.
  4. При 429 выдержите паузу для всего ключа. При сетевой ошибке/5xx пометьте команду как unknown, сверяйте состояние и при допустимом повторе не меняйте ключ/байты.
  5. При 401/403/404/405/422 остановите автоматические повторы до исправления причины. При 409 сверяйте исходную команду и текущее состояние, не обходите блокировку.
  6. При uncertain продолжайте редкий polling, учитывая next_check_at и общую квоту. При длительном ожидании создайте обращение без секретов и повторной покупки.
  7. Не делайте вывод о регистрации, оплате или возврате средств только по отсутствию webhook.

Пагинация и журнал интегратора ​

Списки с cursor используют per_page=25 по умолчанию, диапазон 1..100. Передавайте next_cursor без изменений и с URL-кодированием. В webhook-списках ответ содержит только data и meta.next_cursor/per_page: поля total, page и last_page не обещаны. Некоторые другие ресурсы дополняют пагинацию links; проверяйте контракт конкретного метода. Отсутствующий курсор на первой странице и next_cursor: null на последней не одно и то же.

Храните у себя среду, метод/путь без секретов, время, HTTP status, код ошибки, X-Request-ID, Idempotency-Key, UUID заказа/домена/операции и результат сверки. Не пишите Authorization, signing_secret, auth-code, ЭЦП и персональные документы в общие логи. Ошибки авторизации до определения ключа могут не попасть в журнал запросов провайдера. Значение хранения журнала в конфигурации по умолчанию 90 дней не является подтверждённым SLA доступности/удаления логов и не заменяет ваш журнал.

Основания документации ​

Справочник описывает поля, ограничения и ответы, а руководство отдельно объясняет порядок интеграции: такое разделение соответствует Diataxis. Полнота описания публичных элементов и ясность формулировок взяты из Google AIP-192. Машинный контракт использует OpenAPI 3.1. Семантика HTTP и Retry-After опирается на RFC 9110, статус 429 на RFC 6585. Это принципы оформления, а не заявление о реализации всех Google AIP или Problem Details.