Workload identities API

The workload identities API lets you manage the workload identities that your Buildkite organization uses for workload identity federation. A workload identity lets a trusted workload, such as a job running on a Buildkite agent, exchange the OpenID Connect (OIDC) token that its platform issues for a short-lived API access token. The API access token can only reach the pipelines and scopes that the workload identity allows.

Required permissions

These endpoints require both an API access token scope and organization administrator access for the user who owns the token:

- List and get operations require the read_workload_identities scope.

- Create and update operations require the write_workload_identities scope.

- Delete operations require the delete_workload_identities scope.

Workload identities must also be enabled for your Buildkite organization. Otherwise, every endpoint returns 404 Not Found.

Workload identity data model

Workload identity endpoints return objects with the following fields:

uuid UUID of the workload identity.
url Canonical API URL of the workload identity.
client_id Client ID that workloads send in token exchange requests. The client ID isn't a secret.
name Name of the workload identity.
description Description of the workload identity.
oidc_issuer Issuer URL that the OIDC token's iss claim must equal.
oidc_audience Audience that the OIDC token's aud claim must contain.
oidc_subject Pattern that the OIDC token's sub claim must match, where * matches any characters.
oidc_claims Object of top-level claims and the exact values that they must have in the OIDC token. Learn more in Lock tokens to an owner with claims.
oidc_claim_conditions CEL expression that the OIDC token's claims must satisfy. null when not set. Learn more in Write claim conditions.
jwks A JSON Web Key Set (JWKS) URL or inline JWKS JSON, as a string, that Buildkite uses instead of fetching the signing keys from the issuer. null when not set.
grantable_token_scopes Array of the maximum scopes that an API access token can have. A token exchange request can ask for any subset of these scopes.
default_token_scopes Array of the scopes that an API access token gets when the token exchange request doesn't ask for any.
max_token_ttl_seconds Longest lifetime, in seconds, that an API access token can have.
allowed_ip_addresses Space-separated CIDR ranges that can use the API access tokens that the workload identity mints. Any IP address can use them when not set.
allowed_token_exchange_ip_addresses Space-separated CIDR ranges that can make token exchange requests for the workload identity. Any IP address can make them when not set.
resource_grants Array of the pipelines that API access tokens can reach. Learn more in Resource grants.
enabled Whether the workload identity accepts token exchange requests.
created_at When the workload identity was created.
updated_at When the workload identity was last updated.

The API never returns API access tokens that the workload identity minted.

For example, this workload identity accepts OIDC tokens from jobs in one pipeline's builds of its main branch, running on Buildkite agents:

{
  "uuid": "00000000-0000-4000-8000-000000000010",
  "url": "https://api.buildkite.com/v2/organizations/acme-inc/workload-identities/00000000-0000-4000-8000-000000000010",
  "client_id": "0123456789abcdef0123",
  "name": "Deploy pipeline",
  "description": "Jobs in the deploy pipeline on the main branch",
  "oidc_issuer": "https://agent.buildkite.com",
  "oidc_audience": "https://api.buildkite.com/v2/organizations/00000000-0000-4000-8000-000000000001",
  "oidc_subject": "organization:acme-inc:pipeline:deploy:ref:refs/heads/main:commit:*:step:*",
  "oidc_claims": {
    "organization_id": "00000000-0000-4000-8000-000000000001"
  },
  "oidc_claim_conditions": "claims.pipeline_id == \"00000000-0000-4000-8000-000000000002\"",
  "jwks": null,
  "grantable_token_scopes": ["read_builds", "write_builds"],
  "default_token_scopes": ["read_builds"],
  "max_token_ttl_seconds": 600,
  "allowed_ip_addresses": "192.0.2.0/24",
  "allowed_token_exchange_ip_addresses": "192.0.2.0/24",
  "resource_grants": [
    {
      "type": "pipeline",
      "access": "selected",
      "ids": ["00000000-0000-4000-8000-000000000002"]
    }
  ],
  "enabled": true,
  "created_at": "2026-11-01T10:00:00.000Z",
  "updated_at": "2026-11-01T10:00:00.000Z"
}

Resource grants

The resource_grants array holds one object for pipeline access, with the following fields. When the array is empty, API access tokens can't reach any pipelines.

type The resource type. Must be pipeline.
access none for no pipelines, selected for the pipelines in ids, or all for every pipeline in your Buildkite organization, including pipelines created later.
ids UUIDs of up to 20 pipelines in your Buildkite organization. Required when access is selected, with at least one UUID, and not allowed otherwise.

These fields match the Resource Access setting in the Buildkite interface. Learn more in Choose pipeline access.

List workload identities

Returns a cursor-paginated list of the workload identities in your Buildkite organization, ordered from newest to oldest.

curl -H "Authorization: Bearer $TOKEN" \
  -X GET "https://api.buildkite.com/v2/organizations/{org.slug}/workload-identities"

The response contains workload identity objects in items, and pagination URLs in links and the Link response header:

{
  "items": [
    {
      "uuid": "00000000-0000-4000-8000-000000000010",
      "url": "https://api.buildkite.com/v2/organizations/acme-inc/workload-identities/00000000-0000-4000-8000-000000000010",
      "client_id": "0123456789abcdef0123",
      "name": "Deploy pipeline",
      "description": "Jobs in the deploy pipeline on the main branch",
      "oidc_issuer": "https://agent.buildkite.com",
      "oidc_audience": "https://api.buildkite.com/v2/organizations/00000000-0000-4000-8000-000000000001",
      "oidc_subject": "organization:acme-inc:pipeline:deploy:ref:refs/heads/main:commit:*:step:*",
      "oidc_claims": {
        "organization_id": "00000000-0000-4000-8000-000000000001"
      },
      "oidc_claim_conditions": "claims.pipeline_id == \"00000000-0000-4000-8000-000000000002\"",
      "jwks": null,
      "grantable_token_scopes": ["read_builds", "write_builds"],
      "default_token_scopes": ["read_builds"],
      "max_token_ttl_seconds": 600,
      "allowed_ip_addresses": "192.0.2.0/24",
      "allowed_token_exchange_ip_addresses": "192.0.2.0/24",
      "resource_grants": [
        {
          "type": "pipeline",
          "access": "selected",
          "ids": ["00000000-0000-4000-8000-000000000002"]
        }
      ],
      "enabled": true,
      "created_at": "2026-11-01T10:00:00.000Z",
      "updated_at": "2026-11-01T10:00:00.000Z"
    }
  ],
  "links": {
    "self": "https://api.buildkite.com/v2/organizations/acme-inc/workload-identities",
    "next": "https://api.buildkite.com/v2/organizations/acme-inc/workload-identities?after=CURSOR"
  }
}

Optional query string parameters:

per_page Number of results per page.

Default: 30

Maximum: 100

after Return results after this cursor. Can't be used with before.
before Return results before this cursor. Can't be used with after.

Required scope: read_workload_identities

Success response: 200 OK

Error response: 400 Bad Request when the request has both after and before, an invalid cursor, or a per_page value below 1 or above 100.

Get a workload identity

Returns a workload identity object.

curl -H "Authorization: Bearer $TOKEN" \
  -X GET "https://api.buildkite.com/v2/organizations/{org.slug}/workload-identities/{uuid}"

Required scope: read_workload_identities

Success response: 200 OK

Error response: 404 Not Found when your Buildkite organization has no workload identity with this UUID.

Create a workload identity

Creates a workload identity, and returns the new workload identity object, including its client_id.

curl -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.buildkite.com/v2/organizations/{org.slug}/workload-identities" \
  -d '{
    "name": "Deploy pipeline",
    "description": "Jobs in the deploy pipeline on the main branch",
    "oidc_issuer": "https://agent.buildkite.com",
    "oidc_audience": "https://api.buildkite.com/v2/organizations/00000000-0000-4000-8000-000000000001",
    "oidc_subject": "organization:acme-inc:pipeline:deploy:ref:refs/heads/main:commit:*:step:*",
    "oidc_claims": {
      "organization_id": "00000000-0000-4000-8000-000000000001"
    },
    "oidc_claim_conditions": "claims.pipeline_id == \"00000000-0000-4000-8000-000000000002\"",
    "grantable_token_scopes": ["read_builds", "write_builds"],
    "default_token_scopes": ["read_builds"],
    "max_token_ttl_seconds": 600,
    "allowed_ip_addresses": "192.0.2.0/24",
    "allowed_token_exchange_ip_addresses": "192.0.2.0/24",
    "resource_grants": [
      {
        "type": "pipeline",
        "access": "selected",
        "ids": ["00000000-0000-4000-8000-000000000002"]
      }
    ]
  }'

Required request body properties:

name Name of the workload identity.
description Description of the workload identity.
oidc_issuer Issuer URL that the OIDC token's iss claim must equal. Must be an HTTPS URL with no query or fragment.

Example: "https://agent.buildkite.com"

oidc_audience Audience that the OIDC token's aud claim must contain. Use a value that's unique to your Buildkite organization. The API saves a workload identity without an audience, but Buildkite refuses its token exchange requests until it has one.
oidc_subject Pattern that the OIDC token's sub claim must match, where * matches any characters. The pattern must contain at least one character that isn't a wildcard, so * on its own isn't allowed.

Example: "organization:acme-inc:pipeline:deploy:*"

grantable_token_scopes Array of the maximum scopes that an API access token can have. Must include at least one scope. The available scopes are read_pipelines, write_pipelines, read_builds, write_builds, retry_builds, read_job_env, read_build_logs, write_build_logs, read_artifacts, write_artifacts, read_organizations, and write_organizations.

Optional request body properties:

oidc_claims Object of top-level claims and the exact values that they must have in the OIDC token, up to 20. Learn more in Lock tokens to an owner with claims. Workload identities for Buildkite agents need an organization_id or organization_slug claim.

Example: {"organization_id": "00000000-0000-4000-8000-000000000001"}

oidc_claim_conditions CEL expression of up to 4,096 bytes that the OIDC token's claims must satisfy. Learn more in Write claim conditions.

Example: "claims.build_branch == \"main\""

jwks A JWKS URL, or inline JWKS JSON as a string, for when Buildkite can't fetch the signing keys from the issuer. Learn more in OIDC token requirements.
default_token_scopes Array of the scopes that an API access token gets when the token exchange request doesn't ask for any. Must be a subset of grantable_token_scopes.
resource_grants Array of the pipelines that API access tokens can reach. Learn more in Resource grants.

Default: [], which reaches no pipelines

max_token_ttl_seconds Longest lifetime, in seconds, that an API access token can have, from 30 to 3,600. A token also expires when the OIDC token that it was exchanged for expires.

Default: 3600

allowed_ip_addresses Space-separated CIDR ranges that can use the API access tokens that the workload identity mints.

Example: "192.0.2.0/24 198.51.100.12/32"

allowed_token_exchange_ip_addresses Space-separated CIDR ranges that can make token exchange requests for the workload identity.

Example: "192.0.2.0/24"

enabled Whether the workload identity accepts token exchange requests.

Default: true

Required scope: write_workload_identities

Success response: 201 Created

Error response: 422 Unprocessable Entity when the request has an unknown field, a value of the wrong type, or settings that Buildkite rejects, such as an unavailable scope, an invalid claim or claim condition, or a pipeline from another Buildkite organization. The response message describes each problem.

Update a workload identity

Updates the fields in the request body, and returns the updated workload identity object. Fields that the request leaves out keep their values. An array in the request, such as grantable_token_scopes or resource_grants, replaces the existing array.

curl -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -X PATCH "https://api.buildkite.com/v2/organizations/{org.slug}/workload-identities/{uuid}" \
  -d '{
    "resource_grants": [
      {
        "type": "pipeline",
        "access": "none"
      }
    ]
  }'

The request body accepts the same properties as create a workload identity, and all of them are optional.

Set enabled to false to disable the workload identity. Buildkite stops accepting its token exchange requests, and revokes its active API access tokens shortly afterwards. Set enabled to true to accept token exchange requests again. Other changes only apply to new token exchanges. API access tokens that the workload identity already minted keep their pipeline access, scopes, IP restrictions, and expiry.

Required scope: write_workload_identities

Success response: 200 OK

Error responses: 404 Not Found when your Buildkite organization has no workload identity with this UUID, or 422 Unprocessable Entity when the update is invalid. An invalid update changes nothing, including enabled.

Delete a workload identity

Permanently deletes a workload identity, and revokes its API access tokens. You can't undo this action.

curl -H "Authorization: Bearer $TOKEN" \
  -X DELETE "https://api.buildkite.com/v2/organizations/{org.slug}/workload-identities/{uuid}"

Required scope: delete_workload_identities

Success response: 204 No Content

Error response: 404 Not Found when your Buildkite organization has no workload identity with this UUID.