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