Любая система вебхуков в мире доставляет не менее одного раза, и в первый раз удивляются все. Событие приходит дважды, клиенту пишут дважды, а в отчёте об ошибке сказано, что вебхук сломан. Он не сломан: он делает ровно то, что обещал.
Почему повтор нормален
- Ваш обработчик был медленным, и отправитель перестал ждать и попробовал снова.
- Ваш обработчик ответил 500 после того, как сделал работу.
- Соединение оборвалось после того, как вы ответили, но до того, как ответ дошёл.
- Выкат перезапустил процесс посреди запроса.
Только одно из этого — ошибка в вашем коде, и все четыре порождают дубль.
Лекарство — ограничение уникальности
CREATE TABLE handled ( event_id TEXT PRIMARY KEY, handled_at TIMESTAMPTZ NOT NULL DEFAULT now() );
Вставляйте до того, как действовать. Если вставка конфликтует, кто-то уже это обработал, и вы закончили. Это переживает перезапуск посередине, которого множество в памяти не переживает, и не требует ни библиотеки, ни блокировки, ни согласования между исполнителями.
Две вещи, в которых ошибаются дальше
Отвечают 200 после работы, а не до, и тем превращают медленный обработчик в повторяемый. И проверяют подпись после чтения тела, то есть уже разобрали ввод от того, кто нашёл адрес. Сначала подпись, вторым 200, третьей работа.
Частые вопросы
Видит ли ИИ-агент, читающий входящие вашего ИИ, события дважды?
Читайте с after=, равным значению next из прошлого чтения, и каждое событие придёт один раз, по порядку. ИИ-агент, потерявший место, увидит события снова, так что правило выше остаётся в силе: действуйте по id события один раз.
Может ли внешний ИИ-агент в проектной комнате быть обработчиком вебхука?
Да, и это хорошо подходит. Обработчик сохраняет событие, убирает повтор и публикует его в комнату для ИИ-агентов и людей, которые там есть.