{"id":"60135bcd-e18b-4796-bffd-5aac524bfa4b","shortId":"sx9ka7","kind":"skill","title":"cursor-webhooks","tagline":"Receive and verify Cursor Cloud Agent webhooks. Use when setting up Cursor webhook handlers, debugging signature verification, or handling Cloud Agent status change events (ERROR, FINISHED).","description":"# Cursor Webhooks\n\n## When to Use This Skill\n\n- Setting up Cursor Cloud Agent webhook handlers\n- Debugging signature verification failures\n- Understanding Cursor webhook event types and payloads\n- Handling Cloud Agent status change events (ERROR, FINISHED)\n\n## Essential Code (USE THIS)\n\n### Cursor Signature Verification (JavaScript)\n\n```javascript\nconst crypto = require('crypto');\n\nfunction verifyCursorWebhook(rawBody, signatureHeader, secret) {\n  if (!signatureHeader || !secret) return false;\n\n  // Cursor sends: sha256=xxxx\n  const [algorithm, signature] = signatureHeader.split('=');\n  if (algorithm !== 'sha256') return false;\n\n  const expected = crypto\n    .createHmac('sha256', secret)\n    .update(rawBody)\n    .digest('hex');\n\n  try {\n    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));\n  } catch {\n    return false;\n  }\n}\n```\n\n### Express Webhook Handler\n\n```javascript\nconst express = require('express');\nconst app = express();\n\n// CRITICAL: Use express.raw() - Cursor requires raw body for signature verification\napp.post('/webhooks/cursor',\n  express.raw({ type: 'application/json' }),\n  (req, res) => {\n    const signature = req.headers['x-webhook-signature'];\n    const webhookId = req.headers['x-webhook-id'];\n    const event = req.headers['x-webhook-event'];\n\n    // Verify signature\n    if (!verifyCursorWebhook(req.body, signature, process.env.CURSOR_WEBHOOK_SECRET)) {\n      console.error('Cursor signature verification failed');\n      return res.status(401).send('Invalid signature');\n    }\n\n    // Parse payload after verification\n    const payload = JSON.parse(req.body.toString());\n\n    console.log(`Received ${event} (id: ${webhookId})`);\n\n    // Handle status changes\n    if (event === 'statusChange') {\n      console.log(`Agent ${payload.id} status: ${payload.status}`);\n\n      if (payload.status === 'FINISHED') {\n        console.log(`Summary: ${payload.summary}`);\n      } else if (payload.status === 'ERROR') {\n        console.error(`Agent error for ${payload.id}`);\n      }\n    }\n\n    res.json({ received: true });\n  }\n);\n```\n\n### Python Signature Verification (FastAPI)\n\n```python\nimport hmac\nimport hashlib\nfrom fastapi import Request, HTTPException\n\ndef verify_cursor_webhook(body: bytes, signature_header: str, secret: str) -> bool:\n    if not signature_header or not secret:\n        return False\n\n    # Cursor sends: sha256=xxxx\n    parts = signature_header.split('=')\n    if len(parts) != 2 or parts[0] != 'sha256':\n        return False\n\n    signature = parts[1]\n    expected = hmac.new(\n        secret.encode(),\n        body,\n        hashlib.sha256\n    ).hexdigest()\n\n    # Timing-safe comparison\n    return hmac.compare_digest(signature, expected)\n```\n\n## Common Event Types\n\n| Event Type | Description | Common Use Cases |\n|------------|-------------|------------------|\n| `statusChange` | Agent status changed | Monitor agent completion, handle errors |\n\n### Event Payload Structure\n\n```json\n{\n  \"event\": \"statusChange\",\n  \"timestamp\": \"2024-01-01T12:00:00.000Z\",\n  \"id\": \"agent_123456\",\n  \"status\": \"FINISHED\",  // or \"ERROR\"\n  \"source\": {\n    \"repository\": \"https://github.com/user/repo\",\n    \"ref\": \"main\"\n  },\n  \"target\": {\n    \"url\": \"https://github.com/user/repo/pull/123\",\n    \"branchName\": \"feature-branch\",\n    \"prUrl\": \"https://github.com/user/repo/pull/123\"\n  },\n  \"summary\": \"Updated 3 files and fixed linting errors\"\n}\n```\n\n## Environment Variables\n\n```bash\n# Your Cursor webhook signing secret\nCURSOR_WEBHOOK_SECRET=your_webhook_secret_here\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/cursor\n```\n\nNo account required. Provides local tunnel + web UI for inspecting requests.\n\n## Resources\n\n- `overview.md` - What Cursor webhooks are, event types\n- `setup.md` - Configure webhooks in Cursor dashboard\n- `verification.md` - Signature verification details and gotchas\n- `examples/` - Runnable examples per framework\n\n## Recommended: webhook-handler-patterns\n\nFor production-ready webhook handling, also use the webhook-handler-patterns skill:\n\n- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)\n- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)\n- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)\n- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)\n\n## Related Skills\n\n- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe webhook handling\n- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub webhook handling\n- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify webhook handling\n- [openai-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks) - OpenAI webhook handling\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":["cursor","webhooks","webhook","skills","hookdeck","agent-skills","ai-coding","api-integrations","event-driven","github-webhooks","llm-tools","shopify-webhooks"],"capabilities":["skill","source-hookdeck","skill-cursor-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/cursor-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 (5,719 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:46.147Z","embedding":null,"createdAt":"2026-04-18T22:13:49.934Z","updatedAt":"2026-05-02T06:55:46.147Z","lastSeenAt":"2026-05-02T06:55:46.147Z","tsv":"'-01':325,326 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':484 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':475 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':479 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':489 '/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':506 '/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':546 '/hookdeck/webhook-skills/tree/main/skills/openai-webhooks)':524 '/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':515 '/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':497 '/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':534 '/user/repo':342 '/user/repo/pull/123':349,357 '/webhooks/cursor':141,415 '0':277 '00':328 '00.000':329 '1':283 '123456':333 '2':274 '2024':324 '3':360 '3000':413 '401':184 'account':417 'agent':9,24,41,57,208,223,309,313,332 'algorithm':91,95 'also':463 'app':128 'app.post':140 'application/json':144 'automat':555 'bash':368,390,410 'bodi':136,248,287 'bool':255 'branch':353 'branchnam':350 'brew':403 'buffer.from':112,114 'byte':249 'case':307 'catch':116 'chang':26,59,203,311 'cli':389,399 'cloud':8,23,40,56 'code':64 'common':299,305 'comparison':293 'complet':314 'configur':436 'console.error':177,222 'console.log':196,207,215 'const':72,90,99,123,127,147,154,161,192 'createhmac':102 'critic':130 'crypto':73,75,101 'crypto.timingsafeequal':111 'cursor':2,7,15,30,39,49,67,86,133,178,246,265,370,374,430,439 'cursor-webhook':1 'dashboard':440 'debug':18,44 'def':244 'deliveri':554 'descript':304 'detail':444 'develop':382 'digest':107,296 'els':218 'environ':366 'error':28,61,221,224,316,337,365,480,536 'essenti':63 'event':27,51,60,162,167,198,205,300,302,317,321,433,542 'exampl':447,449 'expect':100,115,284,298 'express':119,124,126,129 'express.raw':132,142 'fail':181 'failur':47 'fals':85,98,118,264,280 'fastapi':233,240 'featur':352 'feature-branch':351 'file':361 'finish':29,62,214,335 'fix':363 'framework':451 'function':76 'g':396 'gateway':543 'github':502,507 'github-webhook':501 'github.com':341,348,356,474,478,483,488,496,505,514,523,533,545 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':482 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':473 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':477 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':487 'github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':504 'github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':544 'github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks)':522 'github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':513 'github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':495 'github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':532 'github.com/user/repo':340 'github.com/user/repo/pull/123':347,355 'gotcha':446 'guarante':553 'handl':22,55,201,315,462,481,500,509,518,527,537 'handler':17,43,121,455,468,471,530,565 'hashlib':238 'hashlib.sha256':288 'header':251,259 'hex':108 'hexdigest':289 'hmac':236 'hmac.compare':295 'hmac.new':285 'homebrew':402 'hookdeck':388,398,411,541 'hookdeck-c':397 'hookdeck-event-gateway':540 'hookdeck/hookdeck/hookdeck':405 'httpexcept':243 'id':160,199,331 'idempot':476,535 'import':235,237,241 'infrastructur':548 'inspect':425 'instal':387,391,395,404 'invalid':186 'javascript':70,71,122 'json':320 'json.parse':194 'len':272 'limit':559 'lint':364 'listen':412 'local':381,384,420 'logic':486,539 'main':344 'monitor':312 'npm':393,394 'observ':561 'openai':520,525 'openai-webhook':519 'overview.md':428 'pars':188 'part':269,273,276,282 'path':414 'pattern':456,469,531 'payload':54,189,193,318 'payload.id':209,226 'payload.status':211,213,220 'payload.summary':217 'per':450 'process.env.cursor':174 'product':459 'production-readi':458 'provid':419 'prurl':354 'python':230,234 'queue':552 'rate':558 'raw':135 'rawbodi':78,106 'readi':460 'receiv':4,197,228 'recommend':452 'ref':343 'relat':490 'replac':550 'replay':557 'repositori':339 'req':145 'req.body':172 'req.body.tostring':195 'req.headers':149,156,163 'request':242,426 'requir':74,125,134,418 'res':146 'res.json':227 'res.status':183 'resourc':427 'retri':485,538,556 'return':84,97,110,117,182,263,279,294 'runnabl':448 'safe':292 'secret':80,83,104,176,253,262,373,376,379 'secret.encode':286 'send':87,185,266 'sequenc':472 'set':13,37 'setup.md':435 'sha256':88,96,103,267,278 'shopifi':511,516 'shopify-webhook':510 'sign':372 'signatur':19,45,68,92,113,138,148,153,169,173,179,187,231,250,258,281,297,442 'signature_header.split':270 'signaturehead':79,82 'signatureheader.split':93 'skill':36,470,491 'skill-cursor-webhooks' 'sourc':338 'source-hookdeck' 'start':407 'status':25,58,202,210,310,334 'statuschang':206,308,322 'str':252,254 'stripe':493,498 'stripe-webhook':492 'structur':319 'summari':216,358 't12':327 'target':345 'test':386 'time':291 'timestamp':323 'timing-saf':290 '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' 'tri':109 'true':229 'tunnel':409,421 'type':52,143,301,303,434 'ui':423 'understand':48 'updat':105,359 'url':346 'use':11,34,65,131,306,464 'variabl':367 'verif':20,46,69,139,180,191,232,443 'verifi':6,168,245 'verification.md':441 'verifycursorwebhook':77,171 'via':392,401 'web':422 'webhook':3,10,16,31,42,50,120,152,159,166,175,247,371,375,378,385,431,437,454,461,467,494,499,503,508,512,517,521,526,529,547,564 'webhook-handler-pattern':453,466,528 'webhookid':155,200 'x':151,158,165 'x-webhook-ev':164 'x-webhook-id':157 'x-webhook-signatur':150 'xxxx':89,268 'z':330","prices":[{"id":"55a2ed61-b65b-45c6-8e51-45310cb7244a","listingId":"60135bcd-e18b-4796-bffd-5aac524bfa4b","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:13:49.934Z"}],"sources":[{"listingId":"60135bcd-e18b-4796-bffd-5aac524bfa4b","source":"github","sourceId":"hookdeck/webhook-skills/cursor-webhooks","sourceUrl":"https://github.com/hookdeck/webhook-skills/tree/main/skills/cursor-webhooks","isPrimary":false,"firstSeenAt":"2026-04-18T22:13:49.934Z","lastSeenAt":"2026-05-02T06:55:46.147Z"}],"details":{"listingId":"60135bcd-e18b-4796-bffd-5aac524bfa4b","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"hookdeck","slug":"cursor-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":"1c7fe574844fccdf549aebe1ae2639e26f9964c6","skill_md_path":"skills/cursor-webhooks/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/hookdeck/webhook-skills/tree/main/skills/cursor-webhooks"},"layout":"multi","source":"github","category":"webhook-skills","frontmatter":{"name":"cursor-webhooks","license":"MIT","description":"Receive and verify Cursor Cloud Agent webhooks. Use when setting up Cursor webhook handlers, debugging signature verification, or handling Cloud Agent status change events (ERROR, FINISHED)."},"skills_sh_url":"https://skills.sh/hookdeck/webhook-skills/cursor-webhooks"},"updatedAt":"2026-05-02T06:55:46.147Z"}}