Pleep Docs

Исходящие вебхуки

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.

ПолеТипОписание
idstringИдентификатор события. Не меняется между повторными попытками — используйте его для дедупликации
eventstringИмя события
occurred_atstringВремя события (ISO 8601)
application_idstringИдентификатор приложения
dataobjectПолезная нагрузка, зависит от события

lead.status_changed

{
  "id": "6f1c9a2e-4b55-4a1e-9f0d-2b7c1d3e4f50",
  "event": "lead.status_changed",
  "occurred_at": "2026-08-21T10:00:00.000Z",
  "application_id": "7dbbac93-00db-46a7-be99-9fc45d3a705d",
  "data": {
    "thread_id": "thread_0765c2bd-980d-4c65-aeea-d04069646be5",
    "assistant_id": "6cc47669-8659-40ad-9d4d-bf914323120d",
    "phone": "77051234567",
    "channel": "WABA_COEXISTENCE",
    "from": { "id": "7f3a1c20-…", "name": "Визит назначен", "kind": "active" },
    "to":   { "id": "9c8b7a60-…", "name": "Продажа", "kind": "success" },
    "changed_by": "AI"
  }
}
ПолеТипОписание
thread_idstringИдентификатор диалога, тот же что в get-threads и в ссылке на диалог в мониторе
phonestring | nullНомер клиента, если у канала он есть
fromobject | nullЭтап, с которого ушёл лид. null, если этапа не было
toobject | nullНовый этап. null, если этап сняли
changed_bystring | nullКто передвинул: AI, OPERATOR, SCENARIO, SYSTEM или VERIFIED_OUTCOME

crm.status_changed

{
  "id": "8a2d7c11-9e33-4c22-b810-5f6a7b8c9d01",
  "event": "crm.status_changed",
  "occurred_at": "2026-08-21T10:05:00.000Z",
  "application_id": "7dbbac93-00db-46a7-be99-9fc45d3a705d",
  "data": {
    "thread_id": "thread_0765c2bd-980d-4c65-aeea-d04069646be5",
    "assistant_id": "6cc47669-8659-40ad-9d4d-bf914323120d",
    "provider": "amocrm",
    "entity_type": "lead",
    "entity_id": "4242",
    "pipeline_id": "77",
    "from_status_id": "141",
    "to_status_id": "142",
    "contact_phone": "77051234567"
  }
}

thread_id может быть null

Лид, заведённый прямо в CRM руками или импортом, ещё ни разу вам не писал, поэтому диалога у него нет. Это штатная ситуация, а не ошибка: в таком случае thread_id равен null, а единственная зацепка за человека — contact_phone. Обработчик должен это учитывать.

Проверка подписи

Подпись считается как HMAC-SHA256 от строки ${timestamp}.${тело}, где timestamp — значение заголовка X-Pleep-Timestamp, а тело — сырые байты запроса.

Проверяйте по сырому телу

Если распарсить JSON и собрать его обратно, порядок ключей и пробелы изменятся, и подпись не сойдётся. Это самая частая ошибка при подключении. Считайте подпись до разбора тела.

Время подписывается вместе с телом, поэтому перехваченный запрос нельзя переиграть позже: сверьте X-Pleep-Timestamp со своими часами и отклоняйте слишком старые.

const crypto = require('node:crypto');
 
function verify(rawBody, headers, secret) {
  const ts = headers['x-pleep-timestamp'];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
 
  const mac = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');
 
  const expected = Buffer.from(`sha256=${mac}`);
  const received = Buffer.from(headers['x-pleep-signature'] ?? '');
  if (expected.length !== received.length) return false;
  return crypto.timingSafeEqual(expected, received);
}

Повторные попытки

На любой ответ вне диапазона 2xx, а также на таймаут или сетевую ошибку, Pleep повторит доставку. Всего до 5 попыток с нарастающей паузой. Таймаут одного запроса — 10 секунд.

Отвечайте 2xx сразу, а тяжёлую обработку уносите в свою очередь. Обработчик должен быть идемпотентным по полю id: при повторе оно то же самое.

Каждая попытка попадает в журнал доставок в интерфейсе, так что видно и код ответа, и тело ошибки с вашей стороны.

Требования к эндпоинту

  • Только https://. Адреса на http://, localhost и в приватных диапазонах отклоняются.
  • Адрес проверяется не только при сохранении, но и перед каждой отправкой.
  • Выключенный эндпоинт не вызывается: попытка отмечается в журнале как skipped_inactive.

On this page