{"id":"a7515011-5e45-497e-b3e7-81ca5f8a2cc6","shortId":"ccFfRg","kind":"skill","title":"sendgrid-webhooks","tagline":"Receive and verify SendGrid webhooks. Use when setting up SendGrid webhook handlers, debugging signature verification, or handling email delivery events.","description":"# SendGrid Webhooks\n\n## When to Use This Skill\n\n- Setting up SendGrid webhook handlers for email delivery tracking\n- Debugging ECDSA signature verification failures\n- Processing email events (bounce, delivered, open, click, spam report)\n- Implementing email engagement analytics\n\n## Essential Code\n\n### Signature Verification (Manual)\n\nSendGrid uses ECDSA (Elliptic Curve Digital Signature Algorithm) with public key verification.\n\n```javascript\n// Node.js manual verification\nconst crypto = require('crypto');\n\nfunction verifySignature(publicKey, payload, signature, timestamp) {\n  // Decode the base64 signature\n  const decodedSignature = Buffer.from(signature, 'base64');\n\n  // Create the signed content\n  const signedContent = timestamp + payload;\n\n  // Create verifier\n  const verifier = crypto.createVerify('sha256');\n  verifier.update(signedContent);\n\n  // Add PEM headers if not present\n  let pemKey = publicKey;\n  if (!pemKey.includes('BEGIN PUBLIC KEY')) {\n    pemKey = `-----BEGIN PUBLIC KEY-----\\n${publicKey}\\n-----END PUBLIC KEY-----`;\n  }\n\n  // Verify the signature\n  return verifier.verify(pemKey, decodedSignature);\n}\n\n// Express middleware\napp.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {\n  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');\n  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');\n\n  if (!signature || !timestamp) {\n    return res.status(400).send('Missing signature headers');\n  }\n\n  const publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;\n  const payload = req.body.toString();\n\n  if (!verifySignature(publicKey, payload, signature, timestamp)) {\n    return res.status(400).send('Invalid signature');\n  }\n\n  // Process events\n  const events = JSON.parse(payload);\n  console.log(`Received ${events.length} events`);\n\n  res.sendStatus(200);\n});\n```\n\n### Using SendGrid SDK\n\n```javascript\nconst { EventWebhook } = require('@sendgrid/eventwebhook');\n\nconst verifyWebhook = new EventWebhook();\nconst publicKey = process.env.SENDGRID_WEBHOOK_VERIFICATION_KEY;\n\napp.post('/webhooks/sendgrid', express.raw({ type: 'application/json' }), (req, res) => {\n  const signature = req.get('X-Twilio-Email-Event-Webhook-Signature');\n  const timestamp = req.get('X-Twilio-Email-Event-Webhook-Timestamp');\n\n  const isValid = verifyWebhook.verifySignature(\n    publicKey,\n    req.body,\n    signature,\n    timestamp\n  );\n\n  if (!isValid) {\n    return res.status(400).send('Invalid signature');\n  }\n\n  // Process webhook\n  res.sendStatus(200);\n});\n```\n\n## Common Event Types\n\n| Event | Description | Use Cases |\n|-------|-------------|-----------|\n| `processed` | Message has been received and is ready to be delivered | Track email processing |\n| `delivered` | Message successfully delivered to recipient | Delivery confirmation |\n| `bounce` | Message permanently rejected (includes type='blocked' for blocked messages) | Update contact lists, handle failures |\n| `deferred` | Temporary delivery failure | Monitor delays |\n| `open` | Recipient opened the email | Engagement tracking |\n| `click` | Recipient clicked a link | Link tracking, CTR analysis |\n| `spam report` | Email marked as spam | List hygiene, sender reputation |\n| `unsubscribe` | Recipient unsubscribed | Update subscription status |\n| `group unsubscribe` | Recipient unsubscribed from a group | Update group subscription preferences |\n| `group resubscribe` | Recipient resubscribed to a group | Update group subscription preferences |\n\n## Environment Variables\n\n```bash\n# Your SendGrid webhook verification key (public key)\nSENDGRID_WEBHOOK_VERIFICATION_KEY=\"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...\"\n```\n\n## Local Development\n\nFor local webhook testing, use Hookdeck CLI:\n\n```bash\nbrew install hookdeck/hookdeck/hookdeck\nhookdeck listen 3000 --path /webhooks/sendgrid\n```\n\nNo account required. Provides local tunnel + web UI for inspecting requests.\n\n## Resources\n\n- [overview.md](references/overview.md) - What SendGrid webhooks are, common event types\n- [setup.md](references/setup.md) - Configure webhooks in SendGrid dashboard, get verification key\n- [verification.md](references/verification.md) - ECDSA signature verification details and gotchas\n- [examples/](examples/) - Complete implementations for Express, Next.js, and FastAPI\n\n## Related Skills\n\n- `webhook-handler-patterns` - Cross-cutting patterns (idempotency, retries, framework guides)\n- [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","tags":["sendgrid","webhooks","webhook","skills","hookdeck","agent-skills","ai-coding","api-integrations","event-driven","github-webhooks","llm-tools","shopify-webhooks"],"capabilities":["skill","source-hookdeck","skill-sendgrid-webhooks","topic-agent-skills","topic-ai-coding","topic-api-integrations","topic-event-driven","topic-github-webhooks","topic-llm-tools","topic-shopify-webhooks","topic-stripe-webhooks","topic-webhook-security","topic-webhook-signatures","topic-webhooks"],"categories":["webhook-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/hookdeck/webhook-skills/sendgrid-webhooks","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add hookdeck/webhook-skills","source_repo":"https://github.com/hookdeck/webhook-skills","install_from":"skills.sh"}},"qualityScore":"0.484","qualityRationale":"deterministic score 0.48 from registry signals: · indexed on github topic:agent-skills · 69 github stars · SKILL.md body (4,689 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T06:55:47.605Z","embedding":null,"createdAt":"2026-04-18T22:14:00.811Z","updatedAt":"2026-05-02T06:55:47.605Z","lastSeenAt":"2026-05-02T06:55:47.605Z","tsv":"'/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':486 '/webhooks/sendgrid':148,236,417 '200':216,280 '3000':415 '400':179,201,273 'account':419 'add':114 'algorithm':70 'analysi':346 'analyt':57 'app.post':147,235 'application/json':151,239 'automat':495 'base64':91,97 'bash':387,409 'begin':125,129 'block':316,318 'bounc':48,310 'brew':410 'buffer.from':95 'case':287 'cli':408 'click':51,338,340 'code':59 'common':281,436 'complet':459 'configur':441 'confirm':309 'console.log':211 'const':79,93,102,108,154,164,184,190,207,221,225,229,242,252,262 'contact':321 'content':101 'creat':98,106 'cross':473 'cross-cut':472 'crypto':80,82 'crypto.createverify':110 'ctr':345 'curv':67 'cut':474 'dashboard':445 'debug':16,40 'decod':89 'decodedsignatur':94,144 'defer':325 'delay':330 'deliv':49,298,302,305 'deliveri':22,38,308,327,494 'descript':285 'detail':454 'develop':401 'digit':68 'ecdsa':41,65,451 'ellipt':66 'email':21,37,46,55,160,170,248,258,300,335,349 'end':135 'engag':56,336 'environ':385 'essenti':58 'event':23,47,161,171,206,208,214,249,259,282,284,437,482 'events.length':213 'eventwebhook':222,228 'exampl':457,458 'express':145,462 'express.raw':149,237 'failur':44,324,328 'fastapi':465 'framework':478 'function':83 'gateway':483 'get':446 'github.com':485 'github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':484 'gotcha':456 'group':363,369,371,374,380,382 'guarante':493 'guid':479 'handl':20,323 'handler':15,35,470,505 'header':116,183 'hookdeck':407,413,481 'hookdeck-event-gateway':480 'hookdeck/hookdeck/hookdeck':412 'hygien':354 'idempot':476 'implement':54,460 'includ':314 'infrastructur':488 'inspect':427 'instal':411 'invalid':203,275 'isvalid':263,270 'javascript':75,220 'json.parse':209 'key':73,127,131,137,189,234,392,394,398,448 'let':120 'limit':499 'link':342,343 'list':322,353 'listen':414 'local':400,403,422 'manual':62,77 'mark':350 'messag':289,303,311,319 'mfkwewyhkozizj0caqyikozizj0daqcdqgae':399 'middlewar':146 'miss':181 'monitor':329 'n':132,134 'new':227 'next.js':463 'node.js':76 'observ':501 'open':50,331,333 'overview.md':430 'path':416 'pattern':471,475 'payload':86,105,191,196,210 'pem':115 'pemkey':121,128,143 'pemkey.includes':124 'perman':312 'prefer':373,384 'present':119 'process':45,205,277,288,301 'process.env.sendgrid':186,231 'provid':421 'public':72,126,130,136,393 'publickey':85,122,133,185,195,230,265 'queue':492 'rate':498 'readi':295 'receiv':4,212,292 'recipi':307,332,339,358,365,376 'references/overview.md':431 'references/setup.md':440 'references/verification.md':450 'reject':313 'relat':466 'replac':490 'replay':497 'report':53,348 'reput':356 'req':152,240 'req.body':266 'req.body.tostring':192 'req.get':156,166,244,254 'request':428 'requir':81,223,420 'res':153,241 'res.sendstatus':215,279 'res.status':178,200,272 'resourc':429 'resubscrib':375,377 'retri':477,496 'return':141,177,199,271 'sdk':219 'send':180,202,274 'sender':355 'sendgrid':2,7,13,24,33,63,218,389,395,433,444 'sendgrid-webhook':1 'sendgrid/eventwebhook':224 'set':11,31 'setup.md':439 'sha256':111 'sign':100 'signatur':17,42,60,69,87,92,96,140,155,163,175,182,197,204,243,251,267,276,452 'signedcont':103,113 'skill':30,467 'skill-sendgrid-webhooks' 'source-hookdeck' 'spam':52,347,352 'status':362 'subscript':361,372,383 'success':304 'temporari':326 'test':405 'timestamp':88,104,165,173,176,198,253,261,268 'topic-agent-skills' 'topic-ai-coding' 'topic-api-integrations' 'topic-event-driven' 'topic-github-webhooks' 'topic-llm-tools' 'topic-shopify-webhooks' 'topic-stripe-webhooks' 'topic-webhook-security' 'topic-webhook-signatures' 'topic-webhooks' 'track':39,299,337,344 'tunnel':423 'twilio':159,169,247,257 'type':150,238,283,315,438 'ui':425 'unsubscrib':357,359,364,366 'updat':320,360,370,381 'use':9,28,64,217,286,406 'variabl':386 'verif':18,43,61,74,78,188,233,391,397,447,453 'verifi':6,107,109,138 'verification.md':449 'verifier.update':112 'verifier.verify':142 'verifysignatur':84,194 'verifywebhook':226 'verifywebhook.verifysignature':264 'web':424 'webhook':3,8,14,25,34,162,172,187,232,250,260,278,390,396,404,434,442,469,487,504 'webhook-handler-pattern':468 'x':158,168,246,256 'x-twilio-email-event-webhook-signatur':157,245 'x-twilio-email-event-webhook-timestamp':167,255","prices":[{"id":"08acf67d-f04f-4db3-bfa7-a1d157422a6b","listingId":"a7515011-5e45-497e-b3e7-81ca5f8a2cc6","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"hookdeck","category":"webhook-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:14:00.811Z"}],"sources":[{"listingId":"a7515011-5e45-497e-b3e7-81ca5f8a2cc6","source":"github","sourceId":"hookdeck/webhook-skills/sendgrid-webhooks","sourceUrl":"https://github.com/hookdeck/webhook-skills/tree/main/skills/sendgrid-webhooks","isPrimary":false,"firstSeenAt":"2026-04-18T22:14:00.811Z","lastSeenAt":"2026-05-02T06:55:47.605Z"}],"details":{"listingId":"a7515011-5e45-497e-b3e7-81ca5f8a2cc6","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"hookdeck","slug":"sendgrid-webhooks","github":{"repo":"hookdeck/webhook-skills","stars":69,"topics":["agent-skills","ai-coding","api-integrations","event-driven","github-webhooks","llm-tools","shopify-webhooks","stripe-webhooks","webhook-security","webhook-signatures","webhooks"],"license":"mit","html_url":"https://github.com/hookdeck/webhook-skills","pushed_at":"2026-02-25T14:00:40Z","description":"Webhook integration skills for AI coding agents (Claude Code, Cursor, Copilot). Step-by-step guidance for setting up webhook receivers, signature verification, and event handling for Stripe, Shopify, GitHub, and more. Built on the Agent Skills specification.","skill_md_sha":"b324cbb3b7aab01ba9aaaf702dd2fec1c0ab421a","skill_md_path":"skills/sendgrid-webhooks/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/hookdeck/webhook-skills/tree/main/skills/sendgrid-webhooks"},"layout":"multi","source":"github","category":"webhook-skills","frontmatter":{"name":"sendgrid-webhooks","license":"MIT","description":"Receive and verify SendGrid webhooks. Use when setting up SendGrid webhook handlers, debugging signature verification, or handling email delivery events."},"skills_sh_url":"https://skills.sh/hookdeck/webhook-skills/sendgrid-webhooks"},"updatedAt":"2026-05-02T06:55:47.605Z"}}