Hành vi gửi
Lấy từ tài liệu của chính hãng, kiểm tra lần cuối 2026-08-06. Hãng không nêu con số nào thì bảng này cũng không ghi.
| Thời hạn phản hồi | hãng không công bố |
|---|---|
| Thử lại | hãng không công bố |
| Ngừng gửi | hãng không công bố |
Chữ ký
Kiểm chứng bằng một bộ xác minh chạy thật, có bộ test chứng minh, chứ không đọc từ tài liệu. Kiểm chứng lần cuối 2026-08-20.
| Thuật toán | ECDSA P-256 |
|---|---|
| Payload được ký | {timestamp}{body} |
| Encoding | base64 |
| Header | x-twilio-email-event-webhook-signature |
| Dung sai | không quy định |
Xác minh
Dùng raw body của request, đúng từng byte, trước mọi bước parse JSON. Mọi cơ chế trên trang này đều hỏng ngay khi framework serialize lại payload.
// 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),
);Những chỗ dễ vấp
Header ghi x-twilio-email vì Twilio sở hữu SendGrid; cơ chế này là của riêng SendGrid và không dính dáng gì tới HMAC-SHA1 của Twilio. Bộ xác minh chỉ dựa vào tiền tố header sẽ chọn sai thuật toán.
Đọc thêm
AnyHook đứng trước các endpoint nhận webhook từ SendGrid: trả lời sau khoảng 1 giây, tự thử lại theo lịch riêng khi server của bạn ngừng hoạt động, và giữ mọi sự kiện để bạn phát lại được. Đổi một URL, giữ nguyên code.