Guide

API de webhooks do Deskgram 2: métodos, JSON e assinatura

Referência da API REST local: endpoints, eventos, mapeamento, histórico, tentativas, HMAC e diferenças do MCP.

settings15 min2026-10-24

Key Takeaways

  • Crie um endpoint desativado para revisar antes da entrega automática:
  • Obtenha endpoint.id na resposta. Para o ID fictício 12, envie um teste:
  • Verifique success, delivery.response status code, delivery.response body e o recebimento real. O teste não contém um lead real e pode falhar na validação comercial do CRM.
  • Ative pela interface ou envie a configuração completa por PUT com enabled: true. PUT não é atualização parcial.

Visão geral do módulo de webhooks

Contrato conferido no código em 24 de outubro de 2026. Descreve recursos implementados, não planos futuros. Para configurar pela interface, consulte o guia do usuário.

Duas direções das requisições

DireçãoQuem chamaEndereçoFinalidade
API local de gerenciamentoSeu script local → Deskgramhttp://127.0.0.1:5000/api/webhooksConfigurar endpoints e consultar entregas
Webhook de saídaDeskgram → seu receptorURL salva no endpointEnviar eventos por POST com JSON

Host e porta são exemplos do backend local. Use os da sua instalação. Não é a API de licenças, o servidor MCP nem os métodos setWebhook/getUpdates da Telegram Bot API.

Estas rotas REST não fazem uma verificação Bearer separada. Use um ambiente local confiável; não exponha a porta do backend à internet para enviar webhooks. A entrega de saída não exige acesso de entrada ao computador. Uma camada de autorização própria, se houver, possui regras independentes.

As respostas locais são JSON. Use Content-Type: application/json ao enviar configurações por POST/PUT.

Início rápido pela API

Os comandos curl usam um shell que aceita aspas simples. No PowerShell, use curl.exe e as regras da sua versão para parâmetros JSON. Domínios, IDs e segredos são fictícios. O texto russo nos exemplos é um valor ilustrativo, não uma exigência do contrato.

  1. Crie um endpoint desativado para revisar antes da 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. Obtenha endpoint.id na resposta. Para o ID fictício 12, envie um teste:
curl -X POST 'http://127.0.0.1:5000/api/webhooks/endpoints/12/test'
  1. Verifique success, delivery.responsestatuscode, delivery.response_body e o recebimento real. O teste não contém um lead real e pode falhar na validação comercial do CRM.
  2. Ative pela interface ou envie a configuração completa por PUT com enabled: true. PUT não é atualização parcial.
  3. Execute uma ação real correspondente ao evento e consulte o histórico:
curl 'http://127.0.0.1:5000/api/webhooks/deliveries?endpoint_id=12&limit=20'

Objeto endpoint

Campos de criação e atualização

POST e PUT usam o mesmo modelo.

CampoTipoPadrãoRegras
namestringObrigatório1–120 caracteres antes de remover espaços nas pontas; use nome não vazio
urlstringObrigatório8–2000 caracteres, começando por http:// ou https://; HTTPS recomendado
methodstringPOSTApenas POST
secretstring ou nullnull na criaçãoEspaços nas pontas removidos; omitir no PUT preserva, null ou vazio apaga
headersobject de valores string{}Cabeçalhos extras do receptor, não credenciais do Deskgram
eventsarray de string["task.completed"]Seis eventos do catálogo ou "*"; lista vazia vira task.completed
module_filterarray de string[]Vazio significa todos os módulos; exemplo ["spam_pm"]
payload_templatestringdeskgramdeskgram, shortresult, crmlead ou custom
payload_mappingobject{}Configuração dos campos descrita abaixo
enabledbooleantruePermite entregas automáticas dos eventos correspondentes
timeout_secondsnumber15De 3 a 60 segundos

Para endpoints comuns, timeout se aplica às operações de rede do cliente HTTP, não ao tempo total estrito da chamada local. Test e retry aguardam a entrega, incluindo tentativas e pausas.

Nomes de módulos passam para minúsculas e trocam - por _. Eventos e módulos duplicados são removidos. "*" cobre os eventos que realmente chegam ao mecanismo; não cria integrações ausentes em outros módulos.

Campos da resposta

Além das configurações, retornam id integer, hassecret boolean, createdat e updated_at em UTC. secret sempre retorna null, mesmo quando existe um segredo salvo.

A API REST local retorna os valores de headers sem mascarar. Eles podem conter tokens do receptor; não publique respostas completas em logs ou chamados de suporte.

Exemplo de criação:

{  "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 da API

Todos os caminhos incluem /api. Uma operação concluída retorna HTTP 200, inclusive criação e exclusão. endpointid e deliveryid são IDs inteiros dos registros.

GET /api/webhooks/endpoints

Lista do mais novo para o mais antigo por criação, sem parâmetros de paginação. Resposta: {"endpoints": [...]} com objetos endpoint; coleção vazia: {"endpoints": []}.

POST /api/webhooks/endpoints

Cria usando o modelo acima. Resposta: {"success": true, "endpoint": {...}}.

Se enabled for omitido, fica ativado imediatamente. Informe false durante a configuração inicial.

PUT /api/webhooks/endpoints/{endpoint_id}

Atualiza o endpoint. Exige name, url e todas as configurações atuais que devem permanecer. Campos omitidos voltam ao padrão, exceto secret.

Não reenvie o objeto de resposta sem tratar: secret: null apagaria o segredo salvo. Remova a chave secret para preservar. Null ou string vazia explícitos apagam.

Exemplo de ativação preservando o segredo:

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

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

DELETE /api/webhooks/endpoints/{endpoint_id}

Exclui sem corpo de requisição. Resposta: {"success": true, "endpoint_id": 12}. Se não existir, HTTP 404.

O histórico permanece. Excluir não cancela um envio já iniciado. Não é possível repetir entregas depois de excluir o endpoint.

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

Envia um teste apenas ao endpoint indicado. Não exige corpo e não aceita um payload de teste arbitrário.

Funciona também desativado, sem aplicar assinatura de eventos ou filtro de módulos. Aplica o mapping atual e responde depois do envio e das tentativas automáticas.

Exemplo de rejeição HTTP 403 pelo receptor no formato padrão:

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

O status da API local continua HTTP 200. O 403 pertence ao receptor e fica dentro de delivery. Sucesso retorna success: true, delivered: 1, status: "success".

Se nenhuma entrega for criada, delivery_id e delivery podem ser null, com success false. Endpoint inexistente retorna HTTP 404 local.

GET /api/webhooks/deliveries

Lista entregas novas primeiro, por data de criação.

QueryTipoPadrãoSignificado
endpoint_idintegerNão informadoID do endpoint
event_typestringNão informadoNome exato, como message.failed
statusstringNão informadopending, retrying, success ou error
limitinteger100Entre 1 e 500 registros

Resposta: {"deliveries": [...]}. Cada item é uma entrega sem requestpayload e sourcepayload. Não há offset, cursor nem contagem total.

GET /api/webhooks/deliveries/{delivery_id}

Retorna detalhes com requestpayload e sourcepayload: {"delivery": {...}}. Entrega inexistente: HTTP 404.

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

Repete manualmente uma entrega em error. Não exige corpo. Cria novo registro e aguarda suas tentativas. Permitido também com endpoint desativado.

A resposta contém success boolean, originaldeliveryid integer, status string e delivery completo da nova entrega. Seu id e X-Deskgram-Delivery-Id são diferentes; a original permanece. Falha externa retorna HTTP 200 local com success: false.

HTTP 400: origem não está em error ou sua repetição manual já está em execução neste processo. HTTP 404: entrega ou endpoint não encontrado.

GET /api/webhooks/events

Catálogo dos seis eventos com rótulos. O exemplo mantém os rótulos russos efetivamente retornados; traduzir o documento não muda valores da API.

{  "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 é enviado pelo método de teste e não integra o catálogo de assinaturas. "*" é aceito nas configurações, mas também não aparece no catálogo.

Objeto delivery

CampoTipoDescrição
idintegerID na base local
endpoint_idintegerID do endpoint, mesmo excluído depois
event_typestringEvento independente do mapping de saída
task_idstring ou nullID de tarefa quando disponível
statusstringpending, retrying, success ou error
attemptsintegerTentativas automáticas realizadas
responsestatuscodeinteger ou nullÚltimo HTTP; pode ser null na falha de rede
response_bodystring ou nullÚltima resposta truncada; pode ser null na falha de rede
errorstring ou nullDiagnóstico, não campo comercial de erro do CRM
createdat, updatedatstringDatas UTC ISO 8601 com Z
delivered_atstring ou nullHorário de entrega bem-sucedida
request_payloadobjectJSON após mapping; apenas detalhes/test/retry
source_payloadobjectEvento original; pode ser {} em registros antigos; apenas detalhes/test/retry

No transporte comum, a resposta fica limitada a 2000 caracteres; o transporte protegido MCP também limita bytes lidos. O histórico não é uma captura HTTP completa: não guarda cabeçalhos enviados nem todas as respostas intermediárias.

Eventos e JSON de saída

Exemplos para payload_template: "deskgram". O mapping altera a estrutura externa; X-Deskgram-Event mantém o nome do evento.

Eventos finais de tarefas

EventoSignificado
task.completedExecução encerrada, sem garantir sucesso de cada ação
task.failedEstado final de erro
task.cancelledTarefa parada ou 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 e result variam por módulo, sem um esquema universal de contadores. taskname, modulename e created_at podem ser null. Failed/cancelled usam a mesma família estrutural, mudando evento, estado e resultados disponíveis.

Eventos de mensagens em tempo real

message.sent, message.failed e lead.replyreceived estão atualmente ligados a spampm. Filtrar outro módulo não cria novas fontes.

Exemplo de envio:

{  "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 e message dependem dos dados disponíveis. Podem faltar telefone, username, nome, ID ou texto. Para texto, use message.text. message_text é adicional de um caminho específico, não universal.

message.failed contém error e estado disponíveis em message; um ID enviado pode não existir. Destinatários ignorados ou falhas de resolução nem sempre geram esse evento.

lead.replyreceived pode incluir messageid, text, media_type, datetime, timestamp e status: "received". Só surge quando um manipulador ativo realmente recebe a resposta. O webhook não ativa escuta contínua nem garante coleta após o fim da tarefa.

message.sent não confirma leitura. Trate IDs como valores opacos: tarefa, conta e usuário Telegram são strings; um message_id específico pode ser numérico.

Evento de teste

Contém schemaversion, event: "webhook.test", occurredat, source, test: true e endpoint: {id, name}. O exemplo do método test mostra tudo.

Não há task, lead, account ou message. Mapping apenas com fontes de lead produz {}.

Mapeamento de campos

O formato deskgram envia o evento original completo e ignora payload_mapping. Escolha outro para limitar dados.

Para shortresult, crmlead e custom, recomenda-se a forma explícita:

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

Coloque o objeto inteiro em payload_mapping. target é o caminho no receptor; source, no evento original. Não são constantes nem expressões executáveis.

Resultado do exemplo message.sent:

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

Regras:

  • Pontos em target criam objetos aninhados. Evite conflitos como contact e contact.username juntos.
  • source lê chaves de objetos separadas por ponto. Não há índices de arrays nem modelos {{...}}.
  • Ausentes e null são omitidos. Fonte única preserva 0, false e string vazia com seu tipo JSON.
  • Lista de fontes escolhe o primeiro valor diferente de null, string vazia, lista vazia ou objeto vazio. Também aceita "message.status|task.status".
  • enabled: false exclui; omitir enabled inclui.
  • {"fields": []} ou todos desativados produzem {}, sem fallback para payload completo.
  • payloadmapping: {} usa campos internos em shortresult/crm_lead; custom produz {}. fields explícitos fixam o contrato.

shortresult interno usa os disponíveis event, occurredat, taskid, module, status, lead, message, error. crmlead interno usa event, occurredat, taskid, accountid, telegramuserid, username, phone, firstname, lastname, rawidentifier, messageid, messagetext, messagestatus, messageerror.

A interface pode salvar sua própria lista editável de fields do preset. A configuração salva determina o contrato real, não apenas o nome do formato.

Cabeçalhos de saída

CabeçalhoValor
Content-Typeapplication/json
User-AgentDeskgram-Webhooks/1.0
X-Deskgram-Delivery-IdID da entrega como string
X-Deskgram-EventNome do evento
x-secretSegredo compartilhado, quando configurado
X-Deskgram-Webhook-SecretO mesmo segredo
X-Deskgram-SignatureAssinatura sha256=, quando há segredo

O segredo é chave HMAC e também viaja em dois cabeçalhos. Use HTTPS e não publique esses valores nos logs.

headers não pode sobrescrever Content-Type, User-Agent, Delivery-Id, Event nem, com segredo definido, os três cabeçalhos ligados a ele, independentemente de maiúsculas. Outros, como Authorization do receptor, são adicionais.

Verificação da assinatura

Algoritmo: HMAC-SHA256(secretutf8, rawrequest_body). O cabeçalho traz sha256= seguido de digest hexadecimal minúsculo.

A assinatura usa os bytes exatos enviados. JSON é UTF-8, sem escape ASCII de texto não ASCII, chaves ordenadas e separadores compactos. Use o corpo HTTP bruto, não JSON analisado e serializado novamente.

Python usando só a biblioteca padrão:

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

Passe bytes brutos, X-Deskgram-Signature e segredo salvo no receptor. Na falha, não execute a ação comercial. Carregue o segredo da configuração protegida, não confie no x-secret recebido.

A assinatura não impede sozinha a repetição de um corpo assinado. Idade e deduplicação exigem lógica no receptor. Delivery-Id não integra o corpo assinado. O segredo é aparado: use ASCII não vazio, sem espaços nas pontas e até 1000 caracteres.

Tentativas e garantias

  • Sucesso somente HTTP 200–299. Não segue 3xx. HTTP 200 com erro comercial no JSON ainda é sucesso de transporte.
  • Até três tentativas, com pausas aproximadas de 2 e 4 segundos antes da segunda e terceira. Repete falhas de rede e HTTP, inclusive 4xx. Retry-After não recebe tratamento separado.
  • Tentativas automáticas mantêm corpo e Delivery-Id. O receptor deve aceitar repetição sem repetir a ação concluída.
  • Retry manual usa URL, segredo e headers atuais. Com sourcepayload, reaplica mapping atual ao evento original; registros antigos sem ele repetem requestpayload sem novo mapping.
  • O manual cria novo Delivery-Id. Evite duplicatas comerciais também por tarefa/conta/mensagem e tipo de evento, conforme seu fluxo.
  • Delivery-Id é único numa base local, não entre todas as instalações. Diferencie remetentes quando houver vários.
  • Não garante exactly-once, ordem entre eventos nem retomada automática após parar o processo. Pending/retrying interrompido não implica continuação ao reiniciar.
  • Desativar impede novos eventos automáticos, não cancela envios iniciados. Test/retry explícitos podem enviar desativado.

Fluxo recomendado: validar assinatura, salvar ou enfileirar de forma confiável, responder 2xx e processar trabalhos longos separadamente.

Erros da API

HTTP localQuando
200Operação concluída; verificar success/delivery em test/retry
400Método/formato/evento não suportado, URL sem http(s), retry inválido
404Endpoint/entrega ausentes ou endpoint original excluído
422Validação de corpo, tipos ou intervalo de limit/timeout

Exemplo; o texto real do servidor pode permanecer em russo:

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

Em validação, detail pode ser array diagnóstico do FastAPI, não string. Falha de banco ou backend também pode causar erro de servidor, separado da resposta externa do CRM.

Diferenças do MCP

MCP é uma interface separada para agente IA, não sinônimo desta REST API. Veja a conexão MCP.

PropriedadeREST localFerramentas MCP
Novo endpointenabled=true por padrãoDesativado
PréviaExecuta a requisiçãoCriar/alterar usa dry-run por padrão conforme ajustes MCP
AtualizaçãoPUT completo, exceção secretParcial
URLhttp(s); não exponha a API localApenas HTTPS público com proteção de rede
Timeout3–60 segundos3–30 segundos
LeituraURL, valores headers e payload completosSem segredos, valores headers ou corpos completos

Um endpoint criado ou assumido por ação MCP real aplicável mantém as regras protegidas nos próximos envios, inclusive pela interface. PUT comum não remove esse modo. Leitura, dry-run, desativar ou excluir não colocam por si só um endpoint antigo nessa política.

Fontes do contrato

Para manutenção: modelo e rotas web/routes/api/webhooks.py; eventos, assinatura e tentativas web/webhookdispatcher.py; dados ao vivo functions/functions/spampm/crmintegration.py; transporte protegido web/webhooknetwork.py; MCP web/mcp/webhooks.py. São caminhos do repositório para mantenedores; configurar não exige ler o código.

Continuar

Baixar Deskgram 2

Próximo passo

Helpful Links