Вебхуки
При каждом событии жизненного цикла письма MailInfra шлёт HTTP POST на URL, указанный в дашборде. Это альтернатива поллингу GET /v1/emails/{id}/events.
Создание, редактирование, ротация секрета и тестовая отправка — в дашборде: Проект → Вебхуки.
События
| Тип | Когда срабатывает |
|---|---|
queued | Письмо принято в очередь |
sending | Передача в MTA началась |
sent | Передано SMTP-серверу получателя |
delivered | Письмо доставлено |
clicked | Клик по отслеживаемой ссылке |
bounced | Недоставка; адрес попадает в список подавления |
complained | Жалоба на спам; адрес попадает в список подавления |
failed | Внутренняя ошибка отправки |
В дашборде можно подписаться на все события или на конкретное подмножество.
Формат входящего запроса
Заголовки
| Заголовок | Значение |
|---|---|
Content-Type | application/json |
User-Agent | MailInfra-Webhook/1.0 |
X-MailInfra-Event-Id | UUID события — для дедупликации между retry |
X-MailInfra-Signature | HMAC-SHA256 — обязательно проверяйте |
Тело
{
"id": "evt_018e1b7a-2c3d-7000-8d4e-5f6a7b8c9d0e",
"type": "email.delivered",
"created_at": "2026-05-11T20:15:30.123456Z",
"data": {
"email": {
"id": "018e1b7a-1111-7000-aaaa-111111111111",
"from": "hello@mail.example.ru",
"to": ["user@gmail.com"],
"subject": "Добро пожаловать!",
"tags": ["welcome"],
"status": "DELIVERED"
},
"event": {}
}
}
type всегда в формате email.<тип>. Для тестового пинга из дашборда — test.ping:
{
"id": "evt_test_ping",
"type": "test.ping",
"created_at": "2026-05-11T20:00:00Z",
"data": { "message": "This is a test webhook event from MailInfra" }
}
Что должен возвращать ваш endpoint
- Любой
2xxв течение 10 секунд — событие считается обработанным. - Тяжёлую обработку выносите в фоновую очередь: ответьте
200сразу, обрабатывайте после. - Не возвращайте
2xx, если данные не приняли — иначе вы потеряете событие.
Повторы и автоотключение
Если ваш endpoint вернул не-2xx или превысил таймаут, MailInfra повторяет доставку с экспоненциальной задержкой:
| Попытка | Задержка |
|---|---|
| 2-я | 1 минута |
| 3-я | 5 минут |
| 4-я | 30 минут |
| 5-я | 2 часа |
| 6-я | 6 часов |
После 5 последовательных неудач вебхук автоматически деактивируется. В дашборде он отобразится как «отключён» — там же его можно снова активировать после починки endpoint.
Чек-лист интеграции
- URL использует HTTPS
- Подпись проверяется на сыром теле запроса (до парсинга JSON)
-
X-MailInfra-Event-Idлогируется и используется для дедупликации - Endpoint отвечает
2xxв пределах 10 секунд - Тяжёлая обработка вынесена в фоновую задачу
- Секрет вебхука хранится в переменных окружения
Управление вебхуками
CRUD вебхуков, тестовый запуск и ротация секрета — часть Management API. Базовый префикс: /v1/organizations/{organization_id}/projects/{project_id}/webhooks.
| Метод | Путь | Минимальная роль | Что делает |
|---|---|---|---|
GET | /webhooks | VIEWER | Список вебхуков проекта |
POST | /webhooks | DEVELOPER | Создать вебхук, в ответе показывается secret |
GET | /webhooks/{id} | VIEWER | Детали вебхука без секрета |
PATCH | /webhooks/{id} | DEVELOPER | Изменить URL, события или is_active |
POST | /webhooks/{id}/rotate-secret | DEVELOPER | Сгенерировать новый секрет; старый сразу инвалидируется |
POST | /webhooks/{id}/test | DEVELOPER | Послать на endpoint синтетическое событие test.ping |
DELETE | /webhooks/{id} | DEVELOPER | Удалить вебхук |
Создать вебхук
POST /v1/organizations/{organization_id}/projects/{project_id}/webhooks
Authorization: Bearer <session_token>
Content-Type: application/json
{
"url": "https://api.example.com/hooks/mailinfra",
"events": ["delivered", "bounced", "complained"]
}
| Поле | Тип | Описание |
|---|---|---|
url | string (HTTPS) | Куда слать POST с событием |
events | string[] | Подписанные события: 1–20 значений из таблицы выше |
Ответ 201 Created:
{
"data": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"project_id": "...",
"url": "https://api.example.com/hooks/mailinfra",
"events": ["delivered", "bounced", "complained"],
"is_active": true,
"created_at": "2026-05-12T09:00:00Z",
"updated_at": "2026-05-12T09:00:00Z",
"secret": "whsec_a3f4e1c2d7b89012345678901234567890abcdef1234567890abcdef12345678"
}
}
secret возвращается только при создании и при ротации. Сохраните его как пароль — повторно получить нельзя. Без секрета вы не сможете проверять подпись.
Изменить вебхук
PATCH /v1/.../webhooks/{webhook_id}
Authorization: Bearer <session_token>
Content-Type: application/json
{
"events": ["delivered", "bounced"],
"is_active": true
}
Любые из полей url, events, is_active опциональны — обновляется только то, что передали. Чаще всего is_active: true нужен после автоматического отключения вебхука из-за серии неудач.
Тестовый прогон
POST /v1/.../webhooks/{webhook_id}/test
Authorization: Bearer <session_token>
{
"data": {
"success": true,
"http_status": 200,
"error": null
}
}
MailInfra сделает POST на ваш url со специальным событием test.ping и подпишет его текущим секретом. Удобно дёргать из CI после деплоя обработчика.
Ротация секрета
POST /v1/.../webhooks/{webhook_id}/rotate-secret
Authorization: Bearer <session_token>
{ "data": { "secret": "whsec_<новые_64_hex>" } }
Старый секрет немедленно перестаёт быть валидным — обновите переменную окружения до следующего события, иначе обработчик начнёт отвергать входящие как Invalid signature.