Origin API
Origin is in Early Beta and subject to change. Review the OpenAPI specification when updating an integration.
Origin is Cursor's code forge. Its public REST API lets apps and tools work with Origin repositories, commits, checks, pull requests, and app installations.
- Origin Apps authenticate with app JWTs and installation access tokens. See Authentication.
- View the full OpenAPI specification for detailed schemas and examples.
- Agents can load the llms.txt index or the complete reference as Markdown at llms-full.txt.
Overview
Origin apps implement an OAuth-style installation consent and GitHub App–style authentication model:
- The app signs a short-lived EdDSA JWT with its Ed25519 private key.
- The app exchanges that JWT and an installation ID for a short-lived installation access token (
oit_…). - The installation token calls repository APIs and authenticates Git over HTTPS within the installation's approved repositories and scopes.
- Origin sends signed webhook deliveries to the app's registered webhook URL.
Base URL
https://api.cursor.com/v1/originEndpoint paths in the reference include the full /v1/origin prefix.
Protocol conventions
Requests and responses use application/json. JSON field names are camelCase. Timestamps are RFC 3339 strings. Protobuf 64-bit integers, including pull request numbers and version numbers, are encoded as JSON strings.
Getting started
Origin access
- Browse Origin at cursor.com/codebase.
- Manage app settings at cursor.com/codebase/settings/apps.
- Generate an app signing key and register only the public key.
Origin CLI
Install the Origin CLI and sign in:
curl -fsSL https://downloads.cursor.com/origin/install.sh | shorigin auth loginClone an existing repository:
origin repo clone '{ownerSlug}/{repoName}'# or use git directlygit clone 'https://origin.cursor.com/{ownerSlug}/{repoName}.git'Apps clone using Git HTTPS authentication with an installation access token, not a user login.
Installation
Send a customer workspace admin to:
https://cursor.com/codebase/apps/install ?client_id=APP_ID &scope=SPACE_SEPARATED_SCOPES &redirect_uri=REGISTERED_CALLBACK &state=RANDOM_ANTI_FORGERY_VALUE &summary=SHORT_REASON_FOR_ACCESS &include_granted_scopes=true| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Origin App ID. |
scope | Yes | Space-separated scopes. repository:metadata:read is added automatically. |
redirect_uri | Yes for partner-initiated installs | Exact registered callback URI. |
state | Strongly recommended | Random anti-forgery value echoed as the state claim of the installation receipt. Generate it before redirecting and verify the claim on callback. |
summary | No | Short explanation displayed during consent. |
include_granted_scopes | No | When true, retain existing grants and request only additions. |
The workspace admin chooses the target owner, approved scopes, and either all repositories or selected repositories. The customer, not the app, controls repository access.
After approval, Origin redirects to the registered callback:
https://ci.example.com/origin/callback?installation_receipt=RECEIPT_JWTVerify the installation receipt, then store the installation ID from its sub claim. You need it whenever you mint an installation access token.
Installations use one of two repository-selection modes:
all: the installation can access every repository owned by the selected target.selected: the installation can access only repositories selected by the workspace admin.
Both modes cover mirrored repositories as well as native Origin ones, so a mirror appears in GET /installation/repos and can be selected. A mirror is read-only until it becomes a stable outbound mirror: see Mirrored repositories.
Use GET /installation/repos with an installation token to discover the repositories available to that installation. App JWT endpoints can list, inspect, and delete the app's installations. Deleting an installation prevents new tokens from being minted.
Installation receipt
installation_receipt is a short-lived compact JWT signed by Origin. It proves the installation approval came from Origin rather than a forged redirect and carries everything the callback needs. Cursor refuses to redirect without one, so external callbacks always carry it.
JOSE header:
{ "alg": "EdDSA", "kid": "origin-key-id", "typ": "origin-installation-receipt+jwt"}Claims:
{ "iss": "https://api.cursor.com/v1/origin", "aud": "app_01...", "sub": "i_01...", "namespace_id": "ns_01...", "iat": 1786465200, "exp": 1786465500, "jti": "RECEIPT_UUID", "installedBy": { "id": "user_01...", "email": "installer@example.com" }, "state": "ORIGINAL_VALUE"}audis your app ID andsubis the installation ID to use when minting installation access tokens.namespace_idis the stable ID of the namespace the app was installed into.installedByidentifies the user who performed this install or re-consent. It describes the current action, so on a re-consent it can differ from the durableinstalledByon Get App Installation.- Receipts expire five minutes after issuance.
jtiis unique per receipt. stateis present only when the install URL carried a non-emptystate, and echoes that value. Match it against the anti-forgery value you generated before redirecting.
Verify the receipt before trusting the callback: resolve the signing key from the JWKS by the kid header, require alg EdDSA and typ origin-installation-receipt+jwt, and validate the signature, iss, aud, and exp. Reject the callback when verification fails.
The receipt is not an installation access token. Never send it as a Bearer credential; mint installation tokens through Create Installation Access Token instead.
Authentication
Send REST credentials as Bearer tokens:
curl --request GET \ --url https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME \ --header "Authorization: Bearer $ORIGIN_TOKEN"Generate an app signing key
Origin Apps authenticate with an Ed25519 key pair. Generate the pair locally, then register only the public key at cursor.com/codebase/settings/apps. An app can hold up to 10 active signing keys.
The private key must stay secret. Do not upload it, paste it into app settings, commit it to a repository, or share it. Store it in a secrets manager. Cursor stores only the public key.
Create a PKCS#8 private key and a PEM SPKI public key with OpenSSL:
openssl genpkey -algorithm ED25519 -out origin-app-private.pemopenssl pkey -in origin-app-private.pem -pubout -out origin-app-public.pemThe public key file starts with -----BEGIN PUBLIC KEY-----. Paste that PEM when you add a signing key. Use the matching private key only to sign app JWTs.
App JWT
Sign a short-lived JWT with the Ed25519 private key paired with one of the app's active signing keys. Generate that pair as described in Generate an app signing key.
JOSE header:
{ "alg": "EdDSA", "kid": "app_01...", "typ": "JWT"}Claims:
{ "iss": "app_01...", "aud": "origin-apps", "iat": 1782928800, "exp": 1782929100}Set iss and kid to the app ID. Use a lifetime of approximately five minutes.
Authorization: Bearer APP_JWTUse an app JWT for app-level operations such as reading app metadata, managing installations, minting installation tokens, and recovering webhook deliveries.
Installation access token
Call POST /app/installations/{installationId}/access_tokens with an app JWT. Installation tokens begin with oit_.
Authorization: Bearer oit_...The response includes expiresAt. Mint tokens just in time, refresh them before expiration, treat them like passwords, and never log them.
Removing the installation, or deleting the app, invalidates its installation tokens before expiresAt. The REST API and Git over HTTPS then reject the token with 401. Do not retry with the same token; the app must be reinstalled before it can mint a working one.
An installation token cannot exceed the installation's approved scopes or repository access. You can attenuate a token to fewer scopes or repositoryIds. Empty or omitted arrays inherit the complete installation grant.
Use installation tokens for repository-scoped operations, including pull requests, check-run writes, and Git over HTTPS.
Git HTTPS authentication
Installation access tokens authenticate Git over HTTPS. The Git endpoint uses HTTP Basic authentication: the password is the installation token, and the username is x-access-token. Bearer credentials belong on the REST API; Git HTTPS rejects them.
Mint a token from Create Installation Access Token immediately before the Git operation. Tokens expire after at most 15 minutes.
Clone, fetch, and pull require repository:contents:read. Push requires repository:contents:write. The token must include the target repository in its grant.
Pushing also requires the repository's owner to be eligible to write to Origin, the same requirement Create Repo carries. A user owner must be on a Pro, Pro Student, Pro+, Ultra, or Start plan. A team owner must have an active paid team plan, must not be on Privacy Mode (Legacy), and must not have Origin turned off by a team admin. A push to a repository whose owner is ineligible returns 403. Clone, fetch, and pull do not carry this requirement.
Read cloneUrl from Get Repo or List App Installation Repositories. Both the GitHub-shaped path (https://origin.cursor.com/OWNER_SLUG/REPO_NAME.git) and the legacy /git/ path clone.
git clone "https://x-access-token:${INSTALLATION_TOKEN}@origin.cursor.com/OWNER_SLUG/REPO_NAME.git"Embedding the token in the URL stores it in .git/config. After a successful clone, rewrite the remote so later commands do not reuse an expired secret:
git remote set-url origin "https://origin.cursor.com/OWNER_SLUG/REPO_NAME.git"To keep the token out of the remote URL, supply it through Git's credential helper:
git -c credential.helper="!f() { echo username=x-access-token; echo password=${INSTALLATION_TOKEN}; }; f" \ clone "https://origin.cursor.com/OWNER_SLUG/REPO_NAME.git"The Origin CLI credential helper is for user logins. App integrations pass the installation token as shown here. Treat the token like a password, never log it, and mint a fresh one before expiresAt when a job still needs Git access.
On a mirrored repository, an installation token clones, fetches, and pulls, and Origin rejects git push with 403 until the mirror becomes a stable outbound mirror. See Mirrored repositories.
User-authenticated CLI requests
After signing in with the Origin CLI, use origin api for user-authenticated requests. The CLI resolves the user's credential and sends it as a Bearer token. App integrations should use app JWTs and installation access tokens instead.
Discovery and signing keys
Origin publishes unauthenticated discovery metadata and its active signing keys. The same keys sign webhook deliveries and installation receipts.
Discovery metadata identifies the issuer and jwks_uri:
curl https://api.cursor.com/v1/origin/.well-known/openid-configuration{ "issuer": "https://api.cursor.com/v1/origin", "jwks_uri": "https://api.cursor.com/v1/origin/keys", "response_types_supported": ["id_token"], "subject_types_supported": ["public"], "id_token_signing_alg_values_supported": ["EdDSA"]}/keys returns active Ed25519 JWKs:
curl https://api.cursor.com/v1/origin/keys{ "keys": [ { "kty": "OKP", "crv": "Ed25519", "use": "sig", "alg": "EdDSA", "kid": "origin-key-id", "x": "PUBLIC_KEY_MATERIAL" } ]}Cache the JWKS. Webhook signatures do not carry a key ID, so verification should try each active Ed25519 key. Installation receipts carry the signing key's kid in their JOSE header, so receipt verification can resolve the key directly.
Scopes
Request only the minimum scopes your app needs. repository:metadata:read and app or installation metadata access are granted automatically and should not be added separately to installation URLs.
| Scope | Allows |
|---|---|
repository:metadata:read | Read repository metadata. Added automatically. |
repository:contents:read | Read commits, branches, contents, and low-level Git objects. Clone, fetch, and pull over Git HTTPS. Sync a mirrored repository from its upstream source. |
repository:contents:write | Push over Git HTTPS. Merge pull requests. |
repository:pull_requests:read | Read pull requests, changed files, pull request commits, and assigned labels. |
repository:pull_requests:write | Create and update pull requests. Assign and remove pull request labels. |
repository:pull_requests:reviews:read | Read pull request comments and submitted reviews. |
repository:pull_requests:reviews:write | Create and update comments; create, update, and dismiss reviews. |
repository:checks:read | Read check suites, runs, and check run annotations. |
repository:checks:write | Create and update check suites and runs. Append check run annotations. |
repository:labels:read | Read the label definitions a repository owns. |
repository:labels:write | Create, update, and delete repository label definitions. |
repository:rulesets:read | Read repository rulesets. |
repository:rulesets:write | Create, update, and delete repository rulesets. |
The installation token can only narrow these grants. It cannot add a scope or repository the workspace admin did not approve.
Mirrored repositories
An installation uses every scope it holds on a native Origin repository and on a stable outbound mirror. On a repository in any other mirror state, only two scopes apply:
repository:metadata:readrepository:contents:read
Every other scope returns 403 on that repository, whatever the workspace admin approved. Over the REST API, repository and contents reads, commit comparison, and Sync Mirror keep working, and Origin rejects pull requests, reviews, comments, checks, rulesets, and every write. Over Git HTTPS, clone, fetch, pull, and LFS download keep working, and Origin rejects push and LFS upload.
The mirror object on a repository does not tell you whether writes are allowed. A mirror partway through a transition can report mirror.status as outbound and still be read-only, so treat the 403 as authoritative rather than branching on mirror.status.
Rate limits
The Origin API uses a shared per-principal point budget that resets on a rolling one-minute window. Each authenticated principal kind has its own budget:
| Principal | Default budget |
|---|---|
| Installation access token | 3,000 points/minute |
| App JWT | 6,000 points/minute |
| User or team service-account API key | 600 points/minute |
Every endpoint charges a fixed cost against that budget before the handler runs. Authentication and authorization failures are not charged.
| Cost | Operations |
|---|---|
| 0 | Get Rate Limit. Status only; does not consume points. |
| 1 | Most read endpoints, plus Create Installation Access Token |
| 5 | Ordinary writes, plus these heavier reads: Get Commit, List Commit Files, and List Pull Request Files |
| 10 | Create Repo and Merge Pull Request |
Cursor can raise per-app minute budgets for design partners. Contact Cursor if your integration needs a higher limit.
Response headers
Charged responses and Get Rate Limit include:
| Header | Description |
|---|---|
X-RateLimit-Limit | Points available in the current window for this principal |
X-RateLimit-Remaining | Points left in the current window |
X-RateLimit-Used | Points consumed in the current window |
X-RateLimit-Reset | Unix timestamp (UTC seconds) when the window resets |
X-RateLimit-Resource | Always core for the shared public API budget |
X-RateLimit-Reset advertises a full 60-second window from the response time. The counter's window starts at the first charged request in a burst, not on a calendar-minute boundary.
Exceeding the limit
When a request would exceed the budget, the API returns HTTP 429 with:
Retry-After: seconds to wait before retrying (60)- The same
X-RateLimit-*headers, withX-RateLimit-Remainingset to0
{ "code": 8, "message": "Rate limit exceeded: 3000 points per minute for this installation. Retry after 60s.", "details": []}Wait for Retry-After, or until X-RateLimit-Reset, before retrying. Use backoff with jitter when concurrent callers share one installation token.
Checking remaining quota
Call Get Rate Limit to read the current budget without consuming points. The response body mirrors the X-RateLimit-* headers for the shared core resource.
Webhooks
Origin sends signed HTTP POST requests to the app's registered HTTPS webhook URL with content-type: application/json.
Delivery is at least once. Deduplicate retries with webhook-id, durably accept the request, return 2xx quickly, and process the event asynchronously.
Origin retries transport errors, 429, and 5xx responses up to six total attempts. Retry delays are 30 seconds, 1 minute, 2 minutes, 4 minutes, and 8 minutes. Other 4xx responses are terminal.
To confirm a receiver works before any real event reaches it, call Ping Webhook.
Origin delivers events for mirrored repositories, and installation event payloads list them in the selected repository arrays. Delivery does not widen what the installation can call: see Mirrored repositories.
Headers
| Header | Description |
|---|---|
content-type | application/json |
user-agent | Cursor-Origin-Webhook/1.0 |
webhook-id | Stable delivery ID and idempotency key. |
webhook-timestamp | Unix timestamp included in the signature. |
webhook-signature | v1ed,BASE64_SIGNATURE |
webhook-event-type | Event slug for routing. |
webhook-event-id | Underlying Origin event ID mirrored from the signed body. |
webhook-app-id | Target app ID. |
webhook-installation-id | Target installation ID. |
Routing headers are conveniences. After signature verification, the body is authoritative.
Signature verification
Use the raw request body before parsing it. Construct:
lowercaseHex(SHA-256("<webhook-id>.<webhook-timestamp>.<raw-request-body>"))Verify the Ed25519 signature over the UTF-8 bytes of that hexadecimal digest against an active Origin JWKS key. Reject timestamps more than five minutes from the current time.
import { createHash, createPublicKey, verify, type JsonWebKeyInput,} from "node:crypto";export async function verifyOriginWebhook( body: Buffer, headers: Record<string, string | undefined>): Promise<boolean> { const id = headers["webhook-id"]; const timestamp = Number(headers["webhook-timestamp"]); const signature = headers["webhook-signature"] ?.split(/\s+/) .find((value) => value.startsWith("v1ed,")); const now = Math.floor(Date.now() / 1000); if ( !id || !signature || !Number.isInteger(timestamp) || Math.abs(now - timestamp) > 300 ) { return false; } const digest = createHash("sha256") .update(`${id}.${timestamp}.`) .update(body) .digest("hex"); // Cache this response in production. const { keys } = await fetch( "https://api.cursor.com/v1/origin/keys" ).then((response) => response.json()) as { keys: JsonWebKeyInput[]; }; return keys.some((jwk) => { try { return verify( null, Buffer.from(digest), createPublicKey({ key: jwk, format: "jwk" }), Buffer.from(signature.slice(5), "base64") ); } catch { return false; } });}Delivery envelope
Each request wraps the event payload with delivery, app, and installation identity:
{ "deliveryId": "whd_01...", "appId": "app_01...", "installationId": "i_01...", "event": { "id": "evt_01...", "type": "pull_request.comment.created", "eventTime": "2026-07-01T10:03:00Z", "payload": {} }}deliveryId is stable across retries. event.id identifies the underlying domain event.
Events
| Event | Delivered when |
|---|---|
repository.pushed | One or more refs change in a push. |
pull_request.created | A pull request opens. |
pull_request.head_ref.pushed | The pull request head advances. |
pull_request.base_ref.updated | The base ref or resolved base commit changes. |
pull_request.metadata.updated | The title or description changes. |
pull_request.closed | A pull request closes without merging. |
pull_request.merged | A pull request merges. |
pull_request.reopened | A closed pull request reopens. |
pull_request.published | A draft becomes open. |
pull_request.comment.created | A visible pull request comment is created. |
pull_request.review.submitted | A review is submitted with any verdict. |
pull_request.review.dismissed | A submitted review is dismissed, explicitly or by being superseded. |
pull_request.reviewer.added | A reviewer is requested. |
pull_request.reviewer.removed | A reviewer is removed. |
pull_request.reviewer.rerequested | A reviewer is requested again. |
repository.check_run.created | A check run is created. |
repository.check_run.completed | A check run completes. |
installation.created | The app is installed. |
installation.updated | Scopes or repository selection change. |
installation.suspended | The installation is suspended. |
installation.unsuspended | A suspended installation is restored. |
installation.deleted | The app is uninstalled. |
The five installation.* events go to the app itself rather than to a repository subscription. Origin always sends them, so they do not appear in the app's selectable event list. Every other event in this table is a repository-scoped subscription.
Payload families
- Pull request lifecycle events contain a
pullRequestsnapshot andrepositoryreference. Route onevent.type; there is no separate action field. The snapshot omits the pull request's assignedlabels; read them from Get Pull Request or List Pull Requests. - Comment events contain a
pullRequestreference and the affectedcomment. - Review events contain a
pullRequestreference and the affectedreview. Bothpull_request.review.submittedandpull_request.review.dismisseduse this shape; on a dismissal the review carriesreview.dismissalwith the dismissing actor, timestamp, and message. A review superseded by a newer decision carries a server-generated message. - Reviewer events contain a
pullRequestreference, areviewer, how the request was created, an optional actor, and a timestamp. Exactly one ofreviewer.userorreviewer.groupis present:reviewer.user.idis the encoded user ID (user_…, the same format as the organization API), andreviewer.group.idis the group public ID (grp_…). There is no public HTTP API to add, remove, or rerequest reviewers. - Check events contain
repository,checkSuite,checkRun, andactor. - Push events contain one repository snapshot and up to 100
refUpdates.refUpdatesCountis authoritative. Optional tip metadata is best effort and does not provide a complete commits array. Merging a pull request advances the base ref, and that update is delivered as a push event; because Origin itself performs the merge push, the event names no pusher. - Installation events contain the installation snapshot and app identity. The snapshot carries
installedBy, the user who originally installed the app, on all fiveinstallation.*events. Selected repository arrays may be capped at 5,000;repositoriesCountis authoritative.
Recovery
Use an app JWT to query GET /app/webhook/deliveries. Filter by delivery status, event type, installation, time range, or page token. delivered=false returns every delivery the receiver has never acknowledged with 2xx. Deliveries stay listable for seven days, so recover within that window.
Use POST /app/webhook/deliveries:batchRedeliver to queue redelivery for up to 100 delivery IDs. The operation deduplicates IDs and reports the result for each delivery.
Common conventions
Pagination
Paginated endpoints accept:
pageSize: defaults to 30 and is capped at 100.pageToken: opaque token returned by the preceding page. Do not inspect or construct it.
Responses use a resource-specific collection field and nextPageToken. It is empty when no next page exists. Public list responses do not include total counts. Page tokens are bound to their originating resource and filters. Restart pagination when filters change. Invalid or mismatched non-empty tokens return 400.
Errors
Errors use a Google RPC-style body:
{ "code": 5, "message": "resource not found", "details": []}Common HTTP statuses are 400, 401, 403, 404, 429, 500, and 503. Some Git-database operations also return 409 for repository-state conflicts. See Rate limits for 429 headers and retry behavior.
Use the HTTP status and code to branch on errors. Treat message as developer-facing text.
Every error response carries the request ID twice: in an X-Request-ID response header, and as a google.rpc.RequestInfo entry in details. Origin echoes the x-request-id you sent, or generates one when you send none. The RequestInfo entry is present even when message is an opaque internal error, so quote the request ID when you contact Cursor about a failed call.
Unmatched paths under /v1/origin, and requests that use the wrong method on a known path, return this same body rather than a generic router error. The message names the method and path and never echoes the query string.
Repository paths
Repository-scoped paths take the owner slug and repository name as {ownerSlug}/{repoName}. Both segments resolve case-insensitively, so any casing addresses the repository. Responses return the stored name and slug rather than the casing you sent, and Git HTTPS URLs resolve the same way. Compare repository names case-insensitively, and read the canonical casing from Get Repo.
Every repository-scoped path also accepts the repository's stable ID in place of the pair: send _ as the owner slug and the ID as the repository name, as in GET /v1/origin/repos/_/REPO_ID. Read the ID from the id field on Get Repo. The sentinel _ cannot be claimed as an owner slug, so the two forms never collide. In a Connect or JSON request, set ownerSlug to _ and name to the ID.
The ID form survives a rename, which makes it the stable way to address a repository. It grants nothing on its own: after Origin resolves the ID to a repository, your app still needs the same scope on that repository. An ID your app cannot reach returns the same 404 body as an ID that does not exist, so a response never confirms that a repository exists. A malformed ID returns 400. Create Repo takes an owner slug alone and rejects _.
Resource references
Resource snapshots contain the resource's current fields. Container context uses compact references instead of duplicating complete resources:
RepositoryReferenceidentifies a repository.PullRequestReferenceidentifies a pull request and nests its repository reference.ThreadReferenceidentifies the thread containing a pull request comment.OriginActoridentifies a public actor as one ofuser,app, orserviceAccount. Exactly one variant is present; read the identity from that variant.
Current limitations
- Namespace-wide repository listing and repository creation are not part of the partner API. Discover repositories through the installation.
- Commit comparison returns summary data rather than a paginated compare-file or compare-commit stream.
- Pull request comments expose thread IDs, but there is no first-class thread or resolution API.
- Reviewer add, remove, and rerequest operations are webhook-only.
- Push webhooks do not include a complete commit list.
- Pull request merge supports native Origin repositories. Mirrored repositories are rejected.
- A mirrored repository is read-only for an installation until it becomes a stable outbound mirror. See Mirrored repositories.
Implementation checklist
- Store the Ed25519 private key in a secrets manager and rotate keys deliberately. See Generate an app signing key.
- Verify the installation receipt on install callbacks and read the installation ID and
statefrom its claims. - Use short-lived app JWTs and mint installation tokens just in time.
- Use installation tokens, not app JWTs, for repository-scoped APIs, check-run writes, and Git HTTPS.
- Request the minimum scopes and repository access.
- Treat page tokens as opaque and restart pagination when filters change.
- Keep check
keyvalues stable and readable. Use a new immutableexternalIdfor each retry and increasingexternalUpdatedAtvalues for updates. - Verify webhook signatures against the raw request body before parsing.
- Deduplicate deliveries with
webhook-idand process asynchronously after returning2xx. - Ignore unknown JSON fields for forward compatibility.
- Honor
Retry-AfterandX-RateLimit-*headers. Use Get Rate Limit to monitor remaining points without consuming them.
Endpoint reference
Download the OpenAPI specification for the complete component schemas.
Generated OpenAPI path bindings such as identifier.ownerSlug and identifier.name appear here as ownerSlug and repoName. The URL segments and request behavior are unchanged.
The JSON snippets show schema-shaped placeholder values. Response field descriptions reflect the OpenAPI schema and current platform contract.
Apps and installations
Get Rate Limit
/v1/origin/rate_limitReturns the authenticated principal's current public API rate limit status.
Accessing this endpoint does not consume rate limit points. The response covers the shared per-minute point budget used by other public API endpoints for this principal. See Rate limits.
Response Fields
resources object
resources.core object
resources.core.limit integer
resources.core.remaining integer
resources.core.reset integer
resources.core.used integer
rate object
resources.core. Prefer resources.core in new clients.curl --request GET \ --url 'https://api.cursor.com/v1/origin/rate_limit' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "resources": { "core": { "limit": 0, "remaining": 0, "reset": 0, "used": 0 } }, "rate": { "limit": 0, "remaining": 0, "reset": 0, "used": 0 }}Get Authenticated App
/v1/origin/appReturns metadata for the authenticated app.
Response Fields
id string
slug string
displayName string
webhookUrl string
events array
createdAt string
updatedAt string
installationRedirectUris array
curl --request GET \ --url 'https://api.cursor.com/v1/origin/app' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "slug": "string", "displayName": "string", "webhookUrl": "string", "events": [ "string" ], "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "installationRedirectUris": [ "string" ]}List App Installations
/v1/origin/app/installationsLists installations for the authenticated app.
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page.Response Fields
installations array
installations[].id string
installations[].appId string
installations[].target object
installations[].target.slug string
installations[].target.id string
installations[].target.type string
team, user. Omitted when unknown.installations[].createdAt string
installations[].updatedAt string
installations[].repoSelectionMode string
installations[].scopes array
installations[].installedBy object
installations[].installedBy.id string
user_.installations[].installedBy.email string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/app/installations' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "installations": [ { "id": "string", "appId": "string", "target": { "slug": "string", "id": "string", "type": "team" }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "repoSelectionMode": "all", "scopes": [ "string" ], "installedBy": { "id": "string", "email": "string" } } ], "nextPageToken": "string"}Get App Installation
/v1/origin/app/installations/{installationId}Returns a single installation for the authenticated app.
repoSelectionMode is all or selected.
Path Parameters
installationId string Required
Response Fields
id string
appId string
target object
target.slug string
target.id string
target.type string
team, user. Omitted when unknown.createdAt string
updatedAt string
repoSelectionMode string
scopes array
installedBy object
installedBy.id string
user_.installedBy.email string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/app/installations/INSTALLATION_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "appId": "string", "target": { "slug": "string", "id": "string", "type": "team" }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "repoSelectionMode": "all", "scopes": [ "string" ], "installedBy": { "id": "string", "email": "string" }}Delete App Installation
/v1/origin/app/installations/{installationId}Deletes an installation that belongs to the authenticated app and prevents new installation tokens from being minted. Already-issued short-lived tokens may remain valid until they expire (at most 15 minutes). The response body is empty.
Path Parameters
installationId string Required
Response Fields
Successful requests return no response body.
curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/app/installations/INSTALLATION_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response:
204 No ContentCreate Installation Access Token
/v1/origin/app/installations/{installationId}/access_tokensCreates an installation access token for the authenticated app.
Requires app signing-JWT authentication, like GetAuthenticatedApp. The token is scoped to the named installation, which must belong to the authenticated app. Callers may attenuate the token to a subset of the installation's accepted scopes and accessible repositories.
repositoryIds can name a mirrored repository. The resulting token carries the installation's scopes, and Origin still applies the mirror ceiling on each request: see Mirrored repositories.
Path Parameters
installationId string Required
Request Body
scopes array
repositoryIds array
Response Fields
token string
expiresAt string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/app/installations/INSTALLATION_ID/access_tokens' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{}'Response shape:
{ "token": "string", "expiresAt": "2026-01-01T00:00:00Z"}List App Installation Repositories
/v1/origin/installation/reposLists repositories accessible to the authenticated app installation.
Requires an installation access token (oit_) minted by CreateInstallationAccessToken.
Partners discover their repositories through this endpoint. List entries are sparse repository summaries; use Get Repo for full timestamps. Get Repo includes the output-only cloneUrl.
Results include mirrored repositories. A mirror is read-only until it becomes a stable outbound mirror: see Mirrored repositories.
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page.Response Fields
repositories array
repositories[].id string
repositories[].name string
repositories[].fullName string
repositories[].owner object
repositories[].owner.slug string
repositories[].owner.id string
repositories[].owner.type string
team, user. Omitted when unknown.repositories[].defaultBranch string
repositories[].mirror object
repositories[].mirror.source string
github.repositories[].mirror.sourceId string
repositories[].mirror.status string
inbound, outbound.nextPageToken string
repoSelectionMode string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/installation/repos' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "repositories": [ { "id": "string", "name": "string", "fullName": "string", "owner": { "slug": "string", "id": "string", "type": "team" }, "defaultBranch": "string", "mirror": { "source": "github", "sourceId": "string", "status": "inbound" } } ], "nextPageToken": "string", "repoSelectionMode": "all"}List Webhook Deliveries
/v1/origin/app/webhook/deliveriesLists webhook deliveries for the authenticated app, newest first.
A delivery is one event owed to one app; its id is the Standard Webhooks webhook-id header the receiver sees. delivered=false is the recovery predicate: it selects every delivery that has never received a 2xx, including deliveries whose retry ladder ran out during an outage.
Deliveries are listable for seven days after they are created, and only while your app has an active installation in the delivery's namespace. App-targeted lifecycle events such as installation.deleted stay visible after the uninstall they describe.
Query Parameters
delivered boolean
delivered_at. delivered=false is the recovery predicate: it is evaluated server-side, so it cannot miss a delivery whose retry ladder exhausted mid-outage the way a caller-supplied time window silently does.eventType string
pull_request.created.installationId string
WebhookDelivery.installation.id).createdAfter string
createdBefore string
pageSize integer
pageToken string
next_page_token. Empty for the first page.Response Fields
deliveries array
deliveries[].id string
deliveries[].event object
deliveries[].event.id string
deliveries[].event.type string
deliveries[].installation object
id is the current active installation for the target owner; unset when none exists (possible only for app-targeted lifecycle events after an uninstall).deliveries[].installation.id string
deliveries[].installation.target object
deliveries[].installation.target.slug string
deliveries[].installation.target.id string
deliveries[].installation.target.type string
team, user. Omitted when unknown.deliveries[].createdAt string
deliveries[].deliveredAt string
deliveries[].lastAttempt object
deliveries[].lastAttempt.id string
deliveries[].lastAttempt.deliveryId string
deliveries[].lastAttempt.trigger string
automatic, manual.deliveries[].lastAttempt.responseStatusCode integer
deliveries[].lastAttempt.latencyMs integer
deliveries[].lastAttempt.errorMessage string
deliveries[].lastAttempt.attemptedAt string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/app/webhook/deliveries' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "deliveries": [ { "id": "string", "event": { "id": "string", "type": "string" }, "installation": { "id": "string", "target": { "slug": "string", "id": "string", "type": "team" } }, "createdAt": "2026-01-01T00:00:00Z", "deliveredAt": "2026-01-01T00:00:00Z", "lastAttempt": { "id": "string", "deliveryId": "string", "trigger": "automatic", "responseStatusCode": 0, "latencyMs": 0, "errorMessage": "string", "attemptedAt": "2026-01-01T00:00:00Z" } } ], "nextPageToken": "string"}Batch Redeliver Webhook Deliveries
/v1/origin/app/webhook/deliveries:batchRedeliverAsks Origin to send deliveries again.
The request means "ensure a send is in flight for each of these", not "add another send". It returns one result per unique input rather than failing the batch on a bad entry, so a single expired ID cannot block the rest of a recovery page. A 202 means the sends are queued; delivery itself is asynchronous, so poll List Webhook Deliveries for outcomes.
Request Body
deliveryIds array Required
pageSize ceiling on List Webhook Deliveries. Duplicates are removed, keeping first-seen order. An empty list, or more than 100 unique entries, returns InvalidArgument (HTTP 400).Response Fields
results array
results[].deliveryId string
results[].outcome string
queued when a send was created, already_in_flight when a send was already running, and not_found otherwise. already_in_flight is a success, not an error. not_found covers unknown IDs, IDs older than the seven-day retention window, and namespaces where your app is no longer installed.curl --request POST \ --url 'https://api.cursor.com/v1/origin/app/webhook/deliveries:batchRedeliver' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "deliveryIds": [ "DELIVERY_ID" ]}'Response shape:
{ "results": [ { "deliveryId": "string", "outcome": "queued" } ]}Ping Webhook
/v1/origin/app/webhook/pingsSends a test delivery to the authenticated app's webhook URL and reports what the receiver answered.
Use it to verify a receiver while you set an app up, instead of waiting for a real event. Requires app signing-JWT authentication, like Get Authenticated App.
The receiver sees the production shape: the same headers and v1ed signature, verifiable against the signing keys, with webhook-event-type set to ping and a payload naming the app. A ping belongs to no installation, so the webhook-installation-id header and the envelope's installationId are both absent.
Origin sends the ping once, synchronously, and reports the outcome in the response. There are no retries, and a ping is not a domain event: it never appears in List Webhook Deliveries and cannot be redelivered. A receiver that fails is reported in the response rather than as an error. An app with no webhook URL configured returns FailedPrecondition (HTTP 400).
Request Body
The request takes no fields. Send an empty JSON object.
Response Fields
deliveryId string
webhook-id, matching the header the receiver saw.eventId string
event.id.delivered boolean
2xx status before the delivery timeout. Always present.responseStatusCode integer
0 when no response arrived because the connection failed or timed out. Always present.curl --request POST \ --url 'https://api.cursor.com/v1/origin/app/webhook/pings' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{}'Response shape:
{ "deliveryId": "string", "eventId": "string", "delivered": true, "responseStatusCode": 200}Repositories
cloneUrl is an output-only HTTPS clone URL. Get Repo includes cloneUrl.
Partners discover their repositories through List App Installation Repositories. Namespace-wide repository listing and creation are not part of the partner API.
List Repos
/v1/origin/repos/{ownerSlug}Lists repos belonging to an owner entity.
Path Parameters
ownerSlug string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page.filter string
Response Fields
repositories array
repositories[].id string
repositories[].name string
repositories[].fullName string
repositories[].owner object
repositories[].owner.slug string
repositories[].owner.id string
repositories[].owner.type string
team, user. Omitted when unknown.repositories[].defaultBranch string
repositories[].createdAt string
repositories[].updatedAt string
repositories[].pushedAt string
repositories[].cloneUrl string
repositories[].mirror object
repositories[].mirror.source string
github.repositories[].mirror.sourceId string
repositories[].mirror.status string
inbound, outbound.nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "repositories": [ { "id": "string", "name": "string", "fullName": "string", "owner": { "slug": "string", "id": "string", "type": "team" }, "defaultBranch": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "pushedAt": "2026-01-01T00:00:00Z", "cloneUrl": "string", "mirror": { "source": "github", "sourceId": "string", "status": "inbound" } } ], "nextPageToken": "string"}Get Repo
/v1/origin/repos/{ownerSlug}/{repoName}Returns a single repo by its (owner_id, name) identifier.
cloneUrl is an output-only HTTPS clone URL. Get repository includes cloneUrl.
Path Parameters
ownerSlug string Required
repoName string Required
Response Fields
id string
name string
fullName string
owner object
owner.slug string
owner.id string
owner.type string
team, user. Omitted when unknown.defaultBranch string
createdAt string
updatedAt string
pushedAt string
cloneUrl string
mirror object
mirror.source string
github.mirror.sourceId string
mirror.status string
inbound, outbound.curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "name": "string", "fullName": "string", "owner": { "slug": "string", "id": "string", "type": "team" }, "defaultBranch": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "pushedAt": "2026-01-01T00:00:00Z", "cloneUrl": "string", "mirror": { "source": "github", "sourceId": "string", "status": "inbound" }}Create Repo
/v1/origin/repos/{ownerSlug}Creates a repo belonging to an owner.
The owner must be eligible to write to Origin when the request is made. A user owner must be on a Pro, Pro Student, Pro+, Ultra, or Start plan. A team owner must have an active paid team plan, must not be on Privacy Mode (Legacy), and must not have Origin turned off by a team admin. An ineligible owner returns FailedPrecondition (HTTP 400). Reading existing repositories does not carry this requirement.
Repository names are claimed case-insensitively. A name that differs only in case from a repository the owner already has is rejected, so widgets and Widgets cannot coexist in one namespace. The name you send is stored as you send it.
The first push to a new repo can retarget its default branch. When that push only creates branches and none of them is the repo's stored default branch, Origin sets the default branch to the created branch, or to main or master when the push creates several and one of those names is among them. The default branch is otherwise unchanged. Read the current value from Get Repo.
Path Parameters
ownerSlug string Required
Request Body
name string Required
defaultBranch string
Response Fields
id string
name string
fullName string
owner object
owner.slug string
owner.id string
owner.type string
team, user. Omitted when unknown.defaultBranch string
createdAt string
updatedAt string
pushedAt string
cloneUrl string
mirror object
mirror.source string
github.mirror.sourceId string
mirror.status string
inbound, outbound.curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "name": "NAME"}'Response shape:
{ "id": "string", "name": "string", "fullName": "string", "owner": { "slug": "string", "id": "string", "type": "team" }, "defaultBranch": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "pushedAt": "2026-01-01T00:00:00Z", "cloneUrl": "string", "mirror": { "source": "github", "sourceId": "string", "status": "inbound" }}List Branches
/v1/origin/repos/{ownerSlug}/{repoName}/branchesLists the repo's branches and tip commits in ascending name order, paginated with page_size and page_token.
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. Encodes the page offset, so page_size on a follow-up request is ignored when a token is supplied.Response Fields
branches array
branches[].name string
branches[].commit object
branches[].commit.sha string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/branches' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "branches": [ { "name": "string", "commit": { "sha": "string" } } ], "nextPageToken": "string"}Sync Mirror
/v1/origin/repos/{ownerSlug}/{repoName}:syncMirrorSynchronizes one ref of a mirrored repository from its upstream source. Returns HTTP 200 when the sync target is satisfied, or HTTP 202 when the sync is still pending. wait=false (the default) schedules the sync and usually returns 202; it returns 200 immediately when sha is already reachable from ref. wait=true blocks until satisfied or the wait budget (~2 minutes) expires; expiry still returns 202 and the sync continues in the background. Repositories that do not pull from an upstream source are rejected.
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
ref string Required
refs/ and name a ref after that prefix, for example refs/heads/main or refs/tags/v1. Short names such as main are rejected with INVALID_ARGUMENT.wait boolean
sha string
ref. When set and reachable from ref, the call returns early without waiting for other mirror work to drain. Other values are rejected with INVALID_ARGUMENT.Response Fields
synced boolean
200 when true, 202 when false.curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME:syncMirror' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "ref": "refs/heads/main", "wait": true}'Response shape:
{ "synced": true}Checks
- The first run upsert creates its suite automatically.
- Required checks match the installing app plus the suite
key, and optionally a runkey.nameis display-only and is not used for matching. - Keep
keyvalues stable across attempts and readable for users, since required-check configuration is keyed on them. - Reuse
externalIdto update an attempt; use a newexternalIdfor a retry. - Use
checkRun.outputfor human-readable results:title: short result headline, up to 255 characters.summary: primary Markdown summary, up to 65,535 UTF-8 bytes.text: extended Markdown details, up to 65,535 UTF-8 bytes.
- Use
detailsUrlfor a link to the provider's external results page.
Post Check Run
/v1/origin/repos/{ownerSlug}/{repoName}/check-runsUpserts a check suite + check run using an installation access token with repository:checks:write. The write is attributed to the app that owns the authenticated installation. A repeated call with the same (repo, head_sha, suite.key, check.key) updates the existing check run in place rather than creating a duplicate.
The endpoint atomically resolves or creates the suite attempt and upserts one run attempt. externalUpdatedAt orders updates to the same run identity; stale retries cannot overwrite newer state.
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
headSha string Required
checkSuite object Required
checkSuite.key string Required
checkSuite.name string Required
checkSuite.detailsUrl string
checkSuite.externalId string Required
checkRun object Required
checkRun.key string Required
checkRun.name string Required
checkRun.status string Required
CHECK_RUN_LIFECYCLE_STATUS_UNSPECIFIED, queued, in_progress, completed.checkRun.conclusion string
status == completed. Allowed values: CHECK_RUN_CONCLUSION_UNSPECIFIED, success, failure, neutral, cancelled, skipped, timed_out, action_required, stale.checkRun.externalUpdatedAt string Required
checkRun.startedAt string
checkRun.completedAt string
checkRun.detailsUrl string
checkRun.externalId string Required
checkRun.output object
checkRun.output.title string
checkRun.output.summary string
checkRun.output.text string
Response Fields
checkSuite object
checkSuite.id string
checkSuite.repository object
checkSuite.repository.id string
checkSuite.repository.name string
checkSuite.repository.owner object
checkSuite.repository.owner.slug string
checkSuite.repository.owner.id string
checkSuite.repository.owner.type string
team, user. Omitted when unknown.checkSuite.sha string
checkSuite.key string
checkSuite.name string
checkSuite.detailsUrl string
checkSuite.createdAt string
checkSuite.updatedAt string
checkSuite.externalId string
checkSuite.actor object
checkSuite.actor.user object
checkSuite.actor.user.id string
checkSuite.actor.user.email string
checkSuite.actor.app object
checkSuite.actor.app.id string
checkSuite.actor.app.slug string
checkSuite.actor.serviceAccount object
checkSuite.actor.serviceAccount.id string
checkRun object
checkRun.id string
checkRun.repository object
checkRun.repository.id string
checkRun.repository.name string
checkRun.repository.owner object
checkRun.repository.owner.slug string
checkRun.repository.owner.id string
checkRun.repository.owner.type string
team, user. Omitted when unknown.checkRun.checkSuite object
checkRun.checkSuite.id string
checkRun.sha string
checkRun.key string
checkRun.name string
checkRun.status string
checkRun.conclusion string
checkRun.detailsUrl string
checkRun.externalUpdatedAt string
checkRun.startedAt string
checkRun.completedAt string
checkRun.createdAt string
checkRun.updatedAt string
checkRun.externalId string
checkRun.actor object
checkRun.actor.user object
checkRun.actor.user.id string
checkRun.actor.user.email string
checkRun.actor.app object
checkRun.actor.app.id string
checkRun.actor.app.slug string
checkRun.actor.serviceAccount object
checkRun.actor.serviceAccount.id string
checkRun.output object
checkRun.output.title string
checkRun.output.summary string
checkRun.output.text string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "headSha": "HEAD_SHA", "checkSuite": { "key": "KEY", "name": "NAME", "externalId": "EXTERNAL_ID" }, "checkRun": { "key": "KEY", "name": "NAME", "status": "queued", "externalUpdatedAt": "2026-01-01T00:00:00Z", "externalId": "EXTERNAL_ID" }}'Response shape:
{ "checkSuite": { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "sha": "string", "key": "string", "name": "string", "detailsUrl": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } } }, "checkRun": { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "checkSuite": { "id": "string" }, "sha": "string", "key": "string", "name": "string", "status": "queued", "conclusion": "success", "detailsUrl": "string", "externalUpdatedAt": "2026-01-01T00:00:00Z", "startedAt": "2026-01-01T00:00:00Z", "completedAt": "2026-01-01T00:00:00Z", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }, "output": { "title": "string", "summary": "string", "text": "string" } }}Batch Upsert Check Runs
/v1/origin/repos/{ownerSlug}/{repoName}/check-runs:batchUpsertAtomically upserts several check runs belonging to one suite. The request accepts at most 10 runs and rejects duplicate (external_id, key) identities. Every run is committed or the entire request is rolled back.
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
headSha string Required
checkSuite object Required
checkSuite.key string Required
checkSuite.name string Required
checkSuite.detailsUrl string
checkSuite.externalId string Required
checkRuns array Required
(external_id, key) identities.checkRuns[0].key string Required
checkRuns[0].name string Required
checkRuns[0].status string Required
CHECK_RUN_LIFECYCLE_STATUS_UNSPECIFIED, queued, in_progress, completed.checkRuns[0].conclusion string
status == completed. Allowed values: CHECK_RUN_CONCLUSION_UNSPECIFIED, success, failure, neutral, cancelled, skipped, timed_out, action_required, stale.checkRuns[0].externalUpdatedAt string Required
checkRuns[0].startedAt string
checkRuns[0].completedAt string
checkRuns[0].detailsUrl string
checkRuns[0].externalId string Required
checkRuns[0].output object
checkRuns[0].output.title string
checkRuns[0].output.summary string
checkRuns[0].output.text string
Response Fields
checkSuite object
checkSuite.id string
checkSuite.repository object
checkSuite.repository.id string
checkSuite.repository.name string
checkSuite.repository.owner object
checkSuite.repository.owner.slug string
checkSuite.repository.owner.id string
checkSuite.repository.owner.type string
team, user. Omitted when unknown.checkSuite.sha string
checkSuite.key string
checkSuite.name string
checkSuite.detailsUrl string
checkSuite.createdAt string
checkSuite.updatedAt string
checkSuite.externalId string
checkSuite.actor object
checkSuite.actor.user object
checkSuite.actor.user.id string
checkSuite.actor.user.email string
checkSuite.actor.app object
checkSuite.actor.app.id string
checkSuite.actor.app.slug string
checkSuite.actor.serviceAccount object
checkSuite.actor.serviceAccount.id string
checkRuns array
checkRuns[].id string
checkRuns[].repository object
checkRuns[].repository.id string
checkRuns[].repository.name string
checkRuns[].repository.owner object
checkRuns[].repository.owner.slug string
checkRuns[].repository.owner.id string
checkRuns[].repository.owner.type string
team, user. Omitted when unknown.checkRuns[].checkSuite object
checkRuns[].checkSuite.id string
checkRuns[].sha string
checkRuns[].key string
checkRuns[].name string
checkRuns[].status string
checkRuns[].conclusion string
checkRuns[].detailsUrl string
checkRuns[].externalUpdatedAt string
checkRuns[].startedAt string
checkRuns[].completedAt string
checkRuns[].createdAt string
checkRuns[].updatedAt string
checkRuns[].externalId string
checkRuns[].actor object
checkRuns[].actor.user object
checkRuns[].actor.user.id string
checkRuns[].actor.user.email string
checkRuns[].actor.app object
checkRuns[].actor.app.id string
checkRuns[].actor.app.slug string
checkRuns[].actor.serviceAccount object
checkRuns[].actor.serviceAccount.id string
checkRuns[].output object
checkRuns[].output.title string
checkRuns[].output.summary string
checkRuns[].output.text string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs:batchUpsert' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "headSha": "HEAD_SHA", "checkSuite": { "key": "KEY", "name": "NAME", "externalId": "EXTERNAL_ID" }, "checkRuns": [ { "key": "KEY", "name": "NAME", "status": "queued", "externalUpdatedAt": "2026-01-01T00:00:00Z", "externalId": "EXTERNAL_ID" } ]}'Response shape:
{ "checkSuite": { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "sha": "string", "key": "string", "name": "string", "detailsUrl": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } } }, "checkRuns": [ { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "checkSuite": { "id": "string" }, "sha": "string", "key": "string", "name": "string", "status": "queued", "conclusion": "success", "detailsUrl": "string", "externalUpdatedAt": "2026-01-01T00:00:00Z", "startedAt": "2026-01-01T00:00:00Z", "completedAt": "2026-01-01T00:00:00Z", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }, "output": { "title": "string", "summary": "string", "text": "string" } } ]}Get Check Run
/v1/origin/repos/{ownerSlug}/{repoName}/check-runs/{checkRunId}Returns a single check run by server-assigned id (cr_...).
Path Parameters
ownerSlug string Required
repoName string Required
checkRunId string Required
cr_...).Response Fields
id string
repository object
repository.id string
repository.name string
repository.owner object
repository.owner.slug string
repository.owner.id string
repository.owner.type string
team, user. Omitted when unknown.checkSuite object
checkSuite.id string
sha string
key string
name string
status string
conclusion string
detailsUrl string
externalUpdatedAt string
startedAt string
completedAt string
createdAt string
updatedAt string
externalId string
actor object
actor.user object
actor.user.id string
actor.user.email string
actor.app object
actor.app.id string
actor.app.slug string
actor.serviceAccount object
actor.serviceAccount.id string
output object
output.title string
output.summary string
output.text string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs/CHECK_RUN_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "checkSuite": { "id": "string" }, "sha": "string", "key": "string", "name": "string", "status": "queued", "conclusion": "success", "detailsUrl": "string", "externalUpdatedAt": "2026-01-01T00:00:00Z", "startedAt": "2026-01-01T00:00:00Z", "completedAt": "2026-01-01T00:00:00Z", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }, "output": { "title": "string", "summary": "string", "text": "string" }}List Check Run Annotations
/v1/origin/repos/{ownerSlug}/{repoName}/check-runs/{checkRunId}/annotationsLists a check run's annotations in ascending ID order.
Annotation IDs are time-sortable, so ascending ID order is also creation order. A page token fixes the page size and scope for the rest of the sequence, so pageSize is ignored once you send one.
Path Parameters
ownerSlug string Required
repoName string Required
checkRunId string Required
Query Parameters
pageSize integer
pageToken string
nextPageToken. Omit for the first page.Response Fields
annotations array
annotations[].id string
annotations[].checkRunId string
annotations[].annotationLevel string
notice, warning, failure.annotations[].message string
annotations[].title string
annotations[].rawDetails string
annotations[].createdAt string
annotations[].updatedAt string
annotations[].location object
annotations[].location.path string
annotations[].location.startLine integer
annotations[].location.endLine integer
annotations[].location.columns object
annotations[].location.columns.startColumn integer
annotations[].location.columns.endColumn integer
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs/CHECK_RUN_ID/annotations' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "annotations": [ { "id": "string", "checkRunId": "string", "annotationLevel": "warning", "message": "string", "title": "string", "rawDetails": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "location": { "path": "string", "startLine": 0, "endLine": 0, "columns": { "startColumn": 0, "endColumn": 0 } } } ], "nextPageToken": "string"}Create Check Run Annotations
/v1/origin/repos/{ownerSlug}/{repoName}/check-runs/{checkRunId}/annotationsAppends between 1 and 25 annotations to a check run in a single atomic batch.
A check run holds at most 100 annotations. A batch that would take it past that limit is rejected with ResourceExhausted (HTTP 429) and nothing is written; a batch outside the 1 to 25 range is rejected with InvalidArgument (HTTP 400). The operation is append-only and is not idempotent, so retrying after an ambiguous transport failure can append duplicates and consume capacity. Identical content is allowed.
Path Parameters
ownerSlug string Required
repoName string Required
checkRunId string Required
Request Body
annotations array Required
annotations[].annotationLevel string Required
notice, warning, failure.annotations[].message string Required
annotations[].title string
annotations[].rawDetails string
annotations[].location object
annotations[].location.path string Required
annotations[].location.startLine integer Required
annotations[].location.endLine integer Required
startLine.annotations[].location.columns object
startLine and endLine are the same line, and both columns must be sent together.annotations[].location.columns.startColumn integer
annotations[].location.columns.endColumn integer
startColumn.Response Fields
annotations array
annotations[].id string
annotations[].checkRunId string
annotations[].annotationLevel string
notice, warning, failure.annotations[].message string
annotations[].title string
annotations[].rawDetails string
annotations[].createdAt string
annotations[].updatedAt string
annotations[].location object
annotations[].location.path string
annotations[].location.startLine integer
annotations[].location.endLine integer
annotations[].location.columns object
annotations[].location.columns.startColumn integer
annotations[].location.columns.endColumn integer
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-runs/CHECK_RUN_ID/annotations' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "annotations": [ { "annotationLevel": "warning", "message": "MESSAGE", "location": { "path": "src/index.ts", "startLine": 12, "endLine": 12 } } ]}'Response shape:
{ "annotations": [ { "id": "string", "checkRunId": "string", "annotationLevel": "warning", "message": "string", "title": "string", "rawDetails": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "location": { "path": "string", "startLine": 0, "endLine": 0, "columns": { "startColumn": 0, "endColumn": 0 } } } ]}Get Check Suite
/v1/origin/repos/{ownerSlug}/{repoName}/check-suites/{checkSuiteId}Returns check suite metadata by server-assigned id (crg_...). Does not embed check runs; use ListCheckRunsForSuite for the suite's runs.
Path Parameters
ownerSlug string Required
repoName string Required
checkSuiteId string Required
crg_...).Response Fields
id string
repository object
repository.id string
repository.name string
repository.owner object
repository.owner.slug string
repository.owner.id string
repository.owner.type string
team, user. Omitted when unknown.sha string
key string
name string
detailsUrl string
createdAt string
updatedAt string
externalId string
actor object
actor.user object
actor.user.id string
actor.user.email string
actor.app object
actor.app.id string
actor.app.slug string
actor.serviceAccount object
actor.serviceAccount.id string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-suites/CHECK_SUITE_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "sha": "string", "key": "string", "name": "string", "detailsUrl": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }}List Check Runs For Suite
/v1/origin/repos/{ownerSlug}/{repoName}/check-suites/{checkSuiteId}/check-runsLists check runs belonging to a suite. Paginated.
Path Parameters
ownerSlug string Required
repoName string Required
checkSuiteId string Required
crg_...).Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. Encodes the last-seen check-run id scoped to this suite, so page_size on a follow-up request is ignored when a token is supplied.Response Fields
checkRuns array
checkRuns[].id string
checkRuns[].repository object
checkRuns[].repository.id string
checkRuns[].repository.name string
checkRuns[].repository.owner object
checkRuns[].repository.owner.slug string
checkRuns[].repository.owner.id string
checkRuns[].repository.owner.type string
team, user. Omitted when unknown.checkRuns[].checkSuite object
checkRuns[].checkSuite.id string
checkRuns[].sha string
checkRuns[].key string
checkRuns[].name string
checkRuns[].status string
checkRuns[].conclusion string
checkRuns[].detailsUrl string
checkRuns[].externalUpdatedAt string
checkRuns[].startedAt string
checkRuns[].completedAt string
checkRuns[].createdAt string
checkRuns[].updatedAt string
checkRuns[].externalId string
checkRuns[].actor object
checkRuns[].actor.user object
checkRuns[].actor.user.id string
checkRuns[].actor.user.email string
checkRuns[].actor.app object
checkRuns[].actor.app.id string
checkRuns[].actor.app.slug string
checkRuns[].actor.serviceAccount object
checkRuns[].actor.serviceAccount.id string
checkRuns[].output object
checkRuns[].output.title string
checkRuns[].output.summary string
checkRuns[].output.text string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/check-suites/CHECK_SUITE_ID/check-runs' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "checkRuns": [ { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "checkSuite": { "id": "string" }, "sha": "string", "key": "string", "name": "string", "status": "queued", "conclusion": "success", "detailsUrl": "string", "externalUpdatedAt": "2026-01-01T00:00:00Z", "startedAt": "2026-01-01T00:00:00Z", "completedAt": "2026-01-01T00:00:00Z", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }, "output": { "title": "string", "summary": "string", "text": "string" } } ], "nextPageToken": "string"}List Check Runs For Commit
/v1/origin/repos/{ownerSlug}/{repoName}/commits/{sha}/check-runsLists every check run reported against a commit, across all suites. Paginated.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. Encodes the last-seen check-run id scoped to this commit, so page_size on a follow-up request is ignored when a token is supplied.Response Fields
checkRuns array
checkRuns[].id string
checkRuns[].repository object
checkRuns[].repository.id string
checkRuns[].repository.name string
checkRuns[].repository.owner object
checkRuns[].repository.owner.slug string
checkRuns[].repository.owner.id string
checkRuns[].repository.owner.type string
team, user. Omitted when unknown.checkRuns[].checkSuite object
checkRuns[].checkSuite.id string
checkRuns[].sha string
checkRuns[].key string
checkRuns[].name string
checkRuns[].status string
checkRuns[].conclusion string
checkRuns[].detailsUrl string
checkRuns[].externalUpdatedAt string
checkRuns[].startedAt string
checkRuns[].completedAt string
checkRuns[].createdAt string
checkRuns[].updatedAt string
checkRuns[].externalId string
checkRuns[].actor object
checkRuns[].actor.user object
checkRuns[].actor.user.id string
checkRuns[].actor.user.email string
checkRuns[].actor.app object
checkRuns[].actor.app.id string
checkRuns[].actor.app.slug string
checkRuns[].actor.serviceAccount object
checkRuns[].actor.serviceAccount.id string
checkRuns[].output object
checkRuns[].output.title string
checkRuns[].output.summary string
checkRuns[].output.text string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/commits/SHA/check-runs' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "checkRuns": [ { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "checkSuite": { "id": "string" }, "sha": "string", "key": "string", "name": "string", "status": "queued", "conclusion": "success", "detailsUrl": "string", "externalUpdatedAt": "2026-01-01T00:00:00Z", "startedAt": "2026-01-01T00:00:00Z", "completedAt": "2026-01-01T00:00:00Z", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } }, "output": { "title": "string", "summary": "string", "text": "string" } } ], "nextPageToken": "string"}List Check Suites For Commit
/v1/origin/repos/{ownerSlug}/{repoName}/commits/{sha}/check-suitesLists check suites reported against a commit. Returns suite metadata only (no embedded runs). Paginated.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. Encodes the last-seen check-suite id scoped to this commit, so page_size on a follow-up request is ignored when a token is supplied.Response Fields
checkSuites array
checkSuites[].id string
checkSuites[].repository object
checkSuites[].repository.id string
checkSuites[].repository.name string
checkSuites[].repository.owner object
checkSuites[].repository.owner.slug string
checkSuites[].repository.owner.id string
checkSuites[].repository.owner.type string
team, user. Omitted when unknown.checkSuites[].sha string
checkSuites[].key string
checkSuites[].name string
checkSuites[].detailsUrl string
checkSuites[].createdAt string
checkSuites[].updatedAt string
checkSuites[].externalId string
checkSuites[].actor object
checkSuites[].actor.user object
checkSuites[].actor.user.id string
checkSuites[].actor.user.email string
checkSuites[].actor.app object
checkSuites[].actor.app.id string
checkSuites[].actor.app.slug string
checkSuites[].actor.serviceAccount object
checkSuites[].actor.serviceAccount.id string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/commits/SHA/check-suites' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "checkSuites": [ { "id": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }, "sha": "string", "key": "string", "name": "string", "detailsUrl": "string", "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "externalId": "string", "actor": { "user": { "id": "string", "email": "string" } } } ], "nextPageToken": "string"}Commits and contents
A commit separates git-object metadata under commit from top-level repository relationships. List responses omit stats; Get Commit includes whole-commit aggregate stats. Changed files are returned only by the paginated List Commit Files collection. author and committer are git identities recorded in the commit, not Origin user objects.
A comparison is a summary only: it never embeds commit lists or file diffs. status is exactly identical, ahead, behind, or diverged; aheadBy and behindBy are commit counts. baseCommit, headCommit, and mergeBaseCommit use the sparse commit projection (no stats or files).
List Commits
/v1/origin/repos/{ownerSlug}/{repoName}/commitsLists commits on a branch or starting ref.
List results omit stats. Use Get Commit for aggregate stats and List Commit Files for the paginated file diff.
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
sha string
HEAD) to start listing from. Empty means the repo's default branch.pageSize integer
pageToken string
next_page_token. Empty for the first page. Encodes the starting ref and page, so sha/page_size on a follow-up request are ignored when a token is supplied.Response Fields
commits array
commits[].sha string
commits[].commit object
commits[].commit.author object
commits[].commit.author.name string
commits[].commit.author.email string
commits[].commit.author.date string
commits[].commit.committer object
commits[].commit.committer.name string
commits[].commit.committer.email string
commits[].commit.committer.date string
commits[].commit.message string
commits[].commit.tree object
commits[].commit.tree.sha string
commits[].parents array
commits[].parents[].sha string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/commits' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "commits": [ { "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ] } ], "nextPageToken": "string"}Get Commit
/v1/origin/repos/{ownerSlug}/{repoName}/commits/{sha}Returns a single commit by SHA or ref with whole-commit aggregate stats. It does not include changed files; use List Commit Files.
author and committer are git identities recorded in the commit, not Origin user objects.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
HEAD) of the commit to fetch.Response Fields
sha string
commit object
commit.author object
commit.author.name string
commit.author.email string
commit.author.date string
commit.committer object
commit.committer.name string
commit.committer.email string
commit.committer.date string
commit.message string
commit.tree object
commit.tree.sha string
parents array
parents[].sha string
stats object
stats.additions integer
stats.deletions integer
stats.total integer
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/commits/SHA' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ], "stats": { "additions": 0, "deletions": 0, "total": 0 }}List Commit Files
/v1/origin/repos/{ownerSlug}/{repoName}/commits/{sha}/filesLists the files changed by a commit.
sha may be a commit SHA, branch, tag, or symbolic ref such as HEAD. Results default to 30 files and are capped at 100. A page token fixes the resolved commit, page size, and file cursor; on later requests, sha and pageSize must match the token. Each file includes filename, status, additions, deletions, changes, patch, and previousFilename when renamed or copied. patch is empty for binary files.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
HEAD) of the commit whose files should be listed.Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. The token fixes the resolved commit, page size, and file cursor, so sha and page_size on a follow-up request must match the token.Response Fields
files array
files[].filename string
files[].status string
files[].additions integer
files[].deletions integer
files[].changes integer
files[].patch string
files[].previousFilename string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/commits/SHA/files' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "files": [ { "filename": "string", "status": "string", "additions": 0, "deletions": 0, "changes": 0, "patch": "string", "previousFilename": "string" } ], "nextPageToken": "string"}Compare Commits
/v1/origin/repos/{ownerSlug}/{repoName}/compare/{basehead}Compares commits, refs, or tags relative to their merge base. basehead is "{base}...{head}"; refs containing "/" must use their SHA.
base and head may each be a SHA, branch, tag, or symbolic ref such as HEAD. The response is an unpaginated summary: status is identical, ahead, behind, or diverged; the three commit objects are sparse and omit stats and files. No totalCommits, embedded commits, or files fields are returned. Unrelated histories return 404.
Path Parameters
ownerSlug string Required
repoName string Required
basehead string Required
"{base}...{head}", where either revision may be a SHA, branch, tag, or symbolic ref such as HEAD.Response Fields
status string
aheadBy integer
behindBy integer
baseCommit object
baseCommit.sha string
baseCommit.commit object
baseCommit.commit.author object
baseCommit.commit.author.name string
baseCommit.commit.author.email string
baseCommit.commit.author.date string
baseCommit.commit.committer object
baseCommit.commit.committer.name string
baseCommit.commit.committer.email string
baseCommit.commit.committer.date string
baseCommit.commit.message string
baseCommit.commit.tree object
baseCommit.commit.tree.sha string
baseCommit.parents array
baseCommit.parents[].sha string
headCommit object
headCommit.sha string
headCommit.commit object
headCommit.commit.author object
headCommit.commit.author.name string
headCommit.commit.author.email string
headCommit.commit.author.date string
headCommit.commit.committer object
headCommit.commit.committer.name string
headCommit.commit.committer.email string
headCommit.commit.committer.date string
headCommit.commit.message string
headCommit.commit.tree object
headCommit.commit.tree.sha string
headCommit.parents array
headCommit.parents[].sha string
mergeBaseCommit object
mergeBaseCommit.sha string
mergeBaseCommit.commit object
mergeBaseCommit.commit.author object
mergeBaseCommit.commit.author.name string
mergeBaseCommit.commit.author.email string
mergeBaseCommit.commit.author.date string
mergeBaseCommit.commit.committer object
mergeBaseCommit.commit.committer.name string
mergeBaseCommit.commit.committer.email string
mergeBaseCommit.commit.committer.date string
mergeBaseCommit.commit.message string
mergeBaseCommit.commit.tree object
mergeBaseCommit.commit.tree.sha string
mergeBaseCommit.parents array
mergeBaseCommit.parents[].sha string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/compare/BASE...HEAD' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "status": "identical", "aheadBy": 0, "behindBy": 0, "baseCommit": { "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ] }, "headCommit": { "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ] }, "mergeBaseCommit": { "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ] }}Get Contents
/v1/origin/repos/{ownerSlug}/{repoName}/contentsReturns file or directory contents at a ref. The file path is passed as the path query parameter (supports nested paths); omit or leave empty for the repository root directory. Files larger than 1 MiB (decoded) are rejected with FailedPrecondition (HTTP 400).
Files contain base64 content. Directories contain immediate children in entries. Directory entries are sparse children containing type, name, path, sha, and size; fetch a child path to read its content.
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
path string
ref string
HEAD) to read from. Empty means the repository's default branch.Response Fields
type string
encoding string
size string
name string
path string
sha string
content string
entries array
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/contents' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "type": "string", "encoding": "string", "size": "string", "name": "string", "path": "string", "sha": "string", "content": "string", "entries": [ { "type": "string", "encoding": "string", "size": "string", "name": "string", "path": "string", "sha": "string", "content": "string", "entries": [ { "type": "string", "encoding": "string", "size": "string", "name": "string", "path": "string", "sha": "string", "content": "string", "entries": [ {} ] } ] } ]}Batch Get Contents
/v1/origin/repos/{ownerSlug}/{repoName}/contents:batchGetReturns the contents of several explicit paths at a ref in one request. Each requested path yields a result marking whether it was found; a found path carries the same Content shape as GetContents (files as base64, directories as immediate entries, symlinks as files). Paths are matched exactly, with no globs or patterns, and at most 20 may be requested; duplicates are removed. Response results preserve first-seen request order. A single file larger than the Get Contents 1 MiB cap fails the whole batch with FailedPrecondition (HTTP 400). Uses POST because the path list travels in the request body.
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
paths array Required
ref string
HEAD) to read from. Empty means the repository's default branch.Response Fields
results array
results[].path string
results[].found boolean
results[].content object
results[].content.type string
results[].content.encoding string
results[].content.size string
results[].content.name string
results[].content.path string
results[].content.sha string
results[].content.content string
results[].content.entries array
resolvedCommitSha string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/contents:batchGet' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "paths": [ "PATH" ]}'Response shape:
{ "results": [ { "path": "string", "found": false, "content": { "type": "string", "encoding": "string", "size": "string", "name": "string", "path": "string", "sha": "string", "content": "string", "entries": [ {} ] } } ], "resolvedCommitSha": "string"}Git data
Low-level git objects. Scope: repository:contents:read. Empty repositories return 409.
Get Blob
/v1/origin/repos/{ownerSlug}/{repoName}/git/blobs/{sha}Returns a Git blob object by SHA. Default response is JSON with MIME-wrapped base64 content. Pass Accept: application/vnd.origin.raw+json (or application/vnd.origin.raw) on the REST surface to receive raw blob bytes instead. Blobs larger than 4 MiB (decoded) are rejected; fetch larger files by cloning the repository over Git HTTPS. Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
Response Fields
sha string
size integer
encoding string
content string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/blobs/SHA' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "sha": "string", "size": 0, "encoding": "string", "content": "string"}Get Git Commit
/v1/origin/repos/{ownerSlug}/{repoName}/git/commits/{sha}Returns a Git commit object by SHA (or resolvable revision). This is the low-level Git Database commit shape (flat author/message/tree), not the higher-level GetCommit resource under /commits/{sha}. sha accepts a commit SHA, branch, tag, or symbolic ref such as HEAD. Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
HEAD.Response Fields
sha string
author object
author.name string
author.email string
author.date string
committer object
committer.name string
committer.email string
committer.date string
message string
tree object
tree.sha string
parents array
parents[].sha string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/commits/SHA' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "sha": "string", "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" }, "parents": [ { "sha": "string" } ]}Get Git Ref
/v1/origin/repos/{ownerSlug}/{repoName}/git/ref/{ref}Returns a single Git reference by name. ref is typically heads/<branch> or tags/<tag> (with or without a leading refs/), or the symbolic HEAD. Exact match only; use ListMatchingGitRefs for prefixes. Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
ref string Required
heads/<branch> or tags/<tag>; a leading refs/ is accepted and normalized. The symbolic HEAD is also accepted (returned as ref: "HEAD" with the tip commit). Exact match on the full ref name.Response Fields
ref string
object object
object.type is "tag" and object.sha is the tag object SHA.object.sha string
object.type string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/ref/REF' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "ref": "string", "object": { "sha": "string", "type": "string" }}List Matching Git Refs
/v1/origin/repos/{ownerSlug}/{repoName}/git/matching-refsLists Git references whose names start with the given prefix. REST responses unwrap to a JSON array (via response_body). A trailing slash on ref is preserved (heads/ → refs/heads/). The symbolic HEAD is matched exactly (it is not under refs/). Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
ref string
heads/<prefix> or tags/<prefix>; a leading refs/ is accepted and normalized. Empty lists all refs (REST binding without a trailing path segment).Response Fields
The response is an array. Each item contains:
ref string
object object
object.type is "tag" and object.sha is the tag object SHA.object.sha string
object.type string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/matching-refs' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
[ { "ref": "string", "object": { "sha": "string", "type": "string" } }]List Matching Git Refs by Path
/v1/origin/repos/{ownerSlug}/{repoName}/git/matching-refs/{ref}Lists Git references whose names start with the given prefix. REST responses unwrap to a JSON array (via response_body). A trailing slash on ref is preserved (heads/ → refs/heads/). The symbolic HEAD is matched exactly (it is not under refs/). Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
ref string Required
heads/<prefix> or tags/<prefix>; a leading refs/ is accepted and normalized. Empty lists all refs (REST binding without a trailing path segment).Response Fields
The response is an array. Each item contains:
ref string
object object
object.type is "tag" and object.sha is the tag object SHA.object.sha string
object.type string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/matching-refs/REF' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
[ { "ref": "string", "object": { "sha": "string", "type": "string" } }]Get Tag
/v1/origin/repos/{ownerSlug}/{repoName}/git/tags/{sha}Returns an annotated Git tag object by SHA. Lightweight tags are not tag objects and return NotFound. Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
Response Fields
sha string
tag string
message string
tagger object
tagger.name string
tagger.email string
tagger.date string
object object
object.sha string
object.type string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/tags/SHA' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "sha": "string", "tag": "string", "message": "string", "tagger": { "name": "string", "email": "string", "date": "string" }, "object": { "sha": "string", "type": "string" }}Get Tree
/v1/origin/repos/{ownerSlug}/{repoName}/git/trees/{sha}Returns a Git tree object by SHA or resolvable revision. sha accepts a tree SHA, commit SHA, branch, tag, or symbolic ref such as HEAD. Set recursive=true (or 1) to walk the whole tree; omitting the parameter or passing any other value lists immediate children only. Recursive listings truncate at 100,000 entries or 7 MiB and set truncated=true. Empty repositories return 409 Conflict.
Path Parameters
ownerSlug string Required
repoName string Required
sha string Required
HEAD.Query Parameters
recursive boolean
true and 1 enable recursion; omitting the parameter or passing any other value (including false and 0) lists immediate children only.Response Fields
sha string
tree array
tree[].path string
tree[].mode string
tree[].type string
tree[].sha string
tree[].size integer
int32 ensures REST JSON emits a number; individual blobs over 2 GiB are not representable.truncated boolean
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/git/trees/SHA' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "sha": "string", "tree": [ { "path": "string", "mode": "string", "type": "string", "sha": "string", "size": 0 } ], "truncated": false}Labels
A label definition belongs to one repository and is addressed by its name. Assigning labels to a pull request is a separate surface; see Set Pull Request Labels.
List Labels
/v1/origin/repos/{ownerSlug}/{repoName}/labelsLists the labels defined on a repository, ordered by name.
Page tokens are bound to the repository they were minted for. A token replayed against a different repository, or any other malformed token, returns InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
pageSize integer
pageToken string
nextPageToken. Omit for the first page.Response Fields
labels array
labels[].id string
labels[].name string
labels[].color string
#.labels[].description string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "nextPageToken": "string"}Create Label
/v1/origin/repos/{ownerSlug}/{repoName}/labelsCreates a label on a repository.
A name already used by another label on the repository returns AlreadyExists (HTTP 409 Conflict). A color that is not six hexadecimal characters, a name longer than 50 characters, or a description longer than 255 characters returns InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
name string Required
color string Required
#. Uppercase input is stored lowercase.description string
Response Fields
id string
name string
color string
#.description string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "name": "LABEL_NAME", "color": "0e8a16"}'Response shape:
{ "id": "string", "name": "string", "color": "string", "description": "string"}Get Label
/v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}Returns a single repository label by name.
An unknown name returns 404. An empty labelName returns InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
labelName string Required
Response Fields
id string
name string
color string
#.description string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/labels/LABEL_NAME' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "name": "string", "color": "string", "description": "string"}Delete Label
/v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}Deletes a repository label by name. The response body is empty.
Deleting a label also removes it from every pull request it was assigned to. An unknown name returns 404. An empty labelName returns InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
labelName string Required
Response Fields
Successful requests return no response body.
curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/labels/LABEL_NAME' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response:
204 No ContentUpdate Label
/v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}Updates a repository label identified by its current name.
Omitted fields are left unchanged, and a request that omits all three returns the label as it stands. Renaming to a name another label already uses returns AlreadyExists (HTTP 409 Conflict). An unknown labelName returns 404.
Path Parameters
ownerSlug string Required
repoName string Required
labelName string Required
Request Body
name string
color string
#. Omit to leave unchanged.description string
Response Fields
id string
name string
color string
#.description string
curl --request PATCH \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/labels/LABEL_NAME' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "color": "b60205"}'Response shape:
{ "id": "string", "name": "string", "color": "string", "description": "string"}Pull requests
Closed or merged pull requests may additionally include closedAt, mergedAt, and mergeCommitSha. Treat head.ref and base.ref as opaque Origin ref strings; they may be short branch names or fully qualified refs/heads/… values.
Review verdict is approve, request_changes, or comment. submittedAt is absent for an unsubmitted draft review. dismissal is absent while the verdict remains active. Dismissed reviews remain visible in review listings. Reviews automatically superseded by a newer decision carry a server-generated message.
Comments expose a thread reference for grouping. Create-comment requests still accept the scalar threadId command parameter when replying. First-class thread resources and resolution APIs are not part of this release.
List Pull Requests
/v1/origin/repos/{ownerSlug}/{repoName}/pullsLists pull requests in a repo, optionally filtered by head branch, base branch, author, creation-time range, and state. Each pull request includes its assigned labels.
Results come back in creation order, newest first. Set direction=asc for oldest first. Page tokens embed the filters they were minted under, so a token replayed with different filters is rejected; restart pagination when a filter changes.
Path Parameters
ownerSlug string Required
repoName string Required
Query Parameters
head string
state string
pageSize integer
pageToken string
author string
pullRequests[].author.user.id, pullRequests[].author.app.id, or pullRequests[].author.serviceAccount.id (user_…, app_…, or sa_…). An author with no pull requests returns an empty list. Any other value, including the shared origin-cursor-managed-actor ID, returns InvalidArgument (HTTP 400).base string
main) or a fully qualified ref (refs/heads/main). Omit to list across every base.direction string
"desc" returns newest first and is the default; "asc" returns oldest first. Any other value returns InvalidArgument (HTTP 400).since string
2026-08-01T00:00:00Z. Returns only pull requests created at or after that instant. A malformed timestamp returns InvalidArgument (HTTP 400).until string
since. Returns only pull requests created at or before that instant. A malformed timestamp returns InvalidArgument (HTTP 400).Response Fields
pullRequests array
pullRequests[].id string
pullRequests[].number string
pullRequests[].state string
pullRequests[].draft boolean
pullRequests[].merged boolean
pullRequests[].title string
pullRequests[].body string
pullRequests[].head object
pullRequests[].head.ref string
pullRequests[].head.sha string
pullRequests[].base object
pullRequests[].base.ref string
pullRequests[].base.sha string
pullRequests[].author object
pullRequests[].author.user object
pullRequests[].author.user.id string
pullRequests[].author.user.email string
pullRequests[].author.app object
pullRequests[].author.app.id string
pullRequests[].author.app.slug string
pullRequests[].author.serviceAccount object
pullRequests[].author.serviceAccount.id string
pullRequests[].createdAt string
pullRequests[].updatedAt string
pullRequests[].closedAt string
pullRequests[].mergedAt string
pullRequests[].mergeCommitSha string
pullRequests[].additions integer
pullRequests[].deletions integer
pullRequests[].changedFiles integer
pullRequests[].labels array
pullRequests[].labels[].id string
pullRequests[].labels[].name string
pullRequests[].labels[].color string
#.pullRequests[].labels[].description string
pullRequests[].version object
pullRequests[].version.number string
pullRequests[].version.headSha string
pullRequests[].version.baseSha string
pullRequests[].version.createdAt string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "pullRequests": [ { "id": "string", "number": "string", "state": "string", "draft": false, "merged": false, "title": "string", "body": "string", "head": { "ref": "string", "sha": "string" }, "base": { "ref": "string", "sha": "string" }, "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "closedAt": "2026-01-01T00:00:00Z", "mergedAt": "2026-01-01T00:00:00Z", "mergeCommitSha": "string", "additions": 0, "deletions": 0, "changedFiles": 0, "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "version": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" } } ], "nextPageToken": "string"}Get Pull Request
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}Returns a single pull request, including its assigned labels.
Closed or merged pull requests may additionally include closedAt, mergedAt, and mergeCommitSha. Treat head.ref and base.ref as opaque Origin ref strings; they may be short branch names or fully qualified refs/heads/… values.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Response Fields
id string
number string
state string
draft boolean
merged boolean
title string
body string
head object
head.ref string
head.sha string
base object
base.ref string
base.sha string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
closedAt string
mergedAt string
mergeCommitSha string
additions integer
deletions integer
changedFiles integer
labels array
labels[].id string
labels[].name string
labels[].color string
#.labels[].description string
version object
version.number string
version.headSha string
version.baseSha string
version.createdAt string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "number": "string", "state": "string", "draft": false, "merged": false, "title": "string", "body": "string", "head": { "ref": "string", "sha": "string" }, "base": { "ref": "string", "sha": "string" }, "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "closedAt": "2026-01-01T00:00:00Z", "mergedAt": "2026-01-01T00:00:00Z", "mergeCommitSha": "string", "additions": 0, "deletions": 0, "changedFiles": 0, "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "version": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }}Create Pull Request
/v1/origin/repos/{ownerSlug}/{repoName}/pullsCreates a pull request from head into base.
Optional parent_pull_number stacks this change on another open or draft pull request in the same repository.
A title longer than 256 characters, or a body longer than 65,536 characters, returns InvalidArgument (HTTP 400). Both limits count Unicode code points.
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
title string Required
body string
head string Required
base string Required
draft boolean
parentPullNumber string
Response Fields
id string
number string
state string
draft boolean
merged boolean
title string
body string
head object
head.ref string
head.sha string
base object
base.ref string
base.sha string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
closedAt string
mergedAt string
mergeCommitSha string
additions integer
deletions integer
changedFiles integer
labels array
labels[].id string
labels[].name string
labels[].color string
#.labels[].description string
version object
version.number string
version.headSha string
version.baseSha string
version.createdAt string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "title": "TITLE", "head": "HEAD_BRANCH", "base": "BASE_BRANCH"}'Response shape:
{ "id": "string", "number": "string", "state": "string", "draft": false, "merged": false, "title": "string", "body": "string", "head": { "ref": "string", "sha": "string" }, "base": { "ref": "string", "sha": "string" }, "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "closedAt": "2026-01-01T00:00:00Z", "mergedAt": "2026-01-01T00:00:00Z", "mergeCommitSha": "string", "additions": 0, "deletions": 0, "changedFiles": 0, "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "version": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }}Update Pull Request
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}Updates a pull request's title, body, base branch, and/or lifecycle state.
Omitted fields are unchanged. Present fields are applied in order: metadata, then reopen/draft/ready-for-review, then base, then close. Close runs last so a same-request retarget can still see an open change; reopen runs before base so a closed pull can be retargeted. If a later step fails, earlier steps may already have been committed.
A title longer than 256 characters, or a body longer than 65,536 characters, returns InvalidArgument (HTTP 400). Both limits count Unicode code points.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
title string
body string
state string
"open" or "closed". "closed" closes the pull request. "open" without draft: true marks it ready for review, including publishing an existing draft. Merged is not writable. use MergePullRequest.draft boolean
true marks the pull request draft; false marks it ready for review (and reopens it if currently closed). Ignored when state is "closed".base string
Response Fields
id string
number string
state string
draft boolean
merged boolean
title string
body string
head object
head.ref string
head.sha string
base object
base.ref string
base.sha string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
closedAt string
mergedAt string
mergeCommitSha string
additions integer
deletions integer
changedFiles integer
labels array
labels[].id string
labels[].name string
labels[].color string
#.labels[].description string
version object
version.number string
version.headSha string
version.baseSha string
version.createdAt string
curl --request PATCH \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "title": "TITLE"}'Response shape:
{ "id": "string", "number": "string", "state": "string", "draft": false, "merged": false, "title": "string", "body": "string", "head": { "ref": "string", "sha": "string" }, "base": { "ref": "string", "sha": "string" }, "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "closedAt": "2026-01-01T00:00:00Z", "mergedAt": "2026-01-01T00:00:00Z", "mergeCommitSha": "string", "additions": 0, "deletions": 0, "changedFiles": 0, "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "version": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }}List Pull Request Comments
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/commentsLists every comment on a pull request in chronological order. Each comment includes its thread id so clients can group the flat response into threads.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Query Parameters
pageSize integer
pageToken string
Response Fields
comments array
comments[].id string
comments[].thread object
comments[].thread.id string
comments[].body string
comments[].author object
comments[].author.user object
comments[].author.user.id string
comments[].author.user.email string
comments[].author.app object
comments[].author.app.id string
comments[].author.app.slug string
comments[].author.serviceAccount object
comments[].author.serviceAccount.id string
comments[].createdAt string
comments[].updatedAt string
pullRequest object
pullRequest.id string
pullRequest.number string
pullRequest.repository object
pullRequest.repository.id string
pullRequest.repository.name string
pullRequest.repository.owner object
pullRequest.repository.owner.slug string
pullRequest.repository.owner.id string
pullRequest.repository.owner.type string
team, user. Omitted when unknown.nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/comments' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "comments": [ { "id": "string", "thread": { "id": "string" }, "body": "string", "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z" } ], "pullRequest": { "id": "string", "number": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } } }, "nextPageToken": "string"}Get Pull Request Comment
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/comments/{commentId}Returns a single pull request comment by its stable Origin id. A comment outside the authorized repository, or a pending-review comment not visible to the caller, returns 404.
Path Parameters
ownerSlug string Required
repoName string Required
commentId string Required
Response Fields
id string
thread object
thread.id string
body string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/comments/COMMENT_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "thread": { "id": "string" }, "body": "string", "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z"}Create Pull Request Comment
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/commentsCreates a general-discussion comment or reply on an Origin pull request. Omitting thread_id starts a new thread; providing it replies to that thread. These are general-discussion comments, not diff-anchored review comments. Bodies longer than 65,536 characters are rejected with InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
body string Required
threadId string
Response Fields
id string
thread object
thread.id string
body string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/comments' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "body": "BODY"}'Response shape:
{ "id": "string", "thread": { "id": "string" }, "body": "string", "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z"}Update Pull Request Comment
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/comments/{commentId}Updates a pull request comment by its stable Origin id.
Replaces the comment body. The comment must belong to the repository in the path, be visible to the caller, and have been authored by that caller. Cross-repository and hidden pending-review comments return 404; a visible comment owned by another actor returns 403. Bodies longer than 65,536 characters are rejected with InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
commentId string Required
Request Body
body string Required
Response Fields
id string
thread object
thread.id string
body string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
createdAt string
updatedAt string
curl --request PATCH \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/comments/COMMENT_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "body": "BODY"}'Response shape:
{ "id": "string", "thread": { "id": "string" }, "body": "string", "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z"}List Pull Request Commits
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/commitsLists the commits in a pull request.
Returns the pull request's commits as sparse Commit objects (no stats). Results default to 30 and are capped at 100, with at most 250 commits visible overall. A page token fixes the pull request version, page size, and commit cursor; pageSize must match the token on later requests, and a token that no longer matches the current head or base returns 400.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. The token is bound to the repository, pull request version, page size, and commit offset.Response Fields
commits array
commits[].sha string
commits[].commit object
commits[].commit.author object
commits[].commit.author.name string
commits[].commit.author.email string
commits[].commit.author.date string
commits[].commit.committer object
commits[].commit.committer.name string
commits[].commit.committer.email string
commits[].commit.committer.date string
commits[].commit.message string
commits[].commit.tree object
commits[].commit.tree.sha string
commits[].parents array
commits[].parents[].sha string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/commits' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "commits": [ { "sha": "string", "commit": { "author": { "name": "string", "email": "string", "date": "string" }, "committer": { "name": "string", "email": "string", "date": "string" }, "message": "string", "tree": { "sha": "string" } }, "parents": [ { "sha": "string" } ] } ], "nextPageToken": "string"}List Pull Request Files
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/filesLists the files changed in a pull request.
Returns filename, status, line counts, patch, and optional previous filename. Results default to 30 files and are capped at 100. A page token fixes the pull request version, page size, and file cursor; pageSize must match the token on later requests, and a token that no longer matches the current head or base returns 400.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Query Parameters
pageSize integer
pageToken string
next_page_token. Empty for the first page. The token is bound to the repository, pull request version, page size, and changed-file cursor.Response Fields
files array
files[].filename string
files[].status string
files[].additions integer
files[].deletions integer
files[].changes integer
files[].patch string
files[].previousFilename string
nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/files' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "files": [ { "filename": "string", "status": "string", "additions": 0, "deletions": 0, "changes": 0, "patch": "string", "previousFilename": "string" } ], "nextPageToken": "string"}List Pull Request Labels
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labelsLists every label assigned to a pull request, ordered by name.
The response carries the full assigned list rather than a page of it, so this endpoint takes no pagination parameters. A pull request can have at most 100 labels. An unknown pull request returns 404.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Response Fields
labels array
labels[].id string
labels[].name string
labels[].color string
#.labels[].description string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ]}Set Pull Request Labels
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labelsReplaces every label assigned to a pull request with the labels you name.
An empty list removes every assigned label. The labels must already exist in the repository; an unknown name or an unknown pull request returns 404. A pull request can have at most 100 labels, so naming more than 100 returns FailedPrecondition (HTTP 400). The response lists the labels assigned after the replacement, ordered by name.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
labels array
Response Fields
labels array
id, name, color, and description.curl --request PUT \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "labels": [ "LABEL_NAME" ]}'Response shape:
{ "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ]}Add Pull Request Labels
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labelsAdds existing repository labels to a pull request.
Labels already assigned to the pull request stay assigned. The labels must already exist in the repository; an unknown name or an unknown pull request returns 404. The request must name between 1 and 100 labels, and a pull request can have at most 100 labels in total, so a request that would take it past that limit returns FailedPrecondition (HTTP 400). The response lists the labels you named, not the pull request's full set; read the full set with List Pull Request Labels.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
labels array Required
Response Fields
labels array
id, name, color, and description.curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "labels": [ "LABEL_NAME" ]}'Response shape:
{ "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ]}Remove All Pull Request Labels
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labelsRemoves every label from a pull request.
The request succeeds when the pull request has no labels. An unknown pull request returns 404. The response body is empty.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Response Fields
Successful requests return no response body.
curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/labels' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response:
204 No ContentRemove Pull Request Label
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels/{labelName}Removes one label from a pull request.
A label that is not assigned to the pull request returns 404, as does an unknown pull request. The response lists the labels remaining on the pull request, ordered by name.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
labelName string Required
Response Fields
labels array
id, name, color, and description.curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/labels/LABEL_NAME' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ]}Merge Pull Request
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/mergeMerges a pull request into its base.
For a stacked pull request, merges the entire root-to-target prefix ending at this pull number. not only this pull. Supported only on native Origin repositories; mirrored repositories are rejected.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
expectedHeadSha string
ABORTED (HTTP 409 Conflict) and nothing merges. Values that are not a full commit SHA are rejected with InvalidArgument (HTTP 400). Omit to merge whatever the current head is. Not evaluated when the pull request is already merged, which returns idempotent success.Response Fields
mergeCommitSha string
mergedPullNumbers array
pullRequest object
pullRequest.id string
pullRequest.number string
pullRequest.state string
pullRequest.draft boolean
pullRequest.merged boolean
pullRequest.title string
pullRequest.body string
pullRequest.head object
pullRequest.head.ref string
pullRequest.head.sha string
pullRequest.base object
pullRequest.base.ref string
pullRequest.base.sha string
pullRequest.author object
pullRequest.author.user object
pullRequest.author.user.id string
pullRequest.author.user.email string
pullRequest.author.app object
pullRequest.author.app.id string
pullRequest.author.app.slug string
pullRequest.author.serviceAccount object
pullRequest.author.serviceAccount.id string
pullRequest.createdAt string
pullRequest.updatedAt string
pullRequest.closedAt string
pullRequest.mergedAt string
pullRequest.mergeCommitSha string
pullRequest.additions integer
pullRequest.deletions integer
pullRequest.changedFiles integer
pullRequest.labels array
pullRequest.labels[].id string
pullRequest.labels[].name string
pullRequest.labels[].color string
#.pullRequest.labels[].description string
pullRequest.version object
pullRequest.version.number string
pullRequest.version.headSha string
pullRequest.version.baseSha string
pullRequest.version.createdAt string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/merge' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "expectedHeadSha": "EXPECTED_HEAD_SHA" }'Response shape:
{ "mergeCommitSha": "string", "mergedPullNumbers": [ "string" ], "pullRequest": { "id": "string", "number": "string", "state": "string", "draft": false, "merged": false, "title": "string", "body": "string", "head": { "ref": "string", "sha": "string" }, "base": { "ref": "string", "sha": "string" }, "author": { "user": { "id": "string", "email": "string" } }, "createdAt": "2026-01-01T00:00:00Z", "updatedAt": "2026-01-01T00:00:00Z", "closedAt": "2026-01-01T00:00:00Z", "mergedAt": "2026-01-01T00:00:00Z", "mergeCommitSha": "string", "additions": 0, "deletions": 0, "changedFiles": 0, "labels": [ { "id": "string", "name": "string", "color": "string", "description": "string" } ], "version": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" } }}List Pull Request Reviews
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/reviewsLists submitted reviews on a pull request, ordered by submitted_at ascending. Pending reviews are omitted.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Query Parameters
pageSize integer
pageToken string
Response Fields
reviews array
reviews[].id string
reviews[].author object
reviews[].author.user object
reviews[].author.user.id string
reviews[].author.user.email string
reviews[].author.app object
reviews[].author.app.id string
reviews[].author.app.slug string
reviews[].author.serviceAccount object
reviews[].author.serviceAccount.id string
reviews[].verdict string
reviews[].body string
reviews[].submittedAt string
reviews[].pullRequestVersion object
reviews[].pullRequestVersion.number string
reviews[].pullRequestVersion.headSha string
reviews[].pullRequestVersion.baseSha string
reviews[].pullRequestVersion.createdAt string
reviews[].dismissal object
reviews[].dismissal.dismissedBy object
reviews[].dismissal.dismissedBy.user object
reviews[].dismissal.dismissedBy.user.id string
reviews[].dismissal.dismissedBy.user.email string
reviews[].dismissal.dismissedBy.app object
reviews[].dismissal.dismissedBy.app.id string
reviews[].dismissal.dismissedBy.app.slug string
reviews[].dismissal.dismissedBy.serviceAccount object
reviews[].dismissal.dismissedBy.serviceAccount.id string
reviews[].dismissal.dismissedAt string
reviews[].dismissal.message string
pullRequest object
pullRequest.id string
pullRequest.number string
pullRequest.repository object
pullRequest.repository.id string
pullRequest.repository.name string
pullRequest.repository.owner object
pullRequest.repository.owner.slug string
pullRequest.repository.owner.id string
pullRequest.repository.owner.type string
team, user. Omitted when unknown.nextPageToken string
curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/reviews' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "reviews": [ { "id": "string", "author": { "user": { "id": "string", "email": "string" } }, "verdict": "approve", "body": "string", "submittedAt": "2026-01-01T00:00:00Z", "pullRequestVersion": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }, "dismissal": { "dismissedBy": { "user": { "id": "string", "email": "string" } }, "dismissedAt": "2026-01-01T00:00:00Z", "message": "string" } } ], "pullRequest": { "id": "string", "number": "string", "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } } }, "nextPageToken": "string"}Create Pull Request Review
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/reviewsCreates and submits a review on a pull request.
The review is submitted immediately. A new approve or request_changes review supersedes the caller's prior live decision review on the same pull request, which is dismissed. Pull request authors cannot approve their own pull request. Fails with FAILED_PRECONDITION while the caller has an unsubmitted draft review on the pull request.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
Request Body
verdict string Required
PULL_REQUEST_REVIEW_VERDICT_UNSPECIFIED, approve, request_changes, comment.body string
versionNumber string
PullRequestVersion.number). Omit to review the latest version at call time.Response Fields
id string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
verdict string
body string
submittedAt string
pullRequestVersion object
pullRequestVersion.number string
pullRequestVersion.headSha string
pullRequestVersion.baseSha string
pullRequestVersion.createdAt string
dismissal object
dismissal.dismissedBy object
dismissal.dismissedBy.user object
dismissal.dismissedBy.user.id string
dismissal.dismissedBy.user.email string
dismissal.dismissedBy.app object
dismissal.dismissedBy.app.id string
dismissal.dismissedBy.app.slug string
dismissal.dismissedBy.serviceAccount object
dismissal.dismissedBy.serviceAccount.id string
dismissal.dismissedAt string
dismissal.message string
curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/reviews' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "verdict": "approve"}'Response shape:
{ "id": "string", "author": { "user": { "id": "string", "email": "string" } }, "verdict": "approve", "body": "string", "submittedAt": "2026-01-01T00:00:00Z", "pullRequestVersion": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }, "dismissal": { "dismissedBy": { "user": { "id": "string", "email": "string" } }, "dismissedAt": "2026-01-01T00:00:00Z", "message": "string" }}Update Pull Request Review
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/reviews/{reviewId}Updates the body of a review. Only the review author can update it; other callers receive PERMISSION_DENIED. A review that does not belong to the named pull request returns NOT_FOUND.
Unsubmitted draft reviews can be updated too; a draft's response has no submitted_at.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
reviewId string Required
Request Body
body string Required
Response Fields
id string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
verdict string
body string
submittedAt string
pullRequestVersion object
pullRequestVersion.number string
pullRequestVersion.headSha string
pullRequestVersion.baseSha string
pullRequestVersion.createdAt string
dismissal object
dismissal.dismissedBy object
dismissal.dismissedBy.user object
dismissal.dismissedBy.user.id string
dismissal.dismissedBy.user.email string
dismissal.dismissedBy.app object
dismissal.dismissedBy.app.id string
dismissal.dismissedBy.app.slug string
dismissal.dismissedBy.serviceAccount object
dismissal.dismissedBy.serviceAccount.id string
dismissal.dismissedAt string
dismissal.message string
curl --request PATCH \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/reviews/REVIEW_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "body": "BODY"}'Response shape:
{ "id": "string", "author": { "user": { "id": "string", "email": "string" } }, "verdict": "approve", "body": "string", "submittedAt": "2026-01-01T00:00:00Z", "pullRequestVersion": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }, "dismissal": { "dismissedBy": { "user": { "id": "string", "email": "string" } }, "dismissedAt": "2026-01-01T00:00:00Z", "message": "string" }}Dismiss Pull Request Review
/v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/reviews/{reviewId}/dismissalsDismisses a submitted review so its verdict no longer counts toward the pull request's review state. The review itself is retained and keeps appearing in ListPullRequestReviews, with dismissal set.
Dismissing does not require having authored the review; write permission on the repository's pull request reviews is sufficient.
Only approve and request_changes reviews can be dismissed, and only once: a comment review, an unsubmitted draft review, or an already-dismissed review returns FAILED_PRECONDITION, and repeating the call leaves the first dismissal in place. A review that does not belong to the named pull request returns NOT_FOUND.
Path Parameters
ownerSlug string Required
repoName string Required
pullNumber string Required
reviewId string Required
Request Body
message string Required
Response Fields
id string
author object
author.user object
author.user.id string
author.user.email string
author.app object
author.app.id string
author.app.slug string
author.serviceAccount object
author.serviceAccount.id string
verdict string
body string
submittedAt string
pullRequestVersion object
pullRequestVersion.number string
pullRequestVersion.headSha string
pullRequestVersion.baseSha string
pullRequestVersion.createdAt string
dismissal object
dismissal.dismissedBy object
dismissal.dismissedBy.user object
dismissal.dismissedBy.user.id string
dismissal.dismissedBy.user.email string
dismissal.dismissedBy.app object
dismissal.dismissedBy.app.id string
dismissal.dismissedBy.app.slug string
dismissal.dismissedBy.serviceAccount object
dismissal.dismissedBy.serviceAccount.id string
dismissal.dismissedAt string
dismissal.message string
curl --request PUT \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/pulls/PULL_NUMBER/reviews/REVIEW_ID/dismissals' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "message": "MESSAGE"}'Response shape:
{ "id": "string", "author": { "user": { "id": "string", "email": "string" } }, "verdict": "approve", "body": "string", "submittedAt": "2026-01-01T00:00:00Z", "pullRequestVersion": { "number": "string", "headSha": "string", "baseSha": "string", "createdAt": "2026-01-01T00:00:00Z" }, "dismissal": { "dismissedBy": { "user": { "id": "string", "email": "string" } }, "dismissedAt": "2026-01-01T00:00:00Z", "message": "string" }}Rulesets
List Rulesets
/v1/origin/repos/{ownerSlug}/{repoName}/rulesetsLists every ruleset configured on a repository.
Rulesets per repository are bounded configuration, so the full set comes back in one response and this endpoint does not paginate. repository is hoisted once and describes the repository shared by every ruleset in the response.
Path Parameters
ownerSlug string Required
repoName string Required
Response Fields
rulesets array
rulesets[].id string
rulesets[].name string
rulesets[].description string
rulesets[].enforcement string
active, evaluate, disabled.rulesets[].kind string
merge_branch, push_branch, push_tag, push_repository.rulesets[].includedRefNames array
~ALL and ~DEFAULT_BRANCH.rulesets[].excludedRefNames array
rulesets[].includedRefNames.rulesets[].rules array
rulesets[].rules[].id string
rulesets[].rules[].ruleType string
pull_request, require_status_checks, require_branch_up_to_date, deletion, or non_fast_forward.rulesets[].rules[].parameters object
rulesets[].rules[].ruleType.rulesets[].bypassActors array
rulesets[].bypassActors[].id string
rulesets[].bypassActors[].bypassMode string
always, pull_request_only.rulesets[].bypassActors[].user object
user, team, app, or originRole is present.rulesets[].bypassActors[].user.id string
rulesets[].bypassActors[].team object
rulesets[].bypassActors[].team.organizationPublicId string
rulesets[].bypassActors[].team.groupPublicId string
rulesets[].bypassActors[].app object
rulesets[].bypassActors[].app.id string
app_.rulesets[].bypassActors[].originRole object
rulesets[].bypassActors[].originRole.role string
namespace_admin, repository_admin, repository_write.repository object
repository.id string
repository.name string
repository.owner object
repository.owner.slug string
repository.owner.id string
repository.owner.type string
team, user. Omitted when unknown.curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/rulesets' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "rulesets": [ { "id": "string", "name": "string", "description": "string", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "string" ], "excludedRefNames": [ "string" ], "rules": [ { "id": "string", "ruleType": "string", "parameters": {} } ], "bypassActors": [ { "id": "string", "bypassMode": "always", "user": { "id": "string" } } ] } ], "repository": { "id": "string", "name": "string", "owner": { "slug": "string", "id": "string", "type": "team" } }}Create Ruleset
/v1/origin/repos/{ownerSlug}/{repoName}/rulesetsCreates a repository ruleset.
The response carries the stored ruleset, including the IDs Origin assigns to each rule and bypass actor. An empty name is rejected with InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
Request Body
name string Required
description string
enforcement string Required
active, evaluate, disabled.kind string Required
merge_branch, push_branch, push_tag, push_repository.includedRefNames array
~ALL and ~DEFAULT_BRANCH. Values above 64 entries are rejected with InvalidArgument (HTTP 400).excludedRefNames array
includedRefNames.rules array
ruleType and optional parameters; Origin assigns each rule's id. Values above 20 entries are rejected with InvalidArgument (HTTP 400).bypassActors array
bypassMode and exactly one of user, team, app, or originRole; Origin assigns each actor's id. Values above 15 entries are rejected with InvalidArgument (HTTP 400).Response Fields
id string
name string
description string
enforcement string
active, evaluate, disabled.kind string
merge_branch, push_branch, push_tag, push_repository.includedRefNames array
~ALL and ~DEFAULT_BRANCH.excludedRefNames array
includedRefNames.rules array
rules[].id string
rules[].ruleType string
pull_request, require_status_checks, require_branch_up_to_date, deletion, or non_fast_forward.rules[].parameters object
rules[].ruleType.bypassActors array
bypassActors[].id string
bypassActors[].bypassMode string
always, pull_request_only.bypassActors[].user object
user, team, app, or originRole is present.bypassActors[].user.id string
bypassActors[].team object
bypassActors[].team.organizationPublicId string
bypassActors[].team.groupPublicId string
bypassActors[].app object
bypassActors[].app.id string
app_.bypassActors[].originRole object
bypassActors[].originRole.role string
namespace_admin, repository_admin, repository_write.curl --request POST \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/rulesets' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "name": "RULESET_NAME", "description": "RULESET_DESCRIPTION", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "~DEFAULT_BRANCH" ], "excludedRefNames": [], "rules": [ { "ruleType": "pull_request", "parameters": {} } ], "bypassActors": [ { "bypassMode": "always", "app": { "id": "APP_ID" } } ]}'Response shape:
{ "id": "string", "name": "string", "description": "string", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "string" ], "excludedRefNames": [ "string" ], "rules": [ { "id": "string", "ruleType": "string", "parameters": {} } ], "bypassActors": [ { "id": "string", "bypassMode": "always", "user": { "id": "string" } } ]}Get Ruleset
/v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}Returns a single repository ruleset by its stable Origin ID.
An unknown repository and an unknown ruleset both return 404; the message distinguishes them.
Path Parameters
ownerSlug string Required
repoName string Required
rulesetId string Required
Response Fields
id string
name string
description string
enforcement string
active, evaluate, disabled.kind string
merge_branch, push_branch, push_tag, push_repository.includedRefNames array
~ALL and ~DEFAULT_BRANCH.excludedRefNames array
includedRefNames.rules array
rules[].id string
rules[].ruleType string
pull_request, require_status_checks, require_branch_up_to_date, deletion, or non_fast_forward.rules[].parameters object
rules[].ruleType.bypassActors array
bypassActors[].id string
bypassActors[].bypassMode string
always, pull_request_only.bypassActors[].user object
user, team, app, or originRole is present.bypassActors[].user.id string
bypassActors[].team object
bypassActors[].team.organizationPublicId string
bypassActors[].team.groupPublicId string
bypassActors[].app object
bypassActors[].app.id string
app_.bypassActors[].originRole object
bypassActors[].originRole.role string
namespace_admin, repository_admin, repository_write.curl --request GET \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/rulesets/RULESET_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response shape:
{ "id": "string", "name": "string", "description": "string", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "string" ], "excludedRefNames": [ "string" ], "rules": [ { "id": "string", "ruleType": "string", "parameters": {} } ], "bypassActors": [ { "id": "string", "bypassMode": "always", "user": { "id": "string" } } ]}Update Ruleset
/v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}Updates an existing repository ruleset.
The request replaces the whole ruleset configuration. rules and bypassActors are replaced in full rather than merged, and Origin assigns new IDs to the stored entries, so send every rule and bypass actor you want to keep.
Path Parameters
ownerSlug string Required
repoName string Required
rulesetId string Required
Request Body
name string Required
description string
enforcement string Required
active, evaluate, disabled.kind string Required
merge_branch, push_branch, push_tag, push_repository.includedRefNames array
~ALL and ~DEFAULT_BRANCH. Values above 64 entries are rejected with InvalidArgument (HTTP 400).excludedRefNames array
includedRefNames.rules array
ruleType and optional parameters; Origin assigns each rule's id. Values above 20 entries are rejected with InvalidArgument (HTTP 400).bypassActors array
bypassMode and exactly one of user, team, app, or originRole; Origin assigns each actor's id. Values above 15 entries are rejected with InvalidArgument (HTTP 400).Response Fields
id string
name string
description string
enforcement string
active, evaluate, disabled.kind string
merge_branch, push_branch, push_tag, push_repository.includedRefNames array
~ALL and ~DEFAULT_BRANCH.excludedRefNames array
includedRefNames.rules array
rules[].id string
rules[].ruleType string
pull_request, require_status_checks, require_branch_up_to_date, deletion, or non_fast_forward.rules[].parameters object
rules[].ruleType.bypassActors array
bypassActors[].id string
bypassActors[].bypassMode string
always, pull_request_only.bypassActors[].user object
user, team, app, or originRole is present.bypassActors[].user.id string
bypassActors[].team object
bypassActors[].team.organizationPublicId string
bypassActors[].team.groupPublicId string
bypassActors[].app object
bypassActors[].app.id string
app_.bypassActors[].originRole object
bypassActors[].originRole.role string
namespace_admin, repository_admin, repository_write.curl --request PUT \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/rulesets/RULESET_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \ --header 'Content-Type: application/json' \ --data '{ "name": "RULESET_NAME", "description": "RULESET_DESCRIPTION", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "~DEFAULT_BRANCH" ], "excludedRefNames": [], "rules": [ { "ruleType": "pull_request", "parameters": {} } ], "bypassActors": [ { "bypassMode": "always", "app": { "id": "APP_ID" } } ]}'Response shape:
{ "id": "string", "name": "string", "description": "string", "enforcement": "active", "kind": "merge_branch", "includedRefNames": [ "string" ], "excludedRefNames": [ "string" ], "rules": [ { "id": "string", "ruleType": "string", "parameters": {} } ], "bypassActors": [ { "id": "string", "bypassMode": "always", "user": { "id": "string" } } ]}Delete Ruleset
/v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}Deletes a repository ruleset by its stable Origin ID. The response body is empty.
An unknown repository and an unknown ruleset both return 404; the message distinguishes them. A ruleset stored on a different repository reads as an unknown ruleset. An empty rulesetId returns InvalidArgument (HTTP 400).
Path Parameters
ownerSlug string Required
repoName string Required
rulesetId string Required
Response Fields
Successful requests return no response body.
curl --request DELETE \ --url 'https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME/rulesets/RULESET_ID' \ --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'Response:
204 No Content