Gareth Wilson Gareth Wilson

Guide to Cursor Webhooks: Features and Best Practices

Published


Cursor webhooks notify your application when a Cloud Agent finishes or errors. If you kick off background coding agents and need to react when they complete, open a pull request, or fail, webhooks are how you learn about it without polling.

This guide covers how Cursor Cloud Agent webhooks work, the statusChange event, how to verify the X-Webhook-Signature, and the best practices for production.

What are Cursor webhooks?

Cursor Cloud Agent webhooks are JSON POSTs delivered to a URL you configure. Each is signed with an X-Webhook-Signature header formatted sha256=<hex>: an HMAC-SHA256 over the raw request body. Cursor also sends an X-Webhook-Id (a unique delivery ID) and an X-Webhook-Event (the event type, statusChange).

Cursor webhook features

FeatureDetails
ConfigurationCursor dashboard > Cloud Agent settings > Webhooks
Signature headerX-Webhook-Signature, formatted sha256=<hex>
Signature schemeHMAC-SHA256 (hex) over the raw body, keyed with the webhook secret
Extra headersX-Webhook-Id (delivery ID), X-Webhook-Event (statusChange)
Payloadevent, timestamp, id, status, source, target, summary
SDKNone; verify manually

Common events

Cursor has a single event type, statusChange, whose status field carries the outcome:

statusFires when
FINISHEDThe agent completed successfully
ERRORThe agent encountered an error

Branch on the status field, not on separate event names, and read target.prUrl / target.branchName when an agent opens a pull request.

Setting up Cursor webhooks

In the Cursor dashboard > Cloud Agent settings > Webhooks, add your endpoint URL, select the statusChange event, and copy the webhook signing secret into CURSOR_WEBHOOK_SECRET. On setup, Cursor sends a test statusChange event, verify it and return 200.

Securing Cursor webhooks

Split the X-Webhook-Signature on =, confirm the algorithm is sha256, compute an HMAC-SHA256 over the raw body, hex-encode it, and compare in constant time. Verify against the raw body before parsing.

const crypto = require("crypto");

const SECRET = process.env.CURSOR_WEBHOOK_SECRET;

function verify(rawBody, signatureHeader) {
  if (!signatureHeader) return false;
  const parts = signatureHeader.split("=");
  if (parts.length !== 2 || parts[0] !== "sha256") return false;

  const expected = crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
  const a = Buffer.from(parts[1]);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhooks/cursor", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-webhook-signature"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  const payload = JSON.parse(req.body.toString());
  processQueue.add(payload); // branch on payload.status, dedupe on X-Webhook-Id, async
});

The same check in Python:

import hashlib
import hmac
import os

SECRET = os.environ["CURSOR_WEBHOOK_SECRET"].encode()

def verify(raw_body: bytes, signature_header: str) -> bool:
    if not signature_header:
        return False
    parts = signature_header.split("=")
    if len(parts) != 2 or parts[0] != "sha256":
        return False
    expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(parts[1], expected)

Cursor webhook limitations and pain points

One event type, status in the payload

The Problem: There's only statusChange. Code that expects distinct FINISHED or ERROR events never fires, because those are status field values.

Why It Happens: Cursor models outcomes as a status field on a single event.

Workarounds:

  • Branch on payload.status (FINISHED / ERROR), and read target for PR details.

How Hookdeck Can Help: Hookdeck's filters can route on the status field, giving you outcome-based routing.

Verify over the raw body

The Problem: The HMAC is over the exact bytes Cursor sent. Parsing and re-serializing the JSON changes the bytes and breaks verification.

Why It Happens: JSON re-serialization reorders keys and whitespace.

Workarounds:

  • Capture the raw body (express.raw) and verify before parsing.

How Hookdeck Can Help: Hookdeck verifies against the received bytes at the edge.

Dedupe on X-Webhook-Id

The Problem: Retried deliveries repeat the same event, and the signature proves authenticity, not uniqueness.

Why It Happens: At-least-once delivery.

Workarounds:

  • Dedupe on the X-Webhook-Id delivery ID and make handlers idempotent.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge. See our guide to webhook idempotency.

A single, fresh topic

The Problem: Cloud Agent webhooks are new, so patterns and tooling are still thin, and there's no official SDK verification helper.

Why It Happens: It's a recent feature.

Workarounds:

  • Verify manually with the HMAC-SHA256 check above.

How Hookdeck Can Help: Hookdeck gives you verification, observability, and retries at the edge, so a young webhook source is production-ready without waiting for an SDK.

Best practices

Verify HMAC-SHA256 over the raw body

Confirm the sha256= prefix, compute the hex HMAC over the raw body, and compare in constant time.

Branch on the status field

Handle FINISHED and ERROR from payload.status, and read target for PR and branch details.

Dedupe on X-Webhook-Id

Persist the delivery ID and skip repeats.

Acknowledge fast, process asynchronously

Return 200 quickly and defer work to a queue. See why to process webhooks asynchronously.

Make Cursor webhooks production-ready

Hookdeck verifies X-Webhook-Signature, deduplicates, and durably queues every Cloud Agent event

Conclusion

Cursor Cloud Agent webhooks are verified with an X-Webhook-Signature HMAC-SHA256 over the raw body, formatted sha256=<hex>, alongside X-Webhook-Id and X-Webhook-Event headers. There's one event, statusChange, whose status is FINISHED or ERROR. Verify over the raw body, branch on the status, and dedupe on the delivery ID.

Hookdeck verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique events.

Get started with Hookdeck for free and handle Cursor webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.