Email Sending のイベントサブスクリプション を使い、配信の問題が発生したあとにアプリケーションのレコードを更新します。この例では Cloudflare Queues と Workers KV を使い、トランザクション通知の対象から受信者を削除します。
開始する前に、次を用意します。
- Email Sending ドメイン を有効にする
- Worker プロジェクト を作成する
- Workers KV 名前空間 を作成する
対象となる各受信者アドレスを、KV のキーとして保存します。値には通知設定や関連メタデータを含められます。
- Email Sending がバウンスと苦情のイベントを発行する
- キューがそれらのイベントを Worker に配信する
- Worker が対象外の受信者レコードを KV から削除する
すべての message.complained イベントでレコードを削除します。これらのイベントは、受信者がメッセージをスパムとして報告したことを示します。
バウンスのレコードは、payload.bounce.type が "hard" のときだけ削除します。一時的な失敗は、再試行が残っているあいだ message.deferred イベントになります。一時的な再試行が尽きると、"soft" バウンス種別の message.bounced イベントになることがあります。
ペイロードの詳細は、利用可能な Email Sending イベント を参照してください。
キューを作成し、送信ドメインにサブスクライブします。
-
Cloudflare ダッシュボードで Queues ページを開きます。
Queues を開く ↗email-eventsという名前のキューを作成します。 -
email-eventsを選び、Subscriptions > Subscribe to events を選びます。 -
サブスクリプション名を入力し、ソースとして Email Sending を選びます。
-
送信ドメインと、
message.bouncedおよびmessage.complainedイベントを選びます。 -
Subscribe を選びます。
KV 名前空間をバインドし、Worker をキューのコンシューマーとして登録します。
{
"$schema": "./node_modules/wrangler/config-schema.json",
"name": "recipient-record-sync",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-09-20",
"kv_namespaces": [
{
"binding": "RECIPIENTS",
"id": "<RECIPIENTS_KV_NAMESPACE_ID>"
}
],
"queues": {
"consumers": [
{
"queue": "email-events",
"max_batch_size": 10,
"max_retries": 3,
"dead_letter_queue": "email-events-dlq"
}
]
}
}name = "recipient-record-sync"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-09-20"
[[kv_namespaces]]
binding = "RECIPIENTS"
id = "<RECIPIENTS_KV_NAMESPACE_ID>"
[[queues.consumers]]
queue = "email-events"
max_batch_size = 10
max_retries = 3
dead_letter_queue = "email-events-dlq"構成では、デプロイ時に email-events-dlq が作成されます。Queues は 3 回再試行したあと、イベントをそこに移します。
queue() ハンドラー は各イベントを独立して処理します。対象の受信者レコードを削除し、失敗した KV 操作を再試行します。
export default {
async queue(batch, env) {
for (const message of batch.messages) {
try {
const event = message.body;
if (shouldRemove(event)) {
await removeRecipient(env, event);
}
message.ack();
} catch (error) {
console.error("Failed to process Email Sending event", {
eventId: message.body.payload.eventId,
error,
});
message.retry();
}
}
},
};
function shouldRemove(event) {
if (event.type === "cf.email.sending.message.complained") {
return true;
}
return (
event.type === "cf.email.sending.message.bounced" &&
event.payload.bounce?.type === "hard"
);
}
async function removeRecipient(env, event) {
await env.RECIPIENTS.delete(event.payload.recipient);
console.log("Removed recipient record", {
eventId: event.payload.eventId,
reason: event.type,
});
}interface Env {
RECIPIENTS: KVNamespace;
}
interface EmailSendingEvent {
type:
| "cf.email.sending.message.bounced"
| "cf.email.sending.message.complained";
payload: {
eventId: string;
recipient: string;
bounce?: {
type: "hard" | "soft";
};
};
}
export default {
async queue(batch, env): Promise<void> {
for (const message of batch.messages) {
try {
const event = message.body;
if (shouldRemove(event)) {
await removeRecipient(env, event);
}
message.ack();
} catch (error) {
console.error("Failed to process Email Sending event", {
eventId: message.body.payload.eventId,
error,
});
message.retry();
}
}
},
} satisfies ExportedHandler<Env, EmailSendingEvent>;
function shouldRemove(event: EmailSendingEvent): boolean {
if (event.type === "cf.email.sending.message.complained") {
return true;
}
return (
event.type === "cf.email.sending.message.bounced" &&
event.payload.bounce?.type === "hard"
);
}
async function removeRecipient(
env: Env,
event: EmailSendingEvent,
): Promise<void> {
await env.RECIPIENTS.delete(event.payload.recipient);
console.log("Removed recipient record", {
eventId: event.payload.eventId,
reason: event.type,
});
}存在しない KV キーの削除は成功します。そのため、同じイベントが繰り返し配信されても安全です。
ハンドラーは成功した各メッセージに対して ack() を呼びます。失敗した操作は、3 回再試行したあと デッドレターキュー に移ります。
Worker とキューコンシューマーの構成をデプロイします。
npx wrangler deployyarn wrangler deploypnpm wrangler deploy失敗したイベントはデッドレターキューを監視します。原因を直したあと、再処理します。
- Event subscriptions — イベントスキーマを確認します。
- Suppression lists — 自動サプレッションを理解します。
- Queues retries — メッセージの再試行を制御します。
- Workers KV consistency — 伝播の遅延を考慮します。