Skip to main content

Command Palette

Search for a command to run...

Cloud Agents

OIDC tokens

Cloud Agents can mint short-lived OIDC JWTs from the machine where the agent runs and use them to assume cloud roles or call internal services without storing long-lived credentials in Secrets.

Agents call this API with their terminal tools. You don't need to run these requests yourself.

To have an agent mint tokens, include this in your prompt:

To mint OIDC tokens, follow the instructions athttps://cursor.com/docs/cloud-agent/identity

This API is local to the machine running the agent. It is unrelated to the Cloud Agents API, which uses Cursor API keys and manages agents from outside that machine. On a Cursor-managed VM, this socket also serves agent metadata.

Cursor-managed Cloud Agent VMs serve the token socket. Every token they mint carries agent_runtime: managed. Self-Hosted Machines workers serve it when you start them with --identity-socket. Their tokens carry agent_runtime: self_hosted. See Self-hosted workers.

Team Pool controllers can also get a token in the claim response, before any worker starts. See Claim tokens.

How it works

  1. The agent calls the local socket and asks for a token with an audience the verifier expects.
  2. Cursor signs an RS256 JWT bound to that agent and owner.
  3. The agent sends the JWT to your cloud or verifier (AWS STS, GCP, Azure, Vault, or a service you run).
  4. The verifier checks the signature against Cursor's published JWKS and authorizes on claims such as sub, team_id, or cloud_agent_id.

Mint a token

The agent mints a token over the Unix socket at the path in CURSOR_AGENT_SOCKET (on a Cursor-managed VM this is always set to /run/cursor/api.sock; on a self-hosted worker this path changes per claimed agent).

curl --unix-socket "${CURSOR_AGENT_SOCKET}" \  -H 'Content-Type: application/json' \  -d '{"aud":"sts.amazonaws.com"}' \  http://cursor-agent/v1/tokens/oidc

Requests are HTTP over a Unix socket. The hostname in the URL is ignored.

Include an optional nonce when the verifier expects replay binding:

curl --unix-socket "${CURSOR_AGENT_SOCKET}" \  -H 'Content-Type: application/json' \  -d '{"aud":"https://oidc.example.com","nonce":"unpredictable-value"}' \  http://cursor-agent/v1/tokens/oidc

Request

POST /v1/tokens/oidc over the Unix socket. Content-Type: application/json is required. Maximum body size is 4 KB.

FieldRequiredDescription
audYesAudience string your verifier checks. Printable ASCII, no whitespace, up to 512 characters. Examples: sts.amazonaws.com, https://oidc.example.com.
nonceNoOpaque string echoed into the JWT nonce claim. Up to 512 characters.
sub_claimNoClaim name to put in sub as <name>:<value>, for verifiers that only match sub and aud. Up to 64 characters. Discovery lists the supported names in x_cursor_sub_claims_supported; currently team_id, organization_id, and environment_id. Unsupported names are rejected. If the claim has no value for this agent, such as team_id on a personal account, the mint fails instead of falling back to the default subject.

Cursor doesn't allowlist audiences. Your verifier must reject unexpected aud values.

Response

{  "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9...",  "expires_at": 1785500000}
FieldDescription
tokenSigned JWT.
expires_atExpiration as Unix seconds. Matches the JWT exp claim.

Tokens are valid for 5 minutes. There is no refresh endpoint. Mint again when a new token is needed.

When claims appear

Install scripts can mint on the same socket. A token only includes claims that have a value when it is minted: turn_id and turn_start are absent until a coding turn starts, and branch_name is absent until the run records a branch. Owner, team, and repository claims are set from agent creation onward.

If the socket is missing right after boot, retry the connection.

Self-hosted workers

A Self-Hosted Machines worker serves the same API when you start it with --identity-socket:

agent worker --pool gpu --identity-socket start

The flag is off by default. Pass it on the worker command, before start. Pool workers and My Machines workers use the same flag. Omit --pool on a My Machines worker:

agent worker --identity-socket start

With the flag set, the worker opens one socket per claimed agent and sets CURSOR_AGENT_SOCKET to the socket's path in the agent's shells. The request and response contract, error codes, and rate limits match Cursor-managed VMs.

These tokens carry agent_runtime: self_hosted and the same owner, team, and repository claims. The worker serves the token API. It doesn't serve agent metadata. Any process running as the worker's OS user can mint on that claim's socket. See Trust model.

Verify a token

Publish these URLs to your identity provider or resource server:

EndpointURL
Issuerhttps://api.cursor.com
Discoveryhttps://api.cursor.com/.well-known/openid-configuration
JWKShttps://api.cursor.com/keys

Claim tokens use a different issuer.

curl -sS https://api.cursor.com/.well-known/openid-configurationcurl -sS https://api.cursor.com/keys

Discovery follows OpenID Connect Discovery 1.0. Tokens are minted on the agent VM, so the discovery document has no authorization_endpoint or token_endpoint.

Check at least:

  • Signature with RS256 and the JWKS kid
  • iss is https://api.cursor.com
  • aud is the audience your service expects
  • nbf / exp with a small clock-skew allowance (nbf is 5 seconds before iat)
  • sub or other claims your policy uses

Discovery includes x_cursor_audience_bound: true. Every token is minted for the caller-supplied aud. Don't accept a token issued for a different audience. Discovery also publishes x_cursor_sub_claims_supported, the claim names a mint request can project into sub with sub_claim.

JWT claims

Header: alg=RS256, typ=JWT, plus kid.

ClaimAlways presentDescription
issYeshttps://api.cursor.com, or https://api.cursor.com/self-hosted/claims on claim tokens.
subYesStable owner subject: user:<id> or service_account:<id> by default, or <claim>:<value> (for example team_id:123) when the mint request set sub_claim. Not an email.
audYesAudience from the mint request.
iatYesIssued-at, Unix seconds.
nbfYesNot-before (iat - 5).
expYesExpiration (iat + 300).
jtiYesUnique id per mint.
cloud_agent_idYesCloud Agent id (bcId).
nonceNoPresent only when the mint request included one.
agent_runtimeYesmanaged on Cursor-managed Cloud Agent VMs, self_hosted on Self-Hosted Machines workers.
worker_idSelf-hosted onlyId of the self-hosted worker: the worker serving the socket, or the workerId of a claim.
owner_emailWhen knownLowercased user email. Prefer sub or owner_user_id for allowlists; email can change.
owner_user_idWhen knownCursor user id, as a decimal string.
owner_service_account_idWhen knownService account id when a service account owns the agent.
team_idWhen knownOwning team id, as a decimal string.
turn_idWhen a turn is activeId of this coding turn. Different from cloud_agent_id, which is the Cloud Agent id (bcId).
turn_startWhen a turn is activeRun start, Unix seconds.
repo_urlWhen knownPrimary repository in host/path form, such as github.com/acme/widgets. Hostname is lowercased, with no scheme, credentials, port, query, or .git suffix. On a multi-repo agent, this is only the primary repository.
repo_urlsWhen knownEvery repository in the workspace, same form as repo_url. Primary repository first, then the rest sorted. Present only when the set is known complete. Missing means the set isn't known, not that there is only one repo.
repo_countWhen knownNumber of entries in repo_urls. Present exactly when repo_urls is. Use this with repo_url when your verifier can only match a single value (repo_count == 1).
branch_nameWhen knownCurrent branch.
environment_idWhen knownId of the Cursor environment this run used.
sourceWhen knownHow the agent was started, such as WEBSITE, API, SLACK, or AUTOMATIONS.
automation_idFor automationsAutomation id when source is automations.

repo_url is the primary repository. To confine an agent to specific repositories, pin the complete set with repo_urls.

Claim tokens

A Team Pool controller can get a token when it claims a pending request, before any worker starts. The token tells your systems which agent and owner the claim is for, so you can check both before you create the machine or give it credentials. The controller requests it with its service account API key. No socket is involved. The built-in controller requests one on every claim with --identity-audience.

Request a claim token

Add identityToken to the claim body. audience is required. Send a fresh nonce with every claim.

curl --request POST \  --url "https://api.cursor.com/v0/private-workers/claim" \  -u "$CURSOR_API_KEY:" \  --header 'Content-Type: application/json' \  --data '{    "id": "bc-00000000-0000-0000-0000-000000000002",    "workerId": "pw_123",    "identityToken": {      "audience": "https://provisioner.example.com",      "nonce": "3f9c1e7a-claim-pw_123"    }  }'

The response carries the JWT in identityToken.token:

{  "id": "bc-00000000-0000-0000-0000-000000000002",  "workerId": "pw_123",  "identityToken": {    "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ii4uLiJ9...",    "expiresAt": "2026-10-02T21:05:00.000Z"  }}

Claim tokens are valid for 5 minutes, and expiresAt matches the JWT exp claim. If Cursor can't issue the token, nothing is claimed. Claim A Pending Request lists the field limits, the error codes, and what to do when a claim response is lost.

Claim token issuer

Claim tokens have their own issuer. Tokens minted on the socket keep https://api.cursor.com. Both issuers publish the same JWKS.

EndpointURL
Issuerhttps://api.cursor.com/self-hosted/claims
Discoveryhttps://api.cursor.com/self-hosted/claims/.well-known/openid-configuration
JWKShttps://api.cursor.com/keys

The claim discovery document lists the same claims_supported as the root document. Its x_cursor_sub_claims_supported is empty because claim requests take no sub_claim.

A verifier or identity provider set up for https://api.cursor.com, such as the AWS IAM example, rejects claim tokens. Add the claim issuer only to the services that should accept them.

Claim tokens carry the same JWT claims as socket tokens, with these differences:

  • iss is https://api.cursor.com/self-hosted/claims.
  • agent_runtime is self_hosted.
  • worker_id is the workerId from the claim.
  • sub and the owner claims name the agent's owner, not the service account that made the claim. sub is always user:<id> or service_account:<id>.

Verify a claim token

Check at least:

  • Signature with RS256 and the JWKS kid
  • iss is https://api.cursor.com/self-hosted/claims
  • aud is the audience you sent
  • nbf / exp with a small clock-skew allowance
  • nonce is the value you sent with this claim
  • cloud_agent_id is the claimed id, and worker_id is the claimed workerId
  • team_id is your team

Identify the user by sub or owner_user_id, not owner_email. Email can change.

Cursor doesn't track whether a token was used. To make each token single-use, store its jti (or your nonce) and reject a repeat until the token expires.

Trust model

The token identifies the Cloud Agent run, not a specific process on the machine. Any process that can reach the socket can mint a token: the agent, code it runs, and hooks. Scope permissions to what you would grant that run as a whole.

You don't choose which agent the token is for. Cursor fills claims from this run, so a process on the machine can't mint a token for a different agent.

On a self-hosted worker, the agent and the worker process run as the same OS user. Any process running as that user can mint a token for the claimed run. Scope the role to what you would grant that user on the machine.

A claim token names the claimed agent and its owner. Anyone holding the pool's service account key can claim the team's pending requests within the key's repository scope and get a token naming the teammate who started each one. Keep the key on the controller, and accept claim tokens only for an audience you expect.

Rate limits and errors

Each claimed agent can mint 30 tokens per minute, in bursts of up to 10. The socket accepts at most 8 connections at once. On a Cursor-managed VM, those 8 connections are shared with agent metadata. Cache a token until it expires instead of minting per call.

Retry 429, 503, 500, 502, and 504 with backoff. Treat 403 as fatal: this agent isn't allowed to mint.

Error bodies carry a machine-readable code. Invalid-request errors (400, 404, 405, 413, and 415) also include a usage string that restates the full request contract. Rate-limit and saturation errors stay code-only:

{ "error": "invalid_aud", "usage": "POST /v1/tokens/oidc ..." }
{ "error": "rate_limited" }
HTTPerrorWhen
400invalid_json, invalid_aud, invalid_nonce, or invalid_sub_claimBad request body
404not_foundWrong path
405method_not_allowedNot POST
413body_too_largeBody over 4 KB
415invalid_content_typeMissing or non-JSON Content-Type
429rate_limitedOver the per-agent mint budget; honor Retry-After
503saturatedToo many connections; honor Retry-After
500host_errorInternal error; retry
502 / 504backend_unreachableCursor couldn't mint the token; retry
Otherbackend_errorCursor rejected the mint. 400 means fix the request (for example an unsupported sub_claim, or one with no value for this agent). 403 is fatal. 503 is retryable.

AWS IAM example

Use OIDC when you want AWS to trust Cursor-signed JWTs with AssumeRoleWithWebIdentity. For the simpler Cursor-managed assume-role flow (External ID + CURSOR_AWS_ASSUME_IAM_ROLE_ARN), see Using AWS IAM Roles.

  1. Create an IAM OIDC identity provider whose URL is https://api.cursor.com.
  2. Set the audience to sts.amazonaws.com (or another audience your role expects).
  3. Trust the role only for subjects and teams you intend to allow.

Example trust policy:

{  "Version": "2012-10-17",  "Statement": [    {      "Effect": "Allow",      "Principal": {        "Federated": "arn:aws:iam::123456789012:oidc-provider/api.cursor.com"      },      "Action": "sts:AssumeRoleWithWebIdentity",      "Condition": {        "StringEquals": {          "api.cursor.com:aud": "sts.amazonaws.com"        },        "StringLike": {          "api.cursor.com:sub": "user:*"        }      }    }  ]}

Tighten this with an exact sub, such as user:42 for one user or service_account:<id> for an agent that runs as a service account. AWS trust policies only match aud and sub, so scope trust to a team by minting with "sub_claim":"team_id" and matching the projected subject:

"StringEquals": {  "api.cursor.com:aud": "sts.amazonaws.com",  "api.cursor.com:sub": "team_id:123"}

A trust policy sees the same aud and sub on a Cursor-managed token and a self-hosted token. You can't put agent_runtime in sub.

Follow current AWS IAM OIDC instructions for provider creation and thumbprints.

The agent mints with "aud":"sts.amazonaws.com" (plus "sub_claim":"team_id" when the trust policy matches the team subject) and passes the JWT to STS. If you use network allowlists, allow sts.amazonaws.com (and any regional STS host you call).

Other verifiers

The same tokens work with any OIDC-compliant verifier:

  • GCP Workload Identity Federation
  • Azure federated credentials / Entra ID
  • Vault JWT/OIDC auth
  • Internal APIs that validate RS256 JWTs

Point the provider at the discovery URL, require your audience, and authorize on claims such as sub, team_id, or cloud_agent_id. To confine an agent to specific repositories, pin the complete set with repo_urls; repo_url names only the primary repository.

Minting uses the local socket only. Exchanging the JWT with AWS, GCP, Azure, or your service still needs outbound network access to those hosts.