Pular para o conteúdo principal

Documentação de Webhook

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

Entidade: E-mails transacionais (email)​

Eventos sobre os e-mails transacionais enviados pela API e por SMTP. No formulário do webhook, é o grupo «E-mails transacionais — detalhados». Diferentemente dos eventos anteriores de e-mails transacionais, eles incluem o ID do e-mail, o tipo de destinatário e o motivo da devolução — recomendamos este grupo para novas integrações.

Cada evento se refere a um destinatário do e-mail: se o e-mail foi para três endereços, você recebe eventos de cada um.

Payload comum​
{
"emailId": "0199b2f4-6c1e-7a3d-9f2b-4e8c1d7a5b60",
"recipient": {
"id": "c4a07e13-5d2b-4e8f-b916-7f3a2d0c8b44",
"email": "user@example.com",
"type": "to"
}
}
  • emailId — o ID do e-mail que a API retornou no envio (data.id).
  • recipient.id — o ID do destinatário dentro do e-mail.
  • recipient.email — o endereço do destinatário.
  • recipient.type — to é o destinatário principal, cc uma cópia, bcc uma cópia oculta.
Eventos​
  • email.delivered — «Entregue»: o servidor de e-mail do destinatário aceitou o e-mail.
  • email.opened — «Abertura»: o destinatário abriu o e-mail pela primeira vez.
  • email.clicked — «Clique»: o destinatário clicou pela primeira vez em um link do e-mail. O payload também traz url, o link que ele seguiu.
  • email.unsubscribed — «Cancelamento de inscrição»: o destinatário cancelou a inscrição nos e-mails desta chave de envio.
  • email.complained — «Reclamação»: o destinatário marcou o e-mail como spam.
  • email.bounced — «Devolvido»: o e-mail não foi entregue — o endereço não existe, a caixa está cheia, o servidor do destinatário recusou ou o endereço já é conhecido como não entregável.
  • email.failed — «Falha no envio»: não foi possível enviar o e-mail. O endereço do destinatário está correto — você pode enviar o e-mail novamente.
  • email.suppressed — «Não enviado»: o destinatário havia cancelado a inscrição nos e-mails desta chave de envio ou reclamado deles, por isso o e-mail não foi enviado.
Motivo da devolução​

Os eventos email.bounced, email.failed e email.suppressed têm um objeto reason no payload:

{
"emailId": "0199b2f4-6c1e-7a3d-9f2b-4e8c1d7a5b60",
"recipient": {
"id": "c4a07e13-5d2b-4e8f-b916-7f3a2d0c8b44",
"email": "user@example.com",
"type": "to"
},
"reason": {
"code": "DELIVERY_MAILBOX_FULL",
"bounceType": "transient",
"message": "Recipient mailbox is full.",
"smtpResponse": "552 5.2.2 Mailbox full"
}
}
  • code — o código do motivo, por exemplo DELIVERY_MAILBOX_FULL (caixa cheia) ou RECIPIENT_UNSUBSCRIBED (o destinatário cancelou a inscrição).
  • bounceType — somente em email.bounced: permanent — o endereço não existe, remova-o da sua lista; transient — recusa temporária, o endereço ainda pode ser usado; undetermined — o motivo não está claro.
  • message — descrição do motivo (em inglês).
  • smtpResponse — a resposta do servidor de e-mail do destinatário, se houver.

Importante! Remova um endereço da sua lista somente após email.bounced com bounceType: permanent.

Às vezes um servidor de e-mail informa a não entrega depois de ter aceitado o e-mail. Então, para o mesmo destinatário, email.bounced chega depois de email.delivered.

Todos os códigos de motivo estão na documentação para desenvolvedores (em inglês).

Requisitos de Processamento​

1. Resposta do servidor e tempo limite​

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

  • Status HTTP 200–299: 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.

TentativaAtraso após a anterior
11 minuto
22 minutos
34 minutos
48 minutos
515 minutos
615 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.