Skip to main content

Command Palette

Search for a command to run...

API

Origin API

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.

Overview

Origin apps implement an OAuth-style installation consent and GitHub App–style authentication model:

  1. The app signs a short-lived EdDSA JWT with its Ed25519 private key.
  2. The app exchanges that JWT and an installation ID for a short-lived installation access token (oit_…).
  3. The installation token calls repository APIs and authenticates Git over HTTPS within the installation's approved repositories and scopes.
  4. Origin sends signed webhook deliveries to the app's registered webhook URL.

Base URL

https://api.cursor.com/v1/origin

Endpoint 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.

Responses carry fields that sit at their default value rather than dropping them, so a false boolean, a 0 number, an empty string, and an empty array are all present in the body. Read the value itself instead of treating a missing key as the default. Fields documented as absent or omitted are optional in the contract and stay out of the body when they are unset.

Getting started

Origin access

Origin CLI

Install the Origin CLI and sign in:

curl -fsSL https://downloads.cursor.com/origin/install.sh | shorigin auth login

Clone 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
ParameterRequiredDescription
client_idYesOrigin App ID.
scopeYesSpace-separated scopes. repository:metadata:read is added automatically.
redirect_uriYes for partner-initiated installsExact registered callback URI.
stateStrongly recommendedRandom anti-forgery value echoed as the state claim of the installation receipt. Generate it before redirecting and verify the claim on callback.
summaryNoShort explanation displayed during consent.
include_granted_scopesNoWhen 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_JWT

Verify 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",    "displayName": "Jane Doe"  },  "state": "ORIGINAL_VALUE"}
  • aud is your app ID and sub is the installation ID to use when minting installation access tokens.
  • namespace_id is the stable ID of the namespace the app was installed into.
  • installedBy identifies the user who performed this install or re-consent. It describes the current action, so on a re-consent it can differ from the durable installedBy on Get App Installation. It carries displayName when the account has a name, and never carries handle; read the handle from a REST response or a webhook payload instead.
  • Receipts expire five minutes after issuance. jti is unique per receipt.
  • state is present only when the install URL carried a non-empty state, 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 with the Bearer scheme. Each endpoint's Auth badges list the credential types it accepts:

curl --request GET \  --url https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME \  --header "Authorization: Bearer $ORIGIN_BEARER_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.

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.pem

The 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_JWT

Use 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.

Git over HTTPS meters its own budget, separate from the REST budget in Rate limits. A charged Git response carries the same X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Used headers, with X-RateLimit-Resource set to git rather than core. Over-budget Git requests return 429 with Retry-After and X-RateLimit-Reset. Read the headers to pace a job rather than assuming a number; unmetered requests carry no rate-limit headers.

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

Use origin api for user-authenticated requests. For an interactive session, sign in through your browser:

origin auth loginorigin api /repos/OWNER_SLUG/REPO_NAME/pulls

For a non-interactive session, provide a personal user API key from Cursor Dashboard → API Keys:

export CURSOR_API_KEY="YOUR_PERSONAL_USER_API_KEY"origin api /repos/OWNER_SLUG/REPO_NAME/pulls

The CLI exchanges the personal API key for a short-lived user access token, then sends that token in the Authorization header. Do not send the API key itself to an Origin endpoint. 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. /keys sends Cache-Control: public, max-age=600, stale-if-error=600, so reuse a cached response for 10 minutes, then refresh; if a refresh fails, keep the last good keys for at most another 10 minutes before failing verification. Also refresh on a signature that no key verifies, which drops a retired key ID. Keys rotate weekly.

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.

ScopeAllows
repository:metadata:readRead repository metadata. Added automatically.
repository:contents:readRead commits, branches, contents, comparison files, and low-level Git objects. Search file text. Download a repository archive. Clone, fetch, and pull over Git HTTPS. Sync a mirrored repository from its upstream source.
repository:contents:writePush over Git HTTPS. Merge pull requests. Create branches and commit file changes through the Git data endpoints. Re-request a check run.
repository:pull_requests:readRead pull requests, changed files, pull request commits, assigned labels, and merge eligibility.
repository:pull_requests:writeCreate and update pull requests. Assign and remove pull request labels.
repository:pull_requests:reviews:readRead pull request comments, comment threads, submitted reviews, and requested reviewers.
repository:pull_requests:reviews:writeCreate and update comments; resolve and reopen comment threads; create, update, and dismiss reviews; request and remove reviewers.
repository:checks:readRead check suites, runs, and check run annotations.
repository:checks:writeCreate and update check suites and runs. Append check run annotations.
repository:labels:readRead the label definitions a repository owns.
repository:labels:writeCreate, update, and delete repository label definitions.
repository:rulesets:readRead repository rulesets.
repository:rulesets:writeCreate, update, and delete repository rulesets.
repository:settings:readRead the grants held directly on a repository.
repository:settings:writeUpdate repository settings: the default branch, visibility, merge methods, and automatic head-branch deletion. Upsert and delete grants on a repository.
namespace:settings:readRead the grants held directly on an owner.
namespace:settings:writeUpsert and delete grants on an owner.

Requesting a :write scope also grants the matching :read scope, so repository:labels:write covers repository:labels:read and you do not have to list both. The reverse does not hold: a read scope never grants writes.

The installation token can only narrow these grants. It cannot add a scope or repository the workspace admin did not approve.

Mirror-state changes sit outside this table. Transition Repo Mirror, Force Repo Mirror Cutover, and Detach Repo Mirror take repository:mirror:write or repository:mirror:delete, which an app cannot request at installation: they are carried by a Cursor user credential, and the caller must also administer the repository on the mirror's upstream source.

Installation management sits outside it too. Add App Installation Repositories takes namespace:installations:write, which an app cannot request at installation: a namespace admin holds it on a Cursor user credential, and the same credential kind that consented to the installation is the one that can extend it.

App management sits outside it for the same reason. Create App takes namespace:apps:create, List Namespace Apps and Get App take namespace:apps:read, and Update App, Add App Signing Key, and Revoke App Signing Key take app:settings:write. A publisher holds these on a Cursor user credential; an app cannot request them for itself.

The table covers the scopes an app requests at installation. To look up the scope a single operation requires, read its x-origin-scopes extension in the OpenAPI specification. That extension covers every operation, including the app, installation, and namespace scopes that come with the credential itself rather than from an installation grant. An operation whose scopes all come with the credential marks its extension ambient: true: there is nothing to request for it, and presenting the right credential is enough.

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:read
  • repository: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.

Moving a repository out of that state is a user-credential operation rather than something an installation can do: Transition Repo Mirror advances the mirror direction, Force Repo Mirror Cutover cuts over to the upstream source without pushing divergent refs back, and Detach Repo Mirror disconnects the mirror for good.

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:

PrincipalDefault budget
Installation access token3,000 points/minute
App JWT6,000 points/minute
Cursor user or service account600 points/minute

Every endpoint charges a fixed cost against that budget before the handler runs. Authentication and authorization failures are not charged.

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:

HeaderDescription
X-RateLimit-LimitPoints available in the current window for this principal
X-RateLimit-RemainingPoints left in the current window
X-RateLimit-UsedPoints consumed in the current window
X-RateLimit-ResetUnix timestamp (UTC seconds) when the window resets
X-RateLimit-ResourceAlways 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, with X-RateLimit-Remaining set to 0
{  "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.

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.

A 404 never distinguishes a resource that does not exist from one your app cannot reach. Read it as "not available to this installation" rather than as proof the resource is absent.

details carries typed entries: google.rpc.BadRequest field violations on an invalid argument, and a google.rpc.RequestInfo entry on every error. Origin can add detail types at any time, so ignore entries your integration does not recognize.

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:

  • RepositoryReference identifies a repository.
  • PullRequestReference identifies a pull request and nests its repository reference.
  • ThreadReference identifies the thread containing a pull request comment.
  • OriginActor identifies a public actor as one of user, app, or serviceAccount. 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 an embedded commit list. Changed files have their own paginated endpoint, List Comparison Files.
  • Threads are addressable only for resolution. There is no endpoint that lists threads directly; read them from the comments they contain.
  • 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 state from 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 key values stable and readable. Use a new immutable externalId for each retry and increasing externalUpdatedAt values for updates.
  • Verify webhook signatures against the raw request body before parsing.
  • Deduplicate deliveries with webhook-id and process asynchronously after returning 2xx.
  • Ignore unknown JSON fields for forward compatibility.
  • Honor Retry-After and X-RateLimit-* headers. Use Get Rate Limit to monitor remaining points without consuming them.

Endpoint reference

Download the OpenAPI specification for the complete component schemas. The document declares https://api.cursor.com as its server and a bearerAuth HTTP bearer security scheme, and each operation lists the response codes that operation can return, plus a request and response example. Every operation also carries an x-origin-scopes extension: scopes holds the scope the operation requires, and tokenTypes holds the credential kinds it accepts. Path parameters carry the same names the URLs use, ownerSlug and repoName. Every operation carries a unique operationId; where one operation answers two URL shapes, the second shape's id takes a _2 suffix, as in OriginService_GetRepoTarball_2.

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

GET/v1/origin/rate_limit
AuthApp JWTInstallation tokenUser access token

Returns 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

Rate limit resources for the authenticated principal.

resources.core object

Shared per-minute point budget for public API endpoints.

resources.core.limit integer

Maximum points available in the current window.

resources.core.remaining integer

Points remaining in the current window.

resources.core.reset integer

Unix timestamp (UTC seconds) when the current window resets.

resources.core.used integer

Points consumed in the current window.

rate object

Alias of 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": 6000,      "remaining": 5994,      "reset": 1785682800,      "used": 6    }  },  "rate": {    "limit": 6000,    "remaining": 5994,    "reset": 1785682800,    "used": 6  }}

Get Authenticated App

GET/v1/origin/app
AuthApp JWT

Returns metadata for the authenticated app.

Response Fields

id string

Origin app identifier used as the JWT issuer and key ID.

displayName string

Human-readable app display name.

webhookUrl string

Registered HTTPS URL that receives the app's webhook deliveries.

events array

Webhook event subscriptions configured for the app.

createdAt string

RFC 3339 timestamp for app creation.

updatedAt string

RFC 3339 timestamp for the latest app metadata update.

installationRedirectUris array

Registered installation callback URIs; non-local callbacks must match exactly and use HTTPS.

namespaceSlug string

Slug of the namespace that owns the app.

description string

Publisher-provided app description. Empty when unset.

websiteUrl string

Publisher website. Empty when unset.

defaultScopes array

Default scopes offered when the app is installed, as catalog scope strings.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/app' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "id": "app_01k2ja2000e0080000000000a1",  "displayName": "CI Status Bot",  "webhookUrl": "https://ci.acme.dev/webhooks/origin",  "events": [    "pull_request.created",    "pull_request.merged"  ],  "createdAt": "2026-08-01T09:30:00Z",  "updatedAt": "2026-08-02T14:45:00Z",  "installationRedirectUris": [    "https://ci.acme.dev/origin/setup"  ],  "namespaceSlug": "acme",  "description": "Posts CI status on pull requests.",  "websiteUrl": "https://ci.acme.dev",  "defaultScopes": [    "repository:contents:read",    "repository:pull_requests:read"  ]}

List App Installations

GET/v1/origin/app/installations
AuthApp JWT

Lists installations for the authenticated app.

Query Parameters

pageSize integer

Max installations to return. Defaults to 30 when unset or 0. Values above 100 are clamped to 100.

pageToken string

Opaque cursor from a previous response's next_page_token. Empty for the first page.

Response Fields

installations array

Page of installations owned by the authenticated app.

installations[].id string

Installation identifier that the app stores and uses to mint installation access tokens.

installations[].appId string

Identifier of the installed app.

installations[].target object

Owner selected by the customer for this installation.

installations[].target.slug string

URL-facing owner slug used with the owner ID to identify the repository owner.

installations[].target.id string

Origin owner identifier.

installations[].target.type string

Owner namespace type. Output-only. Allowed values: team, user. Omitted when unknown.

installations[].createdAt string

RFC 3339 installation creation timestamp.

installations[].updatedAt string

RFC 3339 timestamp for the latest installation update.

installations[].repoSelectionMode string

Repository grant mode; exactly all or selected.

installations[].scopes array

Scopes approved for the installation.

installations[].installedBy object

The user who originally installed the app, not the most recent re-consent actor. Output-only. Absent when that user record can no longer be read.

installations[].installedBy.id string

Public identifier for the user, prefixed user_.

installations[].installedBy.email string

Email address of the user.

installations[].installedBy.displayName string

Display name of the user: the account's first and last name joined with a space, the same name the product renders. Omitted when the account has no name.

installations[].installedBy.handle string

The user's claimed profile handle, without the @ prefix. Present only while that profile is publicly visible; omitted otherwise.

installations[].suspendedAt string

RFC 3339 timestamp set while the installation is suspended. Omitted while the installation is active.

installations[].deletedAt string

RFC 3339 timestamp for the installation's deletion. Carried only on the installation.deleted webhook snapshot; a deleted installation no longer resolves through the API, so this endpoint never returns it.

nextPageToken string

Opaque cursor for the next page; empty when there are no more pages.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/app/installations' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "installations": [    {      "id": "inst_01k2ja2000e0080000000000b2",      "appId": "app_01k2ja2000e0080000000000a1",      "target": {        "slug": "acme",        "id": "ns_01k2ja2000e0080000000000p3",        "type": "team"      },      "createdAt": "2026-08-01T09:30:00Z",      "updatedAt": "2026-08-02T14:45:00Z",      "repoSelectionMode": "selected",      "scopes": [        "repository:contents:read",        "repository:pull_requests:read"      ]    }  ]}

Get App Installation

GET/v1/origin/app/installations/{installationId}
AuthApp JWT

Returns a single installation for the authenticated app.

repoSelectionMode is all or selected.

Path Parameters

installationId string Required

Installation identifier.

Response Fields

id string

Installation identifier that the app stores and uses to mint installation access tokens.

appId string

Identifier of the installed app.

target object

Owner selected by the customer for this installation.

target.slug string

URL-facing owner slug used with the owner ID to identify the repository owner.

target.id string

Origin owner identifier.

target.type string

Owner namespace type. Output-only. Allowed values: team, user. Omitted when unknown.

createdAt string

RFC 3339 installation creation timestamp.

updatedAt string

RFC 3339 timestamp for the latest installation update.

repoSelectionMode string

Repository grant mode; exactly all or selected.

scopes array

Scopes approved for the installation.

installedBy object

The user who originally installed the app, not the most recent re-consent actor. Output-only. Absent when that user record can no longer be read.

installedBy.id string

Public identifier for the user, prefixed user_.

installedBy.email string

Email address of the user.

installedBy.displayName string

Display name of the user: the account's first and last name joined with a space, the same name the product renders. Omitted when the account has no name.

installedBy.handle string

The user's claimed profile handle, without the @ prefix. Present only while that profile is publicly visible; omitted otherwise.

suspendedAt string

RFC 3339 timestamp set while the installation is suspended. Omitted while the installation is active.

deletedAt string

RFC 3339 timestamp for the installation's deletion. Carried only on the installation.deleted webhook snapshot; a deleted installation no longer resolves through the API, so this endpoint never returns it.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/app/installations/INSTALLATION_ID' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "id": "inst_01k2ja2000e0080000000000b2",  "appId": "app_01k2ja2000e0080000000000a1",  "target": {    "slug": "acme",    "id": "ns_01k2ja2000e0080000000000p3",    "type": "team"  },  "createdAt": "2026-08-01T09:30:00Z",  "updatedAt": "2026-08-02T14:45:00Z",  "repoSelectionMode": "selected",  "scopes": [    "repository:contents:read",    "repository:pull_requests:read"  ]}

Delete App Installation

DELETE/v1/origin/app/installations/{installationId}
AuthApp JWT

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

The unique identifier of the installation to delete. Bound from the URL path; the installation must belong to the authenticated app.

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 Content

Create Installation Access Token

POST/v1/origin/app/installations/{installationId}/access_tokens
AuthApp JWT

Creates 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

The unique identifier of the installation to scope the token to. Bound from the URL path; the installation must belong to the authenticated app.

Request Body

scopes array

Scope strings to grant the token. Values must be unique and included in the installation's accepted scopes. Empty or omitted inherits the full scope grant.

repositoryIds array

Repository IDs to grant the token. Values must be unique, accessible to the installation, and contain at most 50 entries. Empty or omitted inherits all accessible repositories.

Response Fields

token string

Short-lived installation credential with the oit_ prefix.

expiresAt string

RFC 3339 expiration time; the token expires after at most 15 minutes and never outlives the app JWT used to mint it.
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 '{  "scopes": [    "repository:contents:read",    "repository:pull_requests:read"  ],  "repositoryIds": [    "repo_01k2ja2000e0080000000000q4"  ]}'

Response shape:

{  "token": "oit_2v8xkq4m1c7p9t3w5y0z6r4b",  "expiresAt": "2026-08-01T10:30:00Z"}

List App Installation Repositories

GET/v1/origin/installation/repos
AuthInstallation token

Lists 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

Max repositories to return. Defaults to 30 when unset or 0. Values above 100 are clamped to 100.

pageToken string

Opaque cursor from a previous response's next_page_token. Empty for the first page. The same filter must be used when requesting subsequent pages.

filter string

Optional case-insensitive substring filter applied to repository names and owner namespaces. A single-slash owner/repo value matches each half against its corresponding field. Leading and trailing whitespace is ignored; an empty value applies no filter.

Response Fields

repositories array

Sparse repository summaries; use get-repository for full timestamps.

repositories[].id string

Origin repository identifier.

repositories[].name string

Repository name within its owner.

repositories[].fullName string

Combined owner and repository name, such as acme/api.

repositories[].owner object

Owner reference for the repository.

repositories[].owner.slug string

URL-facing owner slug used with the owner ID to identify the repository owner.

repositories[].owner.id string

Origin owner identifier.

repositories[].owner.type string

Owner namespace type. Output-only. Allowed values: team, user. Omitted when unknown.

repositories[].defaultBranch string

Repository default branch name.

repositories[].mirror object

Mirror metadata. Absent for a native repository and before a mirror's initial sync is ready.

repositories[].mirror.source string

Mirror source. Allowed value: github.

repositories[].mirror.sourceId string

Opaque repository identifier assigned by the source.

repositories[].mirror.status string

Effective mirror direction during a transition, until cutover completes. Allowed values: inbound, outbound.

repositories[].visibility string

Repository visibility. Allowed values: internal, private.

repositories[].allowMergeCommit boolean

Whether pull requests can land as merge commits.

repositories[].allowSquashMerge boolean

Whether pull requests can land as squash merges.

repositories[].deleteBranchOnMerge boolean

Whether the head branch is deleted automatically on merge.

nextPageToken string

Opaque cursor for the next page; empty when there are no more pages.

repoSelectionMode string

Reports whether the installation grants all repositories or only selected repositories.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/installation/repos' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "repositories": [    {      "id": "repo_01k2ja2000e0080000000000q4",      "name": "rocket",      "fullName": "acme/rocket",      "owner": {        "slug": "acme",        "id": "ns_01k2ja2000e0080000000000p3",        "type": "team"      },      "defaultBranch": "main",      "createdAt": "2026-08-01T09:30:00Z",      "updatedAt": "2026-08-02T14:45:00Z",      "pushedAt": "2026-08-02T14:45:00Z",      "cloneUrl": "https://origin.cursor.com/git/acme/rocket.git"    }  ],  "repoSelectionMode": "selected"}

List Webhook Deliveries

GET/v1/origin/app/webhook/deliveries
AuthApp JWT

Lists webhook deliveries for the authenticated app, newest first.

A delivery is one event owed to one app; its id is the webhook-id header value 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

Compares against 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

Exact event type, e.g. pull_request.created.

installationId string

Narrow to one installation (WebhookDelivery.installation.id).

createdAfter string

Bound the delivery's creation time. For browsing, not for recovery.

createdBefore string

pageSize integer

Defaults to 30 when unset or 0. Values above 100 are clamped to 100.

pageToken string

Opaque cursor from a previous response's next_page_token. Empty for the first page.

Response Fields

deliveries array

Webhook deliveries for the authenticated app, ordered newest first. Each delivery ID is the webhook-id seen by the receiver.

deliveries[].id string

Stable delivery identifier and the webhook-id value the receiver sees; use it as the idempotency key.

deliveries[].event object

The event this delivery carries.

deliveries[].event.id string

Underlying Origin event identifier. It may also be present alongside the stable delivery ID but is not the idempotency key.

deliveries[].event.type string

Event slug carried by the delivery for routing.

deliveries[].installation object

The installation this delivery belongs to. 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

Installation identifier associated with a webhook delivery list item.

deliveries[].installation.target object

Owner targeted by the installation.

deliveries[].installation.target.slug string

URL-facing owner slug used with the owner ID to identify the repository owner.

deliveries[].installation.target.id string

Origin owner identifier.

deliveries[].installation.target.type string

Owner namespace type. Output-only. Allowed values: team, user. Omitted when unknown.

deliveries[].createdAt string

Delivery creation timestamp used by createdAfter and createdBefore browsing filters.

deliveries[].deliveredAt string

Absence corresponds to delivered=false: the receiver has never acknowledged this delivery with a 2xx response.

deliveries[].lastAttempt object

The most recent HTTP attempt, when one exists: its response status code, latency, transport error, trigger, and time.

deliveries[].lastAttempt.id string

Webhook delivery attempt identifier.

deliveries[].lastAttempt.deliveryId string

Stable delivery identifier associated with this attempt.

deliveries[].lastAttempt.trigger string

Reason this delivery attempt was sent. Allowed values: automatic, manual.

deliveries[].lastAttempt.responseStatusCode integer

Unset when the POST produced no HTTP response (transport error, timeout).

deliveries[].lastAttempt.latencyMs integer

Delivery attempt latency in milliseconds.

deliveries[].lastAttempt.errorMessage string

Transport error detail when there was no HTTP response; empty otherwise.

deliveries[].lastAttempt.attemptedAt string

RFC 3339 timestamp for this delivery attempt.

nextPageToken string

Opaque cursor for the next page; empty when there are no more pages.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/app/webhook/deliveries' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "deliveries": [    {      "id": "whd_01k2ja2000e0080000000000j9",      "event": {        "id": "evt_01k2ja2000e0080000000000r5",        "type": "pull_request.created"      },      "installation": {        "id": "inst_01k2ja2000e0080000000000b2",        "target": {          "slug": "acme",          "id": "ns_01k2ja2000e0080000000000p3",          "type": "team"        }      },      "createdAt": "2026-08-01T09:30:00Z",      "deliveredAt": "2026-08-02T14:45:05Z",      "lastAttempt": {        "id": "wha_01k2ja2000e0080000000000k0",        "deliveryId": "whd_01k2ja2000e0080000000000j9",        "trigger": "automatic",        "responseStatusCode": 200,        "latencyMs": 182,        "attemptedAt": "2026-08-02T14:45:05Z"      }    }  ]}

Batch Redeliver Webhook Deliveries

POST/v1/origin/app/webhook/deliveries:batchRedeliver
AuthApp JWT

Asks 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

Deliveries to send again. At most 100 unique entries, matching the 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

Accepted asynchronous redelivery results, one per unique delivery ID, with queued, already_in_flight, or not_found outcomes.

results[].deliveryId string

Requested stable delivery ID corresponding to this batch result.

results[].outcome string

Redelivery disposition: 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": [    "whd_01k2ja2000e0080000000000j9"  ]}'

Response shape:

{  "results": [    {      "deliveryId": "whd_01k2ja2000e0080000000000j9",      "outcome": "queued"    }  ]}

Ping Webhook

POST/v1/origin/app/webhook/pings
AuthApp JWT

Sends 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

The test delivery's webhook-id, matching the header the receiver saw.

eventId string

Event ID inside the signed envelope, the same value as event.id.

delivered boolean

True when the receiver answered with a 2xx status before the delivery timeout. Always present.

responseStatusCode integer

HTTP status the receiver answered with, or 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": "whd_01k2ja2000e0080000000000j9",  "eventId": "evt_01k2ja2000e0080000000000r5",  "delivered": true,  "responseStatusCode": 200}

Get App

GET/v1/origin/apps/{appId}
Scopenamespace:apps:readAuthUser access token

Returns a single app by its identifier. This is the management read for app publishers; Get Authenticated App is the equivalent self-read for the app's own JWT credential.

Path Parameters

appId string Required

App identifier, prefixed app_.

Response Fields

id string

Globally unique app identifier, prefixed app_.

displayName string

Human-facing app name.

webhookUrl string

Registered HTTPS URL that receives the app's webhook deliveries. Empty when the app receives no deliveries.

events array

Webhook event subscriptions configured for the app.

createdAt string

RFC 3339 timestamp for app creation.

updatedAt string

RFC 3339 timestamp for the latest app metadata update.

installationRedirectUris array

OAuth install callback allowlist: redirect URIs an app-initiated install can return to, matched exactly at authorize time.

namespaceSlug string

Slug of the namespace that owns the app.

description string

Publisher-provided app description. Empty when unset.

websiteUrl string

Publisher website. Empty when unset.

defaultScopes array

Default scopes offered when the app is installed, as catalog scope strings.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/apps/{appId}' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "id": "app_01k2ja2000e0080000000000a1",  "displayName": "CI Status Bot",  "webhookUrl": "https://ci.acme.dev/webhooks/origin",  "events": [    "pull_request.created",    "pull_request.merged"  ],  "createdAt": "2026-08-01T09:30:00Z",  "updatedAt": "2026-08-02T14:45:00Z",  "installationRedirectUris": [    "https://ci.acme.dev/origin/setup"  ],  "namespaceSlug": "acme",  "description": "Posts CI status on pull requests.",  "websiteUrl": "https://ci.acme.dev",  "defaultScopes": [    "repository:contents:read",    "repository:pull_requests:read"  ]}

Update App

PATCH/v1/origin/apps/{appId}
Scopeapp:settings:writeAuthUser access token

Updates an app's settings. Omitted fields are left unchanged, and at least one settable field must be provided. Clearing webhookUrl by sending an empty string disables outbound webhook delivery and cancels the app's pending deliveries; setting a URL again does not resurrect cancelled deliveries.

Path Parameters

appId string Required

App identifier, prefixed app_.

Request Body

displayName string

New human-facing app name. Must not be empty when provided.

webhookUrl string

New outbound webhook delivery URL, an absolute HTTPS URL. An empty string disables webhook delivery and cancels the app's pending deliveries.

events object

Clean replace of the webhook event subscriptions. Omit to leave them unchanged.

events.events array

The app's complete new set of webhook event subscriptions. An empty list clears them.

description string

New app description. Omit to leave it unchanged; an empty string clears it.

websiteUrl string

New publisher website. Omit to leave it unchanged; an empty string clears it.

installationRedirectUris object

Clean replace of the OAuth install callback allowlist. Omit to leave it unchanged.

installationRedirectUris.installationRedirectUris array

The complete new allowlist. An empty list clears it.

defaultScopes object

Clean replace of the app's default install scopes. Omit to leave them unchanged.

defaultScopes.scopes array

The complete new set of default install scopes. An empty list clears them.

Response Fields

id string

Globally unique app identifier, prefixed app_.

displayName string

Human-facing app name.

webhookUrl string

Registered HTTPS URL that receives the app's webhook deliveries. Empty when the app receives no deliveries.

events array

Webhook event subscriptions configured for the app.

createdAt string

RFC 3339 timestamp for app creation.

updatedAt string

RFC 3339 timestamp for the latest app metadata update.

installationRedirectUris array

OAuth install callback allowlist: redirect URIs an app-initiated install can return to, matched exactly at authorize time.

namespaceSlug string

Slug of the namespace that owns the app.

description string

Publisher-provided app description. Empty when unset.

websiteUrl string

Publisher website. Empty when unset.

defaultScopes array

Default scopes offered when the app is installed, as catalog scope strings.
curl --request PATCH \  --url 'https://api.cursor.com/v1/origin/apps/APP_ID' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \  --header 'Content-Type: application/json' \  --data '{  "webhookUrl": "https://ci.acme.dev/webhooks/origin-v2",  "events": {    "events": [      "pull_request.created",      "pull_request.merged",      "repository.pushed"    ]  }}'

Response shape:

{  "id": "app_01k2ja2000e0080000000000a1",  "displayName": "CI Status Bot",  "webhookUrl": "https://ci.acme.dev/webhooks/origin-v2",  "events": [    "pull_request.created",    "pull_request.merged",    "repository.pushed"  ],  "createdAt": "2026-08-01T09:30:00Z",  "updatedAt": "2026-08-02T14:45:00Z",  "installationRedirectUris": [    "https://ci.acme.dev/origin/setup"  ],  "namespaceSlug": "acme",  "description": "Posts CI status on pull requests.",  "websiteUrl": "https://ci.acme.dev",  "defaultScopes": [    "repository:contents:read",    "repository:pull_requests:read"  ]}

Add App Signing Key

POST/v1/origin/apps/{appId}/signing_keys
Scopeapp:settings:writeAuthUser access token

Adds a signing key to an app. Apps hold a bounded set of active signing keys; adding a key beyond the limit returns FailedPrecondition (HTTP 400) until another key is revoked. A key that is already registered returns AlreadyExists (HTTP 409 Conflict).

Path Parameters

appId string Required

App identifier, prefixed app_.

Request Body

publicKey string Required

PEM SPKI Ed25519 public key to add to the app's signing key set.

Response Fields

kid string

Key ID: the base64url-encoded SHA-256 digest of the key's SPKI DER encoding. Use it as the JWT kid header and to revoke the key.

createdAt string

RFC 3339 timestamp for when the key was registered.
curl --request POST \  --url 'https://api.cursor.com/v1/origin/apps/APP_ID/signing_keys' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN' \  --header 'Content-Type: application/json' \  --data '{  "publicKey": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAq9zTf3hL6wXe1cVj0bYs5mKR8uDnG2oAaPp4NiEkKlM=\n-----END PUBLIC KEY-----"}'

Response shape:

{  "kid": "3q2xW9dK5fJm8vB1nY6cT0aZrQpLh4eGkVsN7uMxOdI",  "createdAt": "2026-08-02T14:45:00Z"}

Revoke App Signing Key

DELETE/v1/origin/apps/{appId}/signing_keys/{kid}
Scopeapp:settings:writeAuthUser access token

Revokes an app signing key by its key ID. App JWTs signed with a revoked key stop authenticating. The last active signing key cannot be revoked; that request returns FailedPrecondition (HTTP 400). The response body is empty.

Path Parameters

appId string Required

App identifier, prefixed app_.

kid string Required

Key ID of the signing key to revoke.

Response Fields

Successful requests return no response body.

curl --request DELETE \  --url 'https://api.cursor.com/v1/origin/apps/{appId}/signing_keys/{kid}' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response:

204 No Content

List Namespace Apps

GET/v1/origin/namespaces/{namespaceSlug}/apps
Scopenamespace:apps:readAuthUser access token

Lists the apps a namespace owns, newest first. Responses carry display metadata only; read one app's webhook configuration with Get App.

Path Parameters

namespaceSlug string Required

Slug of the namespace whose apps to list.

Query Parameters

pageSize integer

Max apps to return. Defaults to 30 when unset or 0. Values above 100 are clamped to 100.

pageToken string

Opaque cursor from a previous response's next_page_token. Empty for the first page.

Response Fields

apps array

Page of apps the namespace owns.

apps[].id string

Globally unique app identifier, prefixed app_.

apps[].displayName string

Human-facing app name.

apps[].description string

Publisher-provided description. Empty when unset.

nextPageToken string

Opaque cursor for the next page; empty when there are no more pages.
curl --request GET \  --url 'https://api.cursor.com/v1/origin/namespaces/{namespaceSlug}/apps' \  --header 'Authorization: Bearer YOUR_ORIGIN_TOKEN'

Response shape:

{  "apps": [    {      "id": "app_01k2ja2000e0080000000000a1",      "displayName": "CI Status Bot",      "description": "Posts CI status on pull requests."    },    {      "id": "app_01k2ja2000e0080000000000a2",      "displayName": "Deploy Bot",      "description": ""    }  ],