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.
This calls api.anthropic.com directly, so 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,
and choose the VMs to attach.
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. Call Claude from the VM
Run this on an attached 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
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.