O que é um Webhook

Webhook é um mecanismo que permite ao seu sistema receber notificações automáticas sobre eventos por meio de solicitações HTTP. Quando um determinado evento ocorre em nosso sistema (por exemplo, uma campanha é concluída), enviamos uma solicitação POST com informações detalhadas para a URL que você especificar.

Configuração de Webhook

Você pode configurar webhooks no painel de controle web:

  1. Vá para a seção Integrações.
  2. Clique no cartão Webhooks.
  3. Clique em Criar Webhook.
  4. Preencha os campos:
    • Nome do webhook — para sua identificação pessoal do webhook.
    • URL para enviar o webhook — o endereço do seu servidor que receberá as notificações.
    • Eventos — selecione da lista os eventos sobre os quais deseja receber notificações.
  5. Clique em Criar Webhook.

Após a criação, o webhook aparecerá na lista geral. Seu cartão exibe todas as informações principais:

  • Status (Ativo / Inativo)
  • Nome do webhook e URL para enviar o webhook
  • Lista de eventos rastreados
  • Hora do último envio bem-sucedido
  • Atividade (número total de eventos enviados)

As seguintes ações estão disponíveis no menu (⋮) no canto do cartão:

  • Testar
  • Editar
  • Ativar / Desativar
  • Excluir

Formato de Notificações

{
  "eventId": "<uuid>",
  "trigger": "<entity>.<event>",
  "occurredAt": "2023-10-27T12:35:01.123Z",
  "payload": {
    "someStringField": "string",
    "numberField": 42
  }
}

Campos da solicitação:

  • eventId — identificador único do evento.
  • trigger — tipo de evento no formato entidade.evento (veja a lista abaixo).
  • occurredAt — hora em que o evento ocorreu no formato ISO 8601 (UTC).
  • payload — objeto com dados específicos para cada evento.

Evento de Teste

Quando você clicar no botão Testar no menu (⋮) no canto do cartão, receberá uma solicitação POST com o seguinte payload:

{
  "eventId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "trigger": "emailmassivo.webhook_test",
  "occurredAt": "2030-12-31T20:59:59.999Z",
  "payload": {
    "timestamp": "2030-12-31T20:59:59.999Z",
    "stringField": "someString",
    "numberField": 42,
    "booleanField": true,
    "objectField": {
      "nestedString": "value",
      "nestedNumber": 123
    },
    "arrayField": ["item1", "item2", 3, false]
  }
}

Disparadores Disponíveis

Entidade: Campanhas de email (mail_distribution)

Payload Comum para eventos de campanha

Para os eventos mail_distribution.started, mail_distribution.completed, mail_distribution.moderating, mail_distribution.approved e mail_distribution.rejected, é usada a seguinte estrutura de payload:

{
  "mailDistributionId": 12345,
  "mailDistributionName": "Nome da campanha",
  "mailDistributionSubject": "Assunto do email"
}
  • mail_distribution.started — a campanha foi iniciada (Iniciada).
  • mail_distribution.completed — a campanha foi concluída com sucesso (Concluída).
  • mail_distribution.moderating — a campanha foi enviada para revisão (Em moderação).
  • mail_distribution.approved — a campanha foi aprovada pelo moderador (Aprovada).
  • mail_distribution.rejected — a campanha foi rejeitada pelo moderador (Rejeitada).
  • mail_distribution.banned — a campanha está bloqueada (Interrompida pelo sistema). Este evento tem um payload estendido.

Payload:

{
  "mailDistributionId": 12345,
  "mailDistributionName": "Nome da campanha",
  "mailDistributionSubject": "Assunto do email",
  "negative": {
    "reason": "hard_bounced",
    "explanation": "Descrição detalhada do motivo do bloqueio"
  }
}

O campo reason conterá uma das seguintes strings:

  • hard_bounced
  • soft_bounced
  • error_spam
  • complain

Requisitos de Processamento

1. Resposta do servidor e tempo limite

Seu servidor deve responder à solicitação dentro de 30 segundos.

  • Status HTTP 200299: significa que o evento foi recebido e processado com sucesso.
  • Qualquer outro status (3xx, 4xx, 5xx) ou tempo limite esgotado: é considerado um erro, após o qual tentaremos entregar o evento novamente.

2. Idempotência

Nosso sistema garante a entrega com o princípio de pelo menos uma vez (at-least-once). Isso significa que, devido a problemas de rede ou erros no lado do seu servidor, o mesmo evento pode ser entregue mais de uma vez. Use o campo eventId para prevenir o processamento duplicado.

3. Tentativas

Em caso de falha na entrega, faremos 6 tentativas de retry com intervalos crescentes.

Tentativa Atraso após a anterior
1 1 minuto
2 2 minutos
3 4 minutos
4 8 minutos
5 15 minutos
6 15 minutos

No total, podemos enviar até 7 solicitações para um evento (envio inicial + 6 tentativas). Após a última tentativa falhada, pararemos de entregar o evento e ele será marcado como não entregue.

4. Requisitos de URL

Seu manipulador de URL deve usar o protocolo HTTPS e ter um certificado SSL válido. Notificações para endereços HTTP não serão entregues.

Exemplo de Manipulador

PHP

<?php
// Obtemos o corpo raw da solicitação
$input = file_get_contents('php://input');
$data = json_decode($input, true);

if (!$data || !isset($data['eventId'])) {
    http_response_code(400); // Bad Request
    exit('Invalid payload');
}

// Verificamos se o evento já foi processado
if (isEventProcessed($data['eventId'])) {
    http_response_code(200);
    exit('OK. Already processed.');
}

// Processamos o evento
switch ($data['trigger']) {
    case 'mail_distribution.completed':
        handleMailDistributionCompleted($data['payload']);
        break;
    case 'mail_distribution.banned':
        handleMailDistributionBanned($data['payload']);
        break;
    // ... outros eventos
}

// Marcamos o evento como processado
markEventAsProcessed($data['eventId']);

http_response_code(200);
echo 'OK';
?>

Node.js (Express)

const express = require('express');
const app = express();
app.use(express.json());

app.post('/webhook', (req, res) => {
  const { eventId, trigger, payload } = req.body;

  // Verificamos a idempotência
  if (isEventProcessed(eventId)) {
    return res.status(200).send('OK. Already processed.');
  }

  // Processamos o evento
  switch (trigger) {
    case 'mail_distribution.completed':
      handleMailDistributionCompleted(payload);
      break;
    case 'mail_distribution.banned':
      handleMailDistributionBanned(payload);
      break;
    // ... outros eventos
  }

  // Marcamos como processado
  markEventAsProcessed(eventId);

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

Recomendações de Desenvolvimento

1. Processe as solicitações de forma assíncrona

Seu servidor deve responder com HTTP 200 OK o mais rápido possível. Não execute operações de longa duração (chamadas para outras APIs, processamento de arquivos) no momento de receber o webhook. Em vez disso, adicione a tarefa a uma fila (por exemplo, RabbitMQ, Redis, SQS) e retorne a resposta imediatamente. Isso o protegerá de tempos limite esgotados e facilitará o dimensionamento do processamento.

2. Esteja preparado para novos campos

Novos campos podem ser adicionados ao objeto payload no futuro. Seu código deve ser resiliente a isso e não falhar se chaves desconhecidas aparecerem no JSON.

3. Configure o monitoramento

Configure o monitoramento para seu manipulador de URL. Os alertas ajudarão você a reagir aos problemas rapidamente e corrigi-los antes que os eventos comecem a ser perdidos após todas as tentativas de retry.

4. Use ambientes de teste

Não use sua URL principal (produção) para depuração. Crie um webhook separado para um ambiente de teste ou staging para fazer alterações no código com segurança.

Perguntas Frequentes (FAQ)

  • Configurei um webhook mas não estou recebendo notificações. O que devo fazer?

    Verifique o seguinte:

    1. Status na UI: Certifique-se de que o webhook está Ativo.
    2. Evento de teste: Use o botão Testar no cartão do webhook.
    3. URL pública: Certifique-se de que a URL esteja acessível da internet (não localhost).
    4. HTTPS: Certifique-se de que a URL comece com https:// e tenha um certificado SSL válido.
    5. Logs do servidor: Verifique os logs do seu servidor web (Nginx, Apache) ou da aplicação para ver entradas de solicitações recebidas.
  • Por que vocês enviam o mesmo evento (eventId) várias vezes?

    Repetimos o envio se não recebermos confirmação de entrega bem-sucedida do seu servidor (resposta com código HTTP 200–299) dentro de 30 segundos. Isso pode acontecer por duas razões principais:

    1. Seu servidor retornou um erro (por exemplo, status 500 Internal Server Error).
    2. Ocorreu um problema de rede, devido ao qual não recebemos a resposta, mesmo que sua aplicação a tenha enviado e processado o evento com sucesso.

    Portanto, seu sistema sempre deve estar preparado para duplicatas (veja a seção Idempotência).

  • A ordem de entrega de eventos está garantida?

    Não, não está. Devido ao comportamento da rede ou à lógica de tentativas, um evento que ocorreu mais tarde pode ser entregue antes. Sempre confie no carimbo de data/hora occurredAt no corpo da solicitação, não na ordem em que são recebidos.

  • O que acontece se meu servidor não estiver disponível por muito tempo?

    Tentaremos entregar o evento por aproximadamente 45 minutos. Se seu servidor permanecer indisponível por mais tempo, o evento será perdido. Planeje a manutenção tendo em mente esta janela.

  • Posso usar a mesma URL para vários webhooks?

    Sim. Esta é uma prática comum. Em seu código, use o campo trigger para roteamento e chamada da lógica de processamento apropriada.