{"id":"469ab269-84c5-4cb2-90b6-cfcc66f3cd3c","shortId":"Mj7Thv","kind":"skill","title":"vercel-webhooks","tagline":"Receive and verify Vercel webhooks. Use when setting up Vercel webhook handlers, debugging signature verification, or handling deployment events like deployment.created, deployment.succeeded, or project.created.","description":"# Vercel Webhooks\n\n## When to Use This Skill\n\n- Setting up Vercel webhook handlers\n- Debugging signature verification failures\n- Understanding Vercel event types and payloads\n- Handling deployment, project, domain, or integration events\n- Monitoring deployment status changes\n\n## Essential Code (USE THIS)\n\n### Express Webhook Handler with Manual Verification\n\n```javascript\nconst express = require('express');\nconst crypto = require('crypto');\n\nconst app = express();\n\n// CRITICAL: Use express.raw() for webhook endpoint - Vercel needs raw body\napp.post('/webhooks/vercel',\n  express.raw({ type: 'application/json' }),\n  async (req, res) => {\n    const signature = req.headers['x-vercel-signature'];\n\n    if (!signature) {\n      return res.status(400).send('Missing x-vercel-signature header');\n    }\n\n    // Verify signature using SHA1 HMAC\n    const expectedSignature = crypto\n      .createHmac('sha1', process.env.VERCEL_WEBHOOK_SECRET)\n      .update(req.body)\n      .digest('hex');\n\n    // Use timing-safe comparison\n    let signaturesMatch;\n    try {\n      signaturesMatch = crypto.timingSafeEqual(\n        Buffer.from(signature),\n        Buffer.from(expectedSignature)\n      );\n    } catch (err) {\n      // Buffer length mismatch = invalid signature\n      signaturesMatch = false;\n    }\n\n    if (!signaturesMatch) {\n      console.error('Invalid Vercel webhook signature');\n      return res.status(400).send('Invalid signature');\n    }\n\n    // Parse the verified payload\n    const event = JSON.parse(req.body.toString());\n\n    // Handle the event\n    switch (event.type) {\n      case 'deployment.created':\n        console.log('Deployment created:', event.payload.deployment.id);\n        break;\n      case 'deployment.succeeded':\n        console.log('Deployment succeeded:', event.payload.deployment.id);\n        break;\n      case 'deployment.error':\n        console.log('Deployment failed:', event.payload.deployment.id);\n        break;\n      case 'project.created':\n        console.log('Project created:', event.payload.project.name);\n        break;\n      default:\n        console.log('Unhandled event:', event.type);\n    }\n\n    res.json({ received: true });\n  }\n);\n```\n\n### Python (FastAPI) Webhook Handler\n\n```python\nimport os\nimport hmac\nimport hashlib\nfrom fastapi import FastAPI, Request, HTTPException, Header\n\napp = FastAPI()\nwebhook_secret = os.environ.get(\"VERCEL_WEBHOOK_SECRET\")\n\n@app.post(\"/webhooks/vercel\")\nasync def vercel_webhook(\n    request: Request,\n    x_vercel_signature: str = Header(None)\n):\n    if not x_vercel_signature:\n        raise HTTPException(status_code=400, detail=\"Missing x-vercel-signature header\")\n\n    # Get raw body\n    body = await request.body()\n\n    # Compute expected signature\n    expected_signature = hmac.new(\n        webhook_secret.encode(),\n        body,\n        hashlib.sha1\n    ).hexdigest()\n\n    # Timing-safe comparison\n    if not hmac.compare_digest(x_vercel_signature, expected_signature):\n        raise HTTPException(status_code=400, detail=\"Invalid signature\")\n\n    # Parse verified payload\n    event = await request.json()\n\n    # Handle event\n    if event[\"type\"] == \"deployment.created\":\n        print(f\"Deployment created: {event['payload']['deployment']['id']}\")\n    elif event[\"type\"] == \"deployment.succeeded\":\n        print(f\"Deployment succeeded: {event['payload']['deployment']['id']}\")\n    # ... handle other events\n\n    return {\"received\": True}\n```\n\n> **For complete working examples with tests**, see:\n> - [examples/express/](examples/express/) - Full Express implementation\n> - [examples/nextjs/](examples/nextjs/) - Next.js App Router implementation\n> - [examples/fastapi/](examples/fastapi/) - Python FastAPI implementation\n\n## Common Event Types\n\n| Event | Triggered When | Common Use Cases |\n|-------|----------------|------------------|\n| `deployment.created` | A new deployment starts | Start deployment monitoring, notify team |\n| `deployment.succeeded` | Deployment completes successfully | Update status, trigger post-deploy tasks |\n| `deployment.error` | Deployment fails | Alert team, rollback actions |\n| `deployment.canceled` | Deployment is canceled | Clean up resources |\n| `project.created` | New project is created | Set up monitoring, configure resources |\n| `project.removed` | Project is deleted | Clean up external resources |\n| `domain.created` | Domain is added | Update DNS, SSL configuration |\n\nSee [references/overview.md](references/overview.md) for the complete event list.\n\n## Environment Variables\n\n```bash\n# Required\nVERCEL_WEBHOOK_SECRET=your_webhook_secret_from_dashboard\n\n# Optional (for API calls)\nVERCEL_TOKEN=your_vercel_api_token\n```\n\n## Local Development\n\nFor local webhook testing, install Hookdeck CLI:\n\n```bash\n# Install via npm\nnpm install -g hookdeck-cli\n\n# Or via Homebrew\nbrew install hookdeck/hookdeck/hookdeck\n```\n\nThen start the tunnel:\n\n```bash\nhookdeck listen 3000 --path /webhooks/vercel\n```\n\nNo account required. Provides local tunnel + web UI for inspecting requests.\n\n## Reference Materials\n\n- [Webhook Overview](references/overview.md) - What Vercel webhooks are, all event types\n- [Setup Guide](references/setup.md) - Configure webhooks in Vercel dashboard\n- [Signature Verification](references/verification.md) - SHA1 HMAC verification details\n\n## Recommended: webhook-handler-patterns\n\nFor production-ready webhook handling, also install the `webhook-handler-patterns` skill to learn:\n\n- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md) - Correct order of operations\n- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md) - Handle duplicate webhooks\n- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md) - Proper status codes and retries\n- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md) - Handle transient failures\n\n## Related Skills\n\n- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe payment webhooks\n- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub repository webhooks\n- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify store webhooks\n- [sendgrid-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/sendgrid-webhooks) - SendGrid email webhooks\n- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Idempotency, error handling, retry logic\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":["vercel","webhooks","webhook","skills","hookdeck","agent-skills","ai-coding","api-integrations","event-driven","github-webhooks","llm-tools","shopify-webhooks"],"capabilities":["skill","source-hookdeck","skill-vercel-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/vercel-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 (7,017 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.948Z","embedding":null,"createdAt":"2026-04-18T22:14:03.243Z","updatedAt":"2026-05-02T06:55:47.948Z","lastSeenAt":"2026-05-02T06:55:47.948Z","tsv":"'/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':591 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':575 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':583 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':601 '/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':621 '/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':661 '/hookdeck/webhook-skills/tree/main/skills/sendgrid-webhooks)':639 '/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':630 '/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':612 '/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':649 '/webhooks/vercel':94,249,511 '3000':509 '400':112,169,271,312 'account':513 'action':413 'ad':442 'alert':410 'also':561 'api':469,475 'app':81,240,369 'app.post':93,248 'application/json':97 'async':98,250 'automat':670 'await':283,320 'bash':457,486,506 'bodi':92,281,282,292 'break':192,199,206,213 'brew':499 'buffer':153 'buffer.from':147,149 'call':470 'cancel':417 'case':186,193,200,207,385 'catch':151 'chang':60 'clean':418,435 'cli':485,495 'code':62,270,311,594 'common':377,383 'comparison':141,298 'complet':355,398,452 'comput':285 'configur':429,446,538 'console.error':162 'console.log':188,195,202,209,215 'const':72,76,80,101,125,177 'correct':576 'creat':190,211,331,425 'createhmac':128 'critic':83 'crypto':77,79,127 'crypto.timingsafeequal':146 'dashboard':466,542 'debug':16,40 'def':251 'default':214 'delet':434 'deliveri':669 'deploy':21,51,58,189,196,203,330,334,342,346,389,392,397,405,408,415 'deployment.canceled':414 'deployment.created':24,187,327,386 'deployment.error':201,407 'deployment.succeeded':25,194,339,396 'detail':272,313,549 'develop':478 'digest':135,302 'dns':444 'domain':53,440 'domain.created':439 'duplic':585 'elif':336 'email':641 'endpoint':88 'environ':455 'err':152 'error':587,651 'essenti':61 'event':22,46,56,178,183,217,319,323,325,332,337,344,350,378,380,453,533,657 'event.payload.deployment.id':191,198,205 'event.payload.project.name':212 'event.type':185,218 'exampl':357 'examples/express':361,362 'examples/fastapi':372,373 'examples/nextjs':366,367 'expect':286,288,306 'expectedsignatur':126,150 'express':65,73,75,82,364 'express.raw':85,95 'extern':437 'f':329,341 'fail':204,409 'failur':43,604 'fals':159 'fastapi':223,234,236,241,375 'full':363 'g':492 'gateway':658 'get':279 'github':617,622 'github-webhook':616 'github.com':574,582,590,600,611,620,629,638,648,660 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':589 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':573 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':581 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':599 'github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':619 'github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':659 'github.com/hookdeck/webhook-skills/tree/main/skills/sendgrid-webhooks)':637 'github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':628 'github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':610 'github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':647 'guarante':668 'guid':536 'handl':20,50,181,322,348,560,584,588,602,652 'handler':15,39,67,225,553,566,571,645,680 'hashlib':232 'hashlib.sha1':293 'header':119,239,260,278 'hex':136 'hexdigest':294 'hmac':124,230,547 'hmac.compare':301 'hmac.new':290 'homebrew':498 'hookdeck':484,494,507,656 'hookdeck-c':493 'hookdeck-event-gateway':655 'hookdeck/hookdeck/hookdeck':501 'httpexcept':238,268,309 'id':335,347 'idempot':580,650 'implement':365,371,376 'import':227,229,231,235 'infrastructur':663 'inspect':521 'instal':483,487,491,500,562 'integr':55 'invalid':156,163,171,314 'javascript':71 'json.parse':179 'learn':570 'length':154 'let':142 'like':23 'limit':674 'list':454 'listen':508 'local':477,480,516 'logic':598,654 'manual':69 'materi':524 'mismatch':155 'miss':114,273 'monitor':57,393,428 'need':90 'new':388,422 'next.js':368 'none':261 'notifi':394 'npm':489,490 'observ':676 'oper':579 'option':467 'order':577 'os':228 'os.environ.get':244 'overview':526 'pars':173,316 'path':510 'pattern':554,567,646 'payload':49,176,318,333,345 'payment':614 'post':404 'post-deploy':403 'print':328,340 'process.env.vercel':130 'product':557 'production-readi':556 'project':52,210,423,432 'project.created':27,208,421 'project.removed':431 'proper':592 'provid':515 'python':222,226,374 'queue':667 'rais':267,308 'rate':673 'raw':91,280 'readi':558 'receiv':4,220,352 'recommend':550 'refer':523 'references/overview.md':448,449,527 'references/setup.md':537 'references/verification.md':545 'relat':605 'replac':665 'replay':672 'repositori':623 'req':99 'req.body':134 'req.body.tostring':180 'req.headers':103 'request':237,254,255,522 'request.body':284 'request.json':321 'requir':74,78,458,514 'res':100 'res.json':219 'res.status':111,168 'resourc':420,430,438 'retri':596,597,653,671 'return':110,167,351 'rollback':412 'router':370 'safe':140,297 'secret':132,243,247,461,464 'see':360,447 'send':113,170 'sendgrid':635,640 'sendgrid-webhook':634 'sequenc':572 'set':11,35,426 'setup':535 'sha1':123,129,546 'shopifi':626,631 'shopify-webhook':625 'signatur':17,41,102,107,109,118,121,148,157,166,172,258,266,277,287,289,305,307,315,543 'signaturesmatch':143,145,158,161 'skill':34,568,606 'skill-vercel-webhooks' 'source-hookdeck' 'ssl':445 'start':390,391,503 'status':59,269,310,401,593 'store':632 'str':259 'stripe':608,613 'stripe-webhook':607 'succeed':197,343 'success':399 'switch':184 'task':406 'team':395,411 'test':359,482 'time':139,296 'timing-saf':138,295 'token':472,476 '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' 'transient':603 'tri':144 'trigger':381,402 'true':221,353 'tunnel':505,517 'type':47,96,326,338,379,534 'ui':519 'understand':44 'unhandl':216 'updat':133,400,443 'use':9,32,63,84,122,137,384 'variabl':456 'vercel':2,7,13,28,37,45,89,106,117,164,245,252,257,265,276,304,459,471,474,529,541 'vercel-webhook':1 'verif':18,42,70,544,548 'verifi':6,120,175,317 'via':488,497 'web':518 'webhook':3,8,14,29,38,66,87,131,165,224,242,246,253,460,463,481,525,530,539,552,559,565,586,609,615,618,624,627,633,636,642,644,662,679 'webhook-handler-pattern':551,564,643 'webhook_secret.encode':291 'work':356 'x':105,116,256,264,275,303 'x-vercel-signatur':104,115,274","prices":[{"id":"6d7bcfa4-078a-4e46-a1e9-15edf459ad74","listingId":"469ab269-84c5-4cb2-90b6-cfcc66f3cd3c","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:03.243Z"}],"sources":[{"listingId":"469ab269-84c5-4cb2-90b6-cfcc66f3cd3c","source":"github","sourceId":"hookdeck/webhook-skills/vercel-webhooks","sourceUrl":"https://github.com/hookdeck/webhook-skills/tree/main/skills/vercel-webhooks","isPrimary":false,"firstSeenAt":"2026-04-18T22:14:03.243Z","lastSeenAt":"2026-05-02T06:55:47.948Z"}],"details":{"listingId":"469ab269-84c5-4cb2-90b6-cfcc66f3cd3c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"hookdeck","slug":"vercel-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":"ebac6640e00faa87a2f163c3600093f225ac7aff","skill_md_path":"skills/vercel-webhooks/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/hookdeck/webhook-skills/tree/main/skills/vercel-webhooks"},"layout":"multi","source":"github","category":"webhook-skills","frontmatter":{"name":"vercel-webhooks","license":"MIT","description":"Receive and verify Vercel webhooks. Use when setting up Vercel webhook handlers, debugging signature verification, or handling deployment events like deployment.created, deployment.succeeded, or project.created."},"skills_sh_url":"https://skills.sh/hookdeck/webhook-skills/vercel-webhooks"},"updatedAt":"2026-05-02T06:55:47.948Z"}}