Aller au contenu

Types d'événements

Abonnez-vous à n’importe quelle combinaison de ces types d’événements lorsque vous créez un webhook.

Événement Se déclenche quand Définit last_event à
email.scheduled Un e-mail avec scheduled_at a été accepté. scheduled
email.sent L’e-mail a été transmis à Amazon SES. sent
email.delivered Le serveur de messagerie du destinataire a accepté l’e-mail. delivered
email.delivery_delayed La distribution est retardée ou échoue temporairement ; SES continue de réessayer. delivery_delayed
email.bounced Échec permanent (l’adresse n’existe pas, le domaine rejette). bounced
email.complained Le destinataire a signalé l’e-mail comme spam. complained
email.opened Le destinataire a ouvert l’e-mail (nécessite le suivi des ouvertures). opened
email.clicked Le destinataire a cliqué sur un lien (nécessite le suivi des clics). clicked
email.failed SpacePost ou SES n’a pas pu envoyer l’e-mail (rejeté, échec de rendu, équipe ou tenant en pause). failed
email.suppressed Un destinataire a été ignoré car il figure dans votre liste de suppression. suppressed

Les corps des webhooks sont exactement ceux de Resend : les gestionnaires écrits pour Resend continuent donc de fonctionner.

{
"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 est l’événement ; created_at est le moment où l’événement s’est produit.
  • data.email_id, from, to (tous les destinataires de l’e-mail, pas seulement celui concerné), subject et created_at (date de création de l’e-mail) sont toujours présents.
  • data.broadcast_id n’est présent que pour les e-mails de campagne.
  • data.tenant_id n’est présent que si l’e-mail a été envoyé pour un tenant (mode plateforme). C’est notre id de tenant, pas votre external_id.
  • data.tags n’est présent que si l’e-mail avait des tags, sous forme d’objet ({ "name": "value" }).
  • Certains événements ajoutent un objet supplémentaire à data, nommé d’après l’événement : bounce, click, open, failed ou suppressed. Ils sont listés ci-dessous. Rien d’autre n’est ajouté : il n’y a pas de clés bounceType, delayType ou reason au niveau supérieur.
{
"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"
}
}

Déclenché pour les retards de distribution SES et pour les rebonds temporaires. Un simple retard n’ajoute aucun champ. Un rebond temporaire porte aussi l’objet bounce (voir plus bas, avec 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"
}
}

Seuls les rebonds définitifs atteignent cet événement, et ils ajoutent l’adresse à votre liste de suppression. data.bounce contient type (Permanent), subType et le message du serveur destinataire.

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

Les sous-types de rebond incluent General, NoEmail, Suppressed, OnAccountSuppressionList, MailboxFull, MessageTooLarge, ContentRejected et AttachmentRejected.

Aucun champ supplémentaire. L’adresse est ajoutée à votre liste de suppression.

{
"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 vaut suppression_list (le destinataire figure dans votre liste de suppression) ou recipient_domain_does_not_accept_mail (le domaine du destinataire n’a pas de serveur de messagerie).

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

Le SDK Node exporte une union WebhookPayload discriminée par type, et spacepost.webhooks.verify() la renvoie : en affinant sur event.type, vous obtenez les bons champs 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
}
}