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 inside the VM 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 agent VM. It is unrelated to the Cloud Agents API, which uses Cursor API keys and manages agents from outside the VM. The same socket also serves agent metadata for values that don't belong in a credential.

Cursor-managed Cloud Agent VMs serve the token socket. Every token they mint carries agent_runtime: managed.

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 CURSOR_AGENT_SOCKET. On Cursor-managed VMs the default is /run/cursor/api.sock.

curl --unix-socket "${CURSOR_AGENT_SOCKET:-/run/cursor/api.sock}" \  -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:-/run/cursor/api.sock}" \  -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. 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.

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

Trust model

The token identifies the Cloud Agent run, not a specific process inside the VM. 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 in the VM can't mint a token for a different agent.

Rate limits and errors

Each agent VM can mint 30 tokens per minute, in bursts of up to 10. The socket also accepts at most 8 connections at once. That cap is 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"}

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.