skills/cursor-webhooks/SKILL.md
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).
npx skillsauth add hookdeck/webhook-skills cursor-webhooksInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
const crypto = require('crypto');
function verifyCursorWebhook(rawBody, signatureHeader, secret) {
if (!signatureHeader || !secret) return false;
// Cursor sends: sha256=xxxx
const [algorithm, signature] = signatureHeader.split('=');
if (algorithm !== 'sha256') return false;
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
try {
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
} catch {
return false;
}
}
const express = require('express');
const app = express();
// CRITICAL: Use express.raw() - Cursor requires raw body for signature verification
app.post('/webhooks/cursor',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-webhook-signature'];
const webhookId = req.headers['x-webhook-id'];
const event = req.headers['x-webhook-event'];
// Verify signature
if (!verifyCursorWebhook(req.body, signature, process.env.CURSOR_WEBHOOK_SECRET)) {
console.error('Cursor signature verification failed');
return res.status(401).send('Invalid signature');
}
// Parse payload after verification
const payload = JSON.parse(req.body.toString());
console.log(`Received ${event} (id: ${webhookId})`);
// Handle status changes
if (event === 'statusChange') {
console.log(`Agent ${payload.id} status: ${payload.status}`);
if (payload.status === 'FINISHED') {
console.log(`Summary: ${payload.summary}`);
} else if (payload.status === 'ERROR') {
console.error(`Agent error for ${payload.id}`);
}
}
res.json({ received: true });
}
);
import hmac
import hashlib
from fastapi import Request, HTTPException
def verify_cursor_webhook(body: bytes, signature_header: str, secret: str) -> bool:
if not signature_header or not secret:
return False
# Cursor sends: sha256=xxxx
parts = signature_header.split('=')
if len(parts) != 2 or parts[0] != 'sha256':
return False
signature = parts[1]
expected = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
# Timing-safe comparison
return hmac.compare_digest(signature, expected)
| Event Type | Description | Common Use Cases |
|------------|-------------|------------------|
| statusChange | Agent status changed | Monitor agent completion, handle errors |
{
"event": "statusChange",
"timestamp": "2024-01-01T12:00:00.000Z",
"id": "agent_123456",
"status": "FINISHED", // or "ERROR"
"source": {
"repository": "https://github.com/user/repo",
"ref": "main"
},
"target": {
"url": "https://github.com/user/repo/pull/123",
"branchName": "feature-branch",
"prUrl": "https://github.com/user/repo/pull/123"
},
"summary": "Updated 3 files and fixed linting errors"
}
# Your Cursor webhook signing secret
CURSOR_WEBHOOK_SECRET=your_webhook_secret_here
For local webhook testing, install Hookdeck CLI:
Then start the tunnel:
npx hookdeck-cli listen 3000 cursor --path /webhooks/cursor
No account required. Provides local tunnel + web UI for inspecting requests.
overview.md - What Cursor webhooks are, event typessetup.md - Configure webhooks in Cursor dashboardverification.md - Signature verification details and gotchasexamples/ - Runnable examples per frameworkFor production-ready webhook handling, also use the webhook-handler-patterns skill:
development
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.
development
Receive and verify Twilio webhooks. Use when setting up Twilio webhook handlers, debugging X-Twilio-Signature verification, or handling communications events like incoming SMS, voice calls, message status callbacks (delivered, failed), or recording status callbacks.
development
Receive and verify Stripe webhooks. Use when setting up Stripe webhook handlers, debugging signature verification, or handling payment events like payment_intent.succeeded, customer.subscription.created, or invoice.paid.
development
Receive and verify Slack Events API webhooks. Use when setting up Slack webhook handlers, debugging Slack signature verification, handling the url_verification challenge, or processing events like app_mention, message, reaction_added, team_join, or app_home_opened.