SendGrid Webhooks
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
Essential Code
Signature Verification (Manual)
SendGrid uses ECDSA (Elliptic Curve Digital Signature Algorithm) with public key verification.
// 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
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
| Event |
Description |
Use Cases |
processed |
Message has been received and is ready to be delivered |
Track email processing |
delivered |
Message successfully delivered to recipient |
Delivery confirmation |
bounce |
Message permanently rejected (includes type='blocked' for blocked messages) |
Update contact lists, handle failures |
deferred |
Temporary delivery failure |
Monitor delays |
open |
Recipient opened the email |
Engagement tracking |
click |
Recipient clicked a link |
Link tracking, CTR analysis |
spam report |
Email marked as spam |
List hygiene, sender reputation |
unsubscribe |
Recipient unsubscribed |
Update subscription status |
group unsubscribe |
Recipient unsubscribed from a group |
Update group subscription preferences |
group resubscribe |
Recipient resubscribed to a group |
Update group subscription preferences |
Environment Variables
# Your SendGrid webhook verification key (public key)
SENDGRID_WEBHOOK_VERIFICATION_KEY="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE..."
Local Development
For local webhook testing, use Hookdeck CLI:
npx hookdeck-cli listen 3000 sendgrid --path /webhooks/sendgrid
No account required. Provides local tunnel + web UI for inspecting requests.
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
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
1---2name: sendgrid-webhooks3description: Receive and verify SendGrid webhooks. Use when setting up SendGrid webhook handlers, debugging signature verification, or handling email delivery events.4license: MIT5---6
7# SendGrid Webhooks
8
9## When to Use This Skill
10
11- Setting up SendGrid webhook handlers for email delivery tracking
12- Debugging ECDSA signature verification failures
13- Processing email events (bounce, delivered, open, click, spam report)
14- Implementing email engagement analytics
15
16## Essential Code
17
18### Signature Verification (Manual)
19
20SendGrid uses ECDSA (Elliptic Curve Digital Signature Algorithm) with public key verification.
21
22```javascript
23// Node.js manual verification
24const crypto = require('crypto');
25
26function verifySignature(publicKey, payload, signature, timestamp) {
27 // Decode the base64 signature
28 const decodedSignature = Buffer.from(signature, 'base64');
29
30 // Create the signed content
31 const signedContent = timestamp + payload;
32
33 // Create verifier
34 const verifier = crypto.createVerify('sha256');
35 verifier.update(signedContent);
36
37 // Add PEM headers if not present
38 let pemKey = publicKey;
39 if (!pemKey.includes('BEGIN PUBLIC KEY')) {
40 pemKey = `-----BEGIN PUBLIC KEY-----\n${publicKey}\n-----END PUBLIC KEY-----`;
41 }
42
43 // Verify the signature
44 return verifier.verify(pemKey, decodedSignature);
45}
46
47// Express middleware
48app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
49 const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
50 const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');
51
52 if (!signature || !timestamp) {
53 return res.status(400).send('Missing signature headers');
54 }
55
56 const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;
57 const payload = req.body.toString();
58
59 if (!verifySignature(publicKey, payload, signature, timestamp)) {
60 return res.status(400).send('Invalid signature');
61 }
62
63 // Process events
64 const events = JSON.parse(payload);
65 console.log(`Received ${events.length} events`);
66
67 res.sendStatus(200);
68});
69```
70
71### Using SendGrid SDK
72
73```javascript
74const { EventWebhook } = require('@sendgrid/eventwebhook');
75
76const verifyWebhook = new EventWebhook();
77const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;
78
79app.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {
80 const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');
81 const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');
82
83 const isValid = verifyWebhook.verifySignature(
84 publicKey,
85 req.body,
86 signature,
87 timestamp
88 );
89
90 if (!isValid) {
91 return res.status(400).send('Invalid signature');
92 }
93
94 // Process webhook
95 res.sendStatus(200);
96});
97```
98
99## Common Event Types
100
101| Event | Description | Use Cases |
102|-------|-------------|-----------|
103| `processed` | Message has been received and is ready to be delivered | Track email processing |
104| `delivered` | Message successfully delivered to recipient | Delivery confirmation |
105| `bounce` | Message permanently rejected (includes type='blocked' for blocked messages) | Update contact lists, handle failures |
106| `deferred` | Temporary delivery failure | Monitor delays |
107| `open` | Recipient opened the email | Engagement tracking |
108| `click` | Recipient clicked a link | Link tracking, CTR analysis |
109| `spam report` | Email marked as spam | List hygiene, sender reputation |
110| `unsubscribe` | Recipient unsubscribed | Update subscription status |
111| `group unsubscribe` | Recipient unsubscribed from a group | Update group subscription preferences |
112| `group resubscribe` | Recipient resubscribed to a group | Update group subscription preferences |
113
114## Environment Variables
115
116```bash
117# Your SendGrid webhook verification key (public key)
118SENDGRID_WEBHOOK_VERIFICATION_KEY="MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE..."
119```
120
121## Local Development
122
123For local webhook testing, use Hookdeck CLI:
124
125```bash
126npx hookdeck-cli listen 3000 sendgrid --path /webhooks/sendgrid
127```
128
129No account required. Provides local tunnel + web UI for inspecting requests.
130
131## Resources
132
133- [overview.md](references/overview.md) - What SendGrid webhooks are, common event types
134- [setup.md](references/setup.md) - Configure webhooks in SendGrid dashboard, get verification key
135- [verification.md](references/verification.md) - ECDSA signature verification details and gotchas
136- [examples/](examples/) - Complete implementations for Express, Next.js, and FastAPI
137
138## Related Skills
139
140- `webhook-handler-patterns` - Cross-cutting patterns (idempotency, retries, framework guides)
141- [hookdeck-event-gateway](https://github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway) - Webhook infrastructure that replaces your queue — guaranteed delivery, automatic retries, replay, rate limiting, and observability for your webhook handlers