# Authentication

Send REST credentials with the Bearer scheme. Each endpoint's **Auth** badges list the credential types it accepts:

```bash
curl --request GET \
  --url https://api.cursor.com/v1/origin/repos/OWNER_SLUG/REPO_NAME \
  --header "Authorization: Bearer $ORIGIN_BEARER_TOKEN"
```

Cursor API keys are not Origin Bearer tokens. For user-authenticated requests, use the [Origin CLI](https://cursor.com/docs/api/origin/reference/user-authenticated-cli-requests.md), which exchanges a personal user API key for the short-lived access token that Origin accepts. Do not put a Cursor API key directly in the `Authorization` header.

## 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](https://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:

```bash
openssl genpkey -algorithm ED25519 -out origin-app-private.pem
openssl 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](https://cursor.com/docs/api/origin/reference/app-jwt.md).

## 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](https://cursor.com/docs/api/origin/reference/generate-an-app-signing-key.md).

JOSE header:

```json
{
  "alg": "EdDSA",
  "kid": "app_01...",
  "typ": "JWT"
}
```

Claims:

```json
{
  "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.

```text
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`](https://cursor.com/docs/api/origin/reference/create-installation-access-token.md) with an app JWT. Installation tokens begin with `oit_`.

```text
Authorization: Bearer oit_...
```

The response includes `expiresAt`. Mint tokens just in time, refresh them before expiration, treat them like passwords, and never log them.

A token expires at most 15 minutes after creation, and never later than the app JWT that requested it, so the five-minute JWT recommended above yields a token of at most five minutes. Read `expiresAt` and mint a new token when it passes rather than assuming a duration: Origin tokens are shorter-lived than GitHub App installation tokens, and an integration that reuses a token on GitHub's schedule fails once the token expires. Sign the JWT with a later `exp` when a job needs the full 15 minutes, as the [CloneKit CI recipe](https://cursor.com/docs/origin/clonekit-ci.md#mint-a-token-in-the-job) does.

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](https://cursor.com/docs/api/origin/reference/git-https-authentication.md).

To act as a member of the installation's namespace instead of as the app, mint an installation user token. See [Acting on behalf of users](https://cursor.com/docs/api/origin/acting-as-users.md).

## 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](https://cursor.com/docs/api/origin/reference/create-installation-access-token.md) immediately before the Git operation. Tokens expire after at most 15 minutes.

Clone, fetch, and pull require [`repository:contents:read`](https://cursor.com/docs/api/origin/reference/scopes.md). Push requires [`repository:contents:write`](https://cursor.com/docs/api/origin/reference/scopes.md). 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](https://cursor.com/docs/api/origin/reference/create-repo.md) 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](https://cursor.com/docs/api/origin/reference/get-repo.md) or [List App Installation Repositories](https://cursor.com/docs/api/origin/reference/list-app-installation-repositories.md). Both the GitHub-shaped path (`https://origin.cursor.com/OWNER_SLUG/REPO_NAME.git`) and the legacy `/git/` path clone.

```bash
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:

```bash
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:

```bash
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](https://cursor.com/docs/api/origin/reference/rate-limits.md). 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`. See [Mirrored repositories](https://cursor.com/docs/api/origin/reference/mirrored-repositories.md).

## User-authenticated CLI requests

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

```bash
origin auth login
origin api /repos/OWNER_SLUG/REPO_NAME/pulls
```

For a non-interactive session, provide a personal user API key from [Cursor Dashboard → API Keys](https://cursor.com/dashboard/api):

```bash
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](https://cursor.com/docs/api/origin/reference/installation-receipt.md).

Discovery metadata identifies the issuer and `jwks_uri`:

```bash
curl https://api.cursor.com/v1/origin/.well-known/openid-configuration
```

```json
{
  "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:

```bash
curl https://api.cursor.com/v1/origin/keys
```

```json
{
  "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.


---

## Sitemap

[Origin API docs index](https://cursor.com/docs/api/origin/llms.txt)
