База знаний
Вебхуки
Сообщает вашему эндпоинту о приходе почты, вместо того чтобы заставлять вас опрашивать.
Подробности
- Доступны уже сегодня из «Настройки → Вебхуки» и через API: зарегистрируйте https-эндпоинт, выберите, какие из двадцати событий ему нужны, и скопируйте подписывающий секрет whsec_, который показывается при создании и при ротации и больше никогда. Доставки — это настоящие подписанные POST-запросы, которые поднимает сам почтовый ящик, а не какой-либо вызов API, поэтому они срабатывают на входящую почту, а также на открытия и клики, чем бы письмо ни было отправлено. Отправка срабатывает со всех поверхностей, а раньше срабатывала лишь с некоторых: отправка через API, MCP, шаблон или правило поднимала email.sent, а письмо, отправленное из окна написания в самом приложении, — нет, потому что это окно пишет в почтовый ящик напрямую, а не через сервис отправки, который поднимал событие. Теперь событие поднимается на самом почтовом ящике, там, где все они сходятся, поэтому написать в приложении, запланировать на вторник и отправить через API — это три способа вызвать один и тот же вебхук. Отложенная отправка сообщает о себе дважды: email.scheduled или email.queued при приёме, email.sent, когда она действительно уходит, и email.cancelled, если вы успели её забрать. Десять эндпоинтов на почтовый ящик, и это соблюдается везде, где эндпоинт регистрируется, а не только на этом экране.
- События делятся на три семейства. Пятнадцать относятся к одному письму: email.received, email.replied, email.sent, email.delivered, email.failed, email.cancelled, email.scheduled, email.queued (собрат scheduled со стороны отмены отправки), email.delivery_delayed, email.bounced, email.complained, email.suppressed, email.opened, email.clicked и email.downloaded. email.sent означает, что письмо принял сервис отправки, email.delivered — что его принял получающий сервер, а email.delivery_delayed — что оно ещё не дошло и попытки продолжаются. email.replied срабатывает вместе с email.received, когда пришедшее письмо отвечает на то, что уже есть в ящике, так что потребитель, которому нужны оба, получает оба. email.downloaded срабатывает, когда человек забирает файл, ушедший ссылкой на скачивание, причём тот же классификатор не пускает в счёт сканеры и генераторы превью ссылок, и оно не называет получателя, потому что ссылка одинакова для всех, кому ушло письмо. Три относятся к домену: domain.verified, когда он начинает принимать почту, domain.sending_changed, когда меняется вердикт по его отправке, и domain.deleted, когда он удалён — по вашей просьбе или потому, что семидневный сборщик убрал неподтверждённый. Два относятся к самому списку подавления, а это не то же самое, что email.suppressed: suppression.added, когда адрес туда попадает, и suppression.removed, когда его снова разрешают. Не подписаться ни на одно означает все события о письмах, кроме email.replied, сегодня четырнадцать, и никогда не семейство, добавленное позже, а API читает это обратно как ["*"]. Перечислите нужные события, если предпочитаете явность. Каждая доставка несёт X-OpenEmail-Signature в виде t=<unix>,v1=<hex> — HMAC-SHA-256 по отметке времени, точке и сырому телу, — а также X-OpenEmail-Event и X-OpenEmail-Delivery. Проверяйте по байтам в том виде, в каком они пришли: разбор и повторная сериализация переставляют ключи и ломают подпись. Окно защиты от повторов в 300 секунд обеспечивает получатель, и верификатор в SDK использует его по умолчанию.
- Регистрация отклоняется для всего, что не https или не маршрутизируемо в публичной сети (loopback, RFC1918, link-local, CGNAT и их эквиваленты в IPv6), а редиректы не выполняются, поэтому 3xx записывается как неудачная доставка, а не преследуется куда-то ещё. Получателю даётся 5 секунд, эндпоинты обслуживаются параллельно, так что десять из них всё равно стоят 5 секунд, а не 50, а недавние попытки перечислены на странице этого эндпоинта с кодом ответа и затраченным временем.
- Доставка предпринимается до пяти раз. Первая уходит в момент события; сбой, который правдоподобно может пройти сам, повторяется через 1 минуту, затем через 5, затем через 25, затем через 2 часа, что растягивает одно событие примерно на два с половиной часа. Повторы хранятся как надёжная работа, а не в памяти, поэтому деплой посреди этого окна их не теряет. Повторяются только те сбои, которые стоит повторять: таймаут, отказ в соединении, 408, 425, 429 или любой 5xx. Любой другой 4xx — это намеренный отказ эндпоинта от полезной нагрузки, и спросить ещё четыре раза значило бы вчетверо увеличить нагрузку ради того же ответа. Идентификатор события выпускается один раз, и каждая попытка несёт его в X-OpenEmail-Delivery, так что получатель, увидевший один и тот же идентификатор дважды, может отбросить второй, а не сработать дважды. После 100 событий подряд, у которых провалились все попытки, эндпоинт отключается, рабочему пространству отправляется письмо, а причину можно прочитать на самом эндпоинте. Эндпоинт, отвечающий 410 Gone, отключается сразу.
- Эндпоинт, провалившийся 100 раз подряд, выключается, а не обзванивается вечно, и всем, у кого есть доступ к вебхукам, отправляется письмо об этом: какой именно, что сообщила последняя попытка и что во время сбоев ничего не ставилось в очередь. Счётчик считает ПОДРЯД, и любая успешная попытка его сбрасывает, поэтому неудачный день в марте не может сложиться в отключённый эндпоинт сегодня. Включение обратно сбрасывает счётчик вместе с собой. Консоль различает эти два состояния, а не показывает один переключатель: эндпоинт, который выключили вы, выглядит иначе, чем тот, который выключили мы.
- Управление эндпоинтами — одна задача с двумя входными дверями. Через API это POST /webhooks, патч, удаление, ротация секрета, тест и журнал доставок, с методом на каждое в SDK; в приложении это «Настройки → Вебхуки», работающие с тем же реестром, а не со вторым. Чтение закрыто скоупом webhooks:read, так что любой, кто строит интеграцию, может видеть эндпоинты и историю их доставок (какой сработал, что ответил получатель, сколько это заняло), не будучи владельцем. Регистрация, изменение, тестирование, ротация и удаление требуют webhooks:write И владения почтовым ящиком, на обеих поверхностях, и вторая половина сделана намеренно: у эндпоинта нет оси адресов, поэтому он получает все адреса рабочего пространства с темами и получателями, и отсутствие разрешения означает «ему можно всё это отправлять». Роль, которая строит интеграции и не читает почту, управляет этим через ключ рабочего пространства.