Перейти к содержанию
Версия контракта: 0.3.3 — спека

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

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

Зачем

Сеть рвётся посреди запроса. Клиент не знает, дошёл ли он: ответа нет, а сообщение могло уйти. Повтор без защиты — второе сообщение живому человеку; отказ от повтора — код, который абонент так и не получил.

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

Как пользоваться

Заголовок Idempotency-Key принимается на любом POST API: отправка и пакет, оценка стоимости, шаблоны и их предпросмотр, подписки на вебхуки (создание, включение, ротация секрета, пробная доставка). На GET и прочих методах он игнорируется. Главное — отправка: POST /v1/messages и POST /v1/messages/batch.

Значение — любая строка до 255 символов; UUID удобен, но не обязателен. Длиннее — 422 idempotency_key_reused с param: Idempotency-Key (ключ не обрезается: обрезанный склеил бы два разных запроса). Ключ свой на каждую попытку отправки, а не на сообщение и не на сессию. Повтор из-за обрыва идёт с тем же ключом; новая отправка — с новым.

Ключи раздельны по аккаунту и режиму (live/test): один и тот же ключ на боевом и тестовом ключе — два разных запроса. В отпечаток запроса входят метод, путь и тело: тот же ключ на другой ручке — это 422 idempotency_key_reused, как и на другое тело.

POST /v1/messages
Idempotency-Key: 0f3c9b21-1c4b-4f6a-9a8f-1b2c3d4e5f60

Ключ живёт 24 часа. Дальше он забывается, и запрос с ним выполнится заново.

Что отвечает хаб

Ситуация Ответ
Первый запрос обычный 202 с объектом сообщения
Повтор, то же тело тот же ответ, заголовок Idempotent-Replayed: true
Повтор, другое тело 422 idempotency_key_reused — ключ уже занят другим запросом
Первый запрос ещё выполняется 409 idempotency_conflict с retry_after

Аренда ключа — минута. Если процесс, выполнявший первый запрос, умер, ключ освободится сам, и повтор пройдёт как первый.

Что повторять с тем же ключом

  • 429 (превышен темп, суточный предел хаба, слишком много неудачных входов) — да, с тем же ключом: такие ответы к ключу не приколачиваются;
  • 5xx — да, с тем же ключом: наша неисправность не должна занимать ключ на сутки. Ключ освобождается сразу, и повтор не упрётся в 409; если освободить его не удалось (редкий случай — отказ самого хранилища), повтор до конца аренды — не больше минуты — получит 409 с retry_after. Сюда же относится 503 provider_unavailable на одноразовый код — он не принят, и повтор с тем же ключом и тем же client_ref примет его заново;
  • 409 idempotency_conflict — да, после retry_after;
  • 422 — нет. Запрос понятен и невыполним; повтор того же даст то же. Исправьте то, на что указывает code, и отправьте с новым ключом.

Пакеты

У пакета своя защита — client_ref на каждом элементе. Он обязателен: без него результат элемента не с чем сопоставить, а повтор пакета перестаёт быть безопасным.

Повтор пакета с теми же client_ref не создаёт вторых сообщений: элементы возвращаются с результатом duplicate и ссылкой на уже созданное сообщение. Idempotency-Key на пакете при этом тоже работает — но client_ref надёжнее: он переживает и частичный повтор, когда пакет пересобрали.

Ответ пакета, в котором есть элемент, отвергнутый по нашей причине (internal_error, provider_unavailable), к ключу не приколачивается: повтор с тем же ключом выполняется заново, принятые элементы возвращаются duplicate, отвергнутые принимаются.

Повтор по client_ref не тратит окно анти-флуда: он не создаёт сообщения.

Защита по client_ref держится, пока хранится само сообщение, — три года (см. Данные и сроки хранения). Повтор с тем же client_ref спустя этот срок создаст новое сообщение. Для ретраев это не ограничение, но переиспользовать один и тот же client_ref годами нельзя.