Webhooks de eventos
Um webhook de eventos envia um POST ao seu sistema quando um fato acontece no SquadOS. Ele serve para alimentar CRM, CDP, painel interno ou automações externas sem consultar a API repetidamente.
Acesso e permissões
Section titled “Acesso e permissões”Abra Configurações → Desenvolvedores → Webhooks. Os nomes da navegação e dos botões abaixo correspondem ao catálogo PT-BR atual.
O acesso usa capacidades granulares:
| Capacidade | Contrato do produto |
|---|---|
webhooks.view | Abre a seção e permite ler os destinos e o histórico no backend |
webhooks.write | Cria, edita, testa, gira a chave, ativa/desativa e agenda reenvio |
webhooks.delete | Exclui o destino e o histórico associado |
Proprietários e administradores mantêm essas permissões pelo contrato legado. Em funções personalizadas, conceda webhooks.write e webhooks.delete para gestão completa; ambas dependem de webhooks.view.
A lista mostra nome, URL, estado e até três eventos por destino. Os estados são Ativo, Desativado e Desativado por falha. Uma organização pode ter no máximo 10 webhooks.
Criando um destino
Section titled “Criando um destino”Selecione Novo webhook e complete as quatro seções. O formulário só permite criar quando há nome, URL HTTPS, pelo menos um evento, filtros completos e cabeçalhos válidos.
1. Identificação
Section titled “1. Identificação”Informe um nome e a URL final do receptor. A URL precisa começar com https://.
Na entrega, o SquadOS também bloqueia localhost, redes privadas ou reservadas, endpoints de metadata, o próprio projeto Supabase e hosts cujo DNS não possa ser validado. Essa checagem ocorre depois que o destino foi salvo; uma URL bloqueada encerra a entrega sem retentativa.
2. Eventos
Section titled “2. Eventos”O catálogo atual contém 25 eventos. Marcar tudo em uma família não inclui as três notas internas, que começam desmarcadas.
Mensagens
| Nome na interface | Tipo enviado | Quando ocorre |
|---|---|---|
| Mensagem recebida | message.received | Um contato envia uma mensagem |
| Mensagem enviada | message.sent | Um agente de IA ou pessoa da equipe responde; no streaming, somente após o texto final |
| Mensagem editada | message.updated | O texto de uma mensagem muda |
| Mensagem apagada | message.deleted | A mensagem recebe a marca de exclusão |
Conversas
| Nome na interface | Tipo enviado |
|---|---|
| Conversa aberta | conversation.created |
| Conversa atribuída | conversation.assigned |
| Conversa transferida | conversation.transferred |
| Conversa devolvida à IA | conversation.transferred_to_agent |
| Conversa concluída | conversation.resolved |
| Conversa reaberta | conversation.reopened |
| IA ligada ou desligada | conversation.ai_toggled |
| Conversa adiada | conversation.snoozed |
| Conversa retomada | conversation.unsnoozed |
| Contatos unificados na conversa | conversation.contacts_merged |
| Nota interna criada | conversation.internal_note_created |
| Nota interna editada | conversation.internal_note_updated |
| Nota interna apagada | conversation.internal_note_deleted |
| Etiqueta na conversa | conversation.tag_added |
| Etiqueta retirada da conversa | conversation.tag_removed |
Avaliação e contatos
| Nome na interface | Tipo enviado |
|---|---|
| Avaliação concluída | satisfaction.completed |
| Contato criado | contact.created |
| Contato atualizado | contact.updated |
| Contatos unificados | contact.merged |
| Etiqueta no contato | contact.tag_added |
| Etiqueta retirada do contato | contact.tag_removed |
3. Filtros
Section titled “3. Filtros”Sem filtro, o destino recebe todos os tipos marcados. Os critérios disponíveis são Caixa de entrada, Dono da conversa, Tag da conversa, Tag do contato, Canal e IA da conversa. Condições diferentes são combinadas com E; dentro de um critério de conjunto, os valores selecionados formam OU.
Filtros cujo eixo não existe no evento são ignorados. Por exemplo, eventos de contato não têm conversa, caixa, dono, canal nem estado da IA; um filtro nesses eixos não bloqueia contact.created. O critério Tag do contato continua aplicável.
4. Segurança e estado
Section titled “4. Segurança e estado”Use Cabeçalhos adicionais quando o receptor exigir, por exemplo, Authorization: Bearer .... Content-Type e qualquer nome iniciado por X-Squados- são reservados e não podem ser sobrescritos.
O controle Ativo funciona ao editar um destino existente. Na criação atual, o webhook é gravado ativo mesmo se o controle estiver desligado. Se ele precisar começar pausado, crie-o e desative-o imediatamente na lista.
Chave de verificação e teste
Section titled “Chave de verificação e teste”Depois de criar, copie a Chave de verificação: ela aparece uma única vez. Gerar nova chave na edição invalida a anterior imediatamente; entregas seguintes usam a nova chave, enquanto uma requisição já assinada pode falhar e voltar pela retentativa.
Ainda no painel da chave, Enviar evento de teste envia dados fictícios pelo mesmo guard, assinatura e sender das entregas reais. Na edição, o mesmo botão usa o primeiro tipo marcado. O payload contém data.test: true, aparece no histórico e mostra o status, a duração e o começo da resposta do receptor.
Corpo e cabeçalhos
Section titled “Corpo e cabeçalhos”Toda entrega usa este envelope:
{ "id": "9f2c1b7e-3a44-4d1c-9b0e-52a7c8e1d004", "type": "message.received", "api_version": "2026-08-24", "occurred_at": "2026-09-02T14:03:11.482Z", "organization_id": "6d1b0a52-8f3e-4c77-9a10-2b5e7c9d3311", "data": { "message": { "id": "8f2b1c4e-0000-4000-8000-000000000001", "role": "user", "sender_kind": "contact", "content": "Olá", "attachments": [], "created_at": "2026-09-02T14:03:11.400Z" }, "conversation": { "id": "a41d9e77-0000-4000-8000-000000000001", "status": "open", "ai_enabled": true, "channel_type": "whatsapp_official", "channel_family": "whatsapp", "inbox": { "id": "00000000-0000-4000-8000-0000000010b0", "name": "Comercial" }, "agent": { "id": "00000000-0000-4000-8000-000000000a6e", "name": "Recepcionista" }, "assigned_to": null }, "contact": { "id": "c7e30b12-0000-4000-8000-000000000001", "display_name": "João Ribeiro", "identity_type": "whatsapp", "identity_value": "+5511988887777" } }}data varia por família:
| Família | Blocos principais |
|---|---|
| Mensagem | message, conversation, contact; mensagens do assistente também incluem modelo, tokens, créditos e custo quando disponíveis |
| Conversa | conversation, contact, actor, event; mudanças de etiqueta também incluem tag |
| Avaliação | satisfaction, conversation, contact |
| Contato | contact; mudanças de etiqueta incluem tag, e unificação inclui o contato de destino |
Esses blocos são resumos curados do estado no momento do fato, não cópias completas das linhas internas. Campos novos podem aparecer na mesma api_version; ignore chaves desconhecidas. Use occurred_at para ordenar e o id do envelope para deduplicar.
| Cabeçalho | Conteúdo |
|---|---|
X-Squados-Event | Tipo do evento |
X-Squados-Delivery | ID da entrega; permanece igual nas retentativas automáticas e muda em um reenvio manual |
X-Squados-Timestamp | Momento da tentativa, em segundos Unix |
X-Squados-Signature | HMAC no formato sha256=<hexadecimal> |
Verificando a assinatura
Section titled “Verificando a assinatura”A assinatura é HMAC-SHA256(chave, "<timestamp>.<corpo cru>"). Confira a assinatura com comparação constante, rejeite timestamps antigos conforme a janela de segurança da sua integração e só depois processe o JSON.
import crypto from "node:crypto";
function assinaturaConfere(chave, timestamp, corpoCru, recebida) { const esperada = "sha256=" + crypto.createHmac("sha256", chave).update(`${timestamp}.${corpoCru}`).digest("hex");
const a = Buffer.from(esperada); const b = Buffer.from(recebida ?? ""); return a.length === b.length && crypto.timingSafeEqual(a, b);}Entrega e retentativas
Section titled “Entrega e retentativas”A fila é consultada uma vez por minuto. Uma resposta 2xx conclui a entrega. 3xx, 4xx, 5xx, conexão recusada e timeout de 15 segundos falham; redirects não são seguidos. O histórico guarda até 4.000 caracteres do corpo da resposta e até 1.000 do erro.
O runtime atual faz no máximo seis tentativas:
| Tentativa | Espera depois da falha anterior |
|---|---|
| 1ª | Próximo ciclo da fila |
| 2ª | 1 minuto |
| 3ª | 5 minutos |
| 4ª | 15 minutos |
| 5ª | 1 hora |
| 6ª e terminal | 3 horas |
Assim, uma entrega que falha sempre se torna terminal em aproximadamente 4h21, além do tempo dos ciclos da fila. Uma URL bloqueada pelo guard ou um webhook sem chave morre na primeira tentativa, pois repetir não mudaria o resultado.
A garantia é pelo menos uma vez: uma resposta perdida ou lease recuperado pode repetir o mesmo evento, e a ordem entre eventos não é garantida. Deduplicate pelo id do envelope, não pelo X-Squados-Delivery.
Depois de cinco entregas terminais consecutivas para o mesmo problema e URL, o destino é marcado Desativado por falha. Proprietários e administradores recebem o alerta por e-mail. Corrija o receptor e use Ativar; a reativação zera os contadores e resolve o alerta aberto.
Histórico e reenvio
Section titled “Histórico e reenvio”Abra Histórico de entregas no destino. A lista pagina 25 itens por vez e mostra tipo, estado, HTTP, duração, número de tentativas, payload enviado, resposta recebida e marcações de teste ou reenvio.
Entregas dead — e registros legados failed — oferecem Reenviar. O comando cria uma nova entrega pendente ligada à original, mesmo se o destino estiver desativado. Ele não apaga nem modifica a falha anterior e sai no próximo ciclo da fila.
Eventos e entregas são apagados depois de 30 dias. Excluir o webhook também apaga imediatamente seu segredo e histórico. Eventos ocorridos enquanto o destino está desativado não são acumulados.
Checklist do receptor
Section titled “Checklist do receptor”- Responda
2xxrapidamente e processe trabalho pesado fora da requisição. - Valide
X-Squados-TimestampeX-Squados-Signaturecontra o corpo cru. - Deduplicate pelo
iddo envelope. - Ignore campos desconhecidos e trate blocos opcionais ou
null. - Monitore falhas e use o histórico antes que o destino seja desativado.
- Para recuperar estado atual ou dados fora da retenção, consulte a API REST.