Back to skills
SkillHub ClubShip Full StackFull StackTestingIntegration

cursor-webhooks

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).

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
63
Hot score
92
Updated
March 20, 2026
Overall rating
C3.0
Composite score
3.0
Best-practice grade
S96.0

Install command

npx @skill-hub/cli install hookdeck-webhook-skills-cursor-webhooks

Repository

hookdeck/webhook-skills

Skill path: skills/cursor-webhooks

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).

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Testing, Integration.

Target audience: everyone.

License: MIT.

Original source

Catalog source: SkillHub Club.

Repository owner: hookdeck.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install cursor-webhooks into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/hookdeck/webhook-skills before adding cursor-webhooks to shared team environments
  • Use cursor-webhooks for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: cursor-webhooks
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).
license: MIT
metadata:
  author: hookdeck
  version: "0.1.0"
  repository: https://github.com/hookdeck/webhook-skills
---

# Cursor Webhooks

## When to Use This Skill

- Setting up Cursor Cloud Agent webhook handlers
- Debugging signature verification failures
- Understanding Cursor webhook event types and payloads
- Handling Cloud Agent status change events (ERROR, FINISHED)

## Essential Code (USE THIS)

### Cursor Signature Verification (JavaScript)

```javascript
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;
  }
}
```

### Express Webhook Handler

```javascript
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 });
  }
);
```

### Python Signature Verification (FastAPI)

```python
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)
```

## Common Event Types

| Event Type | Description | Common Use Cases |
|------------|-------------|------------------|
| `statusChange` | Agent status changed | Monitor agent completion, handle errors |

### Event Payload Structure

```json
{
  "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"
}
```

## Environment Variables

```bash
# Your Cursor webhook signing secret
CURSOR_WEBHOOK_SECRET=your_webhook_secret_here
```

## Local Development

For local webhook testing, install Hookdeck CLI:

```bash
# Install via npm
npm install -g hookdeck-cli

# Or via Homebrew
brew install hookdeck/hookdeck/hookdeck
```

Then start the tunnel:

```bash
hookdeck listen 3000 --path /webhooks/cursor
```

No account required. Provides local tunnel + web UI for inspecting requests.

## Resources

- `overview.md` - What Cursor webhooks are, event types
- `setup.md` - Configure webhooks in Cursor dashboard
- `verification.md` - Signature verification details and gotchas
- `examples/` - Runnable examples per framework

## Recommended: webhook-handler-patterns

For production-ready webhook handling, also use the webhook-handler-patterns skill:

- [Handler sequence](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/handler-sequence.md)
- [Idempotency](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/idempotency.md)
- [Error handling](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/error-handling.md)
- [Retry logic](https://github.com/hookdeck/webhook-skills/blob/main/skills/webhook-handler-patterns/references/retry-logic.md)

## Related Skills

- [stripe-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/stripe-webhooks) - Stripe webhook handling
- [github-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/github-webhooks) - GitHub webhook handling
- [shopify-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/shopify-webhooks) - Shopify webhook handling
- [openai-webhooks](https://github.com/hookdeck/webhook-skills/tree/main/skills/openai-webhooks) - OpenAI webhook handling
- [webhook-handler-patterns](https://github.com/hookdeck/webhook-skills/tree/main/skills/webhook-handler-patterns) - Idempotency, error handling, retry logic
- [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

---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### references/overview.md

```markdown
# Cursor Webhooks Overview

## What Are Cursor Webhooks?

Cursor Cloud Agent webhooks are HTTP callbacks that notify your application when agent status changes occur. These webhooks enable real-time monitoring of Cloud Agent operations, allowing you to track when agents complete tasks or encounter errors.

## Common Event Types

| Event | Triggered When | Common Use Cases |
|-------|----------------|------------------|
| `statusChange` | Agent status changes to ERROR or FINISHED | Monitor agent completion, handle errors, update UI |

## Event Payload Structure

All Cursor webhooks share a consistent payload structure:

```json
{
  "event": "statusChange",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "id": "agent_123456",
  "status": "FINISHED",
  "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"
}
```

### Status Values

- `FINISHED` - Agent completed successfully
- `ERROR` - Agent encountered an error

## HTTP Headers

Cursor sends these headers with every webhook:

| Header | Description | Example |
|--------|-------------|---------|
| `X-Webhook-Signature` | HMAC-SHA256 signature | `sha256=abc123...` |
| `X-Webhook-ID` | Unique delivery ID | `msg_01234567890` |
| `X-Webhook-Event` | Event type | `statusChange` |
| `User-Agent` | Identifies Cursor webhooks | `Cursor-Agent-Webhook/1.0` |
| `Content-Type` | Payload format | `application/json` |

## Full Event Reference

For the complete list of events and detailed specifications, see [Cursor's webhook documentation](https://cursor.com/docs/cloud-agent/api/webhooks).
```

### references/setup.md

```markdown
# Setting Up Cursor Webhooks

## Prerequisites

- Cursor Cloud Agent access
- Your application's webhook endpoint URL
- Admin access to configure webhooks in your Cursor settings

## Get Your Signing Secret

1. Log in to your Cursor dashboard
2. Navigate to Cloud Agent settings
3. Go to the Webhooks section
4. Copy your webhook signing secret
   - Keep this secret secure
   - Never commit it to version control
   - Rotate it periodically for security

## Register Your Endpoint

1. In the Cursor Cloud Agent settings, click "Add Webhook"
2. Enter your webhook endpoint URL:
   - Production: `https://yourdomain.com/webhooks/cursor`
   - Development: Use Hookdeck CLI tunnel URL
3. Select the events to receive:
   - `statusChange` - Notifies when agent status changes
4. Save the webhook configuration

## Test Your Webhook

1. Cursor will send a test `statusChange` event to verify your endpoint
2. Your endpoint should:
   - Return a 200 status code
   - Verify the signature
   - Process the test payload

## Environment Configuration

Add your signing secret to your environment:

```bash
# .env file
CURSOR_WEBHOOK_SECRET=your_webhook_secret_here
```

## Security Best Practices

- Always verify webhook signatures
- Use HTTPS endpoints only
- Store secrets in environment variables
- Implement request timeouts
- Log webhook events for debugging
- Return 200 quickly, process asynchronously if needed
```

### references/verification.md

```markdown
# Cursor Signature Verification

## How It Works

Cursor uses HMAC-SHA256 to sign webhook payloads. The signature is sent in the `X-Webhook-Signature` header with the format `sha256=<hex_digest>`.

The signature is computed by:
1. Taking the raw request body (before parsing)
2. Creating an HMAC-SHA256 hash using your webhook secret
3. Encoding the result as hexadecimal
4. Prefixing with `sha256=`

## Implementation

### Manual Verification (Recommended)

```javascript
const crypto = require('crypto');

function verifyCursorWebhook(rawBody, signatureHeader, secret) {
  if (!signatureHeader || !secret) {
    return false;
  }

  // Extract algorithm and signature
  const [algorithm, signature] = signatureHeader.split('=');
  if (algorithm !== 'sha256') {
    return false;
  }

  // Calculate expected signature
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  // Timing-safe comparison
  try {
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected)
    );
  } catch {
    return false;  // Different lengths
  }
}
```

### Python Implementation

```python
import hmac
import hashlib

def verify_cursor_webhook(body: bytes, signature_header: str, secret: str) -> bool:
    if not signature_header or not secret:
        return False

    # Extract algorithm and signature
    parts = signature_header.split('=')
    if len(parts) != 2 or parts[0] != 'sha256':
        return False

    signature = parts[1]

    # Calculate expected signature
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()

    # Timing-safe comparison
    return hmac.compare_digest(signature, expected)
```

## Common Gotchas

### 1. Raw Body Parsing

**Problem**: Using parsed JSON instead of raw body breaks signature verification.

**Solution**: Always use the raw request body:
```javascript
// Express
app.use('/webhooks/cursor', express.raw({ type: 'application/json' }));

// Next.js
export const config = { api: { bodyParser: false } };

// FastAPI
body = await request.body()  # Get raw bytes
```

### 2. Header Case Sensitivity

**Problem**: Some frameworks lowercase headers.

**Solution**: Access headers case-insensitively:
```javascript
// Express normalizes to lowercase
const signature = req.headers['x-webhook-signature'];

// FastAPI preserves case
signature = request.headers.get('X-Webhook-Signature')
```

### 3. Missing Timing-Safe Comparison

**Problem**: Using `===` for comparison is vulnerable to timing attacks.

**Solution**: Always use timing-safe comparison:
```javascript
// Good
crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b))

// Bad
signature === expected
```

### 4. Incorrect Secret Format

**Problem**: Using the wrong secret or format.

**Solution**: Use the exact secret from Cursor dashboard, no modifications.

## Debugging Verification Failures

If signature verification fails:

1. **Check the raw body**: Log the exact bytes being signed
2. **Verify the secret**: Ensure no extra whitespace or encoding issues
3. **Check headers**: Log the exact signature header value
4. **Compare signatures**: Log both calculated and received signatures (in development only)

### Debug Helper

```javascript
function debugWebhook(rawBody, signatureHeader, secret) {
  console.log('=== Webhook Debug ===');
  console.log('Body length:', rawBody.length);
  console.log('Body preview:', rawBody.toString().substring(0, 100));
  console.log('Signature header:', signatureHeader);

  const [algorithm, signature] = signatureHeader.split('=');
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');

  console.log('Received sig:', signature);
  console.log('Expected sig:', expected);
  console.log('Match:', signature === expected);
  console.log('===================');
}
```

**Important**: Only use debug logging in development. Never log signatures or secrets in production.
```