{"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```\n\nThen start the tunnel:\n\n```bash\nnpx hookdeck-cli listen 3000 cursor --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.485","qualityRationale":"deterministic score 0.48 from registry signals: · indexed on github topic:agent-skills · 71 github stars · SKILL.md body (5,629 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:52.591Z","embedding":null,"createdAt":"2026-04-18T22:13:49.934Z","updatedAt":"2026-05-18T18:56:52.591Z","lastSeenAt":"2026-05-18T18:56:52.591Z","tsv":"'-01':325,326 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':473 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':464 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':468 '/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':478 '/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':495 '/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':535 '/hookdeck/webhook-skills/tree/main/skills/openai-webhooks)':513 '/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':504 '/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':486 '/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':523 '/user/repo':342 '/user/repo/pull/123':349,357 '/webhooks/cursor':141,404 '0':277 '00':328 '00.000':329 '1':283 '123456':333 '2':274 '2024':324 '3':360 '3000':401 '401':184 'account':406 'agent':9,24,41,57,208,223,309,313,332 'algorithm':91,95 'also':452 'app':128 'app.post':140 'application/json':144 'automat':544 'bash':368,390,395 'bodi':136,248,287 'bool':255 'branch':353 'branchnam':350 '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':425 '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,402,419,428 'cursor-webhook':1 'dashboard':429 'debug':18,44 'def':244 'deliveri':543 'descript':304 'detail':433 'develop':382 'digest':107,296 'els':218 'environ':366 'error':28,61,221,224,316,337,365,469,525 'essenti':63 'event':27,51,60,162,167,198,205,300,302,317,321,422,531 'exampl':436,438 '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':440 'function':76 'gateway':532 'github':491,496 'github-webhook':490 'github.com':341,348,356,463,467,472,477,485,494,503,512,522,534 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)':471 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)':462 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)':466 'github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)':476 'github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks)':493 'github.com/hookdeck/webhook-skills/tree/main/skills/hookdeck-event-gateway)':533 'github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks)':511 'github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks)':502 'github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks)':484 'github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns)':521 'github.com/user/repo':340 'github.com/user/repo/pull/123':347,355 'gotcha':435 'guarante':542 'handl':22,55,201,315,451,470,489,498,507,516,526 'handler':17,43,121,444,457,460,519,554 'hashlib':238 'hashlib.sha256':288 'header':251,259 'hex':108 'hexdigest':289 'hmac':236 'hmac.compare':295 'hmac.new':285 'hookdeck':388,398,530 'hookdeck-c':397 'hookdeck-event-gateway':529 'httpexcept':243 'id':160,199,331 'idempot':465,524 'import':235,237,241 'infrastructur':537 'inspect':414 'instal':387 'invalid':186 'javascript':70,71,122 'json':320 'json.parse':194 'len':272 'limit':548 'lint':364 'listen':400 'local':381,384,409 'logic':475,528 'main':344 'monitor':312 'npx':396 'observ':550 'openai':509,514 'openai-webhook':508 'overview.md':417 'pars':188 'part':269,273,276,282 'path':403 'pattern':445,458,520 'payload':54,189,193,318 'payload.id':209,226 'payload.status':211,213,220 'payload.summary':217 'per':439 'process.env.cursor':174 'product':448 'production-readi':447 'provid':408 'prurl':354 'python':230,234 'queue':541 'rate':547 'raw':135 'rawbodi':78,106 'readi':449 'receiv':4,197,228 'recommend':441 'ref':343 'relat':479 'replac':539 'replay':546 'repositori':339 'req':145 'req.body':172 'req.body.tostring':195 'req.headers':149,156,163 'request':242,415 'requir':74,125,134,407 'res':146 'res.json':227 'res.status':183 'resourc':416 'retri':474,527,545 'return':84,97,110,117,182,263,279,294 'runnabl':437 'safe':292 'secret':80,83,104,176,253,262,373,376,379 'secret.encode':286 'send':87,185,266 'sequenc':461 'set':13,37 'setup.md':424 'sha256':88,96,103,267,278 'shopifi':500,505 'shopify-webhook':499 'sign':372 'signatur':19,45,68,92,113,138,148,153,169,173,179,187,231,250,258,281,297,431 'signature_header.split':270 'signaturehead':79,82 'signatureheader.split':93 'skill':36,459,480 'skill-cursor-webhooks' 'sourc':338 'source-hookdeck' 'start':392 'status':25,58,202,210,310,334 'statuschang':206,308,322 'str':252,254 'stripe':482,487 'stripe-webhook':481 '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':394,410 'type':52,143,301,303,423 'ui':412 'understand':48 'updat':105,359 'url':346 'use':11,34,65,131,306,453 'variabl':367 'verif':20,46,69,139,180,191,232,432 'verifi':6,168,245 'verification.md':430 'verifycursorwebhook':77,171 'web':411 'webhook':3,10,16,31,42,50,120,152,159,166,175,247,371,375,378,385,420,426,443,450,456,483,488,492,497,501,506,510,515,518,536,553 'webhook-handler-pattern':442,455,517 '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-18T18:56:52.591Z"},{"listingId":"60135bcd-e18b-4796-bffd-5aac524bfa4b","source":"skills_sh","sourceId":"hookdeck/webhook-skills/cursor-webhooks","sourceUrl":"https://skills.sh/hookdeck/webhook-skills/cursor-webhooks","isPrimary":true,"firstSeenAt":"2026-05-07T20:44:50.226Z","lastSeenAt":"2026-05-07T22:43:02.150Z"}],"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":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":"818496fc04784d0177b9d6a2b22d4ff74f36511a","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-18T18:56:52.591Z"}}