Концепция: Webhooks
PartyFlow поддерживает две стороны webhook-интеграций:
- Incoming — внешний сервис шлёт JSON в канал (Grafana, CI/CD, Sentry).
- Outgoing — PartyFlow шлёт событие на ваш HTTPS URL (сообщения, реакции, создание каналов). Подходит для ботов, AI/LLM-агентов, аудита.
Эта страница объясняет жизненный цикл каждой стороны и что нужно знать получателю, чтобы не терять события и не попасть в auto-disable.
Для payload'ов, headers и снипетов подписи см. reference/http-webhook-endpoint.md (incoming) и reference/outgoing-webhooks.md (outgoing).
Incoming webhooks
Жизненный цикл
create webhook → receive POST → verify (token + HMAC) → deliver to channel → log → (optional) auto-disable- Create — admin создаёт webhook в UI, привязывает к конкретному каналу. Получает URL
POST /api/v1/webhooks/incoming/{token}и signing secret (показываются один раз). - Receive — внешний сервис шлёт
POSTс JSON-телом. Endpoint проверяет токен в URL, затем (еслиrequire_signature: true— default) HMAC-подпись и timestamp window. - Format auto-detect — endpoint определяет Slack или Pachca формат по структуре payload. Один endpoint принимает оба.
- Deliver — сообщение попадает в канал как отдельное сообщение от имени webhook'а (с настроенным display name и иконкой). Push-уведомления применяются согласно
silent/severity/mentions[]. - Log — каждая попытка пишется в delivery log (status code, processing time, payload preview). Доступна admin'у через UI.
- Auto-disable — 100 подряд ошибочных ответов → webhook автоматически отключается, последующие запросы получают
HTTP 410 Gone, пока admin не включит webhook вручную. Счётчик сбрасывается на первом2xx.
Формат auto-detect
| Признак в payload | Формат |
|---|---|
text, attachments, blocks |
Slack |
message.content, message.entity_type |
Pachca |
| Ни то ни другое | HTTP 400 |
Endpoint определяет по содержимому, без заголовков — не нужно настраивать формат явно.
Модель доставки
- At-most-once со стороны PartyFlow: принятое и валидное сообщение гарантированно попадает в канал или возвращает клиенту
HTTP 5xx. - Дедупликация на стороне отправителя: если вы ретраите
5xx, PartyFlow не дедуплицирует запросы по содержимому. Либо делайте идемпотентный по смыслу payload, либо не ретраите. - Порядок сообщений внутри одного webhook'а сохраняется при последовательной доставке; параллельные отправки могут прийти в любом порядке.
Outgoing webhooks
Outgoing webhook — подписка на события PartyFlow. Когда случается событие в space'е (сообщение, реакция, новый канал), PartyFlow шлёт POST с JSON на ваш URL. Получатель отвечает 2xx → доставка успешна; любой другой ответ → retry по расписанию или (при исчерпании попыток) dead-letter.
Жизненный цикл одного события
chat event → match filters → sign payload → POST receiver → (2xx? done : retry) → dead-letter → auto-disable- Event — в канале создано сообщение / добавлена реакция / создан канал. PartyFlow формирует envelope с
event_id,event_type,space_id,conversation_id,actor_user_id,payload_json. - Match — для каждой активной подписки в space'е проверяются три фильтра:
event_types,channel_ids(пусто = все каналы),trigger(см. ниже). Несовпавшие подписки пропускаются. - Enrich (опционально) — если у подписки
include_context=true, подтягиваются последниеcontext_sizeсообщений канала (1..50). Полезно для AI/LLM-получателей. Ошибка enrichment не блокирует доставку — добавляется полеcontext_error: true. - Sign — формируется payload, считается HMAC подпись, тело и заголовки фиксируются для всех повторных попыток. Это гарантирует, что ретраи доставят точно те же bytes с той же подписью, даже если admin ротирует signing secret между попытками.
- Deliver — PartyFlow делает HTTPS
POSTполучателю. Ответ2xx→ success. Всё остальное → retry или dead-letter по правилам ниже. - Dead-letter — попытки исчерпаны (или получили не-ретраибельный 4xx). Доставка не повторяется. Счётчик consecutive failures webhook'а инкрементируется.
- Auto-disable — 100 подряд dead-letter'ов → webhook автоматически деактивируется. Admin получает уведомление. Новые события webhook больше не получает, пока не будет включён вручную.
Event types
| Canonical name | Когда шлётся |
|---|---|
MESSAGE_CREATED |
Новое сообщение в канале (включая сообщения ботов и webhook'ов) |
MESSAGE_UPDATED |
Сообщение отредактировано |
MESSAGE_DELETED |
Сообщение удалено |
REACTION_ADDED |
Добавлена реакция на сообщение |
REACTION_REMOVED |
Снята реакция |
CONVERSATION_CREATED |
Создан новый канал в space'е |
CONVERSATION_ARCHIVED |
Канал заархивирован |
MEMBER_JOINED и MEMBER_LEFT объявлены в API, но пока не публикуются (появятся в следующем релизе). Если вы подпишетесь на них, события не будут приходить — матчер сработает, когда publisher начнёт их эмитить.
Trigger modes
Для MESSAGE_CREATED и MESSAGE_UPDATED можно сузить доставку:
| Trigger | Когда срабатывает | Config |
|---|---|---|
all (default) |
На каждое совпавшее событие | {} |
mention |
Только если указанный бот @упомянут в сообщении | {"bot_id": "<uuid>"} |
keywords |
Только если сообщение содержит хотя бы одно ключевое слово как отдельный токен | {"keywords": ["error", "fail"], "case_sensitive": false} |
Keyword matching — по границам слов (\p{L}\p{N}): fail не срабатывает на failover или facepalm. По умолчанию регистронезависимый.
Для остальных event types trigger mention/keywords игнорируется — они имеют смысл только на сообщениях.
Channel filter
Поле channel_ids подписки фильтрует события по конкретным каналам:
- Пустой массив → все каналы space'а.
- Непустой → только перечисленные
conversation_id.
Threads наследуют conversation_id родительского канала, так что фильтр работает и для thread-сообщений.
Retry schedule
Расписание — экспоненциальный backoff, конфигурируемый на стороне платформы. Формула:
delay_before_attempt(N+1) = min(Initial · 2^(N-1), Max) ± JitterТекущие production-дефолты: Initial = 5s, Max = 1h, всего 8 попыток (первая + 7 ретраев), Jitter = ±20%. Клиент получает такую последовательность:
| Попытка | Задержка перед ней (база) | Кумулятивно с момента события |
|---|---|---|
| 1 | — (сразу) | T0 |
| 2 | 5s ±20% | ~T0 + 5s |
| 3 | 10s ±20% | ~T0 + 15s |
| 4 | 20s ±20% | ~T0 + 35s |
| 5 | 40s ±20% | ~T0 + 1m 15s |
| 6 | 80s ±20% | ~T0 + 2m 35s |
| 7 | 160s ±20% | ~T0 + 5m 15s |
| 8 | 320s ±20% | ~T0 + 10m 35s |
| — | После 8-й неудачной → dead-letter |
Полное retry-window (база, без jitter) — ~10 минут 35 секунд; с учётом ±20% jitter — примерно [8m 28s, 12m 42s]. Clamp Max = 1h при текущих значениях не срабатывает (320s < 3600s), но защищает от разгона, если админ платформы увеличит Initial или MaxAttempts.
Jitter — симметричный random в пределах ±20% базовой задержки, чтобы при восстановлении flappy-получателя все отложенные ретраи не попали ровно в одну миллисекунду.
Значения Initial, Max, MaxAttempts могут быть изменены оператором платформы. Если ваши алерты завязаны на "доставка не приходит за N минут = endpoint down", ориентируйтесь на полное retry-window, а не на конкретные числа попыток — расписание может расшириться в будущем без breaking-уведомления.
Ретраибельные статусы: 408, 429, 5xx, transport errors (DNS/TCP/TLS).
Не-ретраибельные: остальные 4xx (включая 400, 401, 403, 404, 422). Dead-letter сразу.
Если получатель прислал 429 или 503 с заголовком Retry-After, PartyFlow уважает указанное время (RFC 7231: delta-seconds или HTTP-date), ограничивая диапазоном 1s..1h. Некорректные значения игнорируются — fallback на exponential schedule.
Auto-disable
consecutive_failures инкрементируется на каждом dead-letter'е. При достижении 100 webhook автоматически отключается. Admin получает уведомление в UI, новые события больше не доставляются этой подписке. Включить обратно — вручную в Admin UI. Счётчик сбрасывается при первом 2xx.
Что гарантируется получателю
- At-least-once доставка: каждое матчнувшееся событие будет доставлено хотя бы один раз при нормальной работе. При сетевых сбоях возможны дубликаты — дедуплицируйте по
X-PartyFlow-Delivery-Id. - Frozen payload + frozen signature: ретраи приходят с точно тем же телом и теми же заголовками подписи. Если вы однажды поймали валидный запрос, все его ретраи пройдут ту же проверку подписи.
- HTTPS only в production. HTTP допускается только в dev-окружениях.
- Стабильное
event_idна все ретраи — это UUIDv7 исходного события,delivery_id— отдельный UUID каждого row'а в очереди, но внутри одного row не меняется между попытками.
Что НЕ гарантируется
- Порядок событий между разными
conversation_id. События из одного канала обычно приходят в порядке возникновения, но это не строгая гарантия — retry одного события может запоздать на час и прийти после следующего. - Доставка событий, случившихся пока webhook был отключён. Auto-disable не буферизует — пропущенные события не перепосылаются после включения.
- Доставка за границу space'а. Подписка изолирована по
space_id; события из других space'ов не попадают.
Что дальше
- reference/outgoing-webhooks.md — полный payload envelope, headers, примеры для каждого event type.
- concepts/security.md — подписи, проверка на стороне получателя, best practices.
- guides/verify-signatures.md — code snippets для верификации (и отправки для incoming).
- concepts/rate-limits.md — что делать при 429, как устроен auto-disable.