Zum Inhalt springen

Event-Typen

Abonniere beim Erstellen eines Webhooks eine beliebige Kombination dieser Event-Typen.

Event Wird ausgelöst, wenn Setzt last_event auf
email.scheduled Eine E-Mail mit scheduled_at angenommen wurde. scheduled
email.sent die E-Mail an Amazon SES übergeben wurde. sent
email.delivered der Mailserver des Empfängers die E-Mail angenommen hat. delivered
email.delivery_delayed die Zustellung verzögert ist oder vorübergehend fehlschlägt; SES versucht es weiter. delivery_delayed
email.bounced ein dauerhafter Fehler vorliegt (die Adresse existiert nicht, die Domain lehnt ab). bounced
email.complained der Empfänger die E-Mail als Spam markiert hat. complained
email.opened der Empfänger die E-Mail geöffnet hat (erfordert Öffnungs-Tracking). opened
email.clicked der Empfänger auf einen Link geklickt hat (erfordert Klick-Tracking). clicked
email.failed SpacePost oder SES die E-Mail nicht senden konnte (abgelehnt, Rendering-Fehler, pausiertes Team oder pausierter Tenant). failed
email.suppressed ein Empfänger übersprungen wurde, weil er auf deiner Sperrliste steht. suppressed

Webhook-Bodys entsprechen exakt denen von Resend, sodass für Resend geschriebene Handler weiter funktionieren.

{
"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 ist das Event; created_at ist der Zeitpunkt, zu dem das Event eingetreten ist.
  • data.email_id, from, to (alle Empfänger der E-Mail, nicht nur der betroffene), subject und created_at (wann die E-Mail erstellt wurde) sind immer vorhanden.
  • data.broadcast_id ist nur bei E-Mails aus Kampagnen vorhanden.
  • data.tenant_id ist nur vorhanden, wenn die E-Mail für einen Tenant gesendet wurde (Plattform-Modus). Es ist unsere Tenant-ID, nicht deine external_id.
  • data.tags ist nur vorhanden, wenn die E-Mail Tags hatte, und zwar als Objekt ({ "name": "value" }).
  • Manche Events fügen data ein weiteres Objekt hinzu, das nach dem Event benannt ist: bounce, click, open, failed oder suppressed. Sie sind unten aufgeführt. Mehr wird nicht hinzugefügt: Es gibt keine Schlüssel bounceType, delayType oder reason auf oberster Ebene.
{
"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"
}
}

Wird bei Zustellverzögerungen von SES und bei vorübergehenden Bounces ausgelöst. Für eine reine Verzögerung gibt es keine zusätzlichen Felder. Ein vorübergehender Bounce enthält zusätzlich das Objekt bounce (siehe unten, mit 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"
}
}

Dieses Event erreichen nur dauerhafte Bounces, und sie tragen die Adresse in deine Sperrliste ein. data.bounce enthält type (Permanent), subType und die message des empfangenden Servers.

{
"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."
}
}
}

Zu den Bounce-Untertypen gehören General, NoEmail, Suppressed, OnAccountSuppressionList, MailboxFull, MessageTooLarge, ContentRejected und AttachmentRejected.

Keine zusätzlichen Felder. Die Adresse wird deiner Sperrliste hinzugefügt.

{
"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 ist suppression_list (der Empfänger steht auf deiner Sperrliste) oder recipient_domain_does_not_accept_mail (die Domain des Empfängers hat keinen Mailserver).

{
"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" }
}
}

Das Node SDK exportiert eine Union WebhookPayload, die über type unterschieden wird, und spacepost.webhooks.verify() gibt sie zurück. Wenn du auf event.type einengst, bekommst du die passenden data-Felder:

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
}
}