cursor-origin-webhooks
v0.2.1
Published
Verify and type Cursor Origin webhooks in WebCrypto runtimes.
Downloads
217
Maintainers
Readme
cursor-origin-webhooks
cursor-origin-webhooks verifies Cursor Origin webhook requests and returns a
typed event payload. It uses Web Crypto and works in Cloudflare Workers, Deno,
Bun, and Node.js.
The library handles the details that are easy to get wrong:
- verifies the signature against Cursor's active Origin signing keys;
- signs the exact raw request bytes before parsing JSON;
- enforces the five-minute timestamp window;
- caches imported JWKS keys inside each runtime isolate or process;
- refreshes the cache for signing-key rotation;
- rejects oversized or malformed requests with typed errors; and
- maps every documented Origin event to its OpenAPI payload type.
Install
npm install cursor-origin-webhooksUse it
Pass the incoming Request to verifyWebhook. It returns only after the
request has been authenticated and its delivery envelope has been validated.
import {
WebhookVerificationError,
verifyWebhook,
} from "cursor-origin-webhooks";
export default {
async fetch(request: Request): Promise<Response> {
try {
const webhook = await verifyWebhook(request);
switch (webhook.event.type) {
case "repository.pushed":
console.log(webhook.event.payload.refUpdates);
break;
case "pull_request.created":
console.log(webhook.event.payload.pullRequest);
break;
}
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof WebhookVerificationError) {
return Response.json(
{ error: error.code },
{ status: error.statusCode },
);
}
throw error;
}
},
};Cursor retries transport failures, 429, and 5xx responses. Return a 2xx
quickly after durable acceptance if your handler hands work to a queue or
workflow.
Deno
Deno can consume the npm package directly:
import { verifyWebhook } from "npm:cursor-origin-webhooks";
const webhook = await verifyWebhook(request);Configure an Origin App
- Open Cursor's Origin app settings and create an app.
- Set its webhook URL to your public HTTPS endpoint.
- Select the events to send, then install the app for the relevant Origin repositories.
See Cursor's Origin webhook documentation for the event list and delivery details.
Options
await verifyWebhook(request, {
maxBodyBytes: 1024 * 1024,
timestampToleranceSeconds: 300,
});| Option | Default | Purpose |
| --- | ---: | --- |
| maxBodyBytes | 1048576 | Reject the body before unbounded buffering. |
| timestampToleranceSeconds | 300 | Maximum permitted clock difference. |
The JWKS URL, key imports, cache lifetime, and refresh behavior are internal. Applications do not supply a webhook secret or manage signing keys.
Errors
All expected failures extend WebhookVerificationError and include a stable
code and suggested HTTP statusCode.
InvalidWebhookMethodInvalidWebhookContentTypeMissingWebhookHeaderInvalidWebhookTimestampWebhookBodyUnavailableWebhookBodyTooLargeWebhookKeyUnavailableInvalidWebhookSignatureInvalidWebhookJsonInvalidWebhookPayload
WebhookKeyUnavailable uses status 503, allowing Cursor to retry a delivery
when its public signing keys cannot be refreshed. Authentication and payload
errors use terminal 4xx responses.
Signing keys and caching
Cursor publishes Origin's active Ed25519 keys at
https://api.cursor.com/v1/origin/keys.
The discovery document is available at
https://api.cursor.com/v1/origin/.well-known/openid-configuration.
The cache is module-scoped. A warm Worker isolate or Deno process reuses the
same imported CryptoKey objects. Cold isolates fetch independently. The
library honors the JWKS response's Cache-Control directives and performs one
rate-limited refresh after a signature miss to handle key rotation.
Expired keys are not used when a refresh fails.
Payload types
OriginWebhook is a discriminated union keyed by webhook.event.type.
OriginWebhookByType provides direct lookup when an application already knows
the event:
import type { OriginWebhookByType } from "cursor-origin-webhooks";
type PushWebhook = OriginWebhookByType["repository.pushed"];The nested payload types are generated from a checked-in snapshot of Cursor's Origin OpenAPI specification. Unknown JSON fields are retained for forward compatibility, but unknown event names are rejected until their payload mapping is added to the library.
Cloudflare Worker example
examples/cloudflare-workers/basic
contains a deployable Worker that verifies a request and logs the authenticated
payload.
npm run build
cd examples/cloudflare-workers/basic
npm install
npm run deployNo secret binding is required.
Development
npm install
npm run generate:types
npm run typecheck
npm test
npm run test:workers
npm run test:deno
npm run test:coverage
npm run validate:package
npm run validate:exampleThe schema snapshot lives at schema/origin-openapi.yaml. Regenerate the
webhook-only type subset after updating that snapshot.
License
Apache-2.0
