sendgrid-webhooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SendGrid Webhooks

SendGrid Webhook

When to Use This Skill

适用场景

  • Setting up SendGrid webhook handlers for email delivery tracking
  • Debugging ECDSA signature verification failures
  • Processing email events (bounce, delivered, open, click, spam report)
  • Implementing email engagement analytics
  • 搭建用于邮件投递追踪的SendGrid Webhook处理器
  • 调试ECDSA签名验证失败问题
  • 处理邮件事件(退信、投递成功、已打开、已点击、垃圾邮件举报)
  • 实现邮件互动分析

Essential Code

核心代码示例

Signature Verification (Manual)

手动签名验证

SendGrid uses ECDSA (Elliptic Curve Digital Signature Algorithm) with public key verification.
javascript
// Node.js manual verification
const crypto = require('crypto');

function verifySignature(publicKey, payload, signature, timestamp) {
  // Decode the base64 signature
  const decodedSignature = Buffer.from(signature, 'base64');

  // Create the signed content
  const signedContent = timestamp + payload;

  // Create verifier
  const verifier = crypto.createVerify('sha256');
  verifier.update(signedContent);

  // Add PEM headers if not present
  let pemKey = publicKey;
  if (!pemKey.includes('BEGIN PUBLIC KEY')) {
    pemKey = `-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`;
  }

  // Verify the signature
  return verifier.verify(pemKey, decodedSignature);
}

// Express middleware
app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');

  if (!signature || !timestamp) {
    return res.status(400).send('Missing signature headers');
  }

  const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;
  const payload = req.body.toString();

  if (!verifySignature(publicKey, payload, signature, timestamp)) {
    return res.status(400).send('Invalid signature');
  }

  // Process events
  const events = JSON.parse(payload);
  console.log(`Received ${events.length} events`);

  res.sendStatus(200);
});
SendGrid 使用带公钥验证的ECDSA(椭圆曲线数字签名算法)。
javascript
// Node.js manual verification
const crypto = require('crypto');

function verifySignature(publicKey, payload, signature, timestamp) {
  // Decode the base64 signature
  const decodedSignature = Buffer.from(signature, 'base64');

  // Create the signed content
  const signedContent = timestamp + payload;

  // Create verifier
  const verifier = crypto.createVerify('sha256');
  verifier.update(signedContent);

  // Add PEM headers if not present
  let pemKey = publicKey;
  if (!pemKey.includes('BEGIN PUBLIC KEY')) {
    pemKey = `-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`;
  }

  // Verify the signature
  return verifier.verify(pemKey, decodedSignature);
}

// Express middleware
app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');

  if (!signature || !timestamp) {
    return res.status(400).send('Missing signature headers');
  }

  const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;
  const payload = req.body.toString();

  if (!verifySignature(publicKey, payload, signature, timestamp)) {
    return res.status(400).send('Invalid signature');
  }

  // Process events
  const events = JSON.parse(payload);
  console.log(`Received ${events.length} events`);

  res.sendStatus(200);
});

Using SendGrid SDK

使用SendGrid SDK

javascript
const { EventWebhook } = require('@sendgrid/eventwebhook');

const verifyWebhook = new EventWebhook();
const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;

app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');

  const isValid = verifyWebhook.verifySignature(
    publicKey,
    req.body,
    signature,
    timestamp
  );

  if (!isValid) {
    return res.status(400).send('Invalid signature');
  }

  // Process webhook
  res.sendStatus(200);
});
javascript
const { EventWebhook } = require('@sendgrid/eventwebhook');

const verifyWebhook = new EventWebhook();
const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;

app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');

  const isValid = verifyWebhook.verifySignature(
    publicKey,
    req.body,
    signature,
    timestamp
  );

  if (!isValid) {
    return res.status(400).send('Invalid signature');
  }

  // Process webhook
  res.sendStatus(200);
});

Common Event Types

常见事件类型

EventDescriptionUse Cases
processed
Message has been received and is ready to be deliveredTrack email processing
delivered
Message successfully delivered to recipientDelivery confirmation
bounce
Message permanently rejected (includes type='blocked' for blocked messages)Update contact lists, handle failures
deferred
Temporary delivery failureMonitor delays
open
Recipient opened the emailEngagement tracking
click
Recipient clicked a linkLink tracking, CTR analysis
spam report
Email marked as spamList hygiene, sender reputation
unsubscribe
Recipient unsubscribedUpdate subscription status
group unsubscribe
Recipient unsubscribed from a groupUpdate group subscription preferences
group resubscribe
Recipient resubscribed to a groupUpdate group subscription preferences
事件类型描述适用场景
processed
邮件已被接收,等待投递追踪邮件处理状态
delivered
邮件成功投递至收件人投递确认
bounce
邮件被永久拒收(包含
blocked
类型,即被拦截的邮件)
更新联系人列表,处理投递失败
deferred
临时投递失败监控投递延迟
open
收件人已打开邮件互动追踪
click
收件人点击了链接链接追踪,点击率分析
spam report
邮件被标记为垃圾邮件列表清理,发件人信誉维护
unsubscribe
收件人取消订阅更新订阅状态
group unsubscribe
收件人取消群组订阅更新群组订阅偏好
group resubscribe
收件人重新订阅群组更新群组订阅偏好

Environment Variables

环境变量

bash
undefined
bash
undefined

Your SendGrid webhook verification key (public key)

Your SendGrid webhook verification key (public key)

SENDGRID_WEBHOOK_VERIFICATION_KEY="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE..."
undefined
SENDGRID_WEBHOOK_VERIFICATION_KEY="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE..."
undefined

Local Development

本地开发

For local webhook testing, use Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/sendgrid
No account required. Provides local tunnel + web UI for inspecting requests.
对于本地Webhook测试,使用Hookdeck CLI:
bash
brew install hookdeck/hookdeck/hookdeck
hookdeck listen 3000 --path /webhooks/sendgrid
无需注册账号,提供本地隧道和Web界面用于查看请求详情。

Resources

参考资源

  • overview.md - What SendGrid webhooks are, common event types
  • setup.md - Configure webhooks in SendGrid dashboard, get verification key
  • verification.md - ECDSA signature verification details and gotchas
  • examples/ - Complete implementations for Express, Next.js, and FastAPI
  • overview.md - 介绍SendGrid Webhook是什么、常见事件类型
  • setup.md - 在SendGrid控制台配置Webhook,获取验证密钥
  • verification.md - ECDSA签名验证的细节和注意事项
  • examples/ - Express、Next.js和FastAPI的完整实现示例

Related Skills

相关技能

  • webhook-handler-patterns
    - Cross-cutting patterns (idempotency, retries, framework guides)
  • hookdeck-event-gateway - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers
  • webhook-handler-patterns
    - 通用模式(幂等性、重试、框架指南)
  • hookdeck-event-gateway - 替代队列的Webhook基础设施——为你的Webhook处理器提供可靠投递、自动重试、重放、速率限制和可观测性