Eventos e payload
Lista de eventos suportados, formato das entregas, headers e validação da assinatura HMAC.
Todo evento chega por POST na URL cadastrada, com a mesma estrutura base e um conjunto de headers de identificação.
Eventos suportados
Há 30 valores no enum WebhookEvent (fonte: schema Prisma).
| Domínio | Eventos |
|---|---|
WHATSAPP_RECEIVED, WHATSAPP_SENT, WHATSAPP_DELIVERED, WHATSAPP_READ, WHATSAPP_FAILED | |
EMAIL_SENT, EMAIL_DELIVERED, EMAIL_OPENED, EMAIL_CLICKED, EMAIL_BOUNCED, EMAIL_UNSUBSCRIBED, EMAIL_COMPLAINED | |
| Campanhas | CAMPAIGN_SCHEDULED, CAMPAIGN_STARTED, CAMPAIGN_COMPLETED, CAMPAIGN_FAILED, CAMPAIGN_PAUSED, CAMPAIGN_CANCELLED |
| Contatos (consent) | CONTACT_SUBSCRIBED, CONTACT_UNSUBSCRIBED, CONTACT_CONSENT_GIVEN, CONTACT_CONSENT_REVOKED |
| Contatos / CRM | CONTACT_CREATED, CONTACT_UPDATED, TAG_ADDED |
| Conversas | CONVERSATION_CREATED, CONVERSATION_ASSIGNED, CONVERSATION_CLOSED |
| Negócios | DEAL_STAGE_CHANGED |
| Flows | FLOW_COMPLETED |
Estrutura do payload
Estrutura base
{
"event": "WHATSAPP_RECEIVED",
"timestamp": "2026-02-04T18:00:00.000Z",
"data": {},
"metadata": {
"tenantId": "workspace_...",
"eventId": "..."
}
}WHATSAPP_RECEIVED
{
"event": "WHATSAPP_RECEIVED",
"timestamp": "2026-02-04T18:00:00.000Z",
"data": {
"message": {
"id": "msg_...",
"whatsappMessageId": "wamid...",
"conversationId": "conv_...",
"from": "+5511999999999",
"to": "",
"content": "Olá",
"type": "text",
"timestamp": "2026-02-04T18:00:00.000Z",
"mediaUrl": null,
"mediaType": null,
"filename": null,
"metadata": {}
},
"conversation": {
"id": "conv_...",
"status": "bot",
"channel": "whatsapp",
"contactName": "Maria"
}
},
"metadata": {
"tenantId": "workspace_...",
"eventId": "wamid..."
}
}WHATSAPP_SENT
{
"event": "WHATSAPP_SENT",
"timestamp": "2026-02-04T18:00:00.000Z",
"data": {
"message": {
"id": "msg_...",
"whatsappMessageId": "wamid...",
"conversationId": "conv_...",
"status": "sent",
"recipientId": "5511999999999",
"timestamp": "1707079200"
}
},
"metadata": {
"tenantId": "workspace_...",
"eventId": "wamid...:sent"
}
}EMAIL_SENT
{
"event": "EMAIL_SENT",
"timestamp": "2026-02-04T18:00:00.000Z",
"data": {
"campaignId": "camp_...",
"recipientId": "rec_...",
"email": "cliente@exemplo.com"
},
"metadata": {
"tenantId": "workspace_..."
}
}DEAL_STAGE_CHANGED
{
"event": "DEAL_STAGE_CHANGED",
"timestamp": "2026-08-04T12:00:00.000Z",
"data": {
"dealId": "deal_...",
"fromStageId": "stage_a",
"toStageId": "stage_b",
"pipelineId": "pipe_..."
},
"metadata": { "tenantId": "workspace_...", "eventId": "..." }
}FLOW_COMPLETED
{
"event": "FLOW_COMPLETED",
"timestamp": "2026-08-04T12:00:00.000Z",
"data": {
"flowId": "flow_...",
"executionId": "exec_...",
"status": "COMPLETED"
},
"metadata": { "tenantId": "workspace_...", "eventId": "..." }
}CONTACT_CREATED
{
"event": "CONTACT_CREATED",
"timestamp": "2026-08-04T12:00:00.000Z",
"data": {
"contactId": "ct_...",
"phone": "+5511987654321"
},
"metadata": { "tenantId": "workspace_...", "eventId": "..." }
}Headers de cada entrega
| Header | Conteúdo |
|---|---|
Content-Type | application/json |
User-Agent | LIP-Webhook/1.0 |
X-LIP-Event | Nome do evento |
X-LIP-Timestamp | Timestamp da entrega |
X-LIP-Attempt | Número da tentativa |
X-LIP-Signature | Assinatura HMAC, quando o secret está configurado |
Assinatura HMAC
A assinatura é um HMAC SHA-256 do payload concatenado ao timestamp:
Fórmula
signature = HMAC_SHA256(secret, "{timestamp}.{payload_json}")Sempre valide a assinatura
Antes de confiar em um evento, recalcule o HMAC com o seu secret e compare com X-LIP-Signature. Use o corpo bruto da requisição, não o JSON re-serializado, para o cálculo bater.
Validação em Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
function isValidSignature(rawBody, timestamp, signature, secret) {
const expected = createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}Retries e timeouts
- O timeout de entrega é de 30 segundos.
- Os retries seguem backoff exponencial, controlado por
backoffMsemaxRetries. - Uma resposta
2xxmarca o webhook como entregue;4xxe5xxdisparam nova tentativa.
Deduplique pelo eventId
Como um evento pode ser reentregue, use metadata.eventId como chave de idempotência no seu lado antes de processar.