Referência
Requisições de webhook
O formato exato das requisições de webhook do Emailit, do método e dos cabeçalhos ao array JSON de eventos, além de ordenação, duplicatas e como responder.
Esta página descreve o que chega ao seu endpoint: a requisição HTTP, os cabeçalhos, o corpo JSON e o que o Emailit espera como resposta. Use-a ao escrever ou depurar um handler de webhook.
Requisição
| Propriedade | Valor |
|---|---|
| Método | POST |
| Corpo | Um array JSON de 1 a 100 eventos, compacto (sem espaços extras) |
| Timeout | 30 segundos |
| Redirecionamentos | Não são seguidos. Uma resposta 3xx conta como falha. |
| Concorrência | Uma requisição por vez por webhook. Os lotes são enviados um após o outro. |
| Latência | O Emailit verifica se há eventos novos a cada poucos segundos, então a maioria dos eventos chega em segundos. |
POST /webhooks/emailit HTTP/1.1
Host: acme.com
Content-Type: application/json
User-Agent: Emailit-Webhooks/2.0
X-Emailit-Signature: 7242b580f77ec488f8ac21b57f092e3db61a431694f13074c7b4c85a4dd04be3
X-Emailit-Timestamp: 1790846045Cabeçalhos
| Cabeçalho | Descrição |
|---|---|
Content-Type |
Sempre application/json. |
User-Agent |
Sempre Emailit-Webhooks/2.0. |
X-Emailit-Signature |
HMAC-SHA256 em hexadecimal de {timestamp}.{raw body}, calculado com o segredo completo do webhook (incluindo whsec_). Consulte Assinatura da requisição. |
X-Emailit-Timestamp |
Timestamp Unix, em segundos, do momento em que o Emailit assinou a requisição. Cada tentativa, incluindo as novas tentativas, é assinada de novo com um timestamp novo. |
Corpo
O corpo é sempre um array JSON, mesmo quando contém um único evento. Cada elemento tem este formato:
| Campo | Tipo | Descrição |
|---|---|---|
event_id |
string | O ID do evento, que começa com evt_. O mesmo evento mantém o mesmo ID em todas as novas tentativas. Eventos de teste começam com evt_test_. |
type |
string | O tipo de evento, como email.delivered. |
object |
objeto | O recurso a que o evento se refere. |
data |
objeto | Contém object de novo. data.object e o object de nível superior são idênticos; leia o que preferir. |
Os campos dentro de object dependem do tipo. Eventos de status de e-mail descrevem o e-mail; email.clicked e email.loaded descrevem o clique ou o carregamento e trazem o e-mail aninhado; eventos de recursos, como contact.created, descrevem o recurso.
Exemplo de lote
Esta requisição traz dois eventos: uma entrega e, depois, um clique em um link do mesmo e-mail.
[
{
"event_id": "evt_2xGk7Nq1VbD5sR8tLmW3eYhC6aP",
"type": "email.delivered",
"object": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"object": "email",
"from": "billing@acme.com",
"to": "ada@example.com",
"subject": "Your receipt #1042",
"status": "delivered",
"meta": { "order_id": "1042" },
"updated_at": "2026-10-01T09:14:03.512000+00:00",
"created_at": "2026-10-01T09:14:01.207000+00:00"
},
"data": {
"object": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"object": "email",
"from": "billing@acme.com",
"to": "ada@example.com",
"subject": "Your receipt #1042",
"status": "delivered",
"meta": { "order_id": "1042" },
"updated_at": "2026-10-01T09:14:03.512000+00:00",
"created_at": "2026-10-01T09:14:01.207000+00:00"
}
}
},
{
"event_id": "evt_2xGkC8w2TnF4yR7bMqL1sVdJ6pE",
"type": "email.clicked",
"object": {
"id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
"object": "click",
"email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"email": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"rcpt_to": "ada@example.com",
"mail_from": "billing@acme.com",
"subject": "Your receipt #1042",
"created_at": "2026-10-01T09:14:01.207000+00:00",
"campaign": null,
"meta": { "order_id": "1042" }
},
"link": {
"id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
"url": "https://acme.com/orders/1042"
},
"contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
"ip_address": "203.0.113.24",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
"created_at": "2026-10-01T09:20:47.884000+00:00"
},
"data": {
"object": {
"id": "click_2xGkC7v5PmD3xQ9aLnK2rUcH8oW",
"object": "click",
"email_id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"email": {
"id": "em_2xGk7Lr3XcB8pQ1wYzK4dTfG9hJ",
"rcpt_to": "ada@example.com",
"mail_from": "billing@acme.com",
"subject": "Your receipt #1042",
"created_at": "2026-10-01T09:14:01.207000+00:00",
"campaign": null,
"meta": { "order_id": "1042" }
},
"link": {
"id": "link_2xGk7Mb6QwE1zS8cNpT4vXfK3gB",
"url": "https://acme.com/orders/1042"
},
"contact": { "id": "con_2xFh3Ka9LmP2nR6tVwB8cDqY5sZ", "email": "ada@example.com" },
"ip_address": "203.0.113.24",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X)",
"created_at": "2026-10-01T09:20:47.884000+00:00"
}
}
}
]Os timestamps estão em ISO 8601, em UTC. meta são os metadados que você anexou ao enviar, ou null. contact é null quando o destinatário não é um dos seus contatos, e campaign é null para e-mails enviados pela API e por SMTP.
Ordenação
Dentro de uma requisição, os eventos vêm do mais antigo para o mais recente. Entre requisições, não há garantia de ordem: um lote que falhou é reenviado mais tarde, quando eventos mais novos podem já ter chegado, e uma nova tentativa manual pode reenviar eventos antigos depois dos novos.
Não conte com a ordem de chegada. Para eventos de e-mail, use o status e o updated_at do e-mail e mantenha o estado mais avançado que você já viu (por exemplo, não sobrescreva clicked com um delivered atrasado). Na dúvida, busque o estado atual com Obter um e-mail.
Duplicatas
O mesmo evento pode chegar mais de uma vez:
- Quando o seu endpoint dá timeout ou retorna um erro depois de processar parte de um lote, o lote inteiro é reenviado, incluindo os eventos que você já tratou.
- Quando você usa Retry failed ou tenta de novo uma única requisição.
Guarde cada event_id processado, com uma restrição de unicidade, e ignore os eventos que você já viu.
Como responder
| A sua resposta | Resultado |
|---|---|
Qualquer 2xx em até 30 segundos |
Todos os eventos do lote são marcados como Delivered. O corpo da resposta é ignorado. |
3xx, 4xx ou 5xx |
O lote falhou e é reenviado. |
| Nenhuma resposta em 30 segundos, erro de conexão, erro de TLS | O lote falhou e é reenviado. O painel mostra o código de status 0. |
Nas requisições com falha, o Emailit guarda os primeiros 2.000 caracteres do corpo da sua resposta. Você pode lê-los na aba Requests do webhook, por isso vale a pena retornar uma mensagem de erro curta para facilitar a depuração.
Recomendações:
- Verifique e confirme o recebimento rápido. Confira a assinatura, coloque os eventos em uma fila ou em uma tabela e retorne
200. Faça o trabalho demorado, como chamar outras APIs, em um job em segundo plano. - Retorne
2xxpara os eventos que você ignora. Caso contrário, eles são reenviados por dias. - Retorne
401ou403para uma assinatura inválida. A requisição é reenviada como qualquer falha, o que dá tempo para corrigir um segredo mal configurado.
Consulte Novas tentativas e falhas para ver o cronograma de novas tentativas e o que acontece quando um endpoint continua falhando.