Pular para o conteúdo principal

Webhooks (Outbound): Receba Eventos de Entrega

🔗 Integração bidirecional: Receba notificações em tempo real sobre entregas, aberturas e cliques

Webhooks permitem que a Notifica chame sua API quando eventos acontecem. Use para:

  • Atualizar status de notificações no seu banco
  • Disparar ações baseadas em entrega
  • Sincronizar analytics
  • Implementar lógica de retry customizada

Como funcionam

  1. Evento acontece (ex: email entregue)
  2. Notifica faz POST para sua URL
  3. Sua API processa e responde 200 OK
  4. Notifica marca como entregue

Se sua API retornar erro, a Notifica faz retry automático.


Criando um webhook

curl -X POST https://app.usenotifica.com.br/v1/webhooks \
-H "Authorization: Bearer nk_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.empresa.com.br/webhooks/notifica",
"events": [
"notification.sent",
"notification.delivered",
"notification.failed",
"notification.opened",
"notification.clicked",
"notification.bounced",
"notification.complained"
],
"secret": "whsec_minha_chave_secreta_32_chars",
"description": "Webhook principal de eventos",
"active": true
}'

Resposta:

{
"id": "wh_abc123",
"url": "https://api.empresa.com.br/webhooks/notifica",
"events": ["notification.sent", "notification.delivered", ...],
"secret": "whsec_***",
"active": true,
"created_at": "2024-01-15T10:00:00Z"
}

Eventos disponíveis

Notificações

EventoDescriçãoDados incluídos
notification.createdNotificação criadanotification, subscriber
notification.sentEnviada ao provedornotification, provider
notification.deliveredConfirmada entreganotification, delivered_at
notification.failedFalha na entreganotification, error
notification.bouncedEmail/telefone inválidonotification, bounce_reason
notification.openedEmail abertonotification, opened_at, ip, user_agent
notification.clickedLink clicadonotification, clicked_at, url
notification.complainedMarcado como spamnotification, complaint_type

Workflows

EventoDescrição
workflow.triggeredWorkflow iniciado
workflow.step.completedStep concluído
workflow.step.failedStep falhou
workflow.completedWorkflow finalizado

Inbox

EventoDescrição
inbox.notification.receivedNova notificação no inbox
inbox.notification.readNotificação marcada como lida

Payload do webhook

Exemplo: notification.delivered

{
"id": "evt_xyz789",
"type": "notification.delivered",
"created_at": "2024-01-15T14:30:05Z",
"data": {
"notification": {
"id": "not_abc123",
"status": "delivered",
"channel": "email",
"to": "[email protected]",
"subject": "Seu pedido foi enviado!",
"template": "order_shipped",
"external_id": "order_456"
},
"subscriber": {
"id": "sub_def456",
"external_id": "user_789",
"email": "[email protected]"
},
"delivery": {
"delivered_at": "2024-01-15T14:30:05Z",
"provider": "sendgrid",
"message_id": "<[email protected]>"
}
}
}

Exemplo: notification.failed

{
"id": "evt_abc456",
"type": "notification.failed",
"created_at": "2024-01-15T14:30:02Z",
"data": {
"notification": {
"id": "not_abc123",
"status": "failed",
"channel": "email"
},
"error": {
"code": "invalid_recipient",
"message": "550 5.1.1 The email account does not exist",
"provider": "sendgrid",
"attempt": 3,
"will_retry": false
}
}
}

Verificando assinatura

Proteja seu endpoint verificando que o webhook vem realmente da Notifica:

Node.js

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');

return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}

// Express middleware
app.post('/webhooks/notifica', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-notifica-signature'];
const payload = req.body;
const secret = process.env.NOTIFICA_WEBHOOK_SECRET;

if (!verifyWebhookSignature(payload, signature, secret)) {
return res.status(401).send('Invalid signature');
}

const event = JSON.parse(payload);

// Processar evento
handleWebhook(event);

res.status(200).send('OK');
});

Python

import hmac
import hashlib

def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()

return hmac.compare_digest(
signature.replace('sha256=', ''),
expected
)

# Flask
@app.route('/webhooks/notifica', methods=['POST'])
def webhook():
signature = request.headers.get('X-Notifica-Signature', '')
secret = os.getenv('NOTIFICA_WEBHOOK_SECRET')

if not verify_webhook_signature(request.data, signature, secret):
return 'Invalid signature', 401

event = request.json
handle_webhook(event)

return 'OK', 200

Processando eventos

Node.js completo

// services/webhookProcessor.js

class WebhookProcessor {
async process(event) {
switch (event.type) {
case 'notification.delivered':
await this.handleDelivered(event.data);
break;
case 'notification.failed':
await this.handleFailed(event.data);
break;
case 'notification.opened':
await this.handleOpened(event.data);
break;
case 'notification.clicked':
await this.handleClicked(event.data);
break;
case 'notification.bounced':
await this.handleBounced(event.data);
break;
default:
console.log(`Evento não tratado: ${event.type}`);
}
}

async handleDelivered({ notification }) {
await db.notifications.update(notification.id, {
status: 'delivered',
delivered_at: new Date()
});

// Atualizar métricas do usuário
await db.users.incrementMetric(
notification.subscriber.external_id,
'emails_delivered'
);
}

async handleFailed({ notification, error }) {
await db.notifications.update(notification.id, {
status: 'failed',
error_code: error.code,
error_message: error.message
});

// Se falhou permanentemente, notificar usuário ou equipe
if (!error.will_retry) {
await this.alertFailedNotification(notification, error);
}
}

async handleBounced({ notification, error }) {
// Marcar email como inválido
await db.subscribers.update(
notification.subscriber.id,
{ email_valid: false, bounce_reason: error.message }
);

// Adicionar à lista de supressão
await db.suppressions.create({
email: notification.to,
reason: 'bounce',
permanent: true
});
}

async handleOpened({ notification, delivery }) {
await db.notifications.update(notification.id, {
opened_at: delivery.opened_at,
open_ip: delivery.ip,
open_user_agent: delivery.user_agent
});
}

async handleClicked({ notification, delivery }) {
await db.click_events.create({
notification_id: notification.id,
url: delivery.url,
clicked_at: delivery.clicked_at
});
}
}

module.exports = new WebhookProcessor();

Retry e garantias de entrega

Comportamento de retry

Se seu endpoint retornar erro:

TentativaDelayAção se falhar
1ImediatoRetry
25 minutosRetry
330 minutosRetry
42 horasRetry
5+6 horasDescartar

Verificar deliveries perdidos

curl https://app.usenotifica.com.br/v1/webhooks/wh_abc123/deliveries \
-H "Authorization: Bearer nk_live_sua_chave"

Testando webhooks

Teste via dashboard

Vá em Webhooks → Testar e envie um evento de teste.

Teste via API

curl -X POST https://app.usenotifica.com.br/v1/webhooks/wh_abc123/test \
-H "Authorization: Bearer nk_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"event": "notification.delivered",
"override_url": "https://webhook.site/seu-uuid-aqui"
}'

Ferramentas de teste

  • webhook.site — URLs temporárias para teste
  • ngrok — Expor localhost para testes locais

Listando e gerenciando webhooks

Listar webhooks

curl https://app.usenotifica.com.br/v1/webhooks \
-H "Authorization: Bearer nk_live_sua_chave"

Atualizar webhook

curl -X PUT https://app.usenotifica.com.br/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer nk_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{
"events": ["notification.delivered", "notification.failed"],
"url": "https://nova-url.com.br/webhook"
}'

Desativar webhook

curl -X PUT https://app.usenotifica.com.br/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer nk_live_sua_chave" \
-H "Content-Type: application/json" \
-d '{"active": false}'

Deletar webhook

curl -X DELETE https://app.usenotifica.com.br/v1/webhooks/wh_abc123 \
-H "Authorization: Bearer nk_live_sua_chave"

Boas práticas

✅ Faça

  • Verifique assinatura de todos os webhooks
  • Responda 200 OK rapidamente (< 5s)
  • Processe webhook em background (fila)
  • Idempotência: mesmo evento pode chegar 2x
  • Logue todos os webhooks recebidos

❌ Não faça

  • Processamento síncrono pesado no handler
  • Depender de ordem dos eventos
  • Ignorar erros de assinatura
  • Retornar 4xx para erros temporários

FAQ

Q: Posso ter múltiplos webhooks?
A: Sim! Máximo 10 por workspace.

Q: Webhooks são garantidos?
A: Fazemos retry mas não garantimos 100%. Use polling de backup para dados críticos.

Q: Qual o timeout?
A: 30 segundos. Responda 200 OK imediatamente e processe em background.

Q: Eventos chegam em ordem?
A: Não garantimos ordenação. Use created_at para ordenar.

Q: Posso filtrar por notificação específica?
A: Não no webhook. Filtre no seu código após receber.


Próximo passo

Explore o Analytics para visualizar métricas de entrega e otimizar suas notificações.