Guide

API de webhooks de Deskgram 2: métodos, JSON y firma

Referencia de la API REST local: endpoints, eventos, campos, historial, reintentos, HMAC y diferencias con MCP.

settings16 min2026-10-24

Key Takeaways

  • Crea un endpoint desactivado para revisar la configuración antes de la entrega automática:
  • Obtén endpoint.id de la respuesta. Para el ID ficticio 12 , envía la prueba:
  • Comprueba success , delivery.response status code , delivery.response body y la recepción efectiva. La prueba no contiene un lead real y puede no superar la validación comercial del CRM.
  • Activa el endpoint en la interfaz o envía su configuración completa mediante PUT con enabled: true . PUT no es una actualización parcial.

Resumen del módulo de webhooks

Contrato comprobado con el código el 24 de octubre de 2026. Describe funciones implementadas, no planes futuros. Para configurar desde la interfaz, consulta la guía de usuario.

Dos direcciones de las solicitudes

DirecciónQuién llamaDirecciónFinalidad
API local de gestiónTu script local → Deskgramhttp://127.0.0.1:5000/api/webhooksConfigurar endpoints y consultar entregas
Webhook salienteDeskgram → tu receptorURL guardada en el endpointEnviar eventos mediante POST con JSON

El host y puerto son un ejemplo del backend local; usa los de tu instalación. No es la API de licencias, el servidor MCP ni los métodos setWebhook o getUpdates de Telegram Bot API.

Estas rutas REST no realizan una comprobación Bearer independiente. Úsalas en un entorno local de confianza; no publiques el puerto del backend para enviar webhooks. La entrega saliente no necesita conexiones entrantes al equipo de Deskgram. Si añadiste una pasarela de autorización, configura sus reglas por separado.

Las respuestas locales son JSON. Al enviar ajustes mediante POST/PUT, utiliza Content-Type: application/json.

Inicio rápido con la API

Los comandos curl usan una shell compatible con comillas simples. En PowerShell utiliza curl.exe y las reglas de tu versión para pasar JSON. Los dominios, ID y secretos son ficticios. El texto ruso de los ejemplos es un valor ilustrativo, no un requisito del contrato.

  1. Crea un endpoint desactivado para revisar la configuración antes de la entrega automática:
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. Obtén endpoint.id de la respuesta. Para el ID ficticio 12, envía la prueba:
curl -X POST 'http://127.0.0.1:5000/api/webhooks/endpoints/12/test'
  1. Comprueba success, delivery.responsestatuscode, delivery.response_body y la recepción efectiva. La prueba no contiene un lead real y puede no superar la validación comercial del CRM.
  2. Activa el endpoint en la interfaz o envía su configuración completa mediante PUT con enabled: true. PUT no es una actualización parcial.
  3. Ejecuta una acción real que corresponda al evento elegido y consulta el historial:
curl 'http://127.0.0.1:5000/api/webhooks/deliveries?endpoint_id=12&limit=20'

Objeto endpoint

Campos de creación y actualización

POST y PUT usan el mismo modelo.

CampoTipoValor predeterminadoReglas
namestringObligatorio1–120 caracteres antes de recortar espacios; usa un nombre no vacío
urlstringObligatorio8–2000 caracteres; empieza por http:// o https://; se recomienda HTTPS
methodstringPOSTSolo POST
secretstring o nullnull al crearSe recortan espacios; en PUT, omitir conserva, null o vacío borra
headersobject con valores string{}Cabeceras adicionales del receptor, no credenciales de Deskgram
eventsarray de string["task.completed"]Seis eventos del catálogo o "*"; una lista vacía se sustituye por task.completed
module_filterarray de string[]Vacío significa todos los módulos; ejemplo ["spam_pm"]
payload_templatestringdeskgramdeskgram, shortresult, crmlead o custom
payload_mappingobject{}Configuración de campos descrita abajo
enabledbooleantrueAutoriza envíos automáticos de eventos coincidentes
timeout_secondsnumber15Entre 3 y 60 segundos

Para un endpoint ordinario, el timeout se aplica a las operaciones de red del cliente HTTP, no al tiempo total estricto de la llamada local. Test y retry esperan la entrega, incluidos intentos automáticos y pausas.

Los nombres de módulos pasan a minúsculas y sustituyen - por _. Se eliminan duplicados de eventos y módulos. "*" abarca los eventos que realmente llegan al mecanismo; no incorpora nuevas integraciones a otros módulos.

Campos de respuesta

Además de los ajustes, se devuelven id integer, hassecret boolean, createdat y updated_at en UTC. secret siempre es null, aunque haya uno guardado.

La API REST local devuelve los valores de headers sin ocultarlos. Pueden contener tokens del receptor: no publiques la respuesta completa en registros o consultas de soporte.

Ejemplo de creación:

{  "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"  }}

Métodos de la API

Las rutas incluyen /api. Una operación completada usa HTTP 200, también al crear o borrar. endpointid y deliveryid son ID enteros de registros.

GET /api/webhooks/endpoints

Lista los endpoints del más reciente al más antiguo por fecha de creación. No hay parámetros de paginación. Respuesta: {"endpoints": [...]} con objetos endpoint; si está vacía, {"endpoints": []}.

POST /api/webhooks/endpoints

Crea un endpoint con el modelo anterior. Respuesta: {"success": true, "endpoint": {...}}.

Si omites enabled, queda activado inmediatamente. Envía false explícitamente durante la configuración inicial.

PUT /api/webhooks/endpoints/{endpoint_id}

Actualiza el endpoint. Requiere name, url y los demás ajustes actuales que quieras conservar. Los campos omitidos vuelven a sus valores predeterminados, excepto secret.

No reenvíes el objeto de respuesta sin procesarlo: su secret: null eliminaría el secreto guardado. Omite completamente esa clave para conservarlo. Null o una cadena vacía lo borran.

Ejemplo de activación conservando el secreto:

{  "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}

Respuesta: {"success": true, "endpoint": {...}}. Endpoint inexistente: HTTP 404.

DELETE /api/webhooks/endpoints/{endpoint_id}

Borra el endpoint sin cuerpo de solicitud. Respuesta: {"success": true, "endpoint_id": 12}. Si no existe: HTTP 404.

El historial permanece. Borrar no cancela un envío ya iniciado; no se puede reintentar una entrega después de borrar su endpoint.

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

Envía una prueba únicamente al endpoint indicado. No requiere cuerpo y no permite proporcionar un payload de prueba arbitrario.

Funciona también con endpoints desactivados, independientemente de suscripciones y filtros de módulos. Aplica el mapping actual y responde después del envío y sus reintentos automáticos.

Ejemplo de rechazo HTTP 403 del receptor con formato estándar:

{  "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: рассылка"}    }  }}

El estado de la API local sigue siendo HTTP 200. El 403 pertenece al receptor y aparece dentro de delivery. Si funciona, devuelve success: true, delivered: 1 y status: "success".

Si no se creó una entrega, delivery_id y delivery pueden ser null y success false. Un endpoint inexistente produce HTTP 404 local.

GET /api/webhooks/deliveries

Lista entregas de nuevas a antiguas por fecha de creación.

Parámetro queryTipoPredeterminadoSignificado
endpoint_idintegerNo indicadoID del endpoint
event_typestringNo indicadoNombre exacto, por ejemplo message.failed
statusstringNo indicadopending, retrying, success o error
limitinteger100De 1 a 500 registros

Respuesta: {"deliveries": [...]}. Cada elemento es una entrega sin requestpayload ni sourcepayload. No proporciona offset, cursor ni cantidad total.

GET /api/webhooks/deliveries/{delivery_id}

Obtiene los detalles, incluidos requestpayload y sourcepayload. Respuesta: {"delivery": {...}}. Entrega inexistente: HTTP 404.

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

Reintenta manualmente una entrega en estado error. No requiere cuerpo. Crea un registro nuevo y espera sus intentos. También se permite con un endpoint desactivado.

La respuesta incluye success boolean, originaldeliveryid integer, status string y delivery completo de la nueva entrega. Su id y X-Deskgram-Delivery-Id cambian; la original permanece. Un fallo del receptor se comunica con HTTP 200 local y success: false.

HTTP 400: la entrega original no está en error o ya se está reintentando manualmente en este proceso. HTTP 404: no existe la entrega o su endpoint.

GET /api/webhooks/events

Catálogo de los seis eventos con sus etiquetas. El ejemplo conserva las etiquetas rusas que devuelve realmente la API; traducir esta documentación no modifica esos valores.

{  "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 se envía con el método de prueba y no figura entre las suscripciones. "*" se acepta en ajustes, pero tampoco aparece en el catálogo.

Objeto delivery

CampoTipoDescripción
idintegerID en la base local
endpoint_idintegerID del endpoint, aunque se haya borrado después
event_typestringEvento, independiente del mapping saliente
task_idstring o nullID de tarea si existe
statusstringpending, retrying, success o error
attemptsintegerIntentos automáticos realizados
responsestatuscodeinteger o nullÚltimo estado HTTP; puede ser null por fallo de red
response_bodystring o nullÚltima respuesta truncada; puede ser null por fallo de red
errorstring o nullDiagnóstico, no un campo de error comercial del CRM
createdat, updatedatstringFechas ISO 8601 UTC terminadas en Z
delivered_atstring o nullMomento de entrega correcta
request_payloadobjectJSON tras mapping; solo detalles/test/retry
source_payloadobjectEvento original; puede ser {} en registros antiguos; solo detalles/test/retry

En transporte ordinario se conservan como máximo 2000 caracteres de respuesta; el transporte protegido de MCP también limita los bytes leídos. El historial no es una traza HTTP completa: no guarda las cabeceras enviadas ni todas las respuestas intermedias.

Eventos y JSON saliente

Estos ejemplos corresponden a payload_template: "deskgram". Con mapping cambia la estructura externa; X-Deskgram-Event conserva el nombre del evento.

Eventos finales de tareas

EventoSignificado
task.completedTerminó la ejecución; no garantiza que cada acción fuera correcta
task.failedEstado final de error
task.cancelledTarea detenida o cancelada
{  "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 y result dependen del módulo; no existe un esquema universal de contadores. taskname, modulename y created_at pueden ser null. Failed/cancelled mantienen la familia de estructura, con el evento, estado y resultados correspondientes.

Eventos de mensajes en tiempo real

Actualmente message.sent, message.failed y lead.replyreceived están conectados a spampm. Un filtro de otro módulo no crea nuevas fuentes de eventos.

Ejemplo de mensaje enviado:

{  "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 y message dependen de los datos disponibles. Pueden faltar teléfono, username, nombre, ID o texto. Para mapear texto usa message.text. message_text es adicional de una ruta concreta, no universal.

message.failed incluye el error y estado disponibles en message; puede no existir un ID de mensaje enviado. Omitir destinatarios o fallar al resolverlos no siempre genera el evento.

lead.replyreceived puede incluir messageid, text, media_type, datetime, timestamp y status: "received". Solo existe si un manejador activo recibió realmente la respuesta. El webhook no activa escucha permanente ni garantiza respuestas después de terminar la tarea.

message.sent no confirma lectura. Trata los ID como valores opacos: los de tarea, cuenta y usuario Telegram son strings; un message_id concreto puede ser numérico.

Evento de prueba

Contiene schemaversion, event: "webhook.test", occurredat, source, test: true y endpoint: {id, name}. El método test muestra su estructura completa.

No contiene task, lead, account ni message. Un mapping compuesto solo de fuentes de lead produce {}.

Mapping de campos

El formato deskgram envía el evento original completo e ignora payload_mapping. Selecciona otro formato para limitar campos.

Para shortresult, crmlead y custom se recomienda este mapping explícito:

{  "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}  ]}

El objeto completo se asigna a payload_mapping. target es una ruta en el receptor; source, una ruta en el evento original. No son constantes ni expresiones ejecutables.

Resultado del ejemplo message.sent:

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

Reglas:

  • Los puntos en target crean objetos anidados. Evita rutas incompatibles como contact y contact.username simultáneamente.
  • source lee claves anidadas separadas por puntos. No admite índices de arrays ni plantillas {{...}}.
  • Valores ausentes o null se omiten. Un origen único conserva 0, false y cadena vacía con su tipo JSON.
  • Una lista toma el primer valor distinto de null, cadena vacía, lista vacía u objeto vacío. También se admite "message.status|task.status".
  • enabled: false excluye; omitir enabled significa incluir.
  • {"fields": []} o todos los campos desactivados producen {}, sin sustituir el payload completo.
  • Con payloadmapping: {}, shortresult/crm_lead usan campos internos; custom produce {}. fields explícitos fijan el contrato.

shortresult interno contiene los disponibles event, occurredat, taskid, module, status, lead, message, error. crmlead interno contiene event, occurredat, taskid, accountid, telegramuserid, username, phone, firstname, lastname, rawidentifier, messageid, messagetext, messagestatus, messageerror.

La interfaz puede guardar su propia lista editable de fields para el preajuste. Los ajustes guardados del endpoint determinan el contenido real, no solo su nombre.

Cabeceras salientes

CabeceraValor
Content-Typeapplication/json
User-AgentDeskgram-Webhooks/1.0
X-Deskgram-Delivery-IdID de entrega como string
X-Deskgram-EventNombre del evento
x-secretSecreto compartido, si existe
X-Deskgram-Webhook-SecretEl mismo secreto
X-Deskgram-SignatureFirma sha256=, con secreto configurado

El secreto se utiliza para HMAC y además se envía en dos cabeceras. Usa HTTPS y evita registrarlas públicamente.

headers no puede sustituir Content-Type, User-Agent, Delivery-Id, Event ni, con secreto configurado, las tres cabeceras del secreto, independientemente de mayúsculas. Otras, como Authorization del receptor, se añaden aparte.

Verificación de firma

Algoritmo: HMAC-SHA256(secretutf8, rawrequest_body). Se envía sha256= seguido de digest hexadecimal en minúsculas.

Se firman los bytes exactos enviados. JSON usa UTF-8, sin escape ASCII del texto no ASCII, claves ordenadas y separadores compactos. Usa el cuerpo HTTP original, no JSON analizado y vuelto a serializar.

Ejemplo Python con biblioteca estándar:

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

Pasa bytes originales, X-Deskgram-Signature y el secreto guardado en el receptor. Si falla, no ejecutes la acción comercial. Carga el secreto de configuración protegida, no del x-secret entrante.

La firma sola no impide reutilizar un cuerpo firmado. El receptor necesita comprobación de antigüedad y deduplicación. Delivery-Id no pertenece al cuerpo firmado. Se recortan espacios del secreto; usa uno ASCII no vacío, sin espacios exteriores y de hasta 1000 caracteres.

Reintentos y garantías

  • Solo HTTP 200–299 es correcto; no se siguen redirecciones. Un 200 con error comercial en JSON sigue siendo éxito de transporte.
  • Hasta tres intentos, con pausas aproximadas de 2 y 4 segundos antes del segundo y tercero. Se repiten errores de red y HTTP, incluidos 4xx. Retry-After no se procesa por separado.
  • Los intentos automáticos conservan cuerpo y Delivery-Id. El receptor debe evitar repetir una acción ya completada.
  • Retry manual utiliza URL, secreto y headers actuales. Si existe sourcepayload, aplica mapping actual al evento original; registros antiguos sin él reutilizan requestpayload sin volver a mapear.
  • El retry manual crea otro Delivery-Id. Añade deduplicación comercial mediante identificadores de tarea/cuenta/mensaje y tipo de evento, según el caso.
  • Delivery-Id es único en una base local, no en todas las instalaciones. Distingue los remitentes si recibes de varias.
  • No se garantiza exactly-once, orden entre eventos ni recuperación automática tras reiniciar. Un registro pending/retrying interrumpido no implica que se reanude.
  • Desactivar evita nuevos eventos automáticos, no cancela envíos iniciados; test/retry explícitos pueden enviar aun desactivado.

Flujo recomendado: validar firma, almacenar de forma fiable o encolar, responder 2xx y realizar procesamiento largo aparte.

Errores de la API

HTTP localSituación
200Operación completada; en test/retry revisa también success y delivery
400Método/formato/evento no admitido, URL sin http(s), retry inválido
404Endpoint/entrega inexistentes o endpoint original borrado
422Error de validación del cuerpo, tipos o límites de limit/timeout

Ejemplo de error; el servidor puede conservar texto ruso:

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

En validación, detail puede ser un array de diagnósticos FastAPI en vez de string. Un fallo de base de datos o backend puede producir error del servidor, distinto de la respuesta del CRM.

Diferencias con MCP

MCP es otra interfaz de gestión para un agente IA, no un sinónimo de REST. Consulta la conexión MCP.

PropiedadREST localHerramientas MCP
Nuevo endpointenabled=true predeterminadoDesactivado
Vista previaEjecuta la solicitudCrear/cambiar suele usar dry-run según ajustes MCP
ActualizaciónPUT completo, excepción secretParcial
URLhttp(s); no publiques la API localSolo HTTPS público con protección de red
Timeout3–60 segundos3–30 segundos
LecturaURL, headers y payload completosSin secretos, valores de headers ni cuerpos completos

Los endpoints creados o asumidos mediante una acción MCP real aplicable conservan la protección para envíos posteriores, incluidos los de la interfaz. Un PUT ordinario no la elimina. Lectura, dry-run, desactivación o borrado no transfieren por sí solos un endpoint antiguo a la nueva política.

Fuentes del contrato

Para mantenimiento: modelo y rutas web/routes/api/webhooks.py; eventos, firma y reintentos web/webhookdispatcher.py; datos en vivo functions/functions/spampm/crmintegration.py; transporte protegido web/webhooknetwork.py; MCP web/mcp/webhooks.py. Son rutas del repositorio para mantenimiento; configurar webhooks no requiere acceder al código.

Continuar

Descargar Deskgram 2

Siguiente paso

Helpful Links