---
title: Configure Anthropic | Keycard
description: Set up credential brokering so workloads authenticate to Claude API with Keycard-issued tokens instead of static API keys
---

Configure Workload Identity Federation (WIF) between Keycard and Anthropic. Your applications get Keycard OIDC tokens that Anthropic exchanges for short-lived API credentials — no static API keys anywhere.

## Prerequisites

Before starting:

- A [Keycard zone](/concepts/zones/index.md)
- Anthropic Organization Owner or Admin role with access to [Workload Identity Federation settings](https://platform.claude.com/settings/workload-identity-federation)
- Your Anthropic **Organization ID** (UUID) from [Settings → Organization](https://platform.claude.com/settings/organization)

## Configure Keycard

1. **Create the Anthropic resource**

   In [Keycard Console](https://console.keycard.ai), navigate to **Resources** → **Add Resource**.

   | Field                    | Value                          |
   | ------------------------ | ------------------------------ |
   | **Resource Identifier**  | `https://api.anthropic.com`    |
   | **Credentials Provider** | Your zone provider             |
   | **Credential Lifetime**  | `1h` (under Advanced Settings) |

   Tip

   Selecting **Zone Provider** tells Keycard to issue OIDC tokens signed by the zone itself rather than brokering through an external OAuth flow.

2. **Create an application**

   Navigate to **Applications** → **Add Application**.

   | Field    | Value                     |
   | -------- | ------------------------- |
   | **Name** | e.g. `anthropic-workload` |

   Note the **Application ID** from the browser URL bar after creating.

3. **Link the resource**

   Open the application → **Dependencies** → **Add Dependency** → select `https://api.anthropic.com`.

4. **Create application credentials** (local development only)

   Open the application → **Application Credentials** → **Add Credential** → **Client ID & Secret**.

   Note the **Client ID** and **Client Secret** — the secret is shown once.

   Note

   Production workloads use [workload identity](/concepts/providers/#workload-identity/index.md) instead of client credentials.

5. **Note the zone issuer URL**

   Find your zone URL on the zone settings page. Anthropic needs this as the issuer URL:

   ```
   https://<zone-id>.keycard.cloud
   ```

## Configure Anthropic

1. **Create a service account**

   In [Anthropic Platform Console](https://platform.claude.com/settings/service-accounts), go to **Settings → Service accounts → Create service account**.

   | Field    | Value                   |
   | -------- | ----------------------- |
   | **Name** | e.g. `keycard-workload` |

   Note the service account ID (`svac_...`).

2. **Create a workspace**

   The Default Workspace has no ID and can’t be used with WIF.

   Go to **Settings → [Workspaces](https://platform.claude.com/settings/workspaces) → Create workspace**.

   | Field    | Value                    |
   | -------- | ------------------------ |
   | **Name** | e.g. `keycard-workloads` |

   Note the workspace ID (`wrkspc_...`) from the workspaces list.

3. **Link the service account to the workspace**

   Select the workspace from the top navigation dropdown → **Manage → Service accounts → Add service account** → select the service account from step 1.

4. **Register Keycard as an issuer**

   In [Workload Identity Federation](https://platform.claude.com/settings/workload-identity-federation) settings, on the **Issuers** tab → **Create issuer**.

   | Field           | Value                             |
   | --------------- | --------------------------------- |
   | **Name**        | e.g. `keycard-prod`               |
   | **Issuer URL**  | `https://<zone-id>.keycard.cloud` |
   | **JWKS source** | `discovery`                       |

5. **Create a federation rule**

   On the **Rules** tab → **New Rule**.

   | Field                      | Value                            |
   | -------------------------- | -------------------------------- |
   | **Issuer**                 | Select your Keycard issuer       |
   | **Match → Subject prefix** | Your Keycard Application ID      |
   | **Target**                 | Your service account             |
   | **Workspaces**             | Select the workspace from step 2 |
   | **Scope**                  | `workspace:developer`            |
   | **Token lifetime**         | `3600` seconds                   |

   Note the rule ID (`fdrl_...`).

   Caution

   The service account must be a member of each selected workspace. Token exchanges fail silently if it isn’t.

## Use from code

Get a Keycard OIDC token and pass it to the Anthropic SDK for automatic WIF exchange.

- [Python](#tab-panel-124)
- [TypeScript](#tab-panel-125)
- [Go](#tab-panel-126)
- [cURL](#tab-panel-127)

```
from keycardai.oauth import Client, BasicAuth
from anthropic import Anthropic, WorkloadIdentityCredentials


# 1. Get a Keycard OIDC token scoped to Anthropic.
with Client(
    "https://<zone-id>.keycard.cloud",
    auth=BasicAuth("<your-client-id>", "<your-client-secret>"),
) as kc:
    token = kc.exchange_token(
        grant_type="client_credentials",
        resource="https://api.anthropic.com",
    )


# 2. Use it with the Anthropic SDK — WIF exchange happens automatically.
client = Anthropic(
    credentials=WorkloadIdentityCredentials(
        identity_token_provider=lambda: token.access_token,
        federation_rule_id="<fdrl_...>",
        organization_id="<anthropic-org-id>",
        service_account_id="<svac_...>",
        workspace_id="<wrkspc_...>",
    ),
)


message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello from a Keycard workload"}],
)
print(message.content[0].text)
```

```
import Anthropic from "@anthropic-ai/sdk";
import { oidcFederationProvider } from "@anthropic-ai/sdk/lib/credentials/oidc-federation";


/** Get a resource-scoped JWT via client_credentials grant. */
async function keycardClientCredentials(
  tokenUrl: string, clientId: string, clientSecret: string, resource: string,
): Promise<string> {
  const resp = await fetch(tokenUrl, {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${btoa(`${clientId}:${clientSecret}`)}`,
    },
    body: new URLSearchParams({ grant_type: "client_credentials", resource }),
  });
  if (!resp.ok) throw new Error(`client_credentials failed: ${await resp.text()}`);
  const data = (await resp.json()) as { access_token: string };
  return data.access_token;
}


// 1. Get a Keycard OIDC token scoped to Anthropic.
const tokenUrl = "https://<zone-id>.keycard.cloud/oauth/2/token";
const keycardJwt = await keycardClientCredentials(
  tokenUrl, "<your-client-id>", "<your-client-secret>", "https://api.anthropic.com",
);


// 2. Use it with the Anthropic SDK — WIF exchange happens automatically.
const client = new Anthropic({
  credentials: oidcFederationProvider({
    identityTokenProvider: () => keycardJwt,
    federationRuleId: "<fdrl_...>",
    organizationId: "<anthropic-org-id>",
    serviceAccountId: "<svac_...>",
    workspaceId: "<wrkspc_...>",
    baseURL: "https://api.anthropic.com",
    fetch,
  }),
});


const message = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  messages: [{ role: "user", content: "Hello from a Keycard workload" }],
});
console.log(message.content[0].text);
```

```
package main


import (
  "context"
  "fmt"
  "log"


  "github.com/anthropics/anthropic-sdk-go"
  "github.com/anthropics/anthropic-sdk-go/option"
  "github.com/keycardai/credentials-go/oauth"
)


func main() {
  ctx := context.Background()


  // 1. Get a Keycard OIDC token scoped to Anthropic.
  kc := oauth.NewClientCredentialsClient(
    "https://<zone-id>.keycard.cloud",
    oauth.WithCCBasicAuth("<your-client-id>", "<your-client-secret>"),
  )


  keycardToken, err := kc.RequestToken(ctx, oauth.ClientCredentialsRequest{
    Resource: "https://api.anthropic.com",
  })
  if err != nil {
    log.Fatal(err)
  }


  // 2. Use it with the Anthropic SDK — WIF exchange happens automatically.
  client := anthropic.NewClient(
    option.WithFederationTokenProvider(
      func(_ context.Context) (string, error) {
        return keycardToken.AccessToken, nil
      },
      option.FederationOptions{
        FederationRuleID: "<fdrl_...>",
        OrganizationID:   "<anthropic-org-id>",
        ServiceAccountID: "<svac_...>",
        WorkspaceID:      "<wrkspc_...>",
      },
    ),
  )


  message, err := client.Messages.New(ctx, anthropic.MessageNewParams{
    Model:     "claude-sonnet-4-6",
    MaxTokens: 1024,
    Messages: []anthropic.MessageParam{
      anthropic.NewUserMessage(anthropic.NewTextBlock("Hello from a Keycard workload")),
    },
  })
  if err != nil {
    log.Fatal(err)
  }
  fmt.Println(message.Content[0].Text)
}
```

Terminal window

```
# 1. Get a Keycard OIDC token scoped to Anthropic.
KC_TOKEN=$(curl -sS "https://<zone-id>.keycard.cloud/oauth/2/token" \
  -u "<your-client-id>:<your-client-secret>" \
  -d "grant_type=client_credentials" \
  -d "resource=https://api.anthropic.com" \
  | jq -r .access_token)


# 2. Exchange the Keycard token at Anthropic's WIF endpoint.
ANTHROPIC_TOKEN=$(curl -sS https://api.anthropic.com/v1/oauth/token \
  -H "content-type: application/json" \
  -d "{
    \"grant_type\": \"urn:ietf:params:oauth:grant-type:jwt-bearer\",
    \"assertion\": \"${KC_TOKEN}\",
    \"federation_rule_id\": \"<fdrl_...>\",
    \"organization_id\": \"<anthropic-org-id>\",
    \"service_account_id\": \"<svac_...>\",
    \"workspace_id\": \"<wrkspc_...>\"
  }" | jq -r .access_token)


# 3. Call the API.
curl -sS https://api.anthropic.com/v1/messages \
  -H "authorization: Bearer ${ANTHROPIC_TOKEN}" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-6",
    "max_tokens": 1024,
    "messages": [{"role": "user", "content": "Hello from a Keycard workload"}]
  }' | jq -r '.content[0].text'
```

Note

The Python and Go Keycard SDKs support `client_credentials` with a `resource` parameter natively. The TypeScript SDK doesn’t yet — that example hits the token endpoint directly.

## Verify

**In Keycard Console** — open **Audit Log**. Look for:

| Event               | Description                                       |
| ------------------- | ------------------------------------------------- |
| `credentials:issue` | OIDC token issued for `https://api.anthropic.com` |

**In Anthropic Platform Console** — go to **Settings → Workload identity → Authentication events**. You should see the exchange with your zone’s issuer URL and matched federation rule.

## Troubleshooting

**Token exchange returns “service account not a member of workspace”** The service account must be explicitly added to the workspace. Go to the workspace → **Manage → Service accounts** and add it.

**“Invalid issuer” error** The issuer URL in Anthropic must match the Keycard zone URL exactly, including the protocol (`https://`) and no trailing slash.

**Federation rule doesn’t match** Check that the **Subject prefix** in the rule matches your Keycard Application ID. The `sub` claim in the Keycard-issued JWT contains this value.
