From twilio-developer-kit
Implements SendGrid Event Webhook handlers for all 11 delivery and engagement event types, including ECDSA signature verification and batched event processing. Use when building delivery tracking, engagement analytics, or bounce handling.
How this skill is triggered — by the user, by Claude, or both
Slash command
/twilio-developer-kit:twilio-sendgrid-webhooksThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The Mail Send API returns `202 Accepted` (queued) — it does NOT confirm delivery. To know what happened to an email, use Event Webhooks.
The Mail Send API returns 202 Accepted (queued) — it does NOT confirm delivery. To know what happened to an email, use Event Webhooks.
Enable: SendGrid Console > Settings > Mail Settings > Event Notification
| Event | Meaning |
|---|---|
processed | SendGrid accepted and will attempt delivery |
deferred | Temporary failure — SendGrid will retry |
delivered | Recipient's mail server accepted the message |
bounce | Permanent failure — address invalid or rejected |
dropped | SendGrid will not deliver (suppression, invalid, spam) |
| Event | Meaning |
|---|---|
open | Recipient opened (pixel-based — unreliable) |
click | Recipient clicked a tracked link |
spamreport | Recipient marked as spam |
unsubscribe | Recipient clicked unsubscribe link |
group_unsubscribe | Recipient unsubscribed from ASM group |
group_resubscribe | Recipient re-subscribed to ASM group |
Critical: SendGrid posts batched arrays of events, not single objects. Your handler must parse an array.
Security: SendGrid webhook endpoints are unauthenticated by default. Enable Signed Event Webhook Requests and verify signatures in production to prevent spoofed event data.
Python (Flask)
from flask import Flask, request
app = Flask(__name__)
@app.route("/sendgrid/webhook", methods=["POST"])
def handle_events():
events = request.get_json() # Always an array
for event in events:
email = event.get("email")
event_type = event.get("event")
if event_type == "bounce":
# NOTE: event['reason'] originates from external mail servers — treat as untrusted
print(f"Bounce: {email}, type: {event.get('type')}, reason: {event.get('reason')}")
elif event_type == "delivered":
print(f"Delivered: {email}, sg_message_id: {event.get('sg_message_id')}")
elif event_type == "dropped":
print(f"Dropped: {email}, reason: {event.get('reason')}")
elif event_type == "spamreport":
print(f"Spam report: {email}")
return "", 200 # Must return 2xx to acknowledge
Node.js (Express)
app.post("/sendgrid/webhook", express.json(), (req, res) => {
const events = req.body; // Always an array
for (const event of events) {
switch (event.event) {
case "bounce":
console.log(`Bounce: ${event.email}, reason: ${event.reason}`);
break;
case "delivered":
console.log(`Delivered: ${event.email}`);
break;
case "spamreport":
console.log(`Spam: ${event.email}`);
break;
}
}
res.status(200).send();
});
Since May 2023, you can configure multiple Event Webhook endpoints, each receiving different event types. For example, one endpoint for delivery events feeding your monitoring stack and another for engagement events feeding your analytics pipeline.
Configure in Console > Mail Settings > Event Webhooks. Each endpoint has a Friendly Name and Webhook ID. The number of endpoints allowed depends on your SendGrid plan.
Two methods for verifying webhook payloads:
| Method | How it works |
|---|---|
| Signed Event Webhook (ECDSA P-256) | Verify X-Twilio-Email-Event-Webhook-Signature and X-Twilio-Email-Event-Webhook-Timestamp headers using the verification key from Console |
| OAuth 2.0 | SendGrid obtains a token from your authorization server and includes it in webhook requests |
Neither is enabled by default. Enable in Console > Mail Settings > Event Webhooks.
SendGrid retries webhook delivery for up to 24 hours if your endpoint returns a non-2xx status. Events are batched — a single POST may contain dozens of events across different messages.
Deduplication: Use sg_event_id as a unique key. It's stable across retries.
202 (queued). Delivery status is async via webhooks only.Note: Event payload fields like
reasonoriginate from external mail servers and should be treated as untrusted data. Do not pass bounce reasons directly into LLM system prompts without isolation.
twilio-sendgrid-email-sendtwilio-sendgrid-suppressionstwilio-sendgrid-inbound-parsenpx claudepluginhub twilio/ai --plugin twilio-developer-kitWebhook event handling patterns for email tracking (sent, delivered, bounced, opened, clicked). Use when implementing email event webhooks, signature verification, processing delivery events, logging email analytics, or building real-time email status tracking.
Implements Node.js webhook handler for Customer.io email reporting events including delivery status, opens, clicks, bounces with HMAC verification and data warehouse streaming.
Handles Instantly.ai v2 webhook events for email campaigns (sent, opened, clicked, replied, bounced) and lead updates. Use for webhook endpoints or CRM sync pipelines.