One read of Claude Developer Platformapi-20261016T040711Z
11 pages moved out of 782 read.
Pages moved
11
significant first
Pages read
782
in this capture
Captured
04:07 UTC
Corpus hash
7f4e01af8520
index-hash
What this read moved
1-11 of 11agents-and-tools/agent-skills/claude-api-skill Changed · +7 / -7 lines
from line 93
9393npx skills add https://github.com/anthropics/skills --skill claude-api
9494```
9595
96Or install it as a [Claude Code plugin](https://code.claude.com/docs/en/plugins):
96Or ask Claude Code to install it as a [Claude Code plugin](https://code.claude.com/docs/en/plugins):
9797
9898Let Claude Code install this for you
9999
from line 101
101101Install the claude-api plugin from the anthropics/skills plugin marketplace
102102```
103103
104Or run these commands in Claude Code:
105
106```text wrap
107/plugin marketplace add anthropics/skills
108/plugin install claude-api@anthropic-agent-skills
109```
104<Accordion title="Manual install commands">
105 ```text wrap
106 /plugin marketplace add anthropics/skills
107 /plugin install claude-api@anthropic-agent-skills
108 ```
109</Accordion>
110110
111111## Migrating to a newer Claude model
112112
manage-claude/wif-providers/aws Changed · +79 / -37 lines
from line 95
9595
9696### Acquire and use the token
9797
98Call `GetWebIdentityToken` with `https://api.anthropic.com` as the audience, then pass the result to the SDK's federation credentials. Because `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`) takes a callable, the SDK re-invokes STS on each refresh.
98Call `GetWebIdentityToken` with `https://api.anthropic.com` as the audience, then pass the result to the SDK's federation credentials through `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`). Except in Java (see the comment in that example), that option takes a token provider, so the SDK re-invokes STS on each refresh.
9999
100100<Note>
101101 `GetWebIdentityToken` is available only on regional STS endpoints. If you receive `'STS' object has no attribute 'get_web_identity_token'` or a similar error, pin your STS client to a region (for example, `boto3.client("sts", region_name="us-east-1")`) and ensure your AWS SDK is recent enough to include the API.
from line 263
263263 ```
264264
265265 ```java Java
266 StsClient sts = StsClient.builder().region(Region.US_EAST_1).build();
266 import com.anthropic.config.AuthenticationConfig;
267 import com.anthropic.config.AuthenticationType;
268 import com.anthropic.config.IdentityTokenConfig;
269 import com.anthropic.config.InMemoryProfileConfigProvider;
270 import com.anthropic.config.ProfileConfig;
271 // ...
272 import software.amazon.awssdk.regions.Region;
273 import software.amazon.awssdk.services.sts.StsClient;
274 import software.amazon.awssdk.services.sts.model.GetWebIdentityTokenRequest;
267275
268 IdentityTokenProvider getStsToken = () -> sts.getWebIdentityToken(
269 GetWebIdentityTokenRequest.builder()
270 .audience("https://api.anthropic.com")
271 .signingAlgorithm("RS256")
272 .durationSeconds(900)
273 .build())
274 .webIdentityToken();
276 void main() {
277 StsClient sts = StsClient.builder().region(Region.US_EAST_1).build();
275278
276 AnthropicClient client = AnthropicOkHttpClient.builder()
277 .federationTokenProvider(
278 getStsToken,
279 System.getenv("ANTHROPIC_FEDERATION_RULE_ID"),
280 System.getenv("ANTHROPIC_ORGANIZATION_ID"),
281 System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
282 .build();
279 // The Java SDK takes the token itself or a file path, not a function; with
280 // source("value"), path(...) holds the token. The SDK sends that same token
281 // at every refresh of the Anthropic access token, which fails once it has
282 // expired or, if it has a jti claim, been used. For a longer-running
283 // process, pass source("file") and write a new token to the file before
284 // each refresh.
285 String stsToken = sts.getWebIdentityToken(
286 GetWebIdentityTokenRequest.builder()
287 .audience("https://api.anthropic.com")
288 .signingAlgorithm("RS256")
289 .durationSeconds(900)
290 .build())
291 .webIdentityToken();
283292
284 var message = client.messages().create(MessageCreateParams.builder()
285 .model(Model.CLAUDE_OPUS_5_5)
286 .maxTokens(1024)
287 .addUserMessage("Hello from AWS")
288 .build());
293 AnthropicClient client = AnthropicOkHttpClient.builder()
294 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
295 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
296 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
297 .authentication(AuthenticationConfig.builder()
298 .type(AuthenticationType.OIDC_FEDERATION)
299 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
300 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
301 .identityToken(IdentityTokenConfig.builder()
302 .source("value")
303 .path(stsToken)
304 .build())
305 .build())
306 .build()))
307 .build();
289308
290 IO.println(message.content());
309 var message = client.messages().create(MessageCreateParams.builder()
310 .model(Model.CLAUDE_OPUS_5_5)
311 .maxTokens(1024)
312 .addUserMessage("Hello from AWS")
313 .build());
314
315 IO.println(message.content());
316 }
291317 ```
292318
293319 ```csharp C#
from line 384
358384
359385 ```php PHP
360386 use Anthropic\Client;
361 use Anthropic\Credentials\WorkloadIdentityCredentials;
387 use Anthropic\Lib\Credentials\CredentialResult;
388 use Anthropic\Lib\Credentials\IdentityTokenProvider;
389 use Anthropic\Lib\Credentials\TokenCache;
390 use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
362391 use Aws\Sts\StsClient;
363392
364393 $sts = new StsClient(['region' => 'us-east-1', 'version' => 'latest']);
365 $client = new Client(credentials: new WorkloadIdentityCredentials(
366 identityTokenProvider: fn() => $sts->getWebIdentityToken([
367 'Audience' => ['https://api.anthropic.com'],
368 'SigningAlgorithm' => 'RS256',
369 'DurationSeconds' => 900,
370 ])['WebIdentityToken'],
371 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
372 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
373 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
374 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
394
395 class StsIdentityToken implements IdentityTokenProvider
396 {
397 public function __construct(private StsClient $sts) {}
398
399 public function getIdentityToken(): string
400 {
401 return $this->sts->getWebIdentityToken([
402 'Audience' => ['https://api.anthropic.com'],
403 'SigningAlgorithm' => 'RS256',
404 'DurationSeconds' => 900,
405 ])['WebIdentityToken'];
406 }
407 }
408
409 $client = new Client(credentials: new CredentialResult(
410 provider: new TokenCache(new WorkloadIdentityCredentials(
411 identityProvider: new StsIdentityToken($sts),
412 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
413 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
414 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
415 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
416 )),
375417 ));
376418
377419 $message = $client->messages->create(
manage-claude/wif-providers/azure Changed · +196 / -131 lines
from line 141
141141
142142### Acquire and use the token
143143
144At runtime your workload fetches its Entra token, exchanges it at `POST /v1/oauth/token`, and uses the returned bearer token to call Claude. The Claude SDK handles the exchange and refresh loop when you supply `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`), as shown in the following examples. The cURL tab shows the raw flow.
144At runtime your workload fetches its Entra token, exchanges it at `POST /v1/oauth/token`, and uses the returned bearer token to call Claude. The Claude SDK handles the exchange and refresh loop when you supply `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`), as shown in the following examples. Except in Java (see the comment in that example), that option takes a token provider, and the SDK calls it again for each refresh. The cURL tab shows the raw flow.
145145
146146The samples fetch the managed identity token from the platform's token endpoint: IMDS on VMs and VM Scale Sets, or the `IDENTITY_ENDPOINT` service on App Service, Functions, and Container Apps. Replace `<APP_ID>` in the `api://<APP_ID>` resource value with the audience app registration's client ID from [Register the token audience](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#register-the-token-audience).
147147
148148<Tip>
149 If your workload already uses the Azure Identity client library, pass its token acquisition (`DefaultAzureCredential` with the scope `api://<APP_ID>/.default`) to `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`) instead of calling the token endpoints directly. The library selects the correct endpoint on every Azure platform, including AKS with Entra Workload Identity.
149 If your workload already uses the Azure Identity client library, get the Entra token from it (`DefaultAzureCredential` with the scope `api://<APP_ID>/.default`) instead of calling the token endpoints directly. Supply it to `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`) the way the examples do. The library selects the correct endpoint on every Azure platform, including AKS with Entra Workload Identity.
150150</Tip>
151151
152152<CodeGroup>
from line 374
374374 ```
375375
376376 ```java Java
377 HttpClient http = HttpClient.newHttpClient();
378 // The audience app registration's identifier URI (see Register the token audience).
379 String audience = "api://<APP_ID>";
380 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
381 // VMs and VM Scale Sets use IMDS.
382 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
383 String identityEndpoint = System.getenv("IDENTITY_ENDPOINT");
384 HttpRequest tokenRequest = identityEndpoint != null
385 ? HttpRequest.newBuilder(URI.create(identityEndpoint + "?api-version=2019-08-01&resource=" + audience))
386 .header("X-IDENTITY-HEADER", System.getenv("IDENTITY_HEADER"))
387 .build()
388 : HttpRequest.newBuilder(URI.create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + audience))
389 .header("Metadata", "true")
390 .build();
377 import com.anthropic.config.AuthenticationConfig;
378 import com.anthropic.config.AuthenticationType;
379 import com.anthropic.config.IdentityTokenConfig;
380 import com.anthropic.config.InMemoryProfileConfigProvider;
381 import com.anthropic.config.ProfileConfig;
382 // ...
383 import com.fasterxml.jackson.databind.ObjectMapper;
384 import java.net.URI;
385 import java.net.http.HttpClient;
386 import java.net.http.HttpRequest;
387 import java.net.http.HttpResponse;
391388
392 IdentityTokenProvider fetchEntraToken = () -> {
393 try {
394 var response = http.send(tokenRequest, HttpResponse.BodyHandlers.ofString());
395 return new ObjectMapper().readTree(response.body()).get("access_token").asText();
396 } catch (Exception e) {
397 throw new RuntimeException(e);
398 }
399 };
389 void main() throws Exception {
390 HttpClient http = HttpClient.newHttpClient();
391 // The audience app registration's identifier URI (see Register the token audience).
392 String audience = "api://<APP_ID>";
393 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
394 // VMs and VM Scale Sets use IMDS.
395 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
396 String identityEndpoint = System.getenv("IDENTITY_ENDPOINT");
397 HttpRequest tokenRequest = identityEndpoint != null
398 ? HttpRequest.newBuilder(URI.create(identityEndpoint + "?api-version=2019-08-01&resource=" + audience))
399 .header("X-IDENTITY-HEADER", System.getenv("IDENTITY_HEADER"))
400 .build()
401 : HttpRequest.newBuilder(URI.create("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=" + audience))
402 .header("Metadata", "true")
403 .build();
400404
401 AnthropicClient client = AnthropicOkHttpClient.builder()
402 .federationTokenProvider(
403 fetchEntraToken,
404 System.getenv("ANTHROPIC_FEDERATION_RULE_ID"),
405 System.getenv("ANTHROPIC_ORGANIZATION_ID"),
406 System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
407 System.getenv("ANTHROPIC_WORKSPACE_ID"))
408 .build();
405 // The Java SDK takes the token itself or a file path, not a function; with
406 // source("value"), path(...) holds the token. The SDK sends that same token
407 // at every refresh of the Anthropic access token, which fails once it has
408 // expired or, if it has a jti claim, been used. For a longer-running
409 // process, pass source("file") and write a new token to the file before
410 // each refresh.
411 var response = http.send(tokenRequest, HttpResponse.BodyHandlers.ofString());
412 String entraToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
409413
410 var message = client.messages().create(MessageCreateParams.builder()
411 .model(Model.CLAUDE_OPUS_5_5)
412 .maxTokens(1024)
413 .addUserMessage("Hello from Azure")
414 .build());
414 AnthropicClient client = AnthropicOkHttpClient.builder()
415 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
416 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
417 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
418 .authentication(AuthenticationConfig.builder()
419 .type(AuthenticationType.OIDC_FEDERATION)
420 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
421 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
422 .identityToken(IdentityTokenConfig.builder()
423 .source("value")
424 .path(entraToken)
425 .build())
426 .build())
427 .build()))
428 .build();
415429
416 IO.println(message.content());
430 var message = client.messages().create(MessageCreateParams.builder()
431 .model(Model.CLAUDE_OPUS_5_5)
432 .maxTokens(1024)
433 .addUserMessage("Hello from Azure")
434 .build());
435
436 IO.println(message.content());
437 }
417438 ```
418439
419440 ```csharp C#
from line 500
479500
480501 ```php PHP
481502 use Anthropic\Client;
482 use Anthropic\Credentials\WorkloadIdentityCredentials;
503 use Anthropic\Lib\Credentials\CredentialResult;
504 use Anthropic\Lib\Credentials\IdentityTokenProvider;
505 use Anthropic\Lib\Credentials\TokenCache;
506 use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
483507
484508 // The audience app registration's identifier URI (see Register the token audience).
485509 const AUDIENCE = 'api://<APP_ID>';
486510
487 function fetchEntraToken(): string
511 class EntraIdentityToken implements IdentityTokenProvider
488512 {
489 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
490 // VMs and VM Scale Sets use IMDS.
491 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
492 $identityEndpoint = getenv('IDENTITY_ENDPOINT');
493 if ($identityEndpoint !== false) {
494 $url = $identityEndpoint . '?api-version=2019-08-01&resource=' . AUDIENCE;
495 $header = 'X-IDENTITY-HEADER: ' . getenv('IDENTITY_HEADER');
496 } else {
497 $url = 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' . AUDIENCE;
498 $header = 'Metadata: true';
513 public function getIdentityToken(): string
514 {
515 // App Service, Functions, and Container Apps inject IDENTITY_ENDPOINT;
516 // VMs and VM Scale Sets use IMDS.
517 // With multiple user-assigned identities, append &client_id=<IDENTITY_CLIENT_ID>.
518 $identityEndpoint = getenv('IDENTITY_ENDPOINT');
519 if ($identityEndpoint !== false) {
520 $url = $identityEndpoint . '?api-version=2019-08-01&resource=' . AUDIENCE;
521 $header = 'X-IDENTITY-HEADER: ' . getenv('IDENTITY_HEADER');
522 } else {
523 $url = 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' . AUDIENCE;
524 $header = 'Metadata: true';
525 }
526 $context = stream_context_create([
527 'http' => ['header' => $header . "\r\n"],
528 ]);
529 $body = json_decode(file_get_contents($url, false, $context), true);
530 return $body['access_token'];
499531 }
500 $context = stream_context_create([
501 'http' => ['header' => $header . "\r\n"],
502 ]);
503 $body = json_decode(file_get_contents($url, false, $context), true);
504 return $body['access_token'];
505532 }
506533
507 $credentials = new WorkloadIdentityCredentials(
508 identityTokenProvider: fetchEntraToken(...),
509 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
510 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
511 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
512 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
534 $credentials = new CredentialResult(
535 provider: new TokenCache(new WorkloadIdentityCredentials(
536 identityProvider: new EntraIdentityToken(),
537 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
538 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
539 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
540 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
541 )),
513542 );
514543 $client = new Client(credentials: $credentials);
515544
from line 790
761790
762791### Acquire and use the token
763792
764At runtime the pod performs the two-hop exchange: it sends the Kubernetes-projected token (the file at `AZURE_FEDERATED_TOKEN_FILE`) to Entra's token endpoint as a federated `client_credentials` assertion, then exchanges the resulting Entra access token at `POST /v1/oauth/token`. The Claude SDK handles the second exchange and the refresh loop when you pass the Entra fetch to `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`), as shown in the following examples. The cURL tab shows the raw flow.
793At runtime the pod performs the two-hop exchange: it sends the Kubernetes-projected token (the file at `AZURE_FEDERATED_TOKEN_FILE`) to Entra's token endpoint as a federated `client_credentials` assertion, then exchanges the resulting Entra access token at `POST /v1/oauth/token`. The Claude SDK handles the second exchange and the refresh loop when you supply `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`), as shown in the following examples. Except in Java (see the comment in that example), that option takes your Entra fetch as a token provider, and the SDK calls it again for each refresh. The cURL tab shows the raw flow.
765794
766795Two different client IDs appear in the samples. `<APP_ID>` is the audience app registration's client ID from [Register the token audience](https://platform.claude.com/docs/en/manage-claude/wif-providers/azure#register-the-token-audience); the scope `api://<APP_ID>/.default` asks Entra for a token addressed to that audience. `$AZURE_CLIENT_ID` is the managed identity's client ID, injected by the webhook, and identifies the caller. Do not substitute one for the other.
767796
768797<Tip>
769 If your workload already uses the Azure Identity client library, pass its token acquisition (`DefaultAzureCredential` with the scope `api://<APP_ID>/.default`) to `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`) instead of performing the two-hop exchange yourself. The library reads the same `AZURE_FEDERATED_TOKEN_FILE`, `AZURE_CLIENT_ID`, and `AZURE_TENANT_ID` environment variables and handles the Entra exchange.
798 If your workload already uses the Azure Identity client library, get the Entra token from it (`DefaultAzureCredential` with the scope `api://<APP_ID>/.default`) instead of performing the two-hop exchange yourself. Supply it to `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`) the way the examples do. The library reads the same `AZURE_FEDERATED_TOKEN_FILE`, `AZURE_CLIENT_ID`, and `AZURE_TENANT_ID` environment variables and handles the Entra exchange.
770799</Tip>
771800
772801<CodeGroup>
from line 1007
9781007 ```
9791008
9801009 ```java Java
981 IdentityTokenProvider fetchEntraTokenViaFederation = () -> {
982 try {
983 var form = Map.of(
984 "client_id", System.getenv("AZURE_CLIENT_ID"),
985 "grant_type", "client_credentials",
986 "scope", "api://<APP_ID>/.default",
987 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
988 "client_assertion", Files.readString(Path.of(System.getenv("AZURE_FEDERATED_TOKEN_FILE"))))
989 .entrySet().stream()
990 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
991 .collect(Collectors.joining("&"));
992 var request = HttpRequest.newBuilder(URI.create(
993 "https://login.microsoftonline.com/" + System.getenv("AZURE_TENANT_ID") + "/oauth2/v2.0/token"))
994 .header("content-type", "application/x-www-form-urlencoded")
995 .POST(HttpRequest.BodyPublishers.ofString(form))
996 .build();
997 var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
998 return new ObjectMapper().readTree(response.body()).get("access_token").asText();
999 } catch (Exception e) {
1000 throw new RuntimeException(e);
1001 }
1002 };
1010 import com.anthropic.config.AuthenticationConfig;
1011 import com.anthropic.config.AuthenticationType;
1012 import com.anthropic.config.IdentityTokenConfig;
1013 import com.anthropic.config.InMemoryProfileConfigProvider;
1014 import com.anthropic.config.ProfileConfig;
1015 // ...
1016 import com.fasterxml.jackson.databind.ObjectMapper;
1017 import java.net.URI;
1018 import java.net.URLEncoder;
1019 import java.net.http.HttpClient;
1020 import java.net.http.HttpRequest;
1021 import java.net.http.HttpResponse;
1022 import java.nio.file.Files;
1023 import java.nio.file.Path;
1024 import java.util.Map;
1025 import java.util.stream.Collectors;
1026 import static java.nio.charset.StandardCharsets.UTF_8;
10031027
1004 AnthropicClient client = AnthropicOkHttpClient.builder()
1005 .federationTokenProvider(
1006 fetchEntraTokenViaFederation,
1007 System.getenv("ANTHROPIC_FEDERATION_RULE_ID"),
1008 System.getenv("ANTHROPIC_ORGANIZATION_ID"),
1009 System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"),
1010 System.getenv("ANTHROPIC_WORKSPACE_ID"))
1011 .build();
1028 void main() throws Exception {
1029 var form = Map.of(
1030 "client_id", System.getenv("AZURE_CLIENT_ID"),
1031 "grant_type", "client_credentials",
1032 "scope", "api://<APP_ID>/.default",
1033 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
1034 "client_assertion", Files.readString(Path.of(System.getenv("AZURE_FEDERATED_TOKEN_FILE"))))
1035 .entrySet().stream()
1036 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
1037 .collect(Collectors.joining("&"));
1038 var request = HttpRequest.newBuilder(URI.create(
1039 "https://login.microsoftonline.com/" + System.getenv("AZURE_TENANT_ID") + "/oauth2/v2.0/token"))
1040 .header("content-type", "application/x-www-form-urlencoded")
1041 .POST(HttpRequest.BodyPublishers.ofString(form))
1042 .build();
10121043
1013 var message = client.messages().create(MessageCreateParams.builder()
1014 .model(Model.CLAUDE_OPUS_5_5)
1015 .maxTokens(1024)
1016 .addUserMessage("Hello from Azure")
1017 .build());
1044 // The Java SDK takes the token itself or a file path, not a function; with
1045 // source("value"), path(...) holds the token. The SDK sends that same token
1046 // at every refresh of the Anthropic access token, which fails once it has
1047 // expired or, if it has a jti claim, been used. For a longer-running
1048 // process, pass source("file") and write a new token to the file before
1049 // each refresh.
1050 var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
1051 String entraToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
10181052
1019 IO.println(message.content());
1053 AnthropicClient client = AnthropicOkHttpClient.builder()
1054 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
1055 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
1056 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
1057 .authentication(AuthenticationConfig.builder()
1058 .type(AuthenticationType.OIDC_FEDERATION)
1059 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
1060 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
1061 .identityToken(IdentityTokenConfig.builder()
1062 .source("value")
1063 .path(entraToken)
1064 .build())
1065 .build())
1066 .build()))
1067 .build();
1068
1069 var message = client.messages().create(MessageCreateParams.builder()
1070 .model(Model.CLAUDE_OPUS_5_5)
1071 .maxTokens(1024)
1072 .addUserMessage("Hello from Azure")
1073 .build());
1074
1075 IO.println(message.content());
1076 }
10201077 ```
10211078
10221079 ```csharp C#
from line 1133
10761133
10771134 ```php PHP
10781135 use Anthropic\Client;
1079 use Anthropic\Credentials\WorkloadIdentityCredentials;
1136 use Anthropic\Lib\Credentials\CredentialResult;
1137 use Anthropic\Lib\Credentials\IdentityTokenProvider;
1138 use Anthropic\Lib\Credentials\TokenCache;
1139 use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
10801140
1081 function fetchEntraTokenViaFederation(): string
1141 class EntraIdentityTokenViaFederation implements IdentityTokenProvider
10821142 {
1083 $ch = curl_init('https://login.microsoftonline.com/' . getenv('AZURE_TENANT_ID') . '/oauth2/v2.0/token');
1084 curl_setopt_array($ch, [
1085 CURLOPT_RETURNTRANSFER => true,
1086 CURLOPT_POSTFIELDS => http_build_query([
1087 'client_id' => getenv('AZURE_CLIENT_ID'),
1088 'grant_type' => 'client_credentials',
1089 'scope' => 'api://<APP_ID>/.default',
1090 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
1091 'client_assertion' => file_get_contents(getenv('AZURE_FEDERATED_TOKEN_FILE')),
1092 ]),
1093 ]);
1094 $body = json_decode(curl_exec($ch), true);
1095 curl_close($ch);
1096 return $body['access_token'];
1143 public function getIdentityToken(): string
1144 {
1145 $ch = curl_init('https://login.microsoftonline.com/' . getenv('AZURE_TENANT_ID') . '/oauth2/v2.0/token');
1146 curl_setopt_array($ch, [
1147 CURLOPT_RETURNTRANSFER => true,
1148 CURLOPT_POSTFIELDS => http_build_query([
1149 'client_id' => getenv('AZURE_CLIENT_ID'),
1150 'grant_type' => 'client_credentials',
1151 'scope' => 'api://<APP_ID>/.default',
1152 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
1153 'client_assertion' => file_get_contents(getenv('AZURE_FEDERATED_TOKEN_FILE')),
1154 ]),
1155 ]);
1156 $body = json_decode(curl_exec($ch), true);
1157 curl_close($ch);
1158 return $body['access_token'];
1159 }
10971160 }
10981161
10991162 $client = new Client(
1100 credentials: new WorkloadIdentityCredentials(
1101 identityTokenProvider: fetchEntraTokenViaFederation(...),
1102 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
1103 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
1104 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
1105 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
1163 credentials: new CredentialResult(
1164 provider: new TokenCache(new WorkloadIdentityCredentials(
1165 identityProvider: new EntraIdentityTokenViaFederation(),
1166 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
1167 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
1168 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
1169 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
1170 )),
11061171 ),
11071172 );
11081173
manage-claude/wif-providers/gcp Changed · +76 / -42 lines
from line 126
126126
127127## Acquire and use the token
128128
129Inside your Google Cloud workload, fetch the identity token from the metadata server, exchange it at `POST /v1/oauth/token`, and use the returned bearer token to call the Claude API. The Claude SDK handles the exchange and refresh loop for you when you pass a callable that returns a fresh identity token from the metadata server to `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`), as shown in the following examples.
129Inside your Google Cloud workload, fetch the identity token from the metadata server, exchange it at `POST /v1/oauth/token`, and use the returned bearer token to call the Claude API. The Claude SDK handles the exchange and refresh loop for you when you supply `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`), as shown in the following examples. Except in Java (see the comment in that example), that option takes a token provider that returns a fresh identity token from the metadata server.
130130
131131<CodeGroup>
132132 ```bash cURL
from line 276
276276 ```
277277
278278 ```java Java
279 HttpClient http = HttpClient.newHttpClient();
280 HttpRequest metadataRequest = HttpRequest.newBuilder()
281 .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
282 .header("Metadata-Flavor", "Google")
283 .build();
279 import com.anthropic.config.AuthenticationConfig;
280 import com.anthropic.config.AuthenticationType;
281 import com.anthropic.config.IdentityTokenConfig;
282 import com.anthropic.config.InMemoryProfileConfigProvider;
283 import com.anthropic.config.ProfileConfig;
284 // ...
285 import java.net.URI;
286 import java.net.http.HttpClient;
287 import java.net.http.HttpRequest;
288 import java.net.http.HttpResponse;
284289
285 IdentityTokenProvider fetchGoogleIdentityToken = () -> {
286 try {
287 return http.send(metadataRequest, HttpResponse.BodyHandlers.ofString()).body();
288 } catch (Exception e) {
289 throw new RuntimeException(e);
290 }
291 };
290 void main() throws Exception {
291 HttpClient http = HttpClient.newHttpClient();
292 HttpRequest metadataRequest = HttpRequest.newBuilder()
293 .uri(URI.create("http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full"))
294 .header("Metadata-Flavor", "Google")
295 .build();
292296
293 AnthropicClient client = AnthropicOkHttpClient.builder()
294 .federationTokenProvider(
295 fetchGoogleIdentityToken,
296 System.getenv("ANTHROPIC_FEDERATION_RULE_ID"),
297 System.getenv("ANTHROPIC_ORGANIZATION_ID"),
298 System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
299 .build();
297 // The Java SDK takes the token itself or a file path, not a function; with
298 // source("value"), path(...) holds the token. The SDK sends that same token
299 // at every refresh of the Anthropic access token, which fails once it has
300 // expired or, if it has a jti claim, been used. For a longer-running
301 // process, pass source("file") and write a new token to the file before
302 // each refresh.
303 String googleIdentityToken = http.send(metadataRequest, HttpResponse.BodyHandlers.ofString()).body();
300304
301 var message = client.messages().create(MessageCreateParams.builder()
302 .model(Model.CLAUDE_OPUS_5_5)
303 .maxTokens(1024)
304 .addUserMessage("Hello from Cloud Run")
305 .build());
305 AnthropicClient client = AnthropicOkHttpClient.builder()
306 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
307 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
308 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
309 .authentication(AuthenticationConfig.builder()
310 .type(AuthenticationType.OIDC_FEDERATION)
311 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
312 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
313 .identityToken(IdentityTokenConfig.builder()
314 .source("value")
315 .path(googleIdentityToken)
316 .build())
317 .build())
318 .build()))
319 .build();
306320
307 IO.println(message.content());
321 var message = client.messages().create(MessageCreateParams.builder()
322 .model(Model.CLAUDE_OPUS_5_5)
323 .maxTokens(1024)
324 .addUserMessage("Hello from Cloud Run")
325 .build());
326
327 IO.println(message.content());
328 }
308329 ```
309330
310331 ```csharp C#
from line 391
370391
371392 ```php PHP
372393 use Anthropic\Client;
373 use Anthropic\Credentials\WorkloadIdentityCredentials;
394 use Anthropic\Lib\Credentials\CredentialResult;
395 use Anthropic\Lib\Credentials\IdentityTokenProvider;
396 use Anthropic\Lib\Credentials\TokenCache;
397 use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
374398
375399 const METADATA_URL = 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full';
376400
377 $context = stream_context_create([
378 'http' => ['header' => "Metadata-Flavor: Google\r\n"],
379 ]);
401 class GoogleIdentityToken implements IdentityTokenProvider
402 {
403 public function getIdentityToken(): string
404 {
405 $context = stream_context_create([
406 'http' => ['header' => "Metadata-Flavor: Google\r\n"],
407 ]);
380408
381 $credentials = new WorkloadIdentityCredentials(
382 identityTokenProvider: fn() => file_get_contents(METADATA_URL, false, $context),
383 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
384 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
385 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
386 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
409 return file_get_contents(METADATA_URL, false, $context);
410 }
411 }
412
413 $credentials = new CredentialResult(
414 provider: new TokenCache(new WorkloadIdentityCredentials(
415 identityProvider: new GoogleIdentityToken(),
416 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
417 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
418 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
419 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
420 )),
387421 );
388422 $client = new Client(credentials: $credentials);
389423
from line 454
420454 ```
421455</CodeGroup>
422456
423Google identity tokens expire after roughly one hour. The SDK re-invokes the token provider and re-exchanges automatically before expiry. For shell scripts that run longer than the access token's `expires_in`, refresh on a timer and repeat the exchange.
457Google identity tokens expire after roughly one hour. Except in Java (see the comment in that example), the SDK re-invokes the token provider and re-exchanges automatically before expiry. For shell scripts that run longer than the access token's `expires_in`, refresh on a timer and repeat the exchange.
424458
425459## Verify the setup
426460
manage-claude/wif-providers/okta Changed · +96 / -61 lines
from line 267
267267 ```
268268
269269 ```java Java
270 IdentityTokenProvider fetchOktaToken = () -> {
271 try {
272 var form = Map.of(
273 "grant_type", "client_credentials",
274 "scope", "anthropic.access",
275 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
276 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
277 "client_assertion", buildSignedClientAssertion())
278 .entrySet().stream()
279 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
280 .collect(Collectors.joining("&"));
281 var request = HttpRequest.newBuilder(URI.create(System.getenv("OKTA_ISSUER") + "/v1/token"))
282 .header("content-type", "application/x-www-form-urlencoded")
283 .POST(HttpRequest.BodyPublishers.ofString(form))
284 .build();
285 var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
286 return new ObjectMapper().readTree(response.body()).get("access_token").asText();
287 } catch (Exception e) {
288 throw new RuntimeException(e);
289 }
290 };
270 import com.anthropic.config.AuthenticationConfig;
271 import com.anthropic.config.AuthenticationType;
272 import com.anthropic.config.IdentityTokenConfig;
273 import com.anthropic.config.InMemoryProfileConfigProvider;
274 import com.anthropic.config.ProfileConfig;
275 // ...
276 import com.fasterxml.jackson.databind.ObjectMapper;
277 import java.net.URI;
278 import java.net.URLEncoder;
279 import java.net.http.HttpClient;
280 import java.net.http.HttpRequest;
281 import java.net.http.HttpResponse;
282 import java.util.Map;
283 import java.util.stream.Collectors;
284 import static java.nio.charset.StandardCharsets.UTF_8;
291285
292 AnthropicClient client = AnthropicOkHttpClient.builder()
293 .federationTokenProvider(
294 fetchOktaToken,
295 System.getenv("ANTHROPIC_FEDERATION_RULE_ID"),
296 System.getenv("ANTHROPIC_ORGANIZATION_ID"),
297 System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
298 .build();
286 void main() throws Exception {
287 var form = Map.of(
288 "grant_type", "client_credentials",
289 "scope", "anthropic.access",
290 "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
291 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
292 "client_assertion", buildSignedClientAssertion())
293 .entrySet().stream()
294 .map(entry -> entry.getKey() + "=" + URLEncoder.encode(entry.getValue(), UTF_8))
295 .collect(Collectors.joining("&"));
296 var request = HttpRequest.newBuilder(URI.create(System.getenv("OKTA_ISSUER") + "/v1/token"))
297 .header("content-type", "application/x-www-form-urlencoded")
298 .POST(HttpRequest.BodyPublishers.ofString(form))
299 .build();
299300
300 var message = client.messages().create(MessageCreateParams.builder()
301 .model(Model.CLAUDE_OPUS_5_5)
302 .maxTokens(1024)
303 .addUserMessage("Hello, Claude")
304 .build());
301 // The Java SDK takes the token itself or a file path, not a function; with
302 // source("value"), path(...) holds the token. The SDK sends that same token
303 // at every refresh of the Anthropic access token, which fails once it has
304 // expired or, if it has a jti claim, been used. For a longer-running
305 // process, pass source("file") and write a new token to the file before
306 // each refresh.
307 var response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
308 String oktaToken = new ObjectMapper().readTree(response.body()).get("access_token").asText();
305309
306 IO.println(message.content());
310 AnthropicClient client = AnthropicOkHttpClient.builder()
311 .configurationProvider(InMemoryProfileConfigProvider.of(ProfileConfig.builder()
312 .organizationId(System.getenv("ANTHROPIC_ORGANIZATION_ID"))
313 .workspaceId(System.getenv("ANTHROPIC_WORKSPACE_ID"))
314 .authentication(AuthenticationConfig.builder()
315 .type(AuthenticationType.OIDC_FEDERATION)
316 .federationRuleId(System.getenv("ANTHROPIC_FEDERATION_RULE_ID"))
317 .serviceAccountId(System.getenv("ANTHROPIC_SERVICE_ACCOUNT_ID"))
318 .identityToken(IdentityTokenConfig.builder()
319 .source("value")
320 .path(oktaToken)
321 .build())
322 .build())
323 .build()))
324 .build();
325
326 var message = client.messages().create(MessageCreateParams.builder()
327 .model(Model.CLAUDE_OPUS_5_5)
328 .maxTokens(1024)
329 .addUserMessage("Hello, Claude")
330 .build());
331
332 IO.println(message.content());
333 }
307334 ```
308335
309336 ```csharp C#
from line 407
380407
381408 ```php PHP
382409 use Anthropic\Client;
383 use Anthropic\Credentials\WorkloadIdentityCredentials;
410 use Anthropic\Lib\Credentials\CredentialResult;
411 use Anthropic\Lib\Credentials\IdentityTokenProvider;
412 use Anthropic\Lib\Credentials\TokenCache;
413 use Anthropic\Lib\Credentials\WorkloadIdentityCredentials;
384414
385 function fetchOktaToken(): string
415 class OktaIdentityToken implements IdentityTokenProvider
386416 {
387 $ch = curl_init(getenv('OKTA_ISSUER') . '/v1/token');
388 curl_setopt_array($ch, [
389 CURLOPT_RETURNTRANSFER => true,
390 CURLOPT_POSTFIELDS => http_build_query([
391 'grant_type' => 'client_credentials',
392 'scope' => 'anthropic.access',
393 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
394 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
395 'client_assertion' => buildSignedClientAssertion(),
396 ]),
397 ]);
398 $body = json_decode(curl_exec($ch), true);
399 curl_close($ch);
400 return $body['access_token'];
417 public function getIdentityToken(): string
418 {
419 $ch = curl_init(getenv('OKTA_ISSUER') . '/v1/token');
420 curl_setopt_array($ch, [
421 CURLOPT_RETURNTRANSFER => true,
422 CURLOPT_POSTFIELDS => http_build_query([
423 'grant_type' => 'client_credentials',
424 'scope' => 'anthropic.access',
425 'client_assertion_type' => 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer',
426 // Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
427 'client_assertion' => buildSignedClientAssertion(),
428 ]),
429 ]);
430 $body = json_decode(curl_exec($ch), true);
431 curl_close($ch);
432 return $body['access_token'];
433 }
401434 }
402435
403436 $client = new Client(
404 credentials: new WorkloadIdentityCredentials(
405 identityTokenProvider: fetchOktaToken(...),
406 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
407 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
408 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
409 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
437 credentials: new CredentialResult(
438 provider: new TokenCache(new WorkloadIdentityCredentials(
439 identityProvider: new OktaIdentityToken(),
440 federationRuleId: getenv('ANTHROPIC_FEDERATION_RULE_ID'),
441 organizationId: getenv('ANTHROPIC_ORGANIZATION_ID'),
442 serviceAccountId: getenv('ANTHROPIC_SERVICE_ACCOUNT_ID'),
443 workspaceId: getenv('ANTHROPIC_WORKSPACE_ID') ?: null,
444 )),
410445 ),
411446 );
412447
from line 490
455490 ```
456491</CodeGroup>
457492
458Each SDK tab shows the callable pattern: the Anthropic SDK calls the function you passed to `identity_token_provider` (typescript, php: `identityTokenProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `federationTokenProvider`) each time the Anthropic access token approaches expiry, so your Okta fetcher should return a fresh token on each call rather than caching one indefinitely. The `ant` CLI re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on each exchange, so refresh that file on a timer for long-running shells.
493Except in Java (see the comment in that example), the SDK example passes your Okta token fetch to `identity_token_provider` (typescript: `identityTokenProvider`; php: `identityProvider`; csharp: `IdentityTokenProvider`; go: `option.WithFederationTokenProvider`; java: `IdentityTokenConfig`) as a token provider. The Claude SDK calls it each time the Anthropic access token approaches expiry, so your Okta fetcher should return a fresh token on each call rather than caching one indefinitely. The `ant` CLI re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on each exchange, so refresh that file on a timer for long-running shells.
459494
460495## Verify the setup
461496
manage-claude/wif-providers/spiffe Changed · +3 / -3 lines
from line 165
165165
166166## Acquire and use the token
167167
168The Claude SDK can either read the JWT-SVID from the file that spiffe-helper maintains or call the SPIFFE Workload API directly through a token-provider callable. The file path is the simplest integration and works in every SDK language. The callable path removes the sidecar but requires a SPIFFE Workload API client in your application's language.
168The Claude SDK can read the JWT-SVID from the file that spiffe-helper maintains or, except in Java, call the SPIFFE Workload API directly through a token provider. The file path is the simplest integration and works in every SDK language. The token-provider path removes the sidecar but requires a SPIFFE Workload API client in your application's language.
169169
170170<Tabs>
171171 <Tab title="File-based with spiffe-helper">
from line 341
341341 </CodeGroup>
342342 </Tab>
343343
344 <Tab title="Callable through the SPIFFE Workload API">
345 Workloads that link a SPIFFE Workload API client directly can skip spiffe-helper and pass the SDK a callable that fetches a fresh JWT-SVID from the agent socket. The SDK invokes the callable before each token exchange, so the workload always presents an unexpired SVID. Python ([py-spiffe](https://github.com/HewlettPackard/py-spiffe)) and Go ([go-spiffe](https://github.com/spiffe/go-spiffe)) have mature Workload API clients.
344 <Tab title="Token provider through the SPIFFE Workload API">
345 Except in Java, workloads that link a SPIFFE Workload API client directly can skip spiffe-helper and pass the SDK a token provider that fetches a fresh JWT-SVID from the agent socket. The SDK invokes the provider before each token exchange, so the workload always presents an unexpired SVID. Python ([py-spiffe](https://github.com/HewlettPackard/py-spiffe)) and Go ([go-spiffe](https://github.com/spiffe/go-spiffe)) have mature Workload API clients.
346346
347347 <CodeGroup exclude="shell, typescript, java, csharp, php, ruby">
348348 ```python Python
api/opentelemetry/spans Changed · +1 / -1 lines
from line 13
1313 OpenTelemetry export is in beta. It's provided under your agreement with Anthropic, including the Beta Services section of the [Service Specific Terms](https://www.anthropic.com/legal/service-specific-terms). Span names and attributes can change during the beta.
1414</Note>
1515
16To set up export, see [Use OpenTelemetry with the Claude SDK](https://platform.claude.com/docs/en/manage-claude/opentelemetry/sdk) and [Use OpenTelemetry with the Claude API](https://platform.claude.com/docs/en/manage-claude/opentelemetry/api).
16To get spans, see [Set up OpenTelemetry with the Claude SDK](https://platform.claude.com/docs/en/manage-claude/opentelemetry/sdk) and [Set up OpenTelemetry with the Claude API](https://platform.claude.com/docs/en/manage-claude/opentelemetry/api).
1717
1818## Span names and kinds
1919
manage-claude/opentelemetry/api Changed · +1 / -1 lines
from line 1
11---
2title: Use OpenTelemetry with the Claude API
2title: Set up OpenTelemetry with the Claude API
33url: https://platform.claude.com/docs/en/manage-claude/opentelemetry/api
44description: Add a destination in the Claude Console to send OpenTelemetry traces of your Messages API requests and Managed Agents sessions to your observability platform.
55featureMetadata:
manage-claude/opentelemetry/sdk Changed · +1 / -1 lines
from line 1
11---
2title: Use OpenTelemetry with the Claude SDK
2title: Set up OpenTelemetry with the Claude SDK
33url: https://platform.claude.com/docs/en/manage-claude/opentelemetry/sdk
44description: Trace each request that the Claude SDK sends and each tool that its tool runner runs, and send the spans to your observability platform.
55featureMetadata:
manage-claude/wif-providers/github-actions Changed · +1 / -1 lines
from line 309
309309 ```
310310</CodeGroup>
311311
312Each GitHub-issued identity token expires roughly five minutes after issuance. The token-request endpoint (`ACTIONS_ID_TOKEN_REQUEST_URL`) stays valid for the entire job, so you can fetch a fresh token at any point. The SDK exchanges the token on first use and caches the resulting Anthropic access token. For jobs that run longer than the Anthropic token's lifetime, the SDK re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on each refresh, so re-run the fetch step periodically (or wrap it in a background loop) to keep the file current. Alternatively, pass a token-provider callback to the SDK that calls `ACTIONS_ID_TOKEN_REQUEST_URL` directly instead of using the file path.
312Each GitHub-issued identity token expires roughly five minutes after issuance. The token-request endpoint (`ACTIONS_ID_TOKEN_REQUEST_URL`) stays valid for the entire job, so you can fetch a fresh token at any point. The SDK exchanges the token on first use and caches the resulting Anthropic access token. For jobs that run longer than the Anthropic token's lifetime, the SDK re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on each refresh, so re-run the fetch step periodically (or wrap it in a background loop) to keep the file current. Alternatively, except in Java, pass the SDK a token provider that calls `ACTIONS_ID_TOKEN_REQUEST_URL` directly instead of using the file path.
313313
314314## Verify the setup
315315
manage-claude/wif-reference Changed · +1 / -1 lines
from line 215
215215
216216### Token exchange errors
217217
218`POST /v1/oauth/token` returns errors in the standard [API error shape](https://platform.claude.com/docs/en/api/errors). The SDK wraps exchange failures in a typed `WorkloadIdentityError` (ruby: `Anthropic::Credentials::WorkloadIdentityError`; go: `*config.FederationExchangeError`; java: `UnexpectedStatusCodeException`; csharp: `WorkloadIdentityException`; php: `OAuthException`) that exposes the HTTP status, the response body, and the `request_id`.
218`POST /v1/oauth/token` returns errors in the standard [API error shape](https://platform.claude.com/docs/en/api/errors). The SDK reports an exchange failure as `WorkloadIdentityError` (ruby: `Anthropic::Credentials::WorkloadIdentityError`; go: `error`; java: `UnexpectedStatusCodeException`; csharp: `WorkloadIdentityException`; php: `OAuthException`), which gives the HTTP status and the error details from the response body. The request ID is on the error in Python, TypeScript, and Ruby, and in the exception's response headers in Java. In Go it is only in the error's message, because the error's type is in an internal package; the C# and PHP errors do not carry it.
219219
220220| Status | Error | Cause | Resolution |
221221| ------ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |