Webhooks Engine
O Building Block Webhooks Engine fornece funcionalidades completas para notificação de eventos em tempo real via webhooks na Catalisa Platform.
Visão Geral
O Webhooks Engine é um módulo tenant-scoped, ou seja, requer um token com organizationId. Ele é responsável por:
- Subscriptions - Configuração de endpoints para receber eventos
- Signing Keys - Chaves para verificação de assinatura das mensagens
- Delivery Logs - Registro de todas as tentativas de entrega
- Retry Policy - Política de reenvio automático em caso de falha
- Event Filtering - Filtros para selecionar tipos de eventos específicos
Base URL
https://api.catalisa.io/webhooks
Recursos
| Recurso | Descrição |
|---|---|
| Subscriptions | Gerenciamento de subscriptions |
Permissões
| Permissão | Descrição |
|---|---|
WEBHOOKS_CREATE | Criar webhook subscriptions |
WEBHOOKS_READ | Listar e visualizar subscriptions |
WEBHOOKS_UPDATE | Atualizar subscriptions |
WEBHOOKS_DELETE | Excluir subscriptions |
WEBHOOKS_MANAGE_KEYS | Gerenciar signing keys |
WEBHOOKS_VIEW_LOGS | Visualizar logs de entrega |
WEBHOOKS_TEST | Testar webhook endpoints |
WEBHOOKS_CONFIG | Gerenciar configuração global |
Exemplo Rápido
Fluxo completo para configurar um webhook:
// 1. Criar uma subscription de webhook
const subscriptionResponse = await fetch('https://api.catalisa.io/webhooks/api/v1/subscriptions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: {
type: 'webhook-subscriptions',
attributes: {
name: 'Notificações de Billing',
endpointUrl: 'https://minha-app.com/webhooks/billing',
eventFilters: [
'billing.invoice.created',
'billing.invoice.paid',
'billing.payment.completed',
],
timeoutMs: 30000,
retryConfig: {
maxRetries: 5,
retryBackoffMs: 1000,
retryBackoffMultiplier: 2.0,
},
},
},
}),
});
const { data: subscription } = await subscriptionResponse.json();
console.log(`Subscription criada: ${subscription.id}`);
// 2. Obter as signing keys para verificação
const keysResponse = await fetch('https://api.catalisa.io/webhooks/api/v1/signing-keys', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const { data: keys } = await keysResponse.json();
const activeKey = keys.find(k => k.attributes.status === 'ACTIVE');
console.log(`Key ID para verificação: ${activeKey.attributes.keyId}`);
// 3. Testar o endpoint
const testResponse = await fetch(`https://api.catalisa.io/webhooks/api/v1/subscriptions/${subscription.id}/test`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
},
});
const { data: testResult } = await testResponse.json();
if (testResult.attributes.success) {
console.log(`Teste bem-sucedido! Tempo de resposta: ${testResult.attributes.responseTimeMs}ms`);
} else {
console.log(`Teste falhou: ${testResult.attributes.errorMessage}`);
}
// 4. Verificar logs de entrega
const logsResponse = await fetch(`https://api.catalisa.io/webhooks/api/v1/delivery-logs?filter[subscriptionId]=${subscription.id}`, {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const { data: logs } = await logsResponse.json();
logs.forEach(log => {
console.log(`${log.attributes.eventType}: ${log.attributes.status}`);
});
Arquitetura
┌─────────────────────────────────────────────────────────────────────┐
│ Webhooks Engine │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Event Publisher │ │
│ │ │ │
│ │ Redis Pub/Sub ──► Event Router ──► Delivery Queue │ │
│ │ │ │ │
│ │ ┌──────▼──────┐ │ │
│ │ │ Filter │ │ │
│ │ │ Events │ │ │
│ │ └─────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Subscriptions│ │ Signing │ │ Delivery │ │
│ │ │ │ Keys │ │ Logs │ │
│ │ - endpoint │ │ │ │ │ │
│ │ - filters │ │ - ED25519 │ │ - attempts │ │
│ │ - retry │ │ - rotation │ │ - status │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ Delivery Worker │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Sign │ │ Send │ │ Handle │ │ │
│ │ │ Request │ ──►│ HTTP │ ──►│ Response │ │ │
│ │ │ │ │ Request │ │ / Retry │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Conceitos Importantes
Status da Subscription
| Status | Descrição |
|---|---|
ACTIVE | Subscription ativa, recebendo eventos |
PAUSED | Subscription pausada temporariamente |
DISABLED | Subscription desabilitada (muitas falhas) |
Retry Policy
O Webhooks Engine implementa uma política de retry com backoff exponencial:
| Configuração | Padrão | Descrição |
|---|---|---|
maxRetries | 5 | Número máximo de tentativas |
retryBackoffMs | 1000 | Tempo inicial de espera (ms) |
retryBackoffMultiplier | 2.0 | Multiplicador do backoff |
Exemplo de intervalos com configuração padrão:
- 1ª tentativa: imediata
- 2ª tentativa: 1 segundo depois
- 3ª tentativa: 2 segundos depois
- 4ª tentativa: 4 segundos depois
- 5ª tentativa: 8 segundos depois
- 6ª tentativa: 16 segundos depois
Limite máximo: 1 hora entre tentativas (para evitar delays muito longos)
Status de Entrega
| Status | Descrição |
|---|---|
PENDING | Aguardando primeira tentativa |
DELIVERED | Entregue com sucesso (2xx) |
FAILED | Falhou após todas as tentativas |
RETRYING | Aguardando próxima tentativa |
Assinatura das Mensagens
Todas as requisições enviadas incluem headers de assinatura para verificação:
| Header | Descrição |
|---|---|
x-webhook-id | ID único da entrega |
x-webhook-timestamp | Timestamp da requisição (ISO 8601) |
x-webhook-signature | Assinatura ED25519 em base64 |
x-webhook-key-id | ID da chave usada |
x-webhook-version | Versão do protocolo (v1) |
Verificação da Assinatura
Para verificar a autenticidade de uma mensagem:
const crypto = require('crypto');
function verifyWebhookSignature(payload, headers, publicKey) {
const message = `${headers['x-webhook-id']}.${headers['x-webhook-timestamp']}.${JSON.stringify(payload)}`;
const signature = Buffer.from(headers['x-webhook-signature'], 'base64');
const key = Buffer.from(publicKey, 'base64');
return crypto.verify(
null, // ED25519 não usa algoritmo de hash
Buffer.from(message),
{ key, format: 'der', type: 'spki' },
signature
);
}
// Uso no handler do webhook
app.post('/webhooks/billing', (req, res) => {
const isValid = verifyWebhookSignature(req.body, req.headers, PUBLIC_KEY);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Processar evento
console.log(`Evento recebido: ${req.body.type}`);
res.status(200).json({ received: true });
});
Formato do Payload
{
"id": "evt_550e8400-e29b-41d4-a716-446655440070",
"type": "billing.invoice.paid",
"data": {
"invoiceId": "550e8400-e29b-41d4-a716-446655440050",
"billingAccountId": "550e8400-e29b-41d4-a716-446655440010",
"amount": 1499.50,
"currency": "BRL",
"paidAt": "2024-02-28T14:00:00Z"
},
"metadata": {
"timestamp": "2024-02-28T14:00:05Z",
"correlationId": "req_550e8400-e29b-41d4-a716-446655440080",
"organizationId": "550e8400-e29b-41d4-a716-446655440000"
}
}
Tipos de Eventos Disponíveis
IAM
iam.user.created,iam.user.updated,iam.user.deletediam.organization.created,iam.organization.updatediam.role.created,iam.role.updated,iam.role.deleted
Billing
billing.account.created,billing.account.updatedbilling.subscription.created,billing.subscription.activatedbilling.subscription.paused,billing.subscription.canceledbilling.invoice.created,billing.invoice.finalized,billing.invoice.paidbilling.payment.completed,billing.payment.refunded
Customers
customers.person.created,customers.person.updated,customers.person.deleted
Products
products.product.created,products.product.updated,products.product.deleted
Decision Engine
decision.executed
Feature Flags
feature-flags.flag.created,feature-flags.flag.enabled,feature-flags.flag.disabled
Boas Práticas
- Responda rapidamente - Retorne 2xx em até 30 segundos
- Processe de forma assíncrona - Coloque eventos em uma fila
- Idempotência - Use o
iddo evento para evitar duplicação - Verifique a assinatura - Sempre valide antes de processar
- Monitore os logs - Acompanhe falhas de entrega
- Use filtros - Configure apenas os eventos necessários