イベントタイプ
Webhook を作成するときに、これらのイベントタイプを任意に組み合わせて購読できます。
| イベント | 発火するタイミング | last_event に設定される値 |
|---|---|---|
email.scheduled |
scheduled_at 付きのメールが受理されたとき。 |
scheduled |
email.sent |
メールが Amazon SES に渡されたとき。 | sent |
email.delivered |
受信者のメールサーバーがメールを受け付けたとき。 | delivered |
email.delivery_delayed |
配信が遅延している、または一時的に失敗しているとき。SES は再試行を続けます。 | delivery_delayed |
email.bounced |
恒久的な失敗(アドレスが存在しない、ドメインが拒否した)。 | bounced |
email.complained |
受信者がメールを迷惑メールとして報告したとき。 | complained |
email.opened |
受信者がメールを開いたとき(開封の追跡が必要です)。 | opened |
email.clicked |
受信者がリンクをクリックしたとき(クリックの追跡が必要です)。 | clicked |
email.failed |
SpacePost または SES がメールを送信できなかったとき(拒否、レンダリングの失敗、チームまたはテナントの一時停止)。 | failed |
email.suppressed |
宛先が送信停止リストに載っているためスキップされたとき。 | suppressed |
ペイロードの形式
Section titled “ペイロードの形式”Webhook のボディは Resend のものとまったく同じなので、Resend 向けに書いたハンドラーもそのまま動作します。
{ "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はイベントで、created_atはイベントが発生した日時です。data.email_id、from、to(影響を受けた宛先だけでなく、メールのすべての宛先)、subject、created_at(メールが作成された日時)は常に含まれます。data.broadcast_idは、一斉配信のメールにのみ含まれます。data.tenant_idは、メールがテナントのために送信された場合(プラットフォームモード)にのみ含まれます。これは SpacePost のテナント id であり、ご自身のexternal_idではありません。data.tagsは、メールにタグがあった場合にのみ含まれ、オブジェクト({ "name": "value" })になります。- 一部のイベントでは、イベント名に対応するオブジェクトが
dataに 1 つ追加されます:bounce、click、open、failed、suppressed。以下に一覧を示します。それ以外は追加されません。トップレベルにbounceType、delayType、reasonというキーはありません。
email.scheduled
Section titled “email.scheduled”{ "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" }}email.sent
Section titled “email.sent”{ "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" }}email.delivered
Section titled “email.delivered”{ "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" }}email.delivery_delayed
Section titled “email.delivery_delayed”SES の配信遅延と、一時的なバウンスのときに発火します。単なる遅延には追加のフィールドはありません。一時的なバウンスには、bounce オブジェクトも付きます(下記を参照。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" }}email.bounced
Section titled “email.bounced”このイベントになるのは恒久的なバウンスだけで、アドレスが送信停止リストに追加されます。data.bounce には、type(Permanent)、subType、受信側サーバーの message が含まれます。
{ "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." } }}バウンスの subType には、General、NoEmail、Suppressed、OnAccountSuppressionList、MailboxFull、MessageTooLarge、ContentRejected、AttachmentRejected があります。
email.complained
Section titled “email.complained”追加のフィールドはありません。アドレスは送信停止リストに追加されます。
{ "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" }}email.opened
Section titled “email.opened”{ "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)" } }}email.clicked
Section titled “email.clicked”{ "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" } }}email.failed
Section titled “email.failed”{ "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" } }}email.suppressed
Section titled “email.suppressed”suppressed.reason は、suppression_list(宛先が送信停止リストに載っている)または recipient_domain_does_not_accept_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" } }}TypeScript でイベントに型を付ける
Section titled “TypeScript でイベントに型を付ける”Node SDK は、type で判別される共用体型 WebhookPayload をエクスポートしており、spacepost.webhooks.verify() もこれを返します。そのため、event.type で絞り込むと、正しい 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 }}