Справочник
События вебхуков
Каждое событие приходит в одном формате, в поле 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"
}