Pular para o conteúdo

Tipos de evento

Assine qualquer combinação destes tipos de evento ao criar um webhook.

Evento Dispara quando Define last_event como
email.scheduled Um e-mail com scheduled_at foi aceito. scheduled
email.sent O e-mail foi entregue ao Amazon SES. sent
email.delivered O servidor de e-mail do destinatário aceitou o e-mail. delivered
email.delivery_delayed A entrega está atrasada ou falhando temporariamente; o SES continua tentando. delivery_delayed
email.bounced Falha permanente (o endereço não existe, o domínio rejeita). bounced
email.complained O destinatário marcou o e-mail como spam. complained
email.opened O destinatário abriu o e-mail (exige o rastreamento de aberturas). opened
email.clicked O destinatário clicou em um link (exige o rastreamento de cliques). clicked
email.failed O SpacePost ou o SES não conseguiu enviar o e-mail (rejeitado, falha de renderização, equipe ou tenant pausado). failed
email.suppressed Um destinatário foi ignorado por estar na sua lista de supressão. suppressed

Os corpos dos webhooks são exatamente os do Resend, então os handlers escritos para o Resend continuam funcionando.

{
"type": "email.delivered",
"created_at": "2026-10-23T14:02:10.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"broadcast_id": "0192e1c4-0000-7000-8000-0000000000bb",
"tenant_id": "0192e1c4-5555-7000-8000-0000000000cc",
"tags": { "category": "welcome" }
}
}
  • type é o evento; created_at é quando o evento aconteceu.
  • data.email_id, from, to (todos os destinatários do e-mail, não apenas o afetado), subject e created_at (quando o e-mail foi criado) estão sempre presentes.
  • data.broadcast_id só está presente em e-mails de campanhas.
  • data.tenant_id só está presente quando o e-mail foi enviado para um tenant (modo plataforma). É o nosso id do tenant, não o seu external_id.
  • data.tags só está presente se o e-mail tinha tags, como um objeto ({ "name": "value" }).
  • Alguns eventos acrescentam mais um objeto a data, com o nome do evento: bounce, click, open, failed ou suppressed. Eles estão listados abaixo. Nada mais é adicionado: não há chaves bounceType, delayType ou reason no nível superior.
{
"type": "email.scheduled",
"created_at": "2026-10-23T14:02:09.700Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Your trial ends tomorrow",
"created_at": "2026-10-23T14:02:09.512Z"
}
}
{
"type": "email.sent",
"created_at": "2026-10-23T14:02:10.100Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z"
}
}
{
"type": "email.delivered",
"created_at": "2026-10-23T14:02:11.350Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z"
}
}

Disparado em atrasos de entrega do SES e em rejeições transitórias. Não há campos extras para um atraso simples. Uma rejeição transitória também traz o objeto bounce (veja abaixo, com type: "Transient").

{
"type": "email.delivery_delayed",
"created_at": "2026-10-23T14:07:00.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z"
}
}

Somente rejeições permanentes chegam a este evento, e elas adicionam o endereço à sua lista de supressão. data.bounce tem type (Permanent), subType e a message do servidor receptor.

{
"type": "email.bounced",
"created_at": "2026-10-23T14:02:12.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["nobody@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"bounce": {
"type": "Permanent",
"subType": "General",
"message": "smtp; 550 5.1.1 The email account that you tried to reach does not exist."
}
}
}

Os subtipos de rejeição incluem General, NoEmail, Suppressed, OnAccountSuppressionList, MailboxFull, MessageTooLarge, ContentRejected e AttachmentRejected.

Sem campos extras. O endereço é adicionado à sua lista de supressão.

{
"type": "email.complained",
"created_at": "2026-10-23T15:30:00.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Our monthly newsletter",
"created_at": "2026-10-23T14:02:09.512Z"
}
}
{
"type": "email.opened",
"created_at": "2026-10-23T14:05:44.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"open": { "ipAddress": "203.0.113.7", "userAgent": "Mozilla/5.0 (Macintosh)" }
}
}
{
"type": "email.clicked",
"created_at": "2026-10-23T14:06:10.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"click": {
"link": "https://acme.com/get-started",
"ipAddress": "203.0.113.7",
"userAgent": "Mozilla/5.0 (Macintosh)",
"timestamp": "2026-10-23T14:06:10.000Z"
}
}
}
{
"type": "email.failed",
"created_at": "2026-10-23T14:02:10.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"failed": { "reason": "Attachment https://files.acme.com/report.pdf returned HTTP 404" }
}
}

suppressed.reason é suppression_list (o destinatário está na sua lista de supressão) ou recipient_domain_does_not_accept_mail (o domínio do destinatário não tem servidor de e-mail).

{
"type": "email.suppressed",
"created_at": "2026-10-23T14:02:10.000Z",
"data": {
"email_id": "0192e1c4-7a3b-7c1e-9d2f-3b5a8e6f4c10",
"from": "Acme <hello@acme.com>",
"to": ["user@example.com", "other@example.com"],
"subject": "Welcome to Acme",
"created_at": "2026-10-23T14:02:09.512Z",
"suppressed": { "recipients": ["other@example.com"], "reason": "suppression_list" }
}
}

O SDK para Node exporta a união WebhookPayload, discriminada por type, e spacepost.webhooks.verify() a retorna; assim, ao restringir por event.type você obtém os campos corretos de data:

import type { WebhookPayload } from "@spacepost/node";
function handle(event: WebhookPayload) {
switch (event.type) {
case "email.bounced":
return event.data.bounce?.subType;
case "email.clicked":
return event.data.click?.link;
case "email.suppressed":
return event.data.suppressed.recipients;
case "email.failed":
return event.data.failed.reason;
default:
return event.data.tenant_id; // present when the email was sent for a tenant
}
}