Configurar
Cadastrando um endpoint
curl -X POST https://api.joinotify.com/webhook-endpoints \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "producao",
"url": "https://seu-servidor.com/webhooks/joinotify",
"events": ["messages"]
}'
{
"data": {
"id": "whe_01J8ZK...",
"name": "producao",
"url": "https://seu-servidor.com/webhooks/joinotify",
"events": ["messages"],
"active": true,
"secret": "whsec_AbCdEf0123456789abcdefgh"
},
"warning": "Store the \"secret\" now — use it to validate the X-Joinotify-Signature-256 header."
}
Diferente das chaves de API, o segredo de webhook pode ser consultado depois:
curl https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../secret \
-H 'Authorization: Bearer sk_live_xxx'
Requisitos da URL
Em produção recusamos:
http://— só HTTPS- Hosts privados:
localhost,127.*,10.*,192.168.*,169.254.*,172.16-31.*,*.internal,*.local
É uma proteção contra SSRF: sem ela, um endpoint apontando para a rede interna viraria um scanner.
Para desenvolvimento local, use um túnel:
ngrok http 3000
Filtros
curl -X POST https://api.joinotify.com/webhook-endpoints \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{
"name": "atendimento-loja-1",
"url": "https://seu-servidor.com/webhooks/loja-1",
"events": ["messages", "smb_message_echoes"],
"phoneNumberId": "106540352242922"
}'
| Campo | Efeito |
|---|---|
events: [] | Assina todos os eventos |
events: ["messages"] | Só o que estiver na lista |
wabaId | Só eventos daquela conta empresarial |
phoneNumberId | Só eventos daquele número |
Eventos de conta (account_update, account_alerts) e de template
(message_template_status_update) não têm número associado. Um endpoint filtrado por
phoneNumberId não os recebe.
Se você quer tudo, deixe um endpoint sem filtro.
Vários endpoints
Nada impede ter mais de um — e costuma ser uma boa ideia:
# Um para o produto
{ "name": "app", "url": "https://app.exemplo.com/webhooks", "events": ["messages"] }
# Outro para auditoria, assinando tudo
{ "name": "auditoria", "url": "https://logs.exemplo.com/webhooks", "events": [] }
Cada endpoint tem seu próprio segredo, sua própria fila e seu próprio contador de falhas. Um endpoint quebrado não afeta os outros.
Testando
curl -X POST https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../test \
-H 'Authorization: Bearer sk_live_xxx'
Dispara um evento sintético e entrega na hora, sem passar pela fila. A resposta traz o status HTTP e o corpo que o seu servidor devolveu — é a forma mais rápida de conferir se a validação de assinatura está correta.
{
"data": {
"id": "whd_01J8ZM...",
"status": "delivered",
"responseStatus": 200,
"responseBody": "OK",
"durationMs": 142
}
}
O payload de teste é um evento messages com joinotify_test: true — filtre por esse
campo se o seu handler grava tudo no banco.
Atualizando
curl -X PATCH https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "events": ["messages", "smb_message_echoes", "history"] }'
Campos omitidos ficam como estão. Reativar um endpoint desativado (active: true) zera o
contador de falhas.
Pausando e removendo
# Pausar — preserva histórico e segredo
curl -X PATCH https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx' \
-H 'Content-Type: application/json' \
-d '{ "active": false }'
# Remover — descarta as entregas pendentes
curl -X DELETE https://api.joinotify.com/webhook-endpoints/whe_01J8ZK... \
-H 'Authorization: Bearer sk_live_xxx'
Para uma manutenção, pause. Remover é definitivo.
Rotacionando o segredo
curl -X POST https://api.joinotify.com/webhook-endpoints/whe_01J8ZK.../rotate-secret \
-H 'Authorization: Bearer sk_live_xxx'
A troca é imediata e não há convivência entre os dois segredos. Duas formas de fazer sem perder evento:
Sem downtime — faça o seu handler aceitar dois segredos por um período: valide contra o novo e, se falhar, contra o antigo. Rotacione, confirme, remova o antigo.
Aceitando falhas — rotacione e publique o novo segredo. As entregas que falharem no intervalo são reentregues automaticamente em 60 segundos.