Anthropic Workload Identity Federation
Let a VM call the Claude API without storing an Anthropic API key. The VM gets a short-lived exe.dev token, and Anthropic exchanges it for a short-lived access token for one of your service accounts.
Usage is billed to your Anthropic organization. To use exe.dev's built-in models instead, see the LLM integration.
Setup uses two browser tabs: the exe.dev Integrations page and the Claude Console's Workload identity settings.
1. Start the integration in exe.dev
On the Integrations page, add an Identity Federation
integration and choose Anthropic. Enter a name, such as anthropic-wif.
To use it through an LLM integration (step 4), you don't need to attach it to
any VMs.
The dialog shows an Issuer and a Subject. You will paste both into Anthropic in the next step. Keep the dialog open; the subject is reserved for 15 minutes.
2. Create the rule in Anthropic
In the Claude Console, open Workload identity settings, select Connect workload, and choose Custom OIDC.
| Anthropic field | Value |
|---|---|
| Issuer URL | The exe.dev Issuer. Use OIDC discovery for signing keys. |
| Subject match | The exe.dev Subject, exactly. Do not use a wildcard. |
| Audience match | api.anthropic.com |
| Workspace | The workspace that receives the API usage |
| Service account | An account in that workspace; the VM acts as it |
| Scope | workspace:inference to call Claude only, or workspace:developer for broader access |
After you save the rule, the Console shows the IDs you need in the next step.
3. Enter the IDs and save
Back in the exe.dev dialog, fill in:
| Field | Where it comes from |
|---|---|
| Organization ID | Your Anthropic organization (a UUID) |
| Federation rule ID | The rule you just created (fdrl_...) |
| Service account ID | The rule's service account (svac_...) |
| Workspace ID | Only if the rule covers more than one workspace (wrkspc_..., or default) |
Click Run. If you don't have the IDs yet, you can save with the fields empty and edit the integration later to add them.
4. Use it from an LLM integration
On the Integrations page, add an LLM integration, set its Anthropic provider to Workload identity, and pick this integration. Attach the LLM integration to your VMs; the workload identity integration doesn't need to be attached. exe.dev exchanges and renews Anthropic tokens for you. Both integrations must be in the same scope: personal for a personal LLM integration, team for a team one.
Or from the CLI:
ssh exe.dev integrations add llm --name claude \
--anthropic=wif --anthropic-wif=anthropic-wif \
--openai=disabled --fireworks=disabled --attach vm:example-vm
On the VM, Claude Code and Anthropic SDKs need only the LLM integration's URL:
ANTHROPIC_API_KEY=implicit \
ANTHROPIC_BASE_URL=https://claude.int.exe.xyz \
claude --model opus
For a team integration, use https://claude.team.exe.xyz.
Other uses: call Claude directly
To call Claude yourself instead of through an LLM integration, for example from code that does its own token exchange, attach this integration to the VM. Then run this on the VM. It reads the IDs from the integration, exchanges a fresh exe.dev token for an Anthropic access token, and sends a message:
EXE_WIF_URL=https://anthropic-wif.int.exe.xyz
META="$(curl -fsS "$EXE_WIF_URL/metadata")"
JWT="$(curl -fsS "$EXE_WIF_URL/token" | jq -er .token)"
ACCESS_TOKEN="$(
jq -n --arg assertion "$JWT" --argjson meta "$META" '{
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: $assertion,
federation_rule_id: $meta.fed_rule_id,
organization_id: $meta.org_id,
service_account_id: $meta.svc_account_id
} + (if $meta.workspace_id then {workspace_id: $meta.workspace_id} else {} end)' |
curl -fsS https://api.anthropic.com/v1/oauth/token \
-H 'content-type: application/json' -d @- |
jq -er .access_token
)"
curl -fsS https://api.anthropic.com/v1/messages \
-H "authorization: Bearer $ACCESS_TOKEN" \
-H 'anthropic-version: 2023-06-01' \
-H 'content-type: application/json' \
-d '{"model":"claude-opus-5-5","max_tokens":128,"messages":[{"role":"user","content":"Hello from exe.dev"}]}'
unset JWT ACCESS_TOKEN
Replace anthropic-wif with your integration's name. For a team integration,
use https://<name>.team.exe.xyz.
Tokens
- Fetch a new exe.dev token from
/tokenfor every exchange. Anthropic rejects a token it has already seen. - Get a new Anthropic access token before
expires_inruns out. - Both tokens are credentials. Don't print, log, or commit them.
Anthropic's SDKs can do the exchange and refresh for you. See Anthropic's WIF reference.
Use the CLI instead
The CLI prints the issuer and subject only after it creates the integration, so add the IDs with a second command:
ssh exe.dev integrations add wif --name anthropic-wif \
--audience api.anthropic.com --consumer anthropic \
--attach vm:example-vm
The --attach is only needed to call Anthropic directly from the VM. Create
the Anthropic rule with the printed Issuer and Subject, then:
ssh exe.dev integrations edit anthropic-wif \
--metadata=org_id=00000000-0000-0000-0000-000000000000 \
--metadata=fed_rule_id=fdrl_EXAMPLE \
--metadata=svc_account_id=svac_EXAMPLE
Add --metadata=workspace_id=... if the rule covers more than one workspace,
and --team to add for a team integration. edit replaces all metadata, so
include every ID each time.
Troubleshooting
- The exchange is rejected. Check that the rule's issuer, subject, and audience exactly match the integration. The Console's authentication history shows why a token was rejected.
/tokenor/metadatadoesn't respond. The integration isn't attached to this VM.- The rule covers more than one workspace. Anthropic then requires a workspace ID in every exchange; add it to the integration.