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, ou um administrador exclui o local ou a fila que a contém |
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) |
Quando um administrador exclui um local ou uma fila, o Jonot envia ticket.cancelled para cada senha que ainda estava aguardando ou chamada, até 50 senhas por exclusão. As senhas além desse limite não geram evento. Excluir uma organização não envia webhooks, porque os endpoints dela são excluídos junto.
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.
Quando as entregas de um evento passariam do limite da organização, todas as entregas desse evento são registradas como Limite atingido e nenhuma é enviada. O Jonot não as repete e elas não contam para as 20 falhas consecutivas que desativam um endpoint automaticamente. A aba Entregas registra as entregas descartadas até um limite por minuto, de modo que um pico prolongado deixa um número limitado de registros em vez de um por entrega descartada.
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 |
| Entregas máximas por organização | 600 / 60 s |
| Descartes registrados máximos por organização | 100 / 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.