Справочник

События вебхуков

Каждое событие приходит в одном формате, в поле data.object - объект целиком, как в ответе API. Формат, подпись и повторы описаны в разделе Вебхуки.

Тип Когда приходит data.object
conversation.created Начался новый диалог Диалог
conversation.closed Диалог закрыли Диалог
conversation.reopened Закрытый диалог снова открыт Диалог
conversation.escalated Нейро передал диалог сотрудникам Диалог
message.created Новое сообщение в диалоге Сообщение
message.updated Изменился статус сообщения или догрузилось вложение Сообщение
contact.created Новый контакт Контакт
contact.updated Изменились данные контакта Контакт
contact.deleted Контакт удалён Заглушка {object, id, deleted}
lead.created Новая заявка на звонок Заявка
lead.updated Изменился статус заявки, итог звонка или оценка Заявка
appointment.created Новая запись Запись
appointment.updated Изменился статус визита или контакт записи Запись
appointment.rescheduled Запись перенесли Запись
appointment.cancelled Запись отменили Запись

Общее для всех событий:

  • Тип события - это факт, а data.object - снимок объекта через 1-3 секунды после изменения. Актуальное состояние читайте через GET.
  • Порядок событий не гарантирован, одно событие может прийти дважды. Отсекайте повторы по id события, а свежесть объекта сравнивайте по updated_at - подробнее в разделе Дубли, порядок и свежесть данных.
  • updated_at объекта меняется и без события, когда меняются служебные данные, которых в объекте нет. Событие приходит, только когда изменилось поле объекта.
  • Новые типы добавляются без смены версии API. Событие незнакомого типа пропускайте и отвечайте на него 2xx.
  • Чтобы подписать адрес на группу событий через API или видеть её в /v1/events, ключу нужно право чтения раздела - см. Права. В личном кабинете это ограничение не действует.

В примерах ниже показан только data.object, и объект сокращён до полей, важных для события. Приходит он целиком, со всеми полями из справочника.

Диалоги

Нужное право: conversations:read.

conversation.created

Начался новый диалог в любом канале: чат на сайте, мессенджер, почта. Сюда же относятся комментарии в соцсетях, переданные сотрудникам, - у них канал image.

Если клиент пишет после того, как закрытый диалог ушёл в архив, начинается новый диалог с новым id, и приходит conversation.created, а не conversation.reopened.

{
  "object": "conversation",
  "id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "project_id": "019cdbe8-e800-76c7-bd8d-a1cee5cdcca0",
  "channel": "telegram",
  "status": "open",
  "customer_name": "Анна Смирнова",
  "message_count": 1,
  "created_at": "2026-09-28T09:12:03Z"
}

conversation.closed

Диалог закрыли - неважно кто: сотрудник в личном кабинете, ваша система через API, Нейро, сам клиент или автоматически, когда клиент долго молчит.

  • Кто закрыл диалог, в объекте не указано. Поле closed_reason - почему закрыт, если причину указали.
  • Если диалог открыли снова через секунду-другую, событие может прийти уже со статусом open. Кроме него придёт conversation.reopened - в любом порядке.
{
  "object": "conversation",
  "id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "status": "closed",
  "closed_reason": "resolved",
  "closed_at": "2026-09-28T09:40:18Z",
  "updated_at": "2026-09-28T09:40:18Z"
}

conversation.reopened

Закрытый диалог снова открыт:

  • клиент написал в закрытый диалог в течение примерно 2 часов после закрытия;
  • сотрудник ответил в закрытом диалоге или открыл его заново в личном кабинете;
  • сотрудник написал клиенту прямо в мессенджере или сообществе, минуя личный кабинет;
  • Нейро ответил в диалог, который закрыли, пока готовился ответ.

Если клиент пишет позже, начинается новый диалог - придёт conversation.created. Если сотрудник ответил в диалог, который уже ушёл в архив, придёт conversation.reopened, но следующее сообщение клиента всё равно начнёт новый диалог.

Возврат из статусов snoozed и spam в open этого события не даёт. Через API закрытый диалог открыть нельзя: сообщение в него получит 409 conversation-closed.

{
  "object": "conversation",
  "id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "status": "open",
  "closed_reason": null,
  "closed_at": null,
  "message_count": 7,
  "last_message_at": "2026-09-28T10:05:51Z"
}

conversation.escalated

Нейро передал диалог сотрудникам. Причину передачи событие не содержит.

{
  "object": "conversation",
  "id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "channel": "web",
  "status": "open",
  "message_count": 4
}

message.created

Новое сообщение в диалоге - любое:

  • от клиента;
  • от сотрудника;
  • отправленное через API, в том числе вашей же системой. У таких сообщений source - api, по нему их удобно пропускать;
  • ответ Нейро, у него author_type - ai;
  • сообщение, которое сотрудник написал прямо в мессенджере или сообществе, минуя личный кабинет.

В объекте сообщения нет project_id - проект есть в самом событии.

  • status у сообщения клиенту - на момент формирования события: обычно уже delivered или failed, иногда pending. Итог отправки придёт в message.updated.
  • Ссылка на вложение в attachments[].url временная: она действует 10 минут с момента отправки события. Свежую ссылку отдают GET /v1/events/{id} и GET /v1/conversations/{id}/messages.
  • Файл от клиента может ещё загружаться из канала - тогда url равен null, а когда файл загрузится, придёт message.updated.
{
  "object": "message",
  "id": "01a0e5a1-4c5d-7e6f-9a7b-8c9d0e1f2a3b",
  "conversation_id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "direction": "inbound",
  "author_type": "customer",
  "source": null,
  "text": "Здравствуйте! Можно записаться на завтра?",
  "attachments": [],
  "status": null,
  "created_at": "2026-09-28T09:12:03Z"
}

message.updated

Изменилось уже отправленное сообщение:

  • итог отправки сообщения клиенту: pending сменился на delivered или failed. При failed причина - в failure.code;
  • клиент прочитал сообщение в чате на сайте: delivered сменился на read. Отметку о прочтении даёт только чат на сайте;
  • догрузилось вложение от клиента: у него появилась ссылка url, а у писем в attachments может добавиться новое вложение.

Учтите при обработке:

  • message.updated может прийти раньше message.created того же сообщения. Обрабатывайте оба события по id сообщения, в любом порядке.
  • Если на этот же адрес уже пришло message.created с итоговым статусом, message.updated с тем же статусом на него не придёт.
  • У сообщения нет updated_at. Чтобы не откатить статус назад, не заменяйте read на delivered или pending.
  • В канале image статус delivered значит, что ответ принят к публикации в соцсети, а не что он уже опубликован. Если соцсеть потом не опубликует ответ, message.updated об этом не придёт.
  • Если клиент отредактировал или удалил сообщение в мессенджере, события нет: такие правки в диалог не попадают.
{
  "object": "message",
  "id": "01a0e5a1-5d6e-7f7a-8b8c-9d0e1f2a3b4c",
  "conversation_id": "01a0e5a1-3b4c-7d5e-8f6a-7b8c9d0e1f2a",
  "direction": "outbound",
  "author_type": "operator",
  "source": "api",
  "text": "Да, завтра в 11:00 свободно.",
  "status": "failed",
  "failure": { "code": "recipient_unavailable" },
  "created_at": "2026-09-28T09:13:40Z"
}

Клиенты

Нужное право: contacts:read.

Импорт контактов из CSV событий не создаёт - ни на новые контакты, ни на изменённые. Служебное приведение телефонов к формату E.164 тоже проходит без событий. Но если после него у двух контактов проекта совпал телефон и их слили, события приходят, как при любом слиянии дублей: contact.deleted и, если поля оставшегося контакта изменились, contact.updated.

contact.created

Появился новый контакт: его создали в личном кабинете или через API, либо Dialogi создал его сам, например из диалога или заявки на звонок.

{
  "object": "contact",
  "id": "01a0d7f0-b6c0-7a1a-82e4-2cab98a901c7",
  "project_id": "019cdbe8-e800-76c7-bd8d-a1cee5cdcca0",
  "name": "Анна Смирнова",
  "phone": "+79101234567",
  "lifecycle": "lead",
  "source": "widget_lead",
  "created_at": "2026-09-28T09:12:05Z"
}

contact.updated

Изменилось поле, которое есть в объекте контакта: имя, телефон, email, этап и другие. Изменения, которых в объекте нет, например смена ответственного сотрудника, события не дают.

Событие приходит и когда контакт дополнился сам: клиент назвал телефон в диалоге, и Dialogi записал его в контакт. При слиянии дублей присоединённый контакт получает contact.deleted, а тот, что остался, - contact.updated, если у него изменились поля, например email или телефон перешли от присоединённого. Так же и при слиянии, которое Dialogi сделал сам, узнав в двух контактах проекта одного человека.

{
  "object": "contact",
  "id": "01a0d7f0-b6c0-7a1a-82e4-2cab98a901c7",
  "email": "anna@example.com",
  "lifecycle": "customer",
  "updated_at": "2026-09-28T10:20:44Z"
}

contact.deleted

Контакт удалили в личном кабинете или присоединили к другому при слиянии дублей - вручную или автоматически, когда Dialogi узнал в двух контактах проекта одного человека.

Вместо контакта приходит заглушка - удалённый контакт через API уже не прочитать, GET ответит 404:

{
  "object": "contact",
  "id": "01a0d3f0-aa80-7592-ae85-1a0309a9fd64",
  "deleted": true
}

Если при слиянии у оставшегося контакта изменились поля, он придёт отдельно в contact.updated. Связать их по событию нельзя - если нужно, найдите оставшийся контакт по телефону или email.

Заявки на звонок

Нужное право: leads:read.

lead.created

Новая заявка на обратный звонок - с виджета на сайте или через API. Повтор запроса с тем же Idempotency-Key и повторная заявка с того же номера, которую Перезвони объединил с первой, второго события не дают.

Объект собирается через 1-3 секунды после создания, поэтому заявка может прийти уже со статусом queued или in_progress.

{
  "object": "lead",
  "id": "01a0e3b4-6a10-7c2d-9e4f-0a1b2c3d4e5f",
  "project_id": "019cdbe8-e800-76c7-bd8d-a1cee5cdcca0",
  "phone": "+79101234567",
  "name": "Анна",
  "status": "queued",
  "source": "widget",
  "call": null,
  "created_at": "2026-09-28T11:01:29Z"
}

lead.updated

Изменилась заявка:

  • статус: in_progress, completed, missed, scheduled, cancelled, not_called. Переход в queued события не даёт - через доли секунды заявка уходит в in_progress;
  • итог звонка в call: чем закончился разговор, в том числе итог разговора с Нейро в call.ai_outcome - он может прийти отдельным событием. Порядок этого события и события со статусом completed не гарантирован: сравнивайте updated_at и не затирайте заполненный ai_outcome значением null из более старого снимка;
  • оценка клиента в rating;
  • сотрудник отметил заявку обработанной - handled_at.

Шаги внутри одного звонка, например «сотрудник ответил», отдельных событий не дают - итог приходит, когда звонок закончился.

Окончательные статусы - completed, cancelled, not_called и missed, если попыток больше не будет. После missed может прийти scheduled (повтор назначен на время) или in_progress (новая попытка дозвониться), поэтому missed не считайте концом заявки сразу. Оценка и handled_at приходят и после окончательного статуса.

updated_at заявки может меняться и без события - по служебным полям, которых нет в объекте.

{
  "object": "lead",
  "id": "01a0e3b4-6a10-7c2d-9e4f-0a1b2c3d4e5f",
  "status": "completed",
  "status_reason": null,
  "call": {
    "started_at": "2026-09-28T11:01:31Z",
    "answered_at": "2026-09-28T11:01:38Z",
    "connected_at": "2026-09-28T11:01:44Z",
    "ended_at": "2026-09-28T11:04:02Z",
    "duration_sec": 138,
    "handled_by": "team",
    "ai_outcome": null
  },
  "rating": null,
  "updated_at": "2026-09-28T11:04:03Z"
}

Записи

Нужное право: appointments:read.

appointment.created

Новая запись из любого источника: виджет или страница записи, сотрудник в личном кабинете, Нейро в чате или по телефону, API. Источник - в поле source.

Сюда же относится место из листа ожидания, которое держат для клиента, пока он не подтвердит запись. Такая запись приходит со статусом pending. Когда клиент подтвердит, придёт appointment.updated со статусом confirmed. Если не подтвердит или откажется - appointment.cancelled.

{
  "object": "appointment",
  "id": "01a0e6c2-7e8f-7a9b-8c0d-1e2f3a4b5c6d",
  "project_id": "019cdbe8-e800-76c7-bd8d-a1cee5cdcca0",
  "service_name": "Стрижка",
  "status": "confirmed",
  "source": "widget",
  "starts_at": "2026-09-29T08:00:00Z",
  "ends_at": "2026-09-29T09:00:00Z",
  "timezone": "Europe/Moscow",
  "customer_name": "Анна Смирнова",
  "customer_phone": "+79101234567"
}

appointment.updated

Изменилась запись, но это не перенос и не отмена:

  • статус визита: arrived - клиент пришёл, completed - визит состоялся, no_show - клиент не пришёл;
  • клиент подтвердил место из листа ожидания: pending сменился на confirmed;
  • запись связали с контактом CRM позже, чем создали: заполнилось contact_id.

Повторная отметка того же статуса визита даёт ещё одно appointment.updated, только если объект записи изменился с прошлого события о ней, отправленного на этот адрес. Если не изменился, события нет.

updated_at записи может меняться и без события - например, когда клиенту отправляется письмо о записи.

{
  "object": "appointment",
  "id": "01a0e6c2-7e8f-7a9b-8c0d-1e2f3a4b5c6d",
  "status": "completed",
  "contact_id": "01a0d7f0-b6c0-7a1a-82e4-2cab98a901c7",
  "updated_at": "2026-09-29T09:02:11Z"
}

appointment.rescheduled

Запись перенесли на другое время. В объекте уже новые starts_at и ends_at.

{
  "object": "appointment",
  "id": "01a0e6c2-7e8f-7a9b-8c0d-1e2f3a4b5c6d",
  "status": "confirmed",
  "starts_at": "2026-09-30T12:00:00Z",
  "ends_at": "2026-09-30T13:00:00Z",
  "updated_at": "2026-09-28T15:30:09Z"
}

appointment.cancelled

Запись отменили - кто бы это ни сделал: клиент, сотрудник, Нейро, ваша система через API или Dialogi автоматически. Сюда же относится место из листа ожидания, которое клиент не подтвердил вовремя или от которого отказался.

Кто отменил - в cancelled_by, причина - в cancel_reason. У отменённой записи resource_ids пустой: время освобождено.

{
  "object": "appointment",
  "id": "01a0e6c2-7e8f-7a9b-8c0d-1e2f3a4b5c6d",
  "status": "cancelled",
  "resource_ids": [],
  "cancelled_by": "client",
  "cancel_reason": "Не получается прийти",
  "updated_at": "2026-09-29T06:15:27Z"
}