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/identityThis 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.
How it works
- The agent calls the local socket and asks for a token with an audience the verifier expects.
- Cursor signs an RS256 JWT bound to that agent and owner.
- The agent sends the JWT to your cloud or verifier (AWS STS, GCP, Azure, Vault, or a service you run).
- The verifier checks the signature against Cursor's published JWKS and authorizes on claims such as
sub,team_id, orcloud_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/oidcRequests 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/oidcRequest
POST /v1/tokens/oidc over the Unix socket. Content-Type: application/json is required. Maximum body size is 4 KB.
| Field | Required | Description |
|---|---|---|
aud | Yes | Audience string your verifier checks. Printable ASCII, no whitespace, up to 512 characters. Examples: sts.amazonaws.com, https://oidc.example.com. |
nonce | No | Opaque string echoed into the JWT nonce claim. Up to 512 characters. |
sub_claim | No | Claim 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}| Field | Description |
|---|---|
token | Signed JWT. |
expires_at | Expiration 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 startThe 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 startWith 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:
| Endpoint | URL |
|---|---|
| Issuer | https://api.cursor.com |
| Discovery | https://api.cursor.com/.well-known/openid-configuration |
| JWKS | https://api.cursor.com/keys |
curl -sS https://api.cursor.com/.well-known/openid-configurationcurl -sS https://api.cursor.com/keysDiscovery follows OpenID Connect Discovery 1.0. Tokens are minted on the agent VM, so the discovery document has no authorization_endpoint or token_endpoint.
Cursor still serves a second discovery document at
https://api2.cursor.sh/cloud-agent/identity. Minted tokens no longer carry
that issuer. Point verifiers at https://api.cursor.com.
Check at least:
- Signature with RS256 and the JWKS
kid issishttps://api.cursor.comaudis the audience your service expectsnbf/expwith a small clock-skew allowance (nbfis 5 seconds beforeiat)subor 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.
| Claim | Always present | Description |
|---|---|---|
iss | Yes | https://api.cursor.com |
sub | Yes | Stable 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. |
aud | Yes | Audience from the mint request. |
iat | Yes | Issued-at, Unix seconds. |
nbf | Yes | Not-before (iat - 5). |
exp | Yes | Expiration (iat + 300). |
jti | Yes | Unique id per mint. |
cloud_agent_id | Yes | Cloud Agent id (bcId). |
nonce | No | Present only when the mint request included one. |
agent_runtime | Yes | managed on Cursor-managed Cloud Agent VMs, self_hosted on Self-Hosted Machines workers. |
owner_email | When known | Lowercased user email. Prefer sub or owner_user_id for allowlists; email can change. |
owner_user_id | When known | Cursor user id, as a decimal string. |
owner_service_account_id | When known | Service account id when a service account owns the agent. |
team_id | When known | Owning team id, as a decimal string. |
turn_id | When a turn is active | Id of this coding turn. Different from cloud_agent_id, which is the Cloud Agent id (bcId). |
turn_start | When a turn is active | Run start, Unix seconds. |
repo_url | When known | Primary 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_urls | When known | Every 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_count | When known | Number 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_name | When known | Current branch. |
environment_id | When known | Id of the Cursor environment this run used. |
source | When known | How the agent was started, such as WEBSITE, API, SLACK, or AUTOMATIONS. |
automation_id | For automations | Automation 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 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.
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" }| HTTP | error | When |
|---|---|---|
| 400 | invalid_json, invalid_aud, invalid_nonce, or invalid_sub_claim | Bad request body |
| 404 | not_found | Wrong path |
| 405 | method_not_allowed | Not POST |
| 413 | body_too_large | Body over 4 KB |
| 415 | invalid_content_type | Missing or non-JSON Content-Type |
| 429 | rate_limited | Over the per-agent mint budget; honor Retry-After |
| 503 | saturated | Too many connections; honor Retry-After |
| 500 | host_error | Internal error; retry |
| 502 / 504 | backend_unreachable | Cursor couldn't mint the token; retry |
| Other | backend_error | Cursor 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.
- Create an IAM OIDC identity provider whose URL is
https://api.cursor.com. - Set the audience to
sts.amazonaws.com(or another audience your role expects). - 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.
Related pages
- Agent metadata for key-value run metadata on a Cursor-managed VM's socket
- Secrets & Network for dashboard secrets and egress controls
- Cloud agent setup for Cursor-managed AWS role assumption
- Security overview for isolation and access model
- Service accounts when agents run as a team service account