Cursor Origin
The Cursor Origin provider covers pull requests on Origin, including stacked pull requests, with their comments, reviews and check runs. It also covers repository contents and Origin App installations. Origin has no issues, discussions, releases or notifications.
| Detail | Value |
|---|---|
| Import | forges/cursor-origin |
| Factories | cursorOrigin(), cursorOriginLite() |
| Auth | token, app |
v1alpha1 (ORIGIN_API_VERSION), which can change. See Capabilities.Authentication
The provider accepts a user access token or Origin App credentials:
import { cursorOrigin } from 'forges/cursor-origin'
const forge = cursorOrigin({
auth: { type: 'token', token: process.env.CURSOR_AUTH_TOKEN! },
}).create()
const app = cursorOrigin({
auth: {
type: 'app',
appId: 'app_01...',
privateKey: process.env.ORIGIN_APP_PRIVATE_KEY!,
installationId: 'i_01...',
},
}).create()
A user access token is the value that the Origin CLI reads from CURSOR_AUTH_TOKEN. Personal Cursor API keys aren't Origin bearer tokens, and the provider doesn't accept them.
For app auth, privateKey is the app's Ed25519 private key in PKCS#8 PEM format. The provider signs an app JWT with it. With installationId, the provider mints installation tokens as it needs them. Without installationId, the provider authenticates as the app itself. In that case, installations.list() lists the app's installations, and installations.provider(installation) returns a provider for one installation.
Origin has no anonymous access, so auth is required.
Instance
The instance is origin.cursor.com, which is the host in clone URLs. baseUrl defaults to https://api.cursor.com, and the provider appends /v1/origin.
Stacked pull requests
For a pull request in a stack, thread.stack contains the stack ID and the parent pull request.
Merging
Origin supports the merge and squash methods only. If you don't pass method, merge() reads the repository settings. When exactly one method is allowed, it uses that method. When both are allowed, it throws MergeMethodRequiredError. whenChecksPass and message are unavailable.
Review comments
A comment anchored to a line in the diff is a review comment. Review comments appear in threads.events() as review_comment events, and threads.comments() doesn't return them. Origin doesn't link inline comments to the review that submitted them, so each review has comments: false.
Webhooks
Origin sends webhooks in the Standard Webhooks format, with these headers:
| Header | Content |
|---|---|
webhook-id | The delivery ID |
webhook-timestamp | The time of the delivery, in seconds |
webhook-signature | An Ed25519 signature over the hex SHA-256 of <webhook-id>.<webhook-timestamp>.<body> |
The provider verifies the signature against the public keys from the /keys endpoint, which it caches for ten minutes. It doesn't use webhookSecret. It rejects deliveries with a timestamp more than five minutes from the current time.
const forge = cursorOrigin({ auth }).create()
const events = await forge.webhooks.ingest({
headers: request.headers,
body: await request.text(),
})
installation.* deliveries become installation events. You register hooks in the app definition, so the webhooks management methods are unavailable.
Recording fixtures
CURSOR_AUTH_TOKEN=... FIXTURE_CURSOR_ORIGIN_REPO=owner/repo \
FIXTURE_CURSOR_ORIGIN_PULL=1 pnpm record-fixtures cursor-origin
Capabilities
Generated by pnpm capability-matrix; do not edit by hand.
| Capability | Support |
|---|---|
experimental | ✅ |
sources.poll | ❌ |
sources.webhook | ✅ |
sources.subscribe | ❌ |
repos.get | ✅ |
users.get | ❌ |
repos.list | experimental |
repos.labels | ❌ |
repos.createLabel | ❌ |
repos.milestones | ❌ |
repos.collaborators | ❌ |
repos.permissionFor | ❌ |
repos.addCollaborator | ❌ |
repos.assignableUsers | ❌ |
repos.reviewerCandidates | ❌ |
threads.get | PR |
threads.list | PR |
threads.getMany | ✅ |
comments.list | PR |
comments.edit | PR (experimental) |
comments.delete | PR (experimental) |
reactions.list | ❌ |
notifications.list | ❌ |
notifications.markRead | ❌ |
notifications.markDone | ❌ |
notifications.unsubscribe | ❌ |
notifications.markAllRead | ❌ |
notifications.markAllDone | ❌ |
notifications.unreadCount | ❌ |
writes.comment | PR |
writes.upsertComment | PR (emulated) |
writes.close | PR |
writes.reopen | PR |
writes.create | PR (experimental) |
writes.update | PR (experimental) |
writes.setLabels | PR (experimental) |
writes.addLabels | ❌ |
writes.removeLabels | ❌ |
writes.setMilestone | ❌ |
writes.react | ❌ |
writes.setAssignees | ❌ |
writes.requestReview | PR (experimental) |
writes.merge | ✅ |
writes.approveAndMerge | ✅ |
writes.transfer | ❌ |
writes.markDuplicate | ❌ |
subscriptions.get | ❌ |
subscriptions.set | ❌ |
installations | ❌ |
checks.thread | PR |
checks.list | experimental |
checks.report | ❌ |
checks.rerun | ❌ |
ci.runs | ❌ |
ci.run | ❌ |
ci.jobs | ❌ |
ci.log | ❌ |
contents.file | experimental |
contents.tree | experimental |
contents.branches | experimental |
contents.tags | experimental |
contents.resolveRef | experimental |
contents.commits | experimental |
contents.commit | experimental |
contents.compare | experimental |
contents.threadFiles | experimental |
contents.threadCommits | experimental |
reviews.list | ✅ |
reviews.create | experimental |
reviews.submit | ❌ |
reviews.approve | experimental |
reviews.resolveThread | experimental |
releases.list | ❌ |
releases.get | ❌ |
releases.latest | ❌ |
releases.getByTag | ❌ |
releases.downloadAsset | ❌ |
webhooks.list | ❌ |
webhooks.create | ❌ |
webhooks.update | ❌ |
webhooks.delete | ❌ |
webhooks.rotateSecret | ❌ |
webhooks.deliveries | ❌ |
webhooks.redeliver | ❌ |
search.threads | ❌ |
search.repos | ❌ |
search.commits | ❌ |
securityAlerts | ❌ |
eventKinds | native |
auth | token, app |
limits | unknown |