Webhooks
Registre endpoints que recebem notificações de eventos assinadas.
Criar um webhook
Cria um endpoint de webhook no seu workspace. O Emailit envia os eventos correspondentes para a URL em lotes de até 100, como um array JSON assinado com o secret do webhook. Consulte Requisições de webhook para ver o formato da requisição. Requer uma chave de API com escopo full.
/webhooksCorpo da requisição
namestringObrigatórioNome do webhook. Deve ser único no workspace; você pode usá-lo no lugar do ID nos outros endpoints de webhooks.
urlstringObrigatórioEndpoint que recebe os eventos. URLs http e https são aceitas; use https em produção.
O Emailit resolve o nome de host quando você salva e rejeita localhost e endereços IP privados, link-local e outros reservados. Os redirecionamentos não são seguidos na entrega, então use a URL final.
all_eventsbooleanEnvia todos os tipos de evento, incluindo os tipos adicionados no futuro. Padrão: false. Quando true, events é ignorado.
enabledbooleanSe o Emailit entrega eventos ao webhook. Padrão: true.
eventsstring[]Tipos de evento a enviar, por exemplo ["email.delivered", "email.bounced"]. Consulte Tipos de evento. Padrão: [], o que, com all_events: false, significa que o webhook não recebe nada.
Os nomes de eventos não são validados. Um tipo digitado errado é salvo, mas nunca corresponde a um evento.
filterobject | nullFiltro de payload. O Emailit só envia os eventos cujo objeto corresponde às regras. Disponível nos planos Pro, Business e Custom; um filtro com regras no Pay as you go retorna 403.
filter.matchstringall (padrão) envia um evento quando todas as regras correspondem. any o envia quando pelo menos uma regra corresponde.
filter.rulesobject[]Até 25 regras.
filter.rules[].fieldstringObrigatórioCaminho com pontos dentro do objeto do evento, por exemplo to, status, meta.plan ou, para eventos de clique e de abertura, email.campaign.id. Um payload. no início é ignorado.
filter.rules[].operatorstringObrigatórioequals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than, is_set, is_not_set, in ou not_in. Os operadores de texto comparam os valores como strings; greater_than e less_than comparam números.
filter.rules[].valueanyValor a comparar. Obrigatório para todos os operadores, exceto is_set e is_not_set. Use um array com in e not_in.
Retorno
Retorna 201 Created com o objeto de webhook, incluindo o secret de assinatura (whsec_ seguido de 64 caracteres hexadecimais). Use o segredo para verificar as assinaturas das requisições. Você pode lê-lo de novo com Obter um webhook e fazer a rotação dele com Fazer a rotação do segredo de assinatura.
| Status | Quando |
|---|---|
400 |
name ou url está ausente, a URL é inválida, não pode ser resolvida ou aponta para um endereço bloqueado, ou o filtro é inválido. |
403 |
O filtro tem regras e o seu plano não inclui filtros de webhook. O corpo é {"error": "plan_required", "required_plan": "pro"}. |
409 |
Já existe um webhook com este nome. O corpo inclui o id e o name do webhook existente em existing. |
422 |
O workspace atingiu o limite de webhooks do plano. O corpo inclui usage.used e usage.limit. Consulte Limites. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": null,
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "URL resolves to a private/reserved IP address"
}{
"error": "plan_required",
"required_plan": "pro"
}{
"error": "Webhook with this name already exists",
"existing": {
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events"
}
}{
"error": "Pay as you go includes 3 webhook endpoints.",
"usage": {
"used": 3,
"limit": 3
}
}{
"name": "Enterprise bounces",
"url": "https://api.acme.com/webhooks/emailit",
"events": ["email.bounced", "email.complained"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" },
{ "field": "to", "operator": "not_contains", "value": "@acme.com" }
]
}
}Obter um webhook
Retorna um webhook, buscado pelo ID ou pelo nome. Este é o único endpoint de leitura que retorna o secret de assinatura. Requer uma chave de API com escopo full.
/webhooks/:idParâmetros de caminho
idstringObrigatórioID do webhook (wh_…) ou o nome do webhook, codificado para URL.
Retorno
Retorna 200 OK com o objeto de webhook, incluindo secret e filters_allowed (se o seu plano permite que o webhook use um filtro de payload). last_used_at é o momento da última entrega bem-sucedida, ou null se nada foi entregue ainda.
Retorna 404 com error: "Webhook not found" se nenhum webhook corresponder.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": {
"match": "all",
"rules": [
{ "field": "meta.plan", "operator": "equals", "value": "enterprise" }
]
},
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T09:41:05.302000+00:00",
"secret": "whsec_175a02aca3fb7dc3107ca21e4224a73d058cacc628356a39e020422aec3ca434"
}{
"error": "Webhook not found"
}Atualizar um webhook
Atualiza um webhook. Envie apenas os campos que você quer alterar; pelo menos um é obrigatório. O segredo de assinatura não muda; faça a rotação dele com Fazer a rotação do segredo de assinatura. Requer uma chave de API com escopo full.
/webhooks/:idParâmetros de caminho
idstringObrigatórioID do webhook (wh_…) ou o nome do webhook, codificado para URL.
Corpo da requisição
namestringNovo nome. Deve ser único no workspace.
urlstringNova URL do endpoint, http ou https. Validada da mesma forma que na criação.
all_eventsbooleantrue envia todos os tipos de evento e limpa a lista events. Se você defini-lo como false, envie também events, ou o webhook não recebe nada.
enabledbooleanfalse interrompe as entregas e true as retoma. Os eventos que acontecem enquanto o webhook está desativado não entram na fila dele e não são enviados depois.
eventsstring[]Substitui a lista de tipos de evento. Ignorado enquanto all_events for true. Os nomes não são validados.
filterobject | nullSubstitui o filtro de payload, no mesmo formato da criação. Envie null para removê-lo. Um filtro com regras exige um plano Pro, Business ou Custom.
Retorno
Retorna 200 OK com o webhook atualizado. O secret não é incluído; use Obter um webhook para lê-lo.
| Status | Quando |
|---|---|
400 |
O corpo não tem nenhum dos campos acima, ou a URL ou o filtro é inválido. |
403 |
O filtro tem regras e o seu plano não inclui filtros de webhook (plan_required). |
404 |
Nenhum webhook corresponde a id. |
409 |
Outro webhook já usa o novo nome. |
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": false,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:15:40.000000+00:00"
}{
"error": "No valid fields provided for update. Provide at least one of: name, url, all_events, enabled, events, filter"
}{
"error": "Webhook not found"
}{
"error": "Another webhook with this name already exists"
}Listar webhooks
Retorna os webhooks do seu workspace, dos mais recentes para os mais antigos, e quantos o seu plano permite. Os segredos de assinatura não são incluídos na listagem. Requer uma chave de API com escopo full.
/webhooksParâmetros de consulta
pageintegerNúmero da página, a partir de 1. Padrão: 1.
limitintegerWebhooks por página, de 1 a 100. Padrão: 10.
searchstringBusca sem diferenciar maiúsculas de minúsculas no nome ou na URL do webhook.
matchstringall (padrão) exige todos os filtros. or corresponde a qualquer filtro. Consulte Filtragem.
orderstringChave de ordenação desta lista. Consulte as chaves de ordenação abaixo.
directionstringasc ou desc.
Filtros e ordenação
Os filtros de listagem são um único nível de parâmetros de consulta key.condition=value. Consulte Filtragem para match, order, direction e a lista de condições por tipo.
Chaves de filtro
| Chave | Tipo | Condições | Observações |
|---|---|---|---|
name | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
url | string | exact, not_exact, contains, not_contains, starts_with, ends_with, empty, not_empty | |
enabled | boolean | exact, not_exact | |
created_at | date | exact, before, after, empty, not_empty |
Chaves de ordenação
Passe em order uma destas chaves e em direction o valor asc ou desc: name, url, enabled, created_at
Retorno
Retorna 200 OK com os webhooks em data, next_page_url e previous_page_url (null em cada extremidade) e um objeto usage: used é o número de webhooks no workspace, limit é o máximo do seu plano e filters_allowed indica se o seu plano inclui filtros de payload.
{
"data": [
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"url": "https://api.acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced"],
"filter": null,
"filters_allowed": true,
"last_used_at": "2026-10-01T10:02:17.845000+00:00",
"created_at": "2026-10-01T09:41:05.302000+00:00",
"updated_at": "2026-10-01T10:02:17.845000+00:00"
}
],
"next_page_url": null,
"previous_page_url": null,
"usage": {
"used": 1,
"limit": 10,
"filters_allowed": true
}
}{
"statusCode": 401,
"error": "Unauthorized",
"message": "Invalid API key"
}Excluir um webhook
Exclui permanentemente um webhook e as inscrições dele em eventos. Para interromper as entregas temporariamente, atualize o webhook com enabled: false. Requer uma chave de API com escopo full.
/webhooks/:idParâmetros de caminho
idstringObrigatórioID do webhook (wh_…) ou o nome do webhook, codificado para URL.
Retorno
Retorna 200 OK com o id e o name do webhook excluído e deleted: true. Retorna 404 com error: "Webhook not found" se nenhum webhook corresponder.
{
"object": "webhook",
"id": "wh_2xLb3Kp9Qr1Vt7Mn5Ws0Yd4Hc8e",
"name": "Production events",
"deleted": true
}{
"error": "Webhook not found"
}Enviar um evento de teste
Envia um evento de exemplo do tipo que você escolher para a URL do webhook e retorna a resposta do seu endpoint. Use-o para confirmar que o seu endpoint está acessível e verifica as assinaturas corretamente. Requer uma chave de API com escopo full.
A requisição tem o mesmo formato, os mesmos cabeçalhos e a mesma assinatura de uma entrega real: um array JSON com um evento cujo event_id começa com evt_test_, assinado com o segredo atual do webhook. Ela é enviada mesmo que o webhook esteja desativado ou não esteja inscrito nesse tipo, não é armazenada como requisição de webhook e não tem novas tentativas. Os dados de exemplo são fixos e não se referem a objetos reais.
Você pode enviar 5 eventos de teste por minuto a partir do mesmo endereço IP; acima disso, a resposta é 429.
/webhooks/{id}/testParâmetros de caminho
idstringobrigatóriowh_…) ou o nome do webhook.Parâmetros do corpo
typestringobrigatório| Recurso | Tipos de evento |
|---|---|
email.accepted, email.scheduled, email.delivered, email.bounced, email.attempted, email.failed, email.rejected, email.clicked, email.loaded, email.complained, email.received, email.suppressed, email.canceled, email.unsubscribed, email.resubscribed |
|
| Domínio | domain.created, domain.updated, domain.deleted |
| Lista de contatos | audience.created, audience.updated, audience.deleted |
| Inscrito | subscriber.created, subscriber.updated, subscriber.deleted |
| Contato | contact.created, contact.updated, contact.deleted |
| Template | template.created, template.updated, template.deleted |
| Supressão | suppression.created, suppression.updated, suppression.deleted |
| Verificação de e-mails | email_verification.created, email_verification.updated, email_verification_list.created, email_verification_list.updated |
| Campanha | campaign.created, campaign.updated, campaign.deleted, campaign.scheduled, campaign.queued, campaign.sending, campaign.testing, campaign.sent, campaign.canceled, campaign.archived |
Consulte Tipos de evento para saber o que cada evento significa.
Retorno
okbooleantrue se o seu endpoint respondeu com um status 2xx.status_codeinteger0 se o Emailit não conseguiu se conectar, se a requisição atingiu o timeout de 30 segundos, se o endpoint redirecionou (os redirecionamentos não são seguidos) ou se a URL aponta para um endereço bloqueado.bodystringtypestringpayloadobject[]Retorna 400 se type estiver ausente ou for desconhecido, 404 se o webhook não existir e 429 quando você exceder o limite de testes.
{
"ok": true,
"status_code": 200,
"type": "email.delivered",
"payload": [
{
"event_id": "evt_test_3G0pUekgBm1uIxi9m4C380H70Zq",
"type": "email.delivered",
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
},
"data": {
"object": {
"id": "eml_test_001",
"email_id": 12345,
"message_id": "<test-token@mydomain.com>",
"from": "sender@mydomain.com",
"to": "recipient@example.com",
"subject": "Test email",
"status": "delivered",
"delivered_at": "2026-01-15T10:30:00.000Z"
}
}
}
],
"body": "{\"received\":true}"
}{
"error": "Unknown event type"
}{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit exceeded, retry in 1 minute"
}Fazer a rotação do segredo de assinatura
Gera um novo segredo de assinatura para o webhook e o retorna. Requer uma chave de API com escopo full.
O segredo antigo deixa de ser usado imediatamente: todas as requisições enviadas após a rotação, incluindo as novas tentativas de eventos anteriores, são assinadas com o novo segredo. Não há período de transição, então atualize o segredo no seu endpoint logo após a rotação ou aceite os dois segredos por um curto período enquanto faz a troca. Consulte Verificar as assinaturas das requisições.
/webhooks/{id}/reset-secretParâmetros de caminho
idstringobrigatóriowh_…) ou o nome do webhook.Retorno
Retorna o objeto de webhook com o novo secret (whsec_ seguido de 64 caracteres hexadecimais). Retorna 404 se o webhook não existir.
{
"object": "webhook",
"id": "wh_3NGa6raJ3s1VJ75lO9km6Dqg4cQ",
"name": "Order notifications",
"url": "https://acme.com/webhooks/emailit",
"all_events": false,
"enabled": true,
"events": ["email.delivered", "email.bounced", "email.complained"],
"filter": null,
"last_used_at": "2026-10-01T12:58:40.000000+00:00",
"created_at": "2026-08-14T09:12:03.000000+00:00",
"updated_at": "2026-10-01T13:20:11.000000+00:00",
"secret": "whsec_a0a593d006bdec5aff2f21e88afd543ba239431acbe7d8dc7a9723d76e0a64e2"
}{
"error": "Webhook not found"
}Tentar de novo as requisições com falha
Coloca de novo na fila de entrega todas as requisições deste webhook que falharam permanentemente nos últimos 7 dias. Requer uma chave de API com escopo full.
Uma requisição falha permanentemente após a última nova tentativa automática (11 tentativas ao longo de vários dias; consulte Novas tentativas e falhas). As requisições reenviadas recomeçam com um ciclo completo de novas tentativas e são entregues em segundos. Se pelo menos uma requisição for colocada na fila e o webhook estiver desativado, por exemplo após 3 dias de falhas contínuas, ele é reativado.
Corrija o seu endpoint primeiro, ou as requisições vão falhar de novo. Para tentar de novo uma única requisição, use Tentar de novo uma requisição.
/webhooks/{id}/retry-failedParâmetros de caminho
idstringobrigatóriowh_…) ou o nome do webhook.Retorno
retriedinteger0 se não havia nada para tentar de novo; nesse caso, o estado de ativação do webhook não muda.Retorna 404 se o webhook não existir.
{
"retried": 37
}{
"error": "Webhook not found"
}Tentar de novo uma requisição
Coloca de novo na fila de entrega uma requisição de webhook com falha permanente, com um novo ciclo de novas tentativas. Se o webhook estiver desativado, ele é reativado. Requer uma chave de API com escopo full.
Só é possível tentar de novo dessa forma as requisições que esgotaram as novas tentativas automáticas; requisições ainda pendentes ou em nova tentativa retornam 400. Encontre os IDs das requisições (whr_…) na aba Requests do webhook, em Email APIWebhooks. Para tentar de novo tudo dos últimos 7 dias de uma vez, use Tentar de novo as requisições com falha.
/webhooks/{id}/requests/{request_id}/retryParâmetros de caminho
idstringobrigatóriowh_…) ou o nome do webhook.request_idstringobrigatóriowhr_…).Retorno
retriedinteger1.idstring| Status | Quando |
|---|---|
400 |
A requisição não falhou permanentemente ou não tem um evento para reenviar. |
404 |
O webhook não existe ou a requisição não pertence a ele. |
{
"retried": 1,
"id": "whr_3tWVVMd2eHv3R9B5DWTLtwwU05X"
}{
"error": "Only permanently failed requests can be retried"
}{
"error": "Webhook request not found"
}