Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Skills for receiving and verifying webhooks from specific providers. Each includ
|----------|-------|--------------|
| Chargebee | [`chargebee-webhooks`](skills/chargebee-webhooks/) | Receive and verify Chargebee webhooks (Basic Auth), handle subscription billing events |
| Clerk | [`clerk-webhooks`](skills/clerk-webhooks/) | Verify Clerk webhook signatures, handle user, session, and organization events |
| Cursor | [`cursor-webhooks`](skills/cursor-webhooks/) | Verify Cursor Cloud Agent webhook signatures, handle agent status events |
| Deepgram | [`deepgram-webhooks`](skills/deepgram-webhooks/) | Receive and verify Deepgram transcription callbacks |
| ElevenLabs | [`elevenlabs-webhooks`](skills/elevenlabs-webhooks/) | Verify ElevenLabs webhook signatures, handle call transcription events |
| FusionAuth | [`fusionauth-webhooks`](skills/fusionauth-webhooks/) | Verify FusionAuth JWT webhook signatures, handle user, login, and registration events |
Expand Down
13 changes: 13 additions & 0 deletions providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ providers:
- user.created
- session.created

- name: cursor
displayName: Cursor
docs:
webhooks: https://docs.cursor.com/account/cloud-agent-webhooks
notes: >
AI code editor. Uses x-webhook-signature header with HMAC-SHA256 (hex encoded).
Signature format is sha256=<hex_digest>. Cloud Agent webhooks notify of
agent status changes (ERROR, FINISHED).
testScenario:
events:
- FINISHED
- ERROR

- name: deepgram
displayName: Deepgram
docs:
Expand Down
23 changes: 15 additions & 8 deletions scripts/validate-provider.sh
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,28 @@ validate_integration() {
errors+=("$provider not found in README.md Provider Skills table")
fi

# Check providers.yaml has entry
# Check providers.yaml has entry with testScenario
# test-agent-scenario.sh now reads scenarios dynamically from providers.yaml
if [ -f "$ROOT_DIR/providers.yaml" ]; then
if ! grep -q "name: $provider_name" "$ROOT_DIR/providers.yaml"; then
errors+=("$provider_name not found in providers.yaml")
else
# Check that the provider has a testScenario defined
# Use awk to find the provider block and check for testScenario
local has_test_scenario
has_test_scenario=$(awk -v provider="$provider_name" '
/^ - name:/ { in_provider = ($3 == provider) }
in_provider && /testScenario:/ { print "yes"; exit }
/^ - name:/ && !($3 == provider) { in_provider = 0 }
' "$ROOT_DIR/providers.yaml")
if [ "$has_test_scenario" != "yes" ]; then
errors+=("No testScenario for $provider_name in providers.yaml")
fi
fi
else
errors+=("providers.yaml not found at repository root")
fi

# Check test-agent-scenario.sh has at least one scenario
if ! grep -q "$provider_name" "$ROOT_DIR/scripts/test-agent-scenario.sh"; then
errors+=("No scenario for $provider_name in scripts/test-agent-scenario.sh")
fi

# Return errors
if [ ${#errors[@]} -gt 0 ]; then
printf '%s\n' "${errors[@]}"
Expand Down Expand Up @@ -324,8 +332,7 @@ if [ ${#FAILED_PROVIDERS[@]} -gt 0 ]; then
log "Please ensure you have updated:"
log " 1. All required skill files (SKILL.md, references/, examples/)"
log " 2. README.md - Add provider to Provider Skills table"
log " 3. providers.yaml - Add provider entry with documentation URLs"
log " 4. scripts/test-agent-scenario.sh - Add at least one test scenario"
log " 3. providers.yaml - Add provider entry with documentation URLs and testScenario"
exit 1
else
log "${GREEN}All ${#PASSED_PROVIDERS[@]} provider(s) passed validation!${NC}"
Expand Down
195 changes: 195 additions & 0 deletions skills/cursor-webhooks/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
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) - Production webhook infrastructure
5 changes: 5 additions & 0 deletions skills/cursor-webhooks/examples/express/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Cursor webhook signing secret
CURSOR_WEBHOOK_SECRET=your_webhook_secret_here

# Server port (optional)
PORT=3000
55 changes: 55 additions & 0 deletions skills/cursor-webhooks/examples/express/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Cursor Webhooks - Express Example

Minimal example of receiving Cursor webhooks with signature verification.

## Prerequisites

- Node.js 18+
- Cursor account with webhook signing secret

## Setup

1. Install dependencies:
```bash
npm install
```

2. Copy environment variables:
```bash
cp .env.example .env
```

3. Add your Cursor webhook signing secret to `.env`

## Run

```bash
npm start
```

Server runs on http://localhost:3000

## Test

Run the test suite:

```bash
npm test
```

## Local Development

Use Hookdeck CLI to receive webhooks locally:

```bash
# Install Hookdeck CLI
npm install -g hookdeck-cli

# Forward webhooks to your local server
hookdeck listen 3000 --path /webhooks/cursor
```

## Endpoints

- `POST /webhooks/cursor` - Receives and verifies Cursor webhooks
- `GET /health` - Health check endpoint
20 changes: 20 additions & 0 deletions skills/cursor-webhooks/examples/express/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "cursor-webhooks-express",
"version": "1.0.0",
"description": "Cursor webhook handling example for Express",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "jest"
},
"dependencies": {
"express": "^5.2.1",
"dotenv": "^16.4.7"
},
"devDependencies": {
"jest": "^30.2.0",
"nodemon": "^3.1.9",
"supertest": "^7.0.0"
}
}
Loading