Руководство

Вебхуки Deskgram: справочник API

Локальный REST API вебхуков Deskgram, форматы событий, заголовки, HMAC-подпись, ошибки и повторная доставка.

settings25 минут2026-10-15

Ключевые выводы

  • Два направления запросов
  • Быстрый старт API
  • Объект endpoint
  • Методы API

Обзор модуля вебхуков

Актуальность: 15 октября 2026 года. Описана реализованная версия, а не план развития.

Для настройки без программирования используйте пользовательское руководство.

Содержание

Два направления запросов

НаправлениеКто вызываетАдресНазначение
Локальный API управленияВаш локальный скрипт → Deskgramhttp://127.0.0.1:5000/api/webhooksСоздать endpoint, изменить настройки, посмотреть журнал
Исходящий вебхукDeskgram → ваш получательURL из настроек endpointПередать событие POST-запросом с JSON

127.0.0.1:5000 — пример адреса локального backend. Используйте адрес и порт своей установки. Это не API облачного сервера лицензий и не MCP-сервер.

Граница доступа: у описанных REST-маршрутов нет отдельной проверки Bearer-токена. Они предназначены для доверенного локального окружения. Не открывайте порт backend в интернет ради вебхуков. Исходящая доставка не требует входящего доступа к компьютеру с Deskgram.

Локальные ответы возвращаются в JSON. Для POST/PUT с настройками передавайте Content-Type: application/json. Если перед вашим backend установлен собственный шлюз авторизации, его правила настраиваются отдельно.

Быстрый старт API

Ниже команды curl для shell с поддержкой одинарных кавычек; в PowerShell используйте curl.exe и учтите правила передачи JSON вашей версии оболочки. Все адреса, ID и секреты в примерах вымышлены.

  1. Создайте выключенный endpoint, чтобы сначала проверить настройки:
curl -X POST 'http://127.0.0.1:5000/api/webhooks/endpoints' \  -H 'Content-Type: application/json' \  --data '{"name":"CRM: рассылка","url":"https://receiver.example.com/deskgram","secret":"YOUR_SHARED_SECRET","events":["message.sent","message.failed","lead.reply_received"],"module_filter":["spam_pm"],"payload_template":"deskgram","enabled":false}'
  1. Из ответа возьмите endpoint.id. Для условного ID 12 отправьте тест:
curl -X POST 'http://127.0.0.1:5000/api/webhooks/endpoints/12/test'
  1. Проверьте success, delivery.responsestatuscode, delivery.response_body и фактический прием запроса. Тест не содержит реального лида и может быть отклонен бизнес-валидацией CRM.
  2. Включите endpoint в интерфейсе или передайте полный объект настроек через PUT с enabled: true. Не отправляйте только enabled: PUT не является частичным обновлением.
  3. Выполните соответствующее реальное действие и проверьте журнал:
curl 'http://127.0.0.1:5000/api/webhooks/deliveries?endpoint_id=12&limit=20'

Объект endpoint

Поля запроса создания и обновления

Одна модель используется в POST и PUT.

ПолеТипПо умолчаниюПравила
namestringОбязательное1–120 символов до удаления крайних пробелов; используйте непустое имя
urlstringОбязательное8–2000 символов, начинается с http:// или https://; рекомендуется HTTPS
methodstringPOSTПоддерживается только POST
secretstring или nullnull при созданииОбщий секрет; крайние пробелы удаляются. При PUT отсутствие поля сохраняет секрет, null или пустая строка очищает
headersobject со строковыми значениями{}Дополнительные заголовки получателя; не ключи доступа к Deskgram
eventsarray of string["task.completed"]Шесть событий из каталога или "*"; пустой список заменяется на task.completed
module_filterarray of string[]Пустой список означает все модули; например ["spam_pm"]
payload_templatestringdeskgramdeskgram, shortresult, crmlead, custom
payload_mappingobject{}Настройка выходных полей; формат ниже
enabledbooleantrueРазрешает автоматическую отправку выбранных событий
timeout_secondsnumber15От 3 до 60 секунд

Для обычного endpoint timeout применяется к сетевым операциям HTTP-клиента, а не является строгим пределом времени всего API-вызова. Тест и повтор ждут завершения доставки, включая автоматические попытки и паузы.

Имена модулей нормализуются в нижний регистр, - заменяется на _; дубликаты событий и модулей удаляются. "*" означает все поступающие в этот механизм события, но не включает отсутствующие интеграции модулей.

Поля ответа

Endpoint содержит все перечисленные настройки, а также id (integer), hassecret (boolean), createdat и updated_at (дата/время UTC). Поле secret всегда возвращается как null, даже если секрет сохранен.

Важно: значения в headers локальный REST API возвращает без маскирования. Ответ может содержать токены получателя; не публикуйте его в логах или обращениях в поддержку.

Пример ответа создания:

{  "success": true,  "endpoint": {    "id": 12,    "name": "CRM: рассылка",    "url": "https://receiver.example.com/deskgram",    "method": "POST",    "secret": null,    "has_secret": true,    "headers": {},    "events": ["message.sent", "message.failed", "lead.reply_received"],    "module_filter": ["spam_pm"],    "payload_template": "deskgram",    "payload_mapping": {},    "enabled": false,    "timeout_seconds": 15.0,    "created_at": "2026-10-15T10:00:00Z",    "updated_at": "2026-10-15T10:00:00Z"  }}

Методы API

Все пути ниже включают /api. Для успешного выполнения используется HTTP 200, в том числе при создании и удалении. endpointid и deliveryid — целочисленные ID соответствующих записей.

GET /api/webhooks/endpoints

Список endpoint-ов от новых к старым по времени создания. Параметров пагинации нет.

Ответ: {"endpoints": [...]}, каждый элемент — объект endpoint. Пустой список: {"endpoints": []}.

POST /api/webhooks/endpoints

Создание endpoint. Тело — модель настроек из раздела выше. Ответ: {"success": true, "endpoint": {...}}.

При пропуске enabled endpoint сразу включен. Для безопасной первичной настройки явно передайте false.

PUT /api/webhooks/endpoints/{endpoint_id}

Обновление endpoint. Требует name, url и остальные актуальные настройки, которые нужно сохранить. Отсутствующие поля сбрасываются к значениям по умолчанию, кроме secret.

Не пересылайте объект ответа целиком без обработки: его secret: null при PUT удалит сохраненный секрет. Для сохранения секрета исключите ключ secret из запроса.

Пример включения существующего endpoint с сохранением секрета:

{  "name": "CRM: рассылка",  "url": "https://receiver.example.com/deskgram",  "method": "POST",  "headers": {},  "events": ["message.sent", "message.failed", "lead.reply_received"],  "module_filter": ["spam_pm"],  "payload_template": "deskgram",  "payload_mapping": {},  "enabled": true,  "timeout_seconds": 15}

Ответ: {"success": true, "endpoint": {...}}. Несуществующий endpoint: HTTP 404.

DELETE /api/webhooks/endpoints/{endpoint_id}

Удаление endpoint. Тело не требуется. Ответ: {"success": true, "endpoint_id": 12}. Несуществующий endpoint: HTTP 404.

История доставок остается. Удаление не отменяет уже начавшуюся отправку; повтор истории после удаления endpoint невозможен.

POST /api/webhooks/endpoints/{endpoint_id}/test

Отправка тестового события только выбранному endpoint. Тело не требуется; передать произвольный тестовый payload через этот метод нельзя.

Тест работает также для выключенного endpoint, независимо от подписки на события и фильтра модулей. Применяет текущий mapping. Возвращает результат после отправки и автоматических повторов.

Пример ответа при отказе получателя с HTTP 403, для стандартного формата:

{  "success": false,  "delivered": 0,  "delivery_id": 45,  "status": "error",  "delivery": {    "id": 45,    "endpoint_id": 12,    "event_type": "webhook.test",    "task_id": null,    "status": "error",    "attempts": 3,    "response_status_code": 403,    "response_body": "Forbidden",    "error": "HTTP 403",    "created_at": "2026-10-15T10:01:00Z",    "updated_at": "2026-10-15T10:01:07Z",    "delivered_at": null,    "request_payload": {      "schema_version": "1.0",      "event": "webhook.test",      "occurred_at": "2026-10-15T10:01:00Z",      "source": "deskgram",      "test": true,      "endpoint": {"id": 12, "name": "CRM: рассылка"}    },    "source_payload": {      "schema_version": "1.0",      "event": "webhook.test",      "occurred_at": "2026-10-15T10:01:00Z",      "source": "deskgram",      "test": true,      "endpoint": {"id": 12, "name": "CRM: рассылка"}    }  }}

HTTP-статус локального API в этом случае — 200. Код 403 относится к получателю и находится внутри delivery. При успехе: success: true, delivered: 1, status: "success".

Если доставка не создана, delivery_id и delivery могут быть null, а success — false. Несуществующий endpoint дает HTTP 404 локального API.

GET /api/webhooks/deliveries

Журнал от новых к старым по времени создания.

Query-параметрТипПо умолчаниюЗначение
endpoint_idintegerНе заданID endpoint
event_typestringНе заданТочное имя события, например message.failed
statusstringНе заданpending, retrying, success или error
limitinteger100От 1 до 500 записей

Ответ: {"deliveries": [...]}. Каждый элемент — объект доставки без requestpayload и sourcepayload. offset, курсор и общее количество записей этим методом не предоставляются.

GET /api/webhooks/deliveries/{delivery_id}

Детали доставки, включая requestpayload и sourcepayload.

Ответ: {"delivery": {...}}. Несуществующая доставка: HTTP 404.

POST /api/webhooks/deliveries/{delivery_id}/retry

Ручной повтор доставки в статусе error. Тело не требуется. Создает новую запись и ждет завершения ее попыток. Явный повтор разрешен также для выключенного endpoint.

Ответ содержит success (boolean), originaldeliveryid (integer), status (string), delivery (полный объект новой доставки).

У новой доставки другие id и X-Deskgram-Delivery-Id; исходная запись не заменяется. Неуспех получателя возвращается в HTTP 200 с success: false.

HTTP 400: исходная доставка не в статусе error либо ее ручной повтор уже выполняется в этом процессе. HTTP 404: не найдена доставка или ее endpoint.

GET /api/webhooks/events

Каталог шести поддерживаемых событий с подписями для интерфейса:

{  "events": [    {"event": "task.completed", "label": "Задача завершена"},    {"event": "task.failed", "label": "Задача завершилась ошибкой"},    {"event": "task.cancelled", "label": "Задача остановлена"},    {"event": "lead.reply_received", "label": "Получен ответ лида"},    {"event": "message.sent", "label": "Сообщение отправлено"},    {"event": "message.failed", "label": "Ошибка отправки сообщения"}  ]}

webhook.test отправляется отдельным методом тестирования и не включен в каталог подписок. "*" принимается в настройках, но также не возвращается каталогом.

Объект доставки

ПолеТипОписание
idintegerID доставки в локальной базе
endpoint_idintegerID endpoint, в том числе ранее удаленного
event_typestringСобытие независимо от выходного mapping
task_idstring или nullID задачи, если доступен
statusstringpending, retrying, success или error
attemptsintegerЧисло сделанных автоматических попыток
responsestatuscodeinteger или nullПоследний HTTP-код; при сетевой ошибке может быть null
response_bodystring или nullОтвет получателя, возможно усеченный
errorstring или nullДиагностическое сообщение, не поле бизнес-ошибки CRM
createdat, updatedatstringUTC-даты в ISO 8601 с Z
delivered_atstring или nullВремя успешной доставки
request_payloadobjectJSON после mapping; только в деталях/test/retry
source_payloadobjectИсходное событие до mapping; у старых записей может быть {}; только в деталях/test/retry

История не является полным HTTP-трассировщиком: заголовки отправленного запроса и все промежуточные ответы отдельных попыток не сохраняются в этом объекте.

События и исходящий JSON

Примеры ниже относятся к payload_template: "deskgram". При mapping внешняя структура меняется; X-Deskgram-Event по-прежнему содержит имя события.

Финальные события задач

СобытиеЗначение
task.completedЗавершение выполнения задачи; не гарантия успешности каждого действия
task.failedФинальный статус ошибки задачи
task.cancelledОстановка или отмена задачи
{  "schema_version": "1.0",  "event": "task.completed",  "occurred_at": "2026-10-15T10:05:00Z",  "source": "deskgram",  "task": {    "task_id": "12345",    "task_name": "Тестовая рассылка",    "module_name": "spam_pm",    "status": "completed",    "created_at": "2026-10-15T10:04:00Z",    "completed_at": "2026-10-15T10:05:00Z",    "accounts_count": 1,    "params": {},    "statistics": {},    "result": {}  }}

params, statistics, result зависят от модуля, общего набора счетчиков для всех задач нет. taskname, modulename, created_at могут быть null. У failed/cancelled меняются событие, статус и доступные результаты, но не семейство структуры.

Живые события рассылки

Сейчас message.sent, message.failed, lead.replyreceived подключены к spampm. Фильтр другого модуля не создает новые источники событий.

Пример отправленного сообщения:

{  "schema_version": "1.0",  "event": "message.sent",  "occurred_at": "2026-10-15T10:04:20Z",  "source": "deskgram",  "module": "spam_pm",  "task": {    "task_id": "12345",    "campaign_id": "spam_pm:12345",    "module_name": "spam_pm"  },  "account": {"id": "987654321"},  "lead": {    "telegram_user_id": "123456789",    "username": "example_client",    "raw_identifier": "@example_client"  },  "message": {    "message_id": 1001,    "message_text": "Здравствуйте!",    "raw_identifier": "@example_client",    "text": "Здравствуйте!",    "status": "sent"  }}

Состав lead и message зависит от доступных данных. Могут отсутствовать телефон, username, имя, ID или текст. Для mapping текста используйте message.text. Не требуйте message_text: это дополнительное поле конкретного пути отправки, не универсальное поле всех событий.

При message.failed в message передается доступная ошибка error и статус; ID успешно отправленного сообщения может отсутствовать. Пропуски и ошибки разрешения получателя не всегда создают это событие.

При lead.replyreceived в message передаются доступные messageid, text, media_type, datetime, timestamp и status: "received". Событие появляется только когда активный обработчик действительно получил ответ; вебхук не включает круглосуточное прослушивание сам по себе.

message.sent не подтверждает прочтение сообщения адресатом. ID рекомендуется обрабатывать как непрозрачные значения; ID задачи, аккаунта и Telegram-пользователя в этом контракте представлены строками, отдельный message_id может быть числом.

Тестовое событие

Исходное событие содержит schemaversion, event: "webhook.test", occurredat, source, test: true, endpoint: {id, name}. Пример полностью приведен в ответе метода test.

Тест не содержит task, lead, account, message. Mapping, состоящий только из источников лида, даст {}.

Настройка полей

deskgram отправляет полное исходное событие и не применяет payload_mapping. Для ограничения передаваемых данных выберите другой формат.

Для shortresult, crmlead, custom рекомендуемый явный формат mapping:

{  "fields": [    {"target": "event", "source": "event", "enabled": true},    {"target": "contact.telegram_id", "source": "lead.telegram_user_id", "enabled": true},    {"target": "contact.username", "source": "lead.username", "enabled": true},    {"target": "comment", "source": "message.text", "enabled": true},    {"target": "status", "source": ["message.status", "task.status"], "enabled": true}  ]}

Весь этот объект передается в payload_mapping. target — путь поля у получателя, source — путь в исходном событии Deskgram. Это не значения-константы и не выражения.

Для примера message.sent выше результат:

{  "event": "message.sent",  "contact": {"telegram_id": "123456789", "username": "example_client"},  "comment": "Здравствуйте!",  "status": "sent"}

Правила:

  • Точка в target создает вложенные объекты; не задавайте одновременно конфликтующие пути contact и contact.username.
  • source читает ключи вложенных объектов через точку. Индексы массивов и шаблоны {{...}} не поддерживаются.
  • Недоступное значение или null не добавляется. Значения 0, false и пустая строка одиночного источника сохраняются с исходным JSON-типом.
  • Список источников берет первое значение, отличное от null, пустой строки, пустого списка и пустого объекта. Также поддерживается строка "message.status|task.status".
  • enabled: false исключает поле; отсутствие enabled означает включение.
  • {"fields": []} или все выключенные поля дают {} без подстановки полного payload.
  • При payloadmapping: {} форматы shortresult/crm_lead используют встроенные поля, а custom дает {}. Явные fields позволяют точно зафиксировать состав.

Встроенный shortresult включает доступные event, occurredat, taskid, module, status, lead, message, error. Встроенный crmlead — event, occurredat, taskid, accountid, telegramuserid, username, phone, firstname, lastname, rawidentifier, messageid, messagetext, messagestatus, messageerror.

Интерфейс может сохранять собственный редактируемый список fields для выбранного пресета. Фактический контракт конкретного endpoint задают его сохраненные настройки, а не только название пресета.

Заголовки исходящего запроса

ЗаголовокЗначение
Content-Typeapplication/json
User-AgentDeskgram-Webhooks/1.0
X-Deskgram-Delivery-IdСтроковое представление ID записи доставки
X-Deskgram-EventИмя события, например message.sent
x-secretОбщий секрет, если настроен
X-Deskgram-Webhook-SecretТот же общий секрет, если настроен
X-Deskgram-SignatureПодпись sha256=, если настроен секрет

Секрет не только используется для HMAC, но и передается получателю в двух заголовках. Используйте HTTPS и не записывайте эти заголовки в открытые логи.

Системные Content-Type, User-Agent, Delivery-Id, Event, а при настроенном секрете также три секретных заголовка нельзя переопределить через headers независимо от регистра. Другие заголовки, например Authorization принимающей системы, добавляются отдельно.

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

Алгоритм: HMAC-SHA256(secretutf8, rawrequest_body). Заголовок содержит sha256= и шестнадцатеричный digest в нижнем регистре.

Подписываются точно те байты, которые отправляются. JSON кодируется UTF-8, без ASCII-экранирования кириллицы, с сортировкой ключей и без пробелов между элементами. На стороне получателя используйте исходное тело HTTP, не сериализуйте разобранный JSON повторно.

Проверка на Python, только стандартная библиотека:

import hashlibimport hmacdef verify_deskgram_signature(raw_body: bytes, signature: str, secret: str) -> bool:    if not secret or not signature:        return False    expected = "sha256=" + hmac.new(        secret.encode("utf-8"), raw_body, hashlib.sha256    ).hexdigest()    try:        return hmac.compare_digest(expected, signature)    except TypeError:        return False

Передайте сюда сырые байты body, заголовок X-Deskgram-Signature и сохраненный на стороне получателя секрет. При отказе не выполняйте бизнес-действие. Сам секрет загружайте из защищенной конфигурации, а не из присланного x-secret.

Подпись не дает защиту от повтора ранее подписанного тела: проверка возраста и дедупликация требуют отдельной логики получателя. Заголовок Delivery-Id не входит в подписанное тело. Секрет с крайними пробелами нормализуется; используйте непустой ASCII-секрет без пробелов длиной до 1000 символов.

Повторы и гарантии

  • Успех — только HTTP 200–299. Переходы по 3xx не выполняются. HTTP 200 с бизнес-ошибкой внутри JSON все равно считается успехом транспорта.
  • До трех автоматических попыток; паузы перед второй и третьей — примерно 2 и 4 секунды. Повторяются и сетевые ошибки, и HTTP-отказы, включая 4xx. Retry-After отдельно не обрабатывается.
  • Автоматические попытки используют одинаковые тело и Delivery-Id. Получатель должен переносить повторный запрос без повторения уже выполненного действия.
  • Для ручного retry используются актуальные URL, секрет и headers. При наличии sourcepayload заново применяется актуальный mapping; у старых записей без исходного события повторяется сохраненный requestpayload.
  • Ручной retry создает новый Delivery-Id. Для защиты от бизнес-дубликатов используйте также идентификаторы события предметной области, например задачу/аккаунт/сообщение и тип события, с учетом своего сценария.
  • Delivery-Id уникален в пределах конкретной локальной базы, а не всех установок Deskgram. При нескольких отправителях различайте их на стороне получателя.
  • Гарантий exactly-once, порядка разных событий и автоматического восстановления очереди после остановки процесса нет. Запись pending/retrying после прерывания не означает, что она возобновится при запуске.
  • Выключение endpoint прекращает выбор для новых автоматических событий, но не отменяет уже начатые отправки; явные test/retry могут отправить запрос выключенному endpoint.

Рекомендуемый прием: проверить подпись, надежно сохранить событие или поставить его в собственную очередь, вернуть 2xx; дальнейшую долгую обработку выполнять отдельно.

Ошибки API

HTTP-код локального APIКогда возникает
200Операция выполнена; у test/retry дополнительно проверяйте success и delivery
400Неподдерживаемый метод/формат/событие, URL без http(s), недопустимый ручной повтор
404Endpoint или доставка не найдены; endpoint исходной доставки удален
422Не пройдена валидация тела, типа параметров или диапазона limit/timeout

Пример прикладной ошибки:

{"detail": "Webhook endpoint не найден"}

В ошибке валидации detail может быть массивом диагностик FastAPI, а не строкой. Сбой базы или backend также может приводить к серверной ошибке; это не ответ внешней CRM.

Отличия MCP

MCP — отдельный интерфейс управления AI-агентом, не синоним описанного REST API. Подключение AI-агента описано в обзоре MCP.

СвойствоЛокальный REST APIMCP-инструменты
Новое подключениеenabled=true по умолчаниюВыключено по умолчанию
Предварительный режимЗапрос реально выполняетсяСоздание/изменения по умолчанию dry-run согласно настройкам MCP
ОбновлениеПолный PUT с исключением для secretЧастичное обновление
Адрес получателяhttp(s); не открывайте локальный API в интернетТолько публичный HTTPS, с сетевой защитой
Timeout в настройках3–60 секунд3–30 секунд
Чтение данныхПолный URL, значения headers, детали payloadБез секретов, значений headers и полных тел доставок

Защищенные сетевые правила endpoint, созданного или взятого под управление через соответствующее реальное MCP-действие, сохраняются для дальнейших отправок, в том числе из интерфейса. Обычный PUT не снимает этот режим. Только чтение, dry-run, отключение или удаление через MCP не переводят прежний endpoint под новую политику.

Источники Контракта

Для сопровождения документации: REST-модель и методы — web/routes/api/webhooks.py; формирование/подпись/повторы — web/webhookdispatcher.py; живые данные — functions/functions/spampm/crmintegration.py; защищенный транспорт — web/webhooknetwork.py; MCP — web/mcp/webhooks.py.

Эти пути предназначены для разработчиков Deskgram. Для публикации на сайте достаточно настоящего справочника и пользовательского руководства; переход к исходникам для настройки не требуется.

Полезные ссылки