Исходящие вебхуки
Pleep вызывает ваш эндпоинт, когда лид меняет этап в воронке или сделка меняет стадию в вашей CRM
Исходящие вебхуки позволяют не опрашивать API, а получать события в момент, когда они происходят. Pleep отправляет POST с JSON на ваш HTTPS-эндпоинт и подписывает каждый запрос.
Это не то же самое, что входящие вебхуки
Входящие вебхуки — когда внешняя система вызывает Pleep, чтобы разбудить агента в диалоге. Здесь наоборот: Pleep вызывает вас.
Настройка
Откройте Настройки → Разработчикам, раздел «Исходящие вебхуки». Укажите название, HTTPS-адрес и события, на которые подписываетесь. После сохранения один раз показывается секрет — сохраните его, повторно он не отображается.
Там же видно последние 20 попыток доставки с кодом ответа, телом и текстом ошибки, а эндпоинт можно временно выключить или сменить ему секрет.
События
| Событие | Когда срабатывает |
|---|---|
lead.status_changed | Лид перешёл на другой этап воронки Pleep. Срабатывает независимо от того, кто его передвинул: AI-агент, оператор в интерфейсе, сценарий или вызов set-status |
crm.status_changed | Сделка или лид сменил стадию во внешней CRM: amoCRM или Битрикс24 |
crm.status_changed требует подключённой внешней CRM
Это событие транслирует изменения из amoCRM или Битрикс24. Если вы ведёте воронку внутри Pleep и внешняя CRM не подключена, событие не сработает никогда. Для воронки Pleep подписывайтесь на lead.status_changed.
Формат запроса
Заголовки
| Заголовок | Описание |
|---|---|
X-Pleep-Event | Имя события |
X-Pleep-Delivery | Идентификатор события, совпадает с полем id в теле |
X-Pleep-Timestamp | Время отправки, unix-секунды |
X-Pleep-Signature | Подпись вида sha256=<hex> |
Конверт
Тело любого события имеет одинаковую обёртку, полезная нагрузка лежит в data.
| Поле | Тип | Описание |
|---|---|---|
id | string | Идентификатор события. Не меняется между повторными попытками — используйте его для дедупликации |
event | string | Имя события |
occurred_at | string | Время события (ISO 8601) |
application_id | string | Идентификатор приложения |
data | object | Полезная нагрузка, зависит от события |
lead.status_changed
| Поле | Тип | Описание |
|---|---|---|
thread_id | string | Идентификатор диалога, тот же что в get-threads и в ссылке на диалог в мониторе |
phone | string | null | Номер клиента, если у канала он есть |
from | object | null | Этап, с которого ушёл лид. null, если этапа не было |
to | object | null | Новый этап. null, если этап сняли |
changed_by | string | null | Кто передвинул: AI, OPERATOR, SCENARIO, SYSTEM или VERIFIED_OUTCOME |
crm.status_changed
thread_id может быть null
Лид, заведённый прямо в CRM руками или импортом, ещё ни разу вам не писал, поэтому диалога у него нет. Это штатная ситуация, а не ошибка: в таком случае thread_id равен null, а единственная зацепка за человека — contact_phone. Обработчик должен это учитывать.
Проверка подписи
Подпись считается как HMAC-SHA256 от строки ${timestamp}.${тело}, где timestamp — значение заголовка X-Pleep-Timestamp, а тело — сырые байты запроса.
Проверяйте по сырому телу
Если распарсить JSON и собрать его обратно, порядок ключей и пробелы изменятся, и подпись не сойдётся. Это самая частая ошибка при подключении. Считайте подпись до разбора тела.
Время подписывается вместе с телом, поэтому перехваченный запрос нельзя переиграть позже: сверьте X-Pleep-Timestamp со своими часами и отклоняйте слишком старые.
Повторные попытки
На любой ответ вне диапазона 2xx, а также на таймаут или сетевую ошибку, Pleep повторит доставку. Всего до 5 попыток с нарастающей паузой. Таймаут одного запроса — 10 секунд.
Отвечайте 2xx сразу, а тяжёлую обработку уносите в свою очередь. Обработчик должен быть идемпотентным по полю id: при повторе оно то же самое.
Каждая попытка попадает в журнал доставок в интерфейсе, так что видно и код ответа, и тело ошибки с вашей стороны.
Требования к эндпоинту
- Только
https://. Адреса наhttp://,localhostи в приватных диапазонах отклоняются. - Адрес проверяется не только при сохранении, но и перед каждой отправкой.
- Выключенный эндпоинт не вызывается: попытка отмечается в журнале как
skipped_inactive.