SendGrid の Webhook 配信の仕組み

SendGrid は HMAC ではなく ECDSA P-256 で署名し、x-twilio-email ヘッダーに載せます。検証済みの署名形式と Web Crypto の検証コードを掲載しています。

配信の挙動

プロバイダーの公式ドキュメントから読み取っています(最終確認 2026-08-06)。プロバイダーが数値を示していない項目には、この表も数値を載せていません。

応答期限プロバイダーは未公表
リトライプロバイダーは未公表
打ち切りプロバイダーは未公表

署名

ドキュメントを読んだだけではなく、テストスイートで裏付けた動作する検証コードで確認しています。最終検証日は 2026-08-20 です。

アルゴリズムECDSA P-256
署名対象{timestamp}{body}
エンコードbase64
ヘッダーx-twilio-email-event-webhook-signature
許容範囲適用なし

検証する

JSON としてパースする前の生のリクエストボディを、バイト単位でそのまま使います。フレームワークがペイロードを再シリアライズした時点で、このページのどの方式も検証に失敗します。

// ECDSA P-256 over "{timestamp}{body}", signature and key both base64.
const b64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));

const key = await crypto.subtle.importKey(
  "spki",
  b64(verificationKey), // Mail Settings → Signed Event Webhook
  { name: "ECDSA", namedCurve: "P-256" },
  false,
  ["verify"],
);
const ts = req.headers["x-twilio-email-event-webhook-timestamp"];
const valid = await crypto.subtle.verify(
  { name: "ECDSA", hash: "SHA-256" },
  key,
  b64(req.headers["x-twilio-email-event-webhook-signature"]),
  new TextEncoder().encode(ts + rawBody),
);

落とし穴

ヘッダー名が x-twilio-email なのは、Twilio が SendGrid を所有しているからです。方式は SendGrid 独自のもので、Twilio の HMAC-SHA1 とは何も共通していません。ヘッダーのプレフィックスだけで判定する検証コードは、誤ったアルゴリズムを選びます。

あわせて読む

AnyHook は、SendGrid から受信するエンドポイントの手前に入ります。約 1 秒で応答し、サーバーがダウンしていれば独自のスケジュールでリトライします。すべてのイベントは再送できる状態で保存されます。 変えるのは URL 1 つだけで、コードはそのままです。

仕組みを見る →