Webhooks
Os webhooks enviam uma solicitação HTTP POST ao seu sistema quando ocorre um evento, como a entrada, a chamada ou a conclusão de uma senha.
Introdução
Seção intitulada “Introdução”- Abra admin.jonot.io/settings/integrations.
- Clique em Adicionar endpoint.
- Insira um URL HTTPS público em que o seu servidor esteja escutando.
- Escolha a que eventos quer assinar.
- Clique em Salvar.
Guarde o seu segredo de assinatura — é mostrado apenas uma vez. Para verificar se o seu endpoint está acessível, use o botão Enviar pedido de teste na página de edição do endpoint.
Tipos de evento
Seção intitulada “Tipos de evento”| Nome do evento | Quando dispara |
|---|---|
ticket.joined | Um cliente entra em uma fila |
ticket.called | Um membro da equipe chama uma senha ao balcão de atendimento |
ticket.completed | Uma senha é marcada como concluída |
ticket.cancelled | Um cliente ou membro da equipe cancela uma senha |
ticket.skipped | Uma senha é pulada (um adiamento chamável de novo) |
ticket.no_show | Uma senha chamada ou pulada é confirmada como ausência |
queue.status_changed | O estado de uma fila muda (ACTIVE, PAUSED ou CLOSED) |
Formato do payload
Seção intitulada “Formato do payload”Toda a entrega é um HTTP POST com:
Content-Type: application/jsonX-Jonot-Event: ticket.calledX-Jonot-Delivery-Id: <uuid>X-Jonot-Timestamp: 2024-06-01T12:00:00.000ZX-Jonot-Signature: v1,<base64-hmac-sha256>X-Jonot-Payload-Version: v1O corpo padrão é um envelope JSON. A forma do campo payload varia de acordo com o evento:
ticket.joined
{ "deliveryId": "<uuid>", "event": "ticket.joined", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "queueId": "q_…" }, "queue": { "id": "q_…", "name": "Main Queue" }, "location": { "id": "loc_…", "name": "Downtown" } }}ticket.called / ticket.completed / ticket.skipped / ticket.no_show
{ "deliveryId": "<uuid>", "event": "ticket.called", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticket": { "id": "tkt_…", "number": 42, "status": "CALLED", "queueId": "q_…", "createdAt": "2024-06-01T11:58:00.000Z", "updatedAt": "2024-06-01T12:00:00.000Z" } }}ticket.cancelled
{ "deliveryId": "<uuid>", "event": "ticket.cancelled", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "ticketId": "tkt_…", "queueId": "q_…" }}queue.status_changed
{ "deliveryId": "<uuid>", "event": "queue.status_changed", "timestamp": "2024-06-01T12:00:00.000Z", "org": { "id": "org_…", "name": "My Org" }, "payload": { "queueId": "q_…", "status": "PAUSED" }}Verificar a assinatura
Seção intitulada “Verificar a assinatura”O Jonot assina cada entrega com HMAC-SHA256 sobre a cadeia:
<deliveryId>.<timestamp>.<body>Para verificar em Node.js (≥18):
import { createHmac, timingSafeEqual } from "node:crypto";
/** * Returns true when the signature header is valid and the timestamp is * within 5 minutes of now. Throws for malformed input. */function verifySignature(secret, deliveryId, timestamp, body, header) { // Replay-attack guard: reject deliveries older than 5 minutes. const ageMs = Date.now() - new Date(timestamp).getTime(); if (Math.abs(ageMs) > 5 * 60 * 1000) return false;
const expected = "v1," + createHmac("sha256", secret) .update(`${deliveryId}.${timestamp}.${body}`) .digest("base64");
// timingSafeEqual prevents timing-oracle attacks. // Buffers must be the same length — if lengths differ the signature is // invalid, but we still compare a dummy value to keep constant time. const expectedBuf = Buffer.from(expected); const headerBuf = Buffer.from(header); if (expectedBuf.length !== headerBuf.length) return false; return timingSafeEqual(expectedBuf, headerBuf);}Modelos de payload
Seção intitulada “Modelos de payload”Pode substituir o corpo JSON padrão por um modelo personalizado. Os modelos usam uma sintaxe Mustache-lite:
{{ path.to.value }}— substituído e escapado de acordo com o tipo de conteúdo: escapado como cadeia JSON paraapplication/json, codificado em percentagem paraapplication/x-www-form-urlencoded, em bruto paratext/plain{{{ path.to.value }}}— substituído em bruto (sem escape)
Exemplo de modelo para application/json (assinado a ticket.joined):
{ "type": "{{ event }}", "ticketId": "{{ payload.ticket.id }}", "queueName": "{{ payload.queue.name }}"}Editor de modelos
Seção intitulada “Editor de modelos”O campo de modelo de payload é um editor de código completo com:
- Realce de sintaxe e correspondência de parênteses para modelos JSON.
- Paleta de variáveis — uma linha sempre visível de botões de inserção, um por cada caminho de variável disponível para os eventos selecionados. Clique em um botão para inserir um token
{{ path }}na posição do cursor. - Validação em tempo real — à medida que escreve, o editor verifica tanto a estrutura JSON (quando o tipo de conteúdo é
application/jsone não existem etiquetas{{{ }}}em bruto) como os caminhos de variável. Os diagnósticos aparecem como marcadores incorporados no editor e como uma faixa de resumo por baixo:- Erro — um caminho desconhecido em todos os eventos selecionados.
- Aviso — um caminho que existe apenas para alguns dos eventos selecionados (ficará vazio nas entregas dos outros eventos).
A validação também é executada no momento de salvar, no servidor — o feedback do editor espelha exatamente as regras do servidor.
Repetições de entrega
Seção intitulada “Repetições de entrega”O Cloudflare Queues faz a tentativa inicial e repete até 3 vezes quando recebe uma resposta diferente de 2xx ou ocorre um erro de conexão. O intervalo entre as tentativas aumenta a cada falha. Se todas falharem, a entrega vai para a fila de mensagens mortas e aumenta o contador de falhas consecutivas do endpoint.
Assim que um endpoint acumula 20 falhas consecutivas, é desativado automaticamente. Reative-o a partir da página de edição do endpoint; o contador repõe-se a zero.
Histórico de entregas
Seção intitulada “Histórico de entregas”A aba Entregas de cada endpoint mostra os últimos 30 dias de tentativas de entrega: tipo de evento, estado HTTP, número de tentativas e registro de data/hora. Use o botão Carregar mais para percorrer registros mais antigos.
Limites
Seção intitulada “Limites”| Limite | Valor |
|---|---|
| Endpoints por organização | 5 |
| Cabeçalhos personalizados por endpoint | 10 |
| Comprimento do valor do cabeçalho | 1024 bytes |
| Tamanho do modelo de payload | 16 KB |
| Tempo limite de entrega | 10 s |
| Retenção do histórico de entregas | 30 dias |
| Taxa máxima de entrega por organização | 120 / 60 s |
Rodar o segredo de assinatura
Seção intitulada “Rodar o segredo de assinatura”- Abra a página de edição do endpoint.
- Clique em Rodar segredo de assinatura.
- Confirme a rotação na caixa de diálogo.
- Copie o novo segredo imediatamente — é mostrado apenas uma vez e não pode ser recuperado. Se fechar a caixa de diálogo sem o salvar, terá de rodar novamente para obter um novo valor em texto simples.
- Atualize o seu servidor para verificar as assinaturas com o novo segredo.
Não há nenhuma janela de sobreposição. O segredo antigo deixa de verificar assim que confirmar a rotação. Planeie implementar o novo segredo no seu receptor imediatamente após a rotação.
A interface mostra Segredo ativo: ····XXXX (os últimos quatro caracteres) junto ao botão Rodar, para que possa verificar qual o segredo atualmente em vigor — útil para confirmar que o seu receptor e o servidor estão sincronizados após uma rotação.
Boas práticas do segredo de assinatura
Seção intitulada “Boas práticas do segredo de assinatura”Quando rodar:
- Suspeita de comprometimento: o segredo apareceu em um arquivo de registro, foi compartilhado com um funcionário que saiu, ou foi capturado em uma gravação de tela.
- Higiene de rotina: rodar periodicamente limita o impacto de uma exposição não detectada.
- Depois de qualquer alteração a que serviços podem ler o segredo (rotação de gestão de chaves).
Como atualizar o receptor:
- Rode na interface de admin e copie o novo segredo.
- Atualize o segredo no armazenamento de segredos do seu receptor (variável de ambiente, gestor de segredos, etc.).
- Implemente o receptor atualizado.
- Confirme que a entrega seguinte é bem-sucedida na aba Entregas.
Se perdeu o novo segredo antes de o salvar:
Execute novamente. Cada rotação gera um novo segredo aleatório. O texto simples anterior não pode ser recuperado — apenas uma forma criptografada com AEAD é salva no servidor.