{"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\nnpx hookdeck-cli listen 3000 sendgrid --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.485","qualityRationale":"deterministic score 0.48 from registry signals: · indexed on github topic:agent-skills · 71 github stars · SKILL.md body (4,666 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-18T18:56:56.306Z","embedding":null,"createdAt":"2026-04-18T22:14:00.811Z","updatedAt":"2026-05-18T18:56:56.306Z","lastSeenAt":"2026-05-18T18:56:56.306Z","tsv":"'/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':487 '/webhooks/sendgrid':148,236,418 '200':216,280 '3000':415 '400':179,201,273 'account':420 'add':114 'algorithm':70 'analysi':346 'analyt':57 'app.post':147,235 'application/json':151,239 'automat':496 'base64':91,97 'bash':387,409 'begin':125,129 'block':316,318 'bounc':48,310 'buffer.from':95 'case':287 'cli':408,413 'click':51,338,340 'code':59 'common':281,437 'complet':460 'configur':442 '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':474 'cross-cut':473 'crypto':80,82 'crypto.createverify':110 'ctr':345 'curv':67 'cut':475 'dashboard':446 'debug':16,40 'decod':89 'decodedsignatur':94,144 'defer':325 'delay':330 'deliv':49,298,302,305 'deliveri':22,38,308,327,495 'descript':285 'detail':455 'develop':401 'digit':68 'ecdsa':41,65,452 '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,438,483 'events.length':213 'eventwebhook':222,228 'exampl':458,459 'express':145,463 'express.raw':149,237 'failur':44,324,328 'fastapi':466 'framework':479 'function':83 'gateway':484 'get':447 'github.com':486 'github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':485 'gotcha':457 'group':363,369,371,374,380,382 'guarante':494 'guid':480 'handl':20,323 'handler':15,35,471,506 'header':116,183 'hookdeck':407,412,482 'hookdeck-c':411 'hookdeck-event-gateway':481 'hygien':354 'idempot':477 'implement':54,461 'includ':314 'infrastructur':489 'inspect':428 'invalid':203,275 'isvalid':263,270 'javascript':75,220 'json.parse':209 'key':73,127,131,137,189,234,392,394,398,449 'let':120 'limit':500 'link':342,343 'list':322,353 'listen':414 'local':400,403,423 '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':464 'node.js':76 'npx':410 'observ':502 'open':50,331,333 'overview.md':431 'path':417 'pattern':472,476 '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':422 'public':72,126,130,136,393 'publickey':85,122,133,185,195,230,265 'queue':493 'rate':499 'readi':295 'receiv':4,212,292 'recipi':307,332,339,358,365,376 'references/overview.md':432 'references/setup.md':441 'references/verification.md':451 'reject':313 'relat':467 'replac':491 'replay':498 'report':53,348 'reput':356 'req':152,240 'req.body':266 'req.body.tostring':192 'req.get':156,166,244,254 'request':429 'requir':81,223,421 'res':153,241 'res.sendstatus':215,279 'res.status':178,200,272 'resourc':430 'resubscrib':375,377 'retri':478,497 'return':141,177,199,271 'sdk':219 'send':180,202,274 'sender':355 'sendgrid':2,7,13,24,33,63,218,389,395,416,434,445 'sendgrid-webhook':1 'sendgrid/eventwebhook':224 'set':11,31 'setup.md':440 'sha256':111 'sign':100 'signatur':17,42,60,69,87,92,96,140,155,163,175,182,197,204,243,251,267,276,453 'signedcont':103,113 'skill':30,468 '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':424 'twilio':159,169,247,257 'type':150,238,283,315,439 'ui':426 '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,448,454 'verifi':6,107,109,138 'verification.md':450 'verifier.update':112 'verifier.verify':142 'verifysignatur':84,194 'verifywebhook':226 'verifywebhook.verifysignature':264 'web':425 'webhook':3,8,14,25,34,162,172,187,232,250,260,278,390,396,404,435,443,470,488,505 'webhook-handler-pattern':469 '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-18T18:56:56.306Z"},{"listingId":"a7515011-5e45-497e-b3e7-81ca5f8a2cc6","source":"skills_sh","sourceId":"hookdeck/webhook-skills/sendgrid-webhooks","sourceUrl":"https://skills.sh/hookdeck/webhook-skills/sendgrid-webhooks","isPrimary":true,"firstSeenAt":"2026-05-07T20:44:11.296Z","lastSeenAt":"2026-05-07T22:42:38.392Z"}],"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":71,"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-05-15T15:30:15Z","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":"559a8817c26bf25c39f268cff1cae68905b0723d","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-18T18:56:56.306Z"}}