One change
wif-admin-api
manage-claude/wif-admin-api
Nearest release: v2.1.247, published under an hour before this site recorded the change. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.
manage-claude/wif-admin-api Changed · +1149 / -82 lines
The two sides of this change are too far apart to line up, so this is the differ's own diff of it.
from line 12
### Interactive (your terminal) -Log in with the [`ant` CLI](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart) under a dedicated profile, requesting the `org:admin` scope (see [Admin access](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/authentication#admin-access)), then export the bearer token: +Log in with the [`ant` CLI](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart) under a dedicated profile, requesting the `org:admin` scope (see [Admin access](https://platform.claude.com/docs/en/cli-sdks-libraries/cli/authentication#admin-access)), then export the bearer token. Logging in with `--profile admin` stores the `org:admin` credential under its own profile name and also makes it the CLI's active profile, and the exported variable applies to every SDK and CLI call in that shell; so use a shell you reserve for administration, unset the variable when you are done, and switch the CLI back with `ant profile activate default`: ```bash CLI ant auth login --profile admin --scope "org:admin" -export ANTHROPIC_OAUTH_TOKEN=$(ant auth print-credentials --profile admin --access-token) +export ANTHROPIC_AUTH_TOKEN=$(ant auth print-credentials --profile admin --access-token) ``` Interactive tokens are short-lived; if requests start returning 401, re-run the export command (it refreshes the token automatically). + +The SDKs and the `ant` CLI read `ANTHROPIC_AUTH_TOKEN` automatically; leave `ANTHROPIC_API_KEY` unset in the same shell, because these endpoints reject API keys and some clients prefer the key when both are set. ### Workload (CI and automation)
from line 41
</Step> <Step title="Exchange the workload's identity token"> - At runtime, the workload exchanges the JWT from its identity provider for a short-lived `org:admin` bearer token using the same [token exchange](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#authenticate-from-your-workload) as any other federated workload. + A workload that uses one of the SDKs or the `ant` CLI does not perform the exchange itself. Point the client at the rule with the federation environment variables and construct it with no arguments, exactly as for inference in [Construct the SDK client](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#construct-the-sdk-client); the client exchanges the identity token on the first request and, before the resulting access token expires, re-reads the identity token and exchanges it again: + + ```bash + export ANTHROPIC_FEDERATION_RULE_ID=fdrl_... # the org:admin rule from step 1 + export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000 + export ANTHROPIC_SERVICE_ACCOUNT_ID=svac_... # the rule's target service account + export ANTHROPIC_IDENTITY_TOKEN_FILE=/path/to/jwt # or ANTHROPIC_IDENTITY_TOKEN + # ANTHROPIC_WORKSPACE_ID is required only if the rule is enabled for all + # workspaces or more than one; the org:admin endpoints ignore the binding. + unset ANTHROPIC_API_KEY ANTHROPIC_AUTH_TOKEN # both take precedence over federation + ``` + + The `ant` CLI reads the same variables, or takes `--federation-rule`, `--organization-id`, `--service-account-id`, and `--identity-token-file` flags. For a workload that runs more than one `ant` command, use a [federation profile](https://platform.claude.com/docs/en/manage-claude/wif-reference#profile-configuration-file) rather than flags or environment variables: with flags or variables the CLI exchanges the identity token again in every process, and identity tokens that carry a `jti` claim (GitHub Actions tokens do) are accepted only once, so a second command would be rejected; a profile is also the only way to give the CLI a `workspace_id` for the exchange when the rule is enabled for all workspaces or more than one, because unlike the SDKs the CLI does not pass `ANTHROPIC_WORKSPACE_ID` or `--workspace-id` into the exchange. Every SDK also accepts the same settings as explicit constructor arguments, shown per language in [Construct the SDK client](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#construct-the-sdk-client). See [Environment variables](https://platform.claude.com/docs/en/manage-claude/wif-reference#environment-variables) and [Credential precedence](https://platform.claude.com/docs/en/manage-claude/wif-reference#credential-precedence) for the full list and ordering. + + A workload that calls the API with curl exchanges the JWT for a short-lived `org:admin` bearer token itself, using the same [token exchange](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#authenticate-from-your-workload) as any other federated workload, and sends it in the `authorization: Bearer` header. </Step> <Step title="Manage issuers and workspace-scoped rules through the API"> - With the minted token in `ANTHROPIC_OAUTH_TOKEN`, the workload creates and manages your federation configuration using the endpoints on this page. + With the client configured (or, for curl, the minted token in `ANTHROPIC_AUTH_TOKEN`), the workload creates and manages your federation configuration using the endpoints on this page. </Step> </Steps>
from line 69
All endpoints live under `https://api.anthropic.com/v1/organizations/`. Every request to the federation and service-account endpoints needs the API version header and the bearer token: -```bash cURL -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" -``` +In the SDKs these endpoints are `client.beta.organization.service_accounts`, `client.beta.organization.federation.issuers`, and `client.beta.organization.federation.rules` (`ant beta:organization:service-accounts`, `federation:issuers`, and `federation:rules` in the CLI). The SDK and CLI examples construct the default client, which sends the bearer token from `ANTHROPIC_AUTH_TOKEN`, or, in an automated workload, performs the federation exchange itself as described in [Bootstrap a workload to manage WIF](https://platform.claude.com/docs/en/manage-claude/wif-admin-api#bootstrap-a-workload-to-manage-wif). SDK list methods fetch further pages on demand, so `limit` sets the page size; the PHP and Ruby examples read one page. + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:service-accounts list + ``` + + ```python Python + client = anthropic.Anthropic() + + service_accounts = client.beta.organization.service_accounts.list() + + for service_account in service_accounts: + print(f"{service_account.id}: {service_account.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + for await (const serviceAccount of client.beta.organization.serviceAccounts.list()) { + console.log(`${serviceAccount.id}: ${serviceAccount.name}`); + } + ``` + + ```csharp C# + AnthropicClient client = new(); + + var page = await client.Beta.Organization.ServiceAccounts.List(); + + await foreach (var serviceAccount in page.Paginate()) + { + Console.WriteLine($"{serviceAccount.ID}: {serviceAccount.Name}"); + } + ``` + + ```go Go + client := anthropic.NewClient() + + serviceAccounts := client.Beta.Organization.ServiceAccounts.ListAutoPaging(context.Background(), anthropic.BetaOrganizationServiceAccountListParams{}) + + for serviceAccounts.Next() { + serviceAccount := serviceAccounts.Current() + fmt.Printf("%s: %s\n", serviceAccount.ID, serviceAccount.Name) + } + if err := serviceAccounts.Err(); err != nil { + log.Fatal(err) + } + ``` + + ```java Java + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var serviceAccounts = client.beta().organization().serviceAccounts().list(); + + for (var serviceAccount : serviceAccounts.autoPager()) { + IO.println(serviceAccount.id() + ": " + serviceAccount.name()); + } + ``` + + ```php PHP + $client = new Client(); + + $serviceAccounts = $client->beta->organization->serviceAccounts->list(); + + foreach ($serviceAccounts->getItems() as $serviceAccount) { + echo "{$serviceAccount->id}: {$serviceAccount->name}\n"; + } + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + service_accounts = client.beta.organization.service_accounts.list + + service_accounts.data.each do |service_account| + puts "#{service_account.id}: #{service_account.name}" + end + ``` +</CodeGroup> Admin API keys are not accepted on these endpoints; the Admin API page's `x-api-key` examples do not apply here.
from line 161
A [service account](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#service-accounts) (`svac_...`) is the non-human identity that a federated token acts as. Set `organization_role` to `developer`. -```bash cURL -# Create a service account -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" \ - -H "content-type: application/json" \ - -d '{ - "name": "inference-worker", - "organization_role": "developer" - }' - -# List service accounts -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts?limit=20" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" - -# Archive a service account -curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/service_accounts/svac_.../archive" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" -``` +Create a service account: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{ + "name": "inference-worker", + "organization_role": "developer" + }' + ``` + + ```bash CLI + ant beta:organization:service-accounts create \ + --name inference-worker \ + --organization-role developer + ``` + + ```python Python + client = anthropic.Anthropic() + + service_account = client.beta.organization.service_accounts.create( + name="inference-worker", organization_role="developer" + ) + + print(f"id: {service_account.id}") + print(f"name: {service_account.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const serviceAccount = await client.beta.organization.serviceAccounts.create({ + name: "inference-worker", + organization_role: "developer" + }); + + console.log(`id: ${serviceAccount.id}`); + console.log(`name: ${serviceAccount.name}`); + ``` + + ```csharp C# + using Anthropic.Models.Beta.Organization.ServiceAccounts; + + AnthropicClient client = new(); + + var serviceAccount = await client.Beta.Organization.ServiceAccounts.Create(new() + { + Name = "inference-worker", + OrganizationRole = OrganizationRole.Developer + }); + + Console.WriteLine($"id: {serviceAccount.ID}"); + Console.WriteLine($"name: {serviceAccount.Name}"); + ``` + + ```go Go + client := anthropic.NewClient() + + serviceAccount, err := client.Beta.Organization.ServiceAccounts.New(context.Background(), anthropic.BetaOrganizationServiceAccountNewParams{ + Name: "inference-worker", + OrganizationRole: anthropic.BetaOrganizationServiceAccountNewParamsOrganizationRoleDeveloper, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", serviceAccount.ID) + fmt.Printf("name: %s\n", serviceAccount.Name) + ``` + + ```java Java + import com.anthropic.models.beta.organization.serviceaccounts.ServiceAccountCreateParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var params = ServiceAccountCreateParams.builder() + .name("inference-worker") + .organizationRole(ServiceAccountCreateParams.OrganizationRole.DEVELOPER) + .build(); + var serviceAccount = client.beta().organization().serviceAccounts().create(params); + + IO.println("id: " + serviceAccount.id()); + IO.println("name: " + serviceAccount.name()); + } + ``` + + ```php PHP + use Anthropic\Beta\Organization\ServiceAccounts\ServiceAccountCreateParams\OrganizationRole; + // ... + + $client = new Client(); + + $serviceAccount = $client->beta->organization->serviceAccounts->create( + name: 'inference-worker', + organizationRole: OrganizationRole::DEVELOPER, + ); + + echo "id: {$serviceAccount->id}\n"; + echo "name: {$serviceAccount->name}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + service_account = client.beta.organization.service_accounts.create( + name: "inference-worker", + organization_role: :developer + ) + + puts "id: #{service_account.id}" + puts "name: #{service_account.name}" + ``` +</CodeGroup> + +List service accounts: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/service_accounts?limit=20" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:service-accounts list --limit 20 + ``` + + ```python Python + client = anthropic.Anthropic() + + service_accounts = client.beta.organization.service_accounts.list(limit=20) + + for service_account in service_accounts: + print(f"{service_account.id}: {service_account.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + for await (const serviceAccount of client.beta.organization.serviceAccounts.list({ + limit: 20 + })) { + console.log(`${serviceAccount.id}: ${serviceAccount.name}`); + } + ``` + + ```csharp C# + AnthropicClient client = new(); + + var page = await client.Beta.Organization.ServiceAccounts.List(new() { Limit = 20 }); + + await foreach (var serviceAccount in page.Paginate()) + { + Console.WriteLine($"{serviceAccount.ID}: {serviceAccount.Name}"); + } + ``` + + ```go Go + client := anthropic.NewClient() + + serviceAccounts := client.Beta.Organization.ServiceAccounts.ListAutoPaging(context.Background(), anthropic.BetaOrganizationServiceAccountListParams{ + Limit: anthropic.Int(20), + }) + + for serviceAccounts.Next() { + serviceAccount := serviceAccounts.Current() + fmt.Printf("%s: %s\n", serviceAccount.ID, serviceAccount.Name) + } + if err := serviceAccounts.Err(); err != nil { + log.Fatal(err) + } + ``` + + ```java Java + import com.anthropic.models.beta.organization.serviceaccounts.ServiceAccountListParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var params = ServiceAccountListParams.builder() + .limit(20) + .build(); + var serviceAccounts = client.beta().organization().serviceAccounts().list(params); + + for (var serviceAccount : serviceAccounts.autoPager()) { + IO.println(serviceAccount.id() + ": " + serviceAccount.name()); + } + } + ``` + + ```php PHP + $client = new Client(); + + $serviceAccounts = $client->beta->organization->serviceAccounts->list(limit: 20); + + foreach ($serviceAccounts->getItems() as $serviceAccount) { + echo "{$serviceAccount->id}: {$serviceAccount->name}\n"; + } + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + service_accounts = client.beta.organization.service_accounts.list(limit: 20) + + service_accounts.data.each do |service_account| + puts "#{service_account.id}: #{service_account.name}" + end + ``` +</CodeGroup> + +Archive a service account: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/service_accounts/svac_01ABCDEFabcdef0123456789XY/archive" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:service-accounts archive svac_01ABCDEFabcdef0123456789XY + ``` + + ```python Python + client = anthropic.Anthropic() + + service_account = client.beta.organization.service_accounts.archive( + "svac_01ABCDEFabcdef0123456789XY" + ) + + print(f"id: {service_account.id}") + print(f"archived_at: {service_account.archived_at}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const serviceAccount = await client.beta.organization.serviceAccounts.archive( + "svac_01ABCDEFabcdef0123456789XY" + ); + + console.log(`id: ${serviceAccount.id}`); + console.log(`archived_at: ${serviceAccount.archived_at}`); + ``` + + ```csharp C# + AnthropicClient client = new(); + + var serviceAccount = await client.Beta.Organization.ServiceAccounts.Archive( + "svac_01ABCDEFabcdef0123456789XY" + ); + + Console.WriteLine($"id: {serviceAccount.ID}"); + Console.WriteLine($"archived_at: {serviceAccount.ArchivedAt:O}"); + ``` + + ```go Go + client := anthropic.NewClient() + + serviceAccount, err := client.Beta.Organization.ServiceAccounts.Archive( + context.Background(), + "svac_01ABCDEFabcdef0123456789XY", + anthropic.BetaOrganizationServiceAccountArchiveParams{}, + ) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", serviceAccount.ID) + fmt.Printf("archived_at: %s\n", serviceAccount.ArchivedAt) + ``` + + ```java Java + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var serviceAccount = client.beta().organization().serviceAccounts() + .archive("svac_01ABCDEFabcdef0123456789XY"); + + IO.println("id: " + serviceAccount.id()); + IO.println("archived_at: " + serviceAccount.archivedAt().orElseThrow()); + ``` + + ```php PHP + $client = new Client(); + + $serviceAccount = $client->beta->organization->serviceAccounts->archive( + serviceAccountID: 'svac_01ABCDEFabcdef0123456789XY', + ); + + echo "id: {$serviceAccount->id}\n"; + echo "archived_at: {$serviceAccount->archivedAt?->format(DATE_ATOM)}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + service_account_id = "svac_01ABCDEFabcdef0123456789XY" + service_account = client.beta.organization.service_accounts.archive(service_account_id) + + puts "id: #{service_account.id}" + puts "archived_at: #{service_account.archived_at}" + ``` +</CodeGroup> The create endpoint returns the new service account:
from line 497
| `{"type": "explicit_url", "url": "..."}` | Point at a JWKS endpoint directly. | | `{"type": "inline", "keys": [...]}` | Upload the key set for providers that are not reachable from the public internet. | -```bash cURL -# Register an issuer (GitHub Actions, with JWKS discovery) -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_issuers" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" \ - -H "content-type: application/json" \ - -d '{ - "name": "github-actions", - "issuer_url": "https://token.actions.githubusercontent.com", - "jwks": {"type": "discovery"} - }' - -# List issuers -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_issuers?limit=20" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" - -# Archive an issuer -curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/federation_issuers/fdis_.../archive" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" -``` +Register an issuer. This example registers GitHub Actions with JWKS discovery: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_issuers" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{ + "name": "github-actions", + "issuer_url": "https://token.actions.githubusercontent.com", + "jwks": {"type": "discovery"} + }' + ``` + + ```bash CLI + ant beta:organization:federation:issuers create \ + --name github-actions \ + --issuer-url https://token.actions.githubusercontent.com \ + --jwks '{type: discovery}' + ``` + + ```python Python + client = anthropic.Anthropic() + + issuer = client.beta.organization.federation.issuers.create( + name="github-actions", + issuer_url="https://token.actions.githubusercontent.com", + jwks={"type": "discovery"}, + ) + + print(f"id: {issuer.id}") + print(f"name: {issuer.name}") + print(f"issuer_url: {issuer.issuer_url}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const issuer = await client.beta.organization.federation.issuers.create({ + name: "github-actions", + issuer_url: "https://token.actions.githubusercontent.com", + jwks: { type: "discovery" } + }); + + console.log(`id: ${issuer.id}`); + console.log(`name: ${issuer.name}`); + console.log(`issuer_url: ${issuer.issuer_url}`); + ``` + + ```csharp C# + using Anthropic.Models.Beta.Organization.Federation.Issuers; + + AnthropicClient client = new(); + + var issuer = await client.Beta.Organization.Federation.Issuers.Create(new() + { + Name = "github-actions", + IssuerUrl = "https://token.actions.githubusercontent.com", + Jwks = new BetaJwksDiscovery() + }); + + Console.WriteLine($"id: {issuer.ID}"); + Console.WriteLine($"name: {issuer.Name}"); + Console.WriteLine($"issuer_url: {issuer.IssuerUrl}"); + ``` + + ```go Go + client := anthropic.NewClient() + + issuer, err := client.Beta.Organization.Federation.Issuers.New(context.Background(), anthropic.BetaOrganizationFederationIssuerNewParams{ + Name: "github-actions", + IssuerURL: "https://token.actions.githubusercontent.com", + JWKS: anthropic.BetaOrganizationFederationIssuerNewParamsJWKSUnion{ + OfDiscovery: &anthropic.BetaJWKSDiscoveryParam{}, + }, + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", issuer.ID) + fmt.Printf("name: %s\n", issuer.Name) + fmt.Printf("issuer_url: %s\n", issuer.IssuerURL) + ``` + + ```java Java + import com.anthropic.models.beta.organization.federation.issuers.BetaJwksDiscovery; + import com.anthropic.models.beta.organization.federation.issuers.IssuerCreateParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var params = IssuerCreateParams.builder() + .name("github-actions") + .issuerUrl("https://token.actions.githubusercontent.com") + .jwks(BetaJwksDiscovery.builder().build()) + .build(); + var issuer = client.beta().organization().federation().issuers().create(params); + + IO.println("id: " + issuer.id()); + IO.println("name: " + issuer.name()); + IO.println("issuer_url: " + issuer.issuerUrl()); + } + ``` + + ```php PHP + $client = new Client(); + + $issuer = $client->beta->organization->federation->issuers->create( + name: 'github-actions', + issuerURL: 'https://token.actions.githubusercontent.com', + jwks: ['type' => 'discovery'], + ); + + echo "id: {$issuer->id}\n"; + echo "name: {$issuer->name}\n"; + echo "issuer_url: {$issuer->issuerURL}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + issuer = client.beta.organization.federation.issuers.create( + name: "github-actions", + issuer_url: "https://token.actions.githubusercontent.com", + jwks: {type: :discovery} + ) + + puts "id: #{issuer.id}" + puts "name: #{issuer.name}" + puts "issuer_url: #{issuer.issuer_url}" + ``` +</CodeGroup> + +List issuers: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_issuers?limit=20" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:federation:issuers list --limit 20 + ``` + + ```python Python + client = anthropic.Anthropic() + + issuers = client.beta.organization.federation.issuers.list(limit=20) + + for issuer in issuers: + print(f"{issuer.id}: {issuer.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + for await (const issuer of client.beta.organization.federation.issuers.list({ limit: 20 })) { + console.log(`${issuer.id}: ${issuer.name}`); + } + ``` + + ```csharp C# + AnthropicClient client = new(); + + var page = await client.Beta.Organization.Federation.Issuers.List(new() { Limit = 20 }); + + await foreach (var issuer in page.Paginate()) + { + Console.WriteLine($"{issuer.ID}: {issuer.Name}"); + } + ``` + + ```go Go + client := anthropic.NewClient() + + issuers := client.Beta.Organization.Federation.Issuers.ListAutoPaging(context.Background(), anthropic.BetaOrganizationFederationIssuerListParams{ + Limit: anthropic.Int(20), + }) + + for issuers.Next() { + issuer := issuers.Current() + fmt.Printf("%s: %s\n", issuer.ID, issuer.Name) + } + if err := issuers.Err(); err != nil { + log.Fatal(err) + } + ``` + + ```java Java + import com.anthropic.models.beta.organization.federation.issuers.IssuerListParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var params = IssuerListParams.builder() + .limit(20) + .build(); + var issuers = client.beta().organization().federation().issuers().list(params); + + for (var issuer : issuers.autoPager()) { + IO.println(issuer.id() + ": " + issuer.name()); + } + } + ``` + + ```php PHP + $client = new Client(); + + $issuers = $client->beta->organization->federation->issuers->list(limit: 20); + + foreach ($issuers->getItems() as $issuer) { + echo "{$issuer->id}: {$issuer->name}\n"; + } + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + issuers = client.beta.organization.federation.issuers.list(limit: 20) + + issuers.data.each do |issuer| + puts "#{issuer.id}: #{issuer.name}" + end + ``` +</CodeGroup> + +Archive an issuer: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/federation_issuers/fdis_01ABCDEFabcdef0123456789XY/archive" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:federation:issuers archive \ + --federation-issuer-id fdis_01ABCDEFabcdef0123456789XY + ``` + + ```python Python + client = anthropic.Anthropic() + + issuer = client.beta.organization.federation.issuers.archive( + "fdis_01ABCDEFabcdef0123456789XY" + ) + + print(f"id: {issuer.id}") + print(f"archived_at: {issuer.archived_at}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const issuer = await client.beta.organization.federation.issuers.archive( + "fdis_01ABCDEFabcdef0123456789XY" + ); + + console.log(`id: ${issuer.id}`); + console.log(`archived_at: ${issuer.archived_at}`); + ``` + + ```csharp C# + AnthropicClient client = new(); + + var issuer = await client.Beta.Organization.Federation.Issuers.Archive( + "fdis_01ABCDEFabcdef0123456789XY" + ); + + Console.WriteLine($"id: {issuer.ID}"); + Console.WriteLine($"archived_at: {issuer.ArchivedAt:O}"); + ``` + + ```go Go + client := anthropic.NewClient() + + issuer, err := client.Beta.Organization.Federation.Issuers.Archive( + context.Background(), + "fdis_01ABCDEFabcdef0123456789XY", + anthropic.BetaOrganizationFederationIssuerArchiveParams{}, + ) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", issuer.ID) + fmt.Printf("archived_at: %s\n", issuer.ArchivedAt) + ``` + + ```java Java + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var issuer = client.beta().organization().federation().issuers() + .archive("fdis_01ABCDEFabcdef0123456789XY"); + + IO.println("id: " + issuer.id()); + IO.println("archived_at: " + issuer.archivedAt().orElseThrow()); + ``` + + ```php PHP + $client = new Client(); + + $issuer = $client->beta->organization->federation->issuers->archive( + federationIssuerID: 'fdis_01ABCDEFabcdef0123456789XY', + ); + + echo "id: {$issuer->id}\n"; + echo "archived_at: {$issuer->archivedAt?->format(DATE_ATOM)}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + issuer_id = "fdis_01ABCDEFabcdef0123456789XY" + issuer = client.beta.organization.federation.issuers.archive(issuer_id) + + puts "id: #{issuer.id}" + puts "archived_at: #{issuer.archived_at}" + ``` +</CodeGroup> To read or update a single issuer, use `GET` and `POST` on `/v1/organizations/federation_issuers/{issuer_id}`. An OAuth caller cannot update an issuer that backs a rule whose `oauth_scope` is anything other than `workspace:developer` or `workspace:inference`; see [Permissions and constraints](https://platform.claude.com/docs/en/manage-claude/wif-admin-api#permissions-and-constraints).
from line 830
A [federation rule](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation#federation-rules) (`fdrl_...`) binds an issuer to a service account: JWTs from the issuer that satisfy the rule's match conditions can mint tokens that act as the rule's target. The `workspace_id` in the create request enables the rule in that workspace at creation; add more workspaces later through the `/federation_rules/{rule_id}/workspaces` sub-resource. Either `workspace_id` or `applies_to_all_workspaces: true` is required on create. -```bash cURL -# Create a rule (GitHub Actions deploys from the main branch) -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_rules" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" \ - -H "content-type: application/json" \ - -d '{ - "name": "gha-deploy", - "issuer_id": "fdis_...", - "match": { - "subject_prefix": "repo:my-org/my-repo:ref:refs/heads/main", - "claims": {"repository_owner": "my-org"} +Create a rule. This example lets GitHub Actions deploys from the main branch act as the service account: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_rules" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" \ + -H "content-type: application/json" \ + -d '{ + "name": "gha-deploy", + "issuer_id": "fdis_01ABCDEFabcdef0123456789XY", + "match": { + "subject_prefix": "repo:my-org/my-repo:ref:refs/heads/main", + "claims": {"repository_owner": "my-org"} + }, + "target": { + "type": "service_account", + "service_account_id": "svac_01ABCDEFabcdef0123456789XY" + }, + "workspace_id": "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", + "oauth_scope": "workspace:developer", + "token_lifetime_seconds": 600 + }' + ``` + + ```bash CLI + ant beta:organization:federation:rules create <<'YAML' + name: gha-deploy + issuer_id: fdis_01ABCDEFabcdef0123456789XY + match: + subject_prefix: "repo:my-org/my-repo:ref:refs/heads/main" + claims: + repository_owner: my-org + target: + type: service_account + service_account_id: svac_01ABCDEFabcdef0123456789XY + workspace_id: wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ + oauth_scope: "workspace:developer" + token_lifetime_seconds: 600 + YAML + ``` + + ```python Python + client = anthropic.Anthropic() + + rule = client.beta.organization.federation.rules.create( + name="gha-deploy", + issuer_id="fdis_01ABCDEFabcdef0123456789XY", + match={ + "subject_prefix": "repo:my-org/my-repo:ref:refs/heads/main", + "claims": {"repository_owner": "my-org"}, + }, + target={ + "type": "service_account", + "service_account_id": "svac_01ABCDEFabcdef0123456789XY", + }, + workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", + oauth_scope="workspace:developer", + token_lifetime_seconds=600, + ) + + print(f"id: {rule.id}") + print(f"name: {rule.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const rule = await client.beta.organization.federation.rules.create({ + name: "gha-deploy", + issuer_id: "fdis_01ABCDEFabcdef0123456789XY", + match: { + subject_prefix: "repo:my-org/my-repo:ref:refs/heads/main", + claims: { repository_owner: "my-org" } }, - "target": { - "type": "service_account", - "service_account_id": "svac_..." + target: { + type: "service_account", + service_account_id: "svac_01ABCDEFabcdef0123456789XY" }, - "workspace_id": "wrkspc_...", - "oauth_scope": "workspace:developer", - "token_lifetime_seconds": 600 - }' - -# List rules, optionally filtered by issuer -curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_rules?issuer_id=fdis_..." \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" - -# Archive a rule -curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/federation_rules/fdrl_.../archive" \ - -H "anthropic-version: 2023-06-01" \ - -H "authorization: Bearer $ANTHROPIC_OAUTH_TOKEN" -``` + workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", + oauth_scope: "workspace:developer", + token_lifetime_seconds: 600 + }); + + console.log(`id: ${rule.id}`); + console.log(`name: ${rule.name}`); + ``` + + ```csharp C# + AnthropicClient client = new(); + + var rule = await client.Beta.Organization.Federation.Rules.Create(new() + { + Name = "gha-deploy", + IssuerID = "fdis_01ABCDEFabcdef0123456789XY", + Match = new() + { + SubjectPrefix = "repo:my-org/my-repo:ref:refs/heads/main", + Claims = new Dictionary<string, string> { ["repository_owner"] = "my-org" } + }, + Target = new() { ServiceAccountID = "svac_01ABCDEFabcdef0123456789XY" }, + WorkspaceID = "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", + OAuthScope = "workspace:developer", + TokenLifetimeSeconds = 600 + }); + + Console.WriteLine($"id: {rule.ID}"); + Console.WriteLine($"name: {rule.Name}"); + ``` + + ```go Go + client := anthropic.NewClient() + + rule, err := client.Beta.Organization.Federation.Rules.New(context.Background(), anthropic.BetaOrganizationFederationRuleNewParams{ + Name: "gha-deploy", + IssuerID: "fdis_01ABCDEFabcdef0123456789XY", + Match: anthropic.BetaFederationRuleMatchParam{ + SubjectPrefix: anthropic.String("repo:my-org/my-repo:ref:refs/heads/main"), + Claims: map[string]string{"repository_owner": "my-org"}, + }, + Target: anthropic.BetaServiceAccountTargetParam{ + ServiceAccountID: "svac_01ABCDEFabcdef0123456789XY", + }, + WorkspaceID: anthropic.String("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"), + OAuthScope: "workspace:developer", + TokenLifetimeSeconds: anthropic.Int(600), + }) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", rule.ID) + fmt.Printf("name: %s\n", rule.Name) + ``` + + ```java Java + import com.anthropic.core.JsonValue; + import com.anthropic.models.beta.organization.federation.rules.BetaFederationRuleMatch; + import com.anthropic.models.beta.organization.federation.rules.BetaServiceAccountTarget; + import com.anthropic.models.beta.organization.federation.rules.RuleCreateParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var match = BetaFederationRuleMatch.builder() + .subjectPrefix("repo:my-org/my-repo:ref:refs/heads/main") + .claims(BetaFederationRuleMatch.Claims.builder() + .putAdditionalProperty("repository_owner", JsonValue.from("my-org")) + .build()) + .build(); + var params = RuleCreateParams.builder() + .name("gha-deploy") + .issuerId("fdis_01ABCDEFabcdef0123456789XY") + .match(match) + .target(BetaServiceAccountTarget.of("svac_01ABCDEFabcdef0123456789XY")) + .workspaceId("wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ") + .oauthScope("workspace:developer") + .tokenLifetimeSeconds(600) + .build(); + var rule = client.beta().organization().federation().rules().create(params); + + IO.println("id: " + rule.id()); + IO.println("name: " + rule.name()); + } + ``` + + ```php PHP + $client = new Client(); + + $rule = $client->beta->organization->federation->rules->create( + name: 'gha-deploy', + issuerID: 'fdis_01ABCDEFabcdef0123456789XY', + match: [ + 'subjectPrefix' => 'repo:my-org/my-repo:ref:refs/heads/main', + 'claims' => ['repository_owner' => 'my-org'], + ], + target: [ + 'type' => 'service_account', + 'serviceAccountID' => 'svac_01ABCDEFabcdef0123456789XY', + ], + workspaceID: 'wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ', + oauthScope: 'workspace:developer', + tokenLifetimeSeconds: 600, + ); + + echo "id: {$rule->id}\n"; + echo "name: {$rule->name}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + rule = client.beta.organization.federation.rules.create( + name: "gha-deploy", + issuer_id: "fdis_01ABCDEFabcdef0123456789XY", + match: { + subject_prefix: "repo:my-org/my-repo:ref:refs/heads/main", + claims: {repository_owner: "my-org"} + }, + target: { + type: :service_account, + service_account_id: "svac_01ABCDEFabcdef0123456789XY" + }, + workspace_id: "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", + oauth_scope: "workspace:developer", + token_lifetime_seconds: 600 + ) + + puts "id: #{rule.id}" + puts "name: #{rule.name}" + ``` +</CodeGroup> + +List rules, optionally filtered by issuer: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS "https://api.anthropic.com/v1/organizations/federation_rules?issuer_id=fdis_01ABCDEFabcdef0123456789XY" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:federation:rules list \ + --issuer-id fdis_01ABCDEFabcdef0123456789XY + ``` + + ```python Python + client = anthropic.Anthropic() + + rules = client.beta.organization.federation.rules.list( + issuer_id="fdis_01ABCDEFabcdef0123456789XY" + ) + + for rule in rules: + print(f"{rule.id}: {rule.name}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + for await (const rule of client.beta.organization.federation.rules.list({ + issuer_id: "fdis_01ABCDEFabcdef0123456789XY" + })) { + console.log(`${rule.id}: ${rule.name}`); + } + ``` + + ```csharp C# + AnthropicClient client = new(); + + var page = await client.Beta.Organization.Federation.Rules.List(new() + { + IssuerID = "fdis_01ABCDEFabcdef0123456789XY" + }); + + await foreach (var rule in page.Paginate()) + { + Console.WriteLine($"{rule.ID}: {rule.Name}"); + } + ``` + + ```go Go + client := anthropic.NewClient() + + rules := client.Beta.Organization.Federation.Rules.ListAutoPaging(context.Background(), anthropic.BetaOrganizationFederationRuleListParams{ + IssuerID: anthropic.String("fdis_01ABCDEFabcdef0123456789XY"), + }) + + for rules.Next() { + rule := rules.Current() + fmt.Printf("%s: %s\n", rule.ID, rule.Name) + } + if err := rules.Err(); err != nil { + log.Fatal(err) + } + ``` + + ```java Java + import com.anthropic.models.beta.organization.federation.rules.RuleListParams; + + void main() { + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var params = RuleListParams.builder() + .issuerId("fdis_01ABCDEFabcdef0123456789XY") + .build(); + var rules = client.beta().organization().federation().rules().list(params); + + for (var rule : rules.autoPager()) { + IO.println(rule.id() + ": " + rule.name()); + } + } + ``` + + ```php PHP + $client = new Client(); + + $rules = $client->beta->organization->federation->rules->list( + issuerID: 'fdis_01ABCDEFabcdef0123456789XY', + ); + + foreach ($rules->getItems() as $rule) { + echo "{$rule->id}: {$rule->name}\n"; + } + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + rules = client.beta.organization.federation.rules.list( + issuer_id: "fdis_01ABCDEFabcdef0123456789XY" + ) + + rules.data.each do |rule| + puts "#{rule.id}: #{rule.name}" + end + ``` +</CodeGroup> + +Archive a rule: + +<CodeGroup> + ```bash cURL + curl --fail-with-body -sS -X POST "https://api.anthropic.com/v1/organizations/federation_rules/fdrl_01ABCDEFabcdef0123456789XY/archive" \ + -H "authorization: Bearer $ANTHROPIC_AUTH_TOKEN" \ + -H "anthropic-version: 2023-06-01" + ``` + + ```bash CLI + ant beta:organization:federation:rules archive \ + --federation-rule-id fdrl_01ABCDEFabcdef0123456789XY + ``` + + ```python Python + client = anthropic.Anthropic() + + rule = client.beta.organization.federation.rules.archive( + "fdrl_01ABCDEFabcdef0123456789XY" + ) + + print(f"id: {rule.id}") + print(f"archived_at: {rule.archived_at}") + ``` + + ```typescript TypeScript + const client = new Anthropic(); + + const rule = await client.beta.organization.federation.rules.archive( + "fdrl_01ABCDEFabcdef0123456789XY" + ); + + console.log(`id: ${rule.id}`); + console.log(`archived_at: ${rule.archived_at}`); + ``` + + ```csharp C# + AnthropicClient client = new(); + + var rule = await client.Beta.Organization.Federation.Rules.Archive( + "fdrl_01ABCDEFabcdef0123456789XY" + ); + + Console.WriteLine($"id: {rule.ID}"); + Console.WriteLine($"archived_at: {rule.ArchivedAt:O}"); + ``` + + ```go Go + client := anthropic.NewClient() + + rule, err := client.Beta.Organization.Federation.Rules.Archive( + context.Background(), + "fdrl_01ABCDEFabcdef0123456789XY", + anthropic.BetaOrganizationFederationRuleArchiveParams{}, + ) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("id: %s\n", rule.ID) + fmt.Printf("archived_at: %s\n", rule.ArchivedAt) + ``` + + ```java Java + AnthropicClient client = AnthropicOkHttpClient.fromEnv(); + + var rule = client.beta().organization().federation().rules() + .archive("fdrl_01ABCDEFabcdef0123456789XY"); + + IO.println("id: " + rule.id()); + IO.println("archived_at: " + rule.archivedAt().orElseThrow()); + ``` + + ```php PHP + $client = new Client(); + + $rule = $client->beta->organization->federation->rules->archive( + federationRuleID: 'fdrl_01ABCDEFabcdef0123456789XY', + ); + + echo "id: {$rule->id}\n"; + echo "archived_at: {$rule->archivedAt?->format(DATE_ATOM)}\n"; + ``` + + ```ruby Ruby + client = Anthropic::Client.new + + rule_id = "fdrl_01ABCDEFabcdef0123456789XY" + rule = client.beta.organization.federation.rules.archive(rule_id) + + puts "id: #{rule.id}" + puts "archived_at: #{rule.archived_at}" + ``` +</CodeGroup> The list endpoint returns a page of rules and the cursor for the next page: