llms.txt@04-API-Reference/Other · git:20261011.c214a3a · 2026-10-11 · sha256 16488cc80c054041
llms.txt@04-API-Reference/Other git:20261011.c214a3aF
Immutable. This exact content never changes and is served at /api/v1/blob/16488cc80c054041.
# API Reference / Other
> Every page below, concatenated. Each section gives the page title, its upstream source and its path in this repository.
---
## MCP connector
Source: https://platform.claude.com/docs/en/agents/mcp-connector
Path: 04-API-Reference/Other/agents-mcp-connector.md
# MCP connector
Claude's Model Context Protocol (MCP) connector feature enables you to connect to remote MCP servers directly from the Messages API without a separate MCP client.
> **Note:** **Current version**: This feature requires the beta header: `"anthropic-beta": "mcp-client-2025-11-20"`
>
> The previous version (`mcp-client-2025-04-04`) is deprecated. See the [deprecated version documentation](#deprecated-version-mcp-client-2025-04-04) below.
> **Note:** This feature is **not** eligible for [Zero Data Retention (ZDR)](../Guides/build-with-claude-api-and-data-retention.md). Data is retained according to the feature's standard retention policy.
## Key features
- **Direct API integration**: Connect to MCP servers without implementing an MCP client
- **Tool calling support**: Access MCP tools through the Messages API
- **Flexible tool configuration**: Enable all tools, allowlist specific tools, or denylist unwanted tools
- **Per-tool configuration**: Configure individual tools with custom settings
- **OAuth authentication**: Support for OAuth Bearer tokens for authenticated servers
- **Multiple servers**: Connect to multiple MCP servers in a single request
## Limitations
- Of the feature set of the [MCP specification](https://modelcontextprotocol.io/introduction#explore-mcp), only [tool calls](https://modelcontextprotocol.io/docs/concepts/tools) are currently supported.
- The server must be publicly exposed through HTTP (supports both Streamable HTTP and SSE transports). Local STDIO servers cannot be connected directly.
- The MCP connector is currently not supported on Amazon Bedrock and Google Vertex.
## Using the MCP connector in the Messages API
The MCP connector uses two components:
1. **MCP Server Definition** (`mcp_servers` array): Defines server connection details (URL, authentication)
2. **MCP Toolset** (`tools` array): Configures which tools to enable and how to configure them
### Basic example
This example enables all tools from an MCP server with default configuration:
**Shell:**
```bash
curl https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "X-API-Key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-d '{
"model": "claude-opus-4-6",
"max_tokens": 1000,
"messages": [{"role": "user", "content": "What tools do you have available?"}],
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}
],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp"
}
]
}'
```
**CLI:**
```bash
ant beta:messages create --beta mcp-client-2025-11-20 <<'YAML'
model: claude-opus-4-6
max_tokens: 1000
messages:
- role: user
content: What tools do you have available?
mcp_servers:
- type: url
url: https://example-server.modelcontextprotocol.io/sse
name: example-mcp
authorization_token: YOUR_TOKEN
tools:
- type: mcp_toolset
mcp_server_name: example-mcp
YAML
```
**Python:**
```python
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-4-6",
max_tokens=1000,
messages=[{"role": "user", "content": "What tools do you have available?"}],
mcp_servers=[
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "example-mcp"}],
betas=["mcp-client-2025-11-20"],
)
print(response)
```
**TypeScript:**
```typescript
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: "claude-opus-4-6",
max_tokens: 1000,
messages: [
{
role: "user",
content: "What tools do you have available?"
}
],
mcp_servers: [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp"
}
],
betas: ["mcp-client-2025-11-20"]
});
console.log(response);
```
**C#:**
```csharp
using Anthropic;
using Anthropic.Models.Beta.Messages;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus4_6,
MaxTokens = 1000,
Messages = new List<BetaMessageParam>
{
new() { Role = Role.User, Content = "What tools do you have available?" }
},
McpServers = new List<BetaRequestMcpServerUrlDefinition>
{
new()
{
Url = "https://example-server.modelcontextprotocol.io/sse",
Name = "example-mcp",
AuthorizationToken = "YOUR_TOKEN"
}
},
Tools = new List<BetaToolUnion>
{
new BetaMcpToolset("example-mcp")
},
Betas = new List<string> { "mcp-client-2025-11-20" }
};
var message = await client.Beta.Messages.Create(parameters);
Console.WriteLine(message);
```
**Go:**
```go
package main
import (
"context"
"fmt"
"log"
"github.com/anthropics/anthropic-sdk-go"
)
func main() {
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus4_6,
MaxTokens: 1000,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("What tools do you have available?")),
},
MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
{
URL: "https://example-server.modelcontextprotocol.io/sse",
Name: "example-mcp",
AuthorizationToken: anthropic.String("YOUR_TOKEN"),
},
},
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
MCPServerName: "example-mcp",
}},
},
Betas: []anthropic.AnthropicBeta{
anthropic.AnthropicBetaMCPClient2025_11_20,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
}
```
**Java:**
```java
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.BetaMcpToolset;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
import com.anthropic.models.beta.messages.MessageCreateParams;
public class Main {
public static void main(String[] args) {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model("claude-opus-4-6")
.maxTokens(1000L)
.addUserMessage("What tools do you have available?")
.addMcpServer(BetaRequestMcpServerUrlDefinition.builder()
.url("https://example-server.modelcontextprotocol.io/sse")
.name("example-mcp")
.authorizationToken("YOUR_TOKEN")
.build())
.addTool(BetaMcpToolset.builder()
.mcpServerName("example-mcp")
.build())
.addBeta("mcp-client-2025-11-20")
.build();
BetaMessage response = client.beta().messages().create(params);
System.out.println(response);
}
}
```
**PHP:**
```php
<?php
use Anthropic\Client;
$client = new Client(apiKey: getenv("ANTHROPIC_API_KEY"));
$message = $client->beta->messages->create(
maxTokens: 1000,
messages: [
['role' => 'user', 'content' => 'What tools do you have available?']
],
model: 'claude-opus-4-6',
mcpServers: [
[
'type' => 'url',
'url' => 'https://example-server.modelcontextprotocol.io/sse',
'name' => 'example-mcp',
'authorization_token' => 'YOUR_TOKEN',
],
],
tools: [
[
'type' => 'mcp_toolset',
'mcp_server_name' => 'example-mcp',
],
],
betas: ['mcp-client-2025-11-20'],
);
echo $message;
```
**Ruby:**
```ruby
require "anthropic"
client = Anthropic::Client.new
response = client.beta.messages.create(
model: "claude-opus-4-6",
max_tokens: 1000,
messages: [
{ role: "user", content: "What tools do you have available?" }
],
mcp_servers: [
{
type: "url",
url: "https://example-server.modelcontextprotocol.io/sse",
name: "example-mcp",
authorization_token: "YOUR_TOKEN"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "example-mcp"
}
],
betas: ["mcp-client-2025-11-20"]
)
puts response
```
## MCP server configuration
Each MCP server in the `mcp_servers` array defines the connection details:
```json
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}
```
### Field descriptions
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | string | Yes | Currently only "url" is supported |
| `url` | string | Yes | The URL of the MCP server. Must start with https:// |
| `name` | string | Yes | A unique identifier for this MCP server. Must be referenced by exactly one MCPToolset in the `tools` array. |
| `authorization_token` | string | No | OAuth authorization token if required by the MCP server. See [MCP specification](https://platform.claude.com/docs/en/06-MCP-Tools/Spec/spec-2025-11-25-authorization-model-context-protocol.md). |
## MCP toolset configuration
The MCPToolset lives in the `tools` array and configures which tools from the MCP server are enabled and how they should be configured.
### Basic structure
```json
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"default_config": {
"enabled": true,
"defer_loading": false
},
"configs": {
"specific_tool_name": {
"enabled": true,
"defer_loading": true
}
}
}
```
### Field descriptions
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `type` | string | Yes | Must be "mcp_toolset" |
| `mcp_server_name` | string | Yes | Must match a server name defined in the `mcp_servers` array |
| `default_config` | object | No | Default configuration applied to all tools in this set. Individual tool configs in `configs` will override these defaults. |
| `configs` | object | No | Per-tool configuration overrides. Keys are tool names, values are configuration objects. |
| `cache_control` | object | No | Cache breakpoint configuration for this toolset |
### Tool configuration options
Each tool (whether configured in `default_config` or in `configs`) supports the following fields:
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `enabled` | boolean | `true` | Whether this tool is enabled |
| `defer_loading` | boolean | `false` | If true, tool description is not sent to the model initially. Used with [Tool Search Tool](../Agents-Tools/agents-and-tools-tool-use-tool-search-tool.md). |
For the full directory of Anthropic-provided tools and optional properties like `defer_loading`, see the [Tool reference](../Agents-Tools/agents-and-tools-tool-use-tool-reference.md). For searching across large tool sets, see [Tool search tool](../Agents-Tools/agents-and-tools-tool-use-tool-search-tool.md).
### Configuration merging
Configuration values merge with this precedence (highest to lowest):
1. Tool-specific settings in `configs`
2. Set-level `default_config`
3. System defaults
Example:
```json
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"defer_loading": true
},
"configs": {
"search_events": {
"enabled": false
}
}
}
```
Results in:
- `search_events`: `enabled: false` (from configs), `defer_loading: true` (from default_config)
- All other tools: `enabled: true` (system default), `defer_loading: true` (from default_config)
## Common configuration patterns
### Enable all tools with default configuration
The simplest pattern - enable all tools from a server:
```json
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp"
}
```
### Allowlist - Enable only specific tools
Set `enabled: false` as the default, then explicitly enable specific tools:
```json
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"enabled": false
},
"configs": {
"search_events": {
"enabled": true
},
"create_event": {
"enabled": true
}
}
}
```
### Denylist - Disable specific tools
Enable all tools by default, then explicitly disable unwanted tools:
```json
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"configs": {
"delete_all_events": {
"enabled": false
},
"share_calendar_publicly": {
"enabled": false
}
}
}
```
### Mixed - Allowlist with per-tool configuration
Combine allowlisting with custom configuration for each tool:
```json
{
"type": "mcp_toolset",
"mcp_server_name": "google-calendar-mcp",
"default_config": {
"enabled": false,
"defer_loading": true
},
"configs": {
"search_events": {
"enabled": true,
"defer_loading": false
},
"list_events": {
"enabled": true
}
}
}
```
In this example:
- `search_events` is enabled with `defer_loading: false`
- `list_events` is enabled with `defer_loading: true` (inherited from default_config)
- All other tools are disabled
## Validation rules
The API enforces these validation rules:
- **Server must exist**: The `mcp_server_name` in an MCPToolset must match a server defined in the `mcp_servers` array
- **Server must be used**: Every MCP server defined in `mcp_servers` must be referenced by exactly one MCPToolset
- **Unique toolset per server**: Each MCP server can only be referenced by one MCPToolset
- **Unknown tool names**: If a tool name in `configs` doesn't exist on the MCP server, a backend warning is logged but no error is returned (MCP servers may have dynamic tool availability)
## Response content types
When Claude uses MCP tools, the response will include two new content block types:
### MCP Tool Use Block
```json
{
"type": "mcp_tool_use",
"id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
"name": "echo",
"server_name": "example-mcp",
"input": { "param1": "value1", "param2": "value2" }
}
```
### MCP Tool Result Block
```json
{
"type": "mcp_tool_result",
"tool_use_id": "mcptoolu_014Q35RayjACSWkSj4X2yov1",
"is_error": false,
"content": [
{
"type": "text",
"text": "Hello"
}
]
}
```
## Multiple MCP servers
You can connect to multiple MCP servers by including multiple server definitions in `mcp_servers` and a corresponding MCPToolset for each in the `tools` array:
```json
{
"model": "claude-opus-4-6",
"max_tokens": 1000,
"messages": [
{
"role": "user",
"content": "Use tools from both mcp-server-1 and mcp-server-2 to complete this task"
}
],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example1.com/sse",
"name": "mcp-server-1",
"authorization_token": "TOKEN1"
},
{
"type": "url",
"url": "https://mcp.example2.com/sse",
"name": "mcp-server-2",
"authorization_token": "TOKEN2"
}
],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "mcp-server-1"
},
{
"type": "mcp_toolset",
"mcp_server_name": "mcp-server-2",
"default_config": {
"defer_loading": true
}
}
]
}
```
## Authentication
For MCP servers that require OAuth authentication, you'll need to obtain an access token. The MCP connector beta supports passing an `authorization_token` parameter in the MCP server definition.
API consumers are expected to handle the OAuth flow and obtain the access token prior to making the API call, as well as refreshing the token as needed.
### Obtaining an access token for testing
The MCP inspector can guide you through the process of obtaining an access token for testing purposes.
1. Run the inspector with the following command. You need Node.js installed on your machine.
```bash
npx @modelcontextprotocol/inspector
```
2. In the sidebar on the left, for "Transport type", select either "SSE" or "Streamable HTTP".
3. Enter the URL of the MCP server.
4. In the right area, click on the "Open Auth Settings" button after "Need to configure authentication?".
5. Click "Quick OAuth Flow" and authorize on the OAuth screen.
6. Follow the steps in the "OAuth Flow Progress" section of the inspector and click "Continue" until you reach "Authentication complete".
7. Copy the `access_token` value.
8. Paste it into the `authorization_token` field in your MCP server configuration.
### Using the access token
Once you've obtained an access token using either OAuth flow above, you can use it in your MCP server configuration:
```json
{
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "authenticated-server",
"authorization_token": "YOUR_ACCESS_TOKEN_HERE"
}
]
}
```
For detailed explanations of the OAuth flow, refer to the [Authorization section](https://platform.claude.com/docs/en/06-MCP-Tools/Spec/spec-2025-11-25-authorization-model-context-protocol.md) in the MCP specification.
## Client-side MCP helpers (TypeScript)
If you manage your own MCP client connection (for example, with local stdio servers, MCP prompts, or MCP resources), the TypeScript SDK provides helper functions that convert between MCP types and Claude API types. This eliminates manual conversion code when using the [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk) alongside the Anthropic SDK.
> **Note:** These helpers are currently available in the TypeScript SDK only.
> **Note:** Use the [`mcp_servers` API parameter](#using-the-mcp-connector-in-the-messages-api) when you have remote servers accessible via URL and only need tool support. Use the client-side helpers when you need local servers, prompts, resources, or more control over the connection with the base SDK.
### Installation
Install both the Anthropic SDK and the MCP SDK:
```bash
npm install @anthropic-ai/sdk @modelcontextprotocol/sdk
```
### Available helpers
Import the helpers from the beta namespace:
```typescript
import {
mcpTools,
mcpMessages,
mcpResourceToContent,
mcpResourceToFile
} from "@anthropic-ai/sdk/helpers/beta/mcp";
```
| Helper | Description |
|--------|-------------|
| `mcpTools(tools, mcpClient)` | Converts MCP tools to Claude API tools for use with `client.beta.messages.toolRunner()` |
| `mcpMessages(messages)` | Converts MCP prompt messages to Claude API message format |
| `mcpResourceToContent(resource)` | Converts an MCP resource to a Claude API content block |
| `mcpResourceToFile(resource)` | Converts an MCP resource to a file object for upload |
### Use MCP tools
Convert MCP tools for use with the SDK's [tool runner](../Agents-Tools/agents-and-tools-tool-use-tool-runner.md), which handles tool execution automatically:
```typescript
import Anthropic from "@anthropic-ai/sdk";
import { mcpTools } from "@anthropic-ai/sdk/helpers/beta/mcp";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const anthropic = new Anthropic();
// Connect to an MCP server
const transport = new StdioClientTransport({ command: "mcp-server", args: [] });
const mcpClient = new Client({ name: "my-client", version: "1.0.0" });
await mcpClient.connect(transport);
// List tools and convert them for the Claude API
const { tools } = await mcpClient.listTools();
const runner = await anthropic.beta.messages.toolRunner({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "What tools do you have available?" }],
tools: mcpTools(tools, mcpClient)
});
```
### Use MCP prompts
Convert MCP prompt messages into Claude API message format:
```typescript
import { mcpMessages } from "@anthropic-ai/sdk/helpers/beta/mcp";
const { messages } = await mcpClient.getPrompt({ name: "my-prompt" });
const response = await anthropic.beta.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: mcpMessages(messages)
});
```
### Use MCP resources
Convert MCP resources into content blocks to include in messages, or into file objects for upload:
```typescript
import { mcpResourceToContent, mcpResourceToFile } from "@anthropic-ai/sdk/helpers/beta/mcp";
// As a content block in a message
const resource = await mcpClient.readResource({ uri: "file:///path/to/doc.txt" });
await anthropic.beta.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
mcpResourceToContent(resource),
{ type: "text", text: "Summarize this document" }
]
}
]
});
// As a file upload
const fileResource = await mcpClient.readResource({ uri: "file:///path/to/data.json" });
await anthropic.beta.files.upload({ file: mcpResourceToFile(fileResource) });
```
### Error handling
The conversion functions throw `UnsupportedMCPValueError` if an MCP value isn't supported by the Claude API. This can happen with unsupported content types, MIME types, or non-HTTP resource links.
## Data retention
The MCP Connector is not covered by ZDR arrangements. Data exchanged with MCP servers, including tool definitions and execution results, is retained according to Anthropic's standard data retention policy.
For ZDR eligibility across all features, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md).
## Migration guide
If you're using the deprecated `mcp-client-2025-04-04` beta header, follow this guide to migrate to the new version.
### Key changes
1. **New beta header**: Change from `mcp-client-2025-04-04` to `mcp-client-2025-11-20`
2. **Tool configuration moved**: Tool configuration now lives in the `tools` array as MCPToolset objects, not in the MCP server definition
3. **More flexible configuration**: New pattern supports allowlisting, denylisting, and per-tool configuration
### Migration steps
**Before (deprecated):**
```json
{
"model": "claude-opus-4-6",
"max_tokens": 1000,
"messages": [],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example.com/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
"tool_configuration": {
"enabled": true,
"allowed_tools": ["tool1", "tool2"]
}
}
]
}
```
**After (current):**
```json
{
"model": "claude-opus-4-6",
"max_tokens": 1000,
"messages": [],
"mcp_servers": [
{
"type": "url",
"url": "https://mcp.example.com/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN"
}
],
"tools": [
{
"type": "mcp_toolset",
"mcp_server_name": "example-mcp",
"default_config": {
"enabled": false
},
"configs": {
"tool1": {
"enabled": true
},
"tool2": {
"enabled": true
}
}
}
]
}
```
### Common migration patterns
| Old pattern | New pattern |
|-------------|-------------|
| No `tool_configuration` (all tools enabled) | MCPToolset with no `default_config` or `configs` |
| `tool_configuration.enabled: false` | MCPToolset with `default_config.enabled: false` |
| `tool_configuration.allowed_tools: [...]` | MCPToolset with `default_config.enabled: false` and specific tools enabled in `configs` |
## Deprecated version: mcp-client-2025-04-04
> **Warning:** This version is deprecated. Migrate to `mcp-client-2025-11-20` using the [migration guide](#migration-guide) above.
The previous version of the MCP connector included tool configuration directly in the MCP server definition:
```json
{
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/sse",
"name": "example-mcp",
"authorization_token": "YOUR_TOKEN",
"tool_configuration": {
"enabled": true,
"allowed_tools": ["example_tool_1", "example_tool_2"]
}
}
]
}
```
### Deprecated field descriptions
| Property | Type | Description |
|----------|------|-------------|
| `tool_configuration` | object | **Deprecated**: Use MCPToolset in the `tools` array instead |
| `tool_configuration.enabled` | boolean | **Deprecated**: Use `default_config.enabled` in MCPToolset |
| `tool_configuration.allowed_tools` | array | **Deprecated**: Use allowlist pattern with `configs` in MCPToolset |
---
## Remote MCP servers
Source: https://platform.claude.com/docs/en/agents/remote-mcp-servers
Path: 04-API-Reference/Other/agents-remote-mcp-servers.md
# Remote MCP servers
Several companies have deployed remote MCP servers that developers can connect to via the Anthropic MCP connector API. These servers expand the capabilities available to developers and end users by providing remote access to various services and tools through the MCP protocol.
> **Note:** The remote MCP servers listed below are third-party services designed to work with the Claude API. These servers are not owned, operated, or endorsed by Anthropic. Users should only connect to remote MCP servers they trust and should review each server's security practices and terms before connecting.
## Connecting to remote MCP servers
To connect to a remote MCP server:
1. Review the documentation for the specific server you want to use.
2. Ensure you have the necessary authentication credentials.
3. Follow the server-specific connection instructions provided by each company.
For more information about using remote MCP servers with the Claude API, see the [MCP connector docs](../Agents-Tools/agents-and-tools-mcp-connector.md).
## Remote MCP server examples
*(See the [MCP connector documentation page](https://platform.claude.com/docs/en/04-API-Reference/Agents-Tools/agents-and-tools-mcp-connector-2b3ff19436.md) for the current list of available remote MCP servers.)*
> **Note:** **Looking for more?** [Find hundreds more MCP servers on GitHub](https://github.com/modelcontextprotocol/servers).
---
## API Key Best Practices: Keeping Your Keys Safe and Secure | Claude Help Center
Source: https://support.claude.com/en/articles/9767949-api-key-best-practices-keeping-your-keys-safe-and-secure
Path: 04-API-Reference/Other/api-key-best-practices-keeping-your-keys-safe-and-secure.md
# API Key Best Practices: Keeping Your Keys Safe and Secure
March 16, 2026
API keys enable access to the Claude API, but they can pose significant security risks if not handled properly. Your API key is a digital key to your account. Much like a credit card number, if someone obtains and uses your API key, they incur charges on your behalf. This article outlines best practices for managing API keys to ensure they remain secure and prevent unauthorized access and charges to your Claude Console account.
## **Common Risks and Vulnerabilities**
One of the most frequent causes of API key leaks is accidental exposure in public code repositories or third-party tools. Developers often inadvertently commit plaintext API keys to public GitHub repositories or input them into third party tools, which can lead to unauthorized access and potential abuse of the associated accounts.
## Best Practices for API Key Security
### 1. *Never* share your API key
- **Keep it confidential**: Just as you wouldn't share your personal password, don't share your API key. If someone needs access to the Claude API, they should obtain their own key.
- **Don’t share your key in public forums**: Don't include your API key in public discussions, emails, or support tickets, even between you and Anthropic.
- **Exercise caution with third-party tools**: Consider that when you upload your API key to third-party tools or platforms (such as an web-based IDE, Cloud Provider, or CI/CD platform), you are giving the developer of that tool access to your Claude Console account. If you don’t trust their reputation, don’t trust them with your API key.
- When using a third-party provider, always add your API key as an encrypted secret. Never include it directly in your code or configuration files.
### 2. Monitor Usage and Logs Closely
We recommend regularly reviewing [logs](https://console.anthropic.com/settings/logs) and [usage](https://console.anthropic.com/settings/usage) patterns for your API keys within the Console.
- **For Custom Rate Limit API orgs**: Implement usage and spend limits in your account settings.
- These limits act as a safeguard against unexpected usage due to leaked keys or errant scripts.
- **For Standard Rate Limit API orgs**: Enable and configure auto-reload settings in your account.
- This feature allows you to set a threshold at which your account will automatically charge the card on file to replenish usage credits.
- Carefully consider auto-reload limits. While they ensure continuous service, they also act as a safeguard against unexpected high usage that could result from leaked keys or mistakes in your code.
### 3. Securely Handling API Keys with environment Variables and Secrets
A best practice for safely handling API keys is to use environment variables to securely inject and share environment variables. When you deploy your application to a cloud environment, you can use their secret management solution to securely pass the API key to your application via an environment variable without inadvertently sharing your API key.
If you are storing secrets locally using dotenv, you must add your `.env` files to your source control ignore file (e.g., `.gitignore` for git) to prevent inadvertently distributing sensitive information publicly. In cloud environments, prefer encypted secret storage instead of dotenv files.
**Python example:**
1\. Create a `.env` file in your project directory.
2\. Add your API key to the `.env` file:
ANTHROPIC_API_KEY=your-api-key-here
3\. Install the `python-dotenv` package:
pip install python-dotenv
4\. Load the API key in your Python script:
from dotenv import load_dotenv
import os
load_dotenv()
my_api_key = os.getenv("ANTHROPIC_API_KEY")
5\. If you are deploying your application to a cloud hosting environment, refer to your cloud provider’s documentation on how to add your Claude API Key and share it with your application ([AWS](https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html), [GCP](https://cloud.google.com/security/products/secret-manager?hl=en#how-it-works), [Azure](https://learn.microsoft.com/en-us/azure/key-vault/general/overview), [Vercel](https://vercel.com/docs/cli/secrets), [Heroku](https://devcenter.heroku.com/articles/config-vars)). Some providers offer multiple ways to securely inject environment variables into your app.
### 4. Rotate API Keys Regularly
Regularly rotate your API keys on a consistent schedule (for example, every 90 days) by creating new ones and deactivating old ones. This routine helps minimize potential risks if a key is ever compromised.
### 5. Use separate keys for different purposes
If possible, use different API keys for development, testing, and production environments. This way, you can correlate your usage to different internal use cases. If your API Key is compromised, this allows you to quickly disable just that use case and limit any potential damage.
### 6. Scan Repositories for Secrets
Regularly check your source control repositories for accidentally committed secrets.
- [Enable secret scanning directly in your source control provider](https://docs.github.com/en/code-security/secret-scanning/enabling-secret-scanning-features/enabling-secret-scanning-for-your-repository) if available.
- Use SAST tools like Gitleaks ([https://github.com/gitleaks/gitleaks](https://github.com/gitleaks/gitleaks)) to scan your repositories for accidentally committed secrets.
- Integrate secret scanning into your CI/CD pipeline to catch any secrets before they are pushed to the main branch..
By incorporating regular secret scanning into your development workflow, you can catch and prevent the accidental exposure of API keys and other sensitive information in your code repositories.
### 7. Use a Secure Key Management System (KMS)
As organizations scale and the number of API keys and other secrets increases, managing these sensitive credentials securely becomes more challenging. This is where Key Management Systems (KMS) come into play. A KMS provides a centralized solution for storing, accessing, and managing secret keys, including API keys.
## Benefits of using a KMS
1. **Centralized Security**: Store all your secrets in one secure, encrypted location.
2. **Access Control**: Implement fine-grained access controls to determine who can view or use specific keys.
3. **Audit Trails**: Track all access and changes to your secrets for compliance and security purposes.
4. **Key Rotation**: Easily rotate keys on a regular basis to enhance security.
5. **Integration**: Many KMS solutions integrate with popular cloud platforms and development tools.
## Anthropic's Partnership with GitHub for API Key Protection
Anthropic has partnered directly with GitHub to provide an extra layer of protection for our users through GitHub's Secret scanning partner program. This partnership offers proactive security measures to prevent the misuse of accidentally exposed API keys:
- GitHub actively scans public repositories for exposed Claude API keys.
- If a Claude API key is detected in a public GitHub repository, GitHub immediately notifies Anthropic.
- To prevent potential abuse, Anthropic automatically deactivates the exposed API key.
- The affected user receives a detailed email notification from Anthropic about the incident.
## What should I do if I suspect my API key has been compromised?
If you suspect that your API key may be compromised, we recommend revoking the key immediately. You can do so by logging into your Claude Console account, going to the [API keys page](playground.md) from your profile, clicking the meatball menu (i.e. the three horizontal dots) next to the key in question, and selecting ‘Delete API Key.’
Refer to this article for more information: [What should I do if I suspect my API key has been compromised?](what-should-i-do-if-i-suspect-my-api-key-has-been-compromised.md)
API key security is an ongoing process that requires vigilance and regular review of your security measure. By following these best practices, you can significantly reduce the risk of API key leaks and unauthorized access.
---
## API Safeguards Tools | Claude Help Center
Source: https://support.claude.com/en/articles/9199617-api-safeguards-tools
Path: 04-API-Reference/Other/api-safeguards-tools.md
# API Safeguards Tools
Whether you are just starting the process of setting up safeguards for your API deployment of Claude, or your deployment is already running, here are some strategies to consider when building your own AI safety program. These suggestions are designed to help you comply with our **[Terms of Service](https://www.anthropic.com/legal/commercial-terms)** and **[Usage Policy](https://www.anthropic.com/legal/aup)**, which prohibit certain uses of Claude. Failure to comply with the **[Terms](https://www.anthropic.com/legal/commercial-terms)** and **[Usage Policy](https://www.anthropic.com/legal/aup)** may result in suspension or termination of your access to the services.
**Basic Safeguards**
- Store IDs linked with each API call, so if you need to pinpoint specific violative content you have the ability to find it in your systems.
- Consider assigning IDs to users, which can help you track specific individuals who are violating Anthropic’s AUP, allowing for more targeted action in cases of misuse.
- The choice to **[pass IDs to Anthropic through the API](https://docs.anthropic.com/claude/reference/messages_post#:~:text=models%20for%20details.-,metadata,object,-An%20object%20describing)** is up to you. But, if provided, we can more precisely pinpoint violations. To help protect end-users' privacy, any IDs passed should be cryptographically hashed.
- Consider requiring customer to sign-up for an account on your platform before utilizing Claude
- Ensure your customers understand permitted uses
- Warn, throttle, or suspend users who repeatedly violate Anthropic’s **[Terms of Service](https://www.anthropic.com/legal/commercial-terms)** and **[Usage Policy](https://www.anthropic.com/legal/aup)**
**Intermediate Safeguards**
- Create customization frameworks that restrict end-user interactions with Claude to a limited set of prompts or only allow Claude to review a specific knowledge corpus that you already have, which will decrease the ability of users to engage in violative behavior.
- Anthropic runs real-time safeguards on API inputs and outputs by default. There is no additional opt-in filter to enable. You can add your own moderation layer in your application; see the moderation guidance in the developer docs.
- *For Bedrock Customers:*
- Activate your private S3 bucket in order to store prompts and completions for your own evaluation
**Advanced Safeguards**
- **[Use Claude for your content moderation](https://docs.anthropic.com/claude/docs/content-moderation)**
- Run a moderation API against all end-user prompts before they are sent to Claude to ensure they are not harmful
**Comprehensive Safeguards**
- Set up an internal human review system to flag prompts that are marked by Claude (being used for content moderation) or a moderation API as harmful so you can intervene to restrict or remove users with high violation rates.
---
## Legal summarization
Source: https://platform.claude.com/docs/en/build/use-case-legal-summarization
Path: 04-API-Reference/Other/build-use-case-legal-summarization.md
# Legal summarization
This guide walks through how to leverage Claude's advanced natural language processing capabilities to efficiently summarize legal documents, extracting key information and expediting legal research. With Claude, you can streamline the review of contracts, litigation prep, and regulatory work, saving time and ensuring accuracy in your legal processes.
---
> Visit the [summarization cookbook](https://platform.claude.com/cookbook/capabilities-summarization-guide) to see an example legal summarization implementation using Claude.
## Before building with Claude
### Decide whether to use Claude for legal summarization
Here are some key indicators that you should employ an LLM like Claude to summarize legal documents:
**You want to review a high volume of documents efficiently and affordably**
Large-scale document review can be time-consuming and expensive when done manually. Claude can process and summarize vast amounts of legal documents rapidly, significantly reducing the time and cost associated with document review. This capability is particularly valuable for tasks like due diligence, contract analysis, or litigation discovery, where efficiency is crucial.
**You require automated extraction of key metadata**
Claude can efficiently extract and categorize important metadata from legal documents, such as parties involved, dates, contract terms, or specific clauses. This automated extraction can help organize information, making it easier to search, analyze, and manage large document sets. It's especially useful for contract management, compliance checks, or creating searchable databases of legal information.
**You want to generate clear, concise, and standardized summaries**
Claude can generate structured summaries that follow predetermined formats, making it easier for legal professionals to quickly grasp the key points of various documents. These standardized summaries can improve readability, facilitate comparison between documents, and enhance overall comprehension, especially when dealing with complex legal language or technical jargon.
**You need precise citations for your summaries**
When creating legal summaries, proper attribution and citation are crucial to ensure credibility and compliance with legal standards. Claude can be prompted to include accurate citations for all referenced legal points, making it easier for legal professionals to review and verify the summarized information.
**You want to streamline and expedite your legal research process**
Claude can assist in legal research by quickly analyzing large volumes of case law, statutes, and legal commentary. It can identify relevant precedents, extract key legal principles, and summarize complex legal arguments. This capability can significantly speed up the research process, allowing legal professionals to focus on higher-level analysis and strategy development.
### Determine the details you want the summarization to extract
There is no single correct summary for any given document. Without clear direction, it can be difficult for Claude to determine which details to include. To achieve optimal results, identify the specific information you want to include in the summary.
For instance, when summarizing a sublease agreement, you might wish to extract the following key points:
```python
details_to_extract = [
"Parties involved (sublessor, sublessee, and original lessor)",
"Property details (address, description, and permitted use)",
"Term and rent (start date, end date, monthly rent, and security deposit)",
"Responsibilities (utilities, maintenance, and repairs)",
"Consent and notices (landlord's consent, and notice requirements)",
"Special provisions (furniture, parking, and subletting restrictions)",
]
```
### Establish success criteria
Evaluating the quality of summaries is a notoriously challenging task. Unlike many other natural language processing tasks, evaluation of summaries often lacks clear-cut, objective metrics. The process can be highly subjective, with different readers valuing different aspects of a summary. Here are criteria you may wish to consider when assessing how well Claude performs legal summarization.
**Factual correctness**
The summary should accurately represent the facts, legal concepts, and key points in the document.
**Legal precision**
Terminology and references to statutes, case law, or regulations must be correct and aligned with legal standards.
**Conciseness**
The summary should condense the legal document to its essential points without losing important details.
**Consistency**
If summarizing multiple documents, the LLM should maintain a consistent structure and approach to each summary.
**Readability**
The text should be clear and easy to understand. If the audience is not legal experts, the summarization should not include legal jargon that could confuse the audience.
**Bias and fairness**
The summary should present an unbiased and fair depiction of the legal arguments and positions.
See the guide on [establishing success criteria](../Test-Evaluate/test-and-evaluate-develop-tests.md) for more information.
---
## How to summarize legal documents using Claude
### Select the right Claude model
Model accuracy is extremely important when summarizing legal documents. Claude Opus 4.6 is an excellent choice for use cases such as this where high accuracy is required. If the size and quantity of your documents is large such that costs start to become a concern, you can also try using a smaller model like Claude Haiku 4.5.
To help estimate these costs, below is a comparison of the cost to summarize 1,000 sublease agreements using both Sonnet and Haiku:
* **Content size**
* Number of agreements: 1,000
* Characters per agreement: 300,000
* Total characters: 300M
* **Estimated tokens**
* Input tokens: 86M (assuming 1 token per 3.5 characters)
* Output tokens per summary: 350
* Total output tokens: 350,000
* **Claude Opus 4.6 estimated cost**
* Input token cost: 86 MTok * $5.00/MTok = $430
* Output token cost: 0.35 MTok * $25.00/MTok = $8.75
* Total cost: $430.00 + $8.75 = $438.75
* **Claude Haiku 3 estimated cost**
* Input token cost: 86 MTok * $0.25/MTok = $21.50
* Output token cost: 0.35 MTok * $1.25/MTok = $0.44
* Total cost: $21.50 + $0.44 = $21.96
> **Tip:** Actual costs may differ from these estimates. These estimates are based on the example highlighted in the section on [prompting](#build-a-strong-prompt).
### Transform documents into a format that Claude can process
Before you begin summarizing documents, you need to prepare your data. This involves extracting text from PDFs, cleaning the text, and ensuring it's ready to be processed by Claude.
Here is a demonstration of this process on a sample pdf:
```python
from io import BytesIO
import re
import pypdf
import requests
def get_llm_text(pdf_file):
reader = pypdf.PdfReader(pdf_file)
text = "\n".join([page.extract_text() for page in reader.pages])
# Remove extra whitespace
text = re.sub(r"\s+", " ", text)
# Remove page numbers
text = re.sub(r"\n\s*\d+\s*\n", "\n", text)
return text
# Create the full URL from the GitHub repository
url = "https://raw.githubusercontent.com/anthropics/anthropic-cookbook/main/skills/summarization/data/Sample Sublease Agreement.pdf"
url = url.replace(" ", "%20")
# Download the PDF file into memory
response = requests.get(url)
# Load the PDF from memory
pdf_file = BytesIO(response.content)
document_text = get_llm_text(pdf_file)
print(document_text[:50000])
```
In this example, you first download a pdf of a sample sublease agreement used in the [summarization cookbook](https://platform.claude.com/cookbook/capabilities-summarization-guide). This agreement was sourced from a publicly available sublease agreement from the [sec.gov website](https://www.sec.gov/Archives/edgar/data/1045425/000119312507044370/dex1032.htm).
The example uses the pypdf library to extract the contents of the pdf and convert it to text. The text data is then cleaned by removing extra whitespace and page numbers.
### Build a strong prompt
Claude can adapt to various summarization styles. You can change the details of the prompt to guide Claude to be more or less verbose, include more or less technical terminology, or provide a higher or lower level summary of the context at hand.
Here's an example of how to create a prompt that ensures the generated summaries follow a consistent structure when analyzing sublease agreements:
```python
import anthropic
# Initialize the Anthropic client
client = anthropic.Anthropic()
def summarize_document(
text, details_to_extract, model="claude-opus-4-6", max_tokens=1000
):
# Format the details to extract to be placed within the prompt's context
details_to_extract_str = "\n".join(details_to_extract)
# Prompt the model to summarize the sublease agreement
prompt = f"""Summarize the following sublease agreement. Focus on these key aspects:
{details_to_extract_str}
Provide the summary in bullet points nested within the XML header for each section. For example:
<parties involved>
- Sublessor: [Name]
// Add more details as needed
</parties involved>
If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
Sublease agreement text:
{text}
"""
response = client.messages.create(
model=model,
max_tokens=max_tokens,
system="You are a legal analyst specializing in real estate law, known for highly accurate and detailed summaries of sublease agreements.",
messages=[
{"role": "user", "content": prompt},
{
"role": "assistant",
"content": "Here is the summary of the sublease agreement: <summary>",
},
],
stop_sequences=["</summary>"],
)
return response.content[0].text
sublease_summary = summarize_document(document_text, details_to_extract)
print(sublease_summary)
```
This code implements a `summarize_document` function that uses Claude to summarize the contents of a sublease agreement. The function accepts a text string and a list of details to extract as inputs. In this example, the code calls the function with the `document_text` and `details_to_extract` variables that were defined in the previous code snippets.
Within the function, a prompt is generated for Claude, including the document to be summarized, the details to extract, and specific instructions for summarizing the document. The prompt instructs Claude to respond with a summary of each detail to extract nested within XML headers.
Because the code outputs each section of the summary within tags, each section can easily be parsed out as a post-processing step. This approach enables structured summaries that can be adapted for your use case, so that each summary follows the same pattern.
### Evaluate your prompt
Prompting often requires testing and optimization for it to be production ready. To determine the readiness of your solution, evaluate the quality of your summaries using a systematic process combining quantitative and qualitative methods. Creating a [strong empirical evaluation](../Test-Evaluate/test-and-evaluate-develop-tests.md#building-evals-and-test-cases) based on your defined success criteria allows you to optimize your prompts. Here are some metrics you may wish to include within your empirical evaluation:
**ROUGE scores**
This measures the overlap between the generated summary and an expert-created reference summary. This metric primarily focuses on recall and is useful for evaluating content coverage.
**BLEU scores**
While originally developed for machine translation, this metric can be adapted for summarization tasks. BLEU scores measure the precision of n-gram matches between the generated summary and reference summaries. A higher score indicates that the generated summary contains similar phrases and terminology to the reference summary.
**Contextual embedding similarity**
This metric involves creating vector representations (embeddings) of both the generated and reference summaries. The similarity between these embeddings is then calculated, often using cosine similarity. Higher similarity scores indicate that the generated summary captures the semantic meaning and context of the reference summary, even if the exact wording differs.
**LLM-based grading**
This method involves using an LLM such as Claude to evaluate the quality of generated summaries against a scoring rubric. The rubric can be tailored to your specific needs, assessing key factors like accuracy, completeness, and coherence. For guidance on implementing LLM-based grading, view these [tips](../Test-Evaluate/test-and-evaluate-develop-tests.md#tips-for-llm-based-grading).
**Human evaluation**
In addition to creating the reference summaries, legal experts can also evaluate the quality of the generated summaries. While this is expensive and time-consuming at scale, this is often done on a few summaries as a sanity check before deploying to production.
### Deploy your prompt
Here are some additional considerations to keep in mind as you deploy your solution to production.
1. **Ensure no liability:** Understand the legal implications of errors in the summaries, which could lead to legal liability for your organization or clients. Provide disclaimers or legal notices clarifying that the summaries are generated by AI and should be reviewed by legal professionals.
2. **Handle diverse document types:** This guide discusses how to extract text from PDFs. In the real-world, documents may come in a variety of formats (PDFs, Word documents, text files, etc.). Ensure your data extraction pipeline can convert all of the file formats you expect to receive.
3. **Parallelize API calls to Claude:** Long documents with a large number of tokens may require up to a minute for Claude to generate a summary. For large document collections, you may want to send API calls to Claude in parallel so that the summaries can be completed in a reasonable timeframe. Refer to Anthropic's [rate limits](../Endpoints/rate-limits.md#rate-limits) to determine the maximum amount of API calls that can be performed in parallel.
---
## Improve performance
In complex scenarios, it may be helpful to consider additional strategies to improve performance beyond standard [prompt engineering techniques](../../10-Prompting-Guides/build-with-claude-prompt-engineering-overview.md). Here are some advanced strategies:
### Perform meta-summarization to summarize long documents
Legal summarization often involves handling long documents or many related documents at once, such that you surpass Claude's context window. You can use a chunking method known as meta-summarization in order to handle this use case. This technique involves breaking down documents into smaller, manageable chunks and then processing each chunk separately. You can then combine the summaries of each chunk to create a meta-summary of the entire document.
Here's an example of how to perform meta-summarization:
```python
import anthropic
# Initialize the Anthropic client
client = anthropic.Anthropic()
def chunk_text(text, chunk_size=20000):
return [text[i : i + chunk_size] for i in range(0, len(text), chunk_size)]
def summarize_long_document(
text, details_to_extract, model="claude-opus-4-6", max_tokens=1000
):
# Format the details to extract to be placed within the prompt's context
details_to_extract_str = "\n".join(details_to_extract)
# Iterate over chunks and summarize each one
chunk_summaries = [
summarize_document(
chunk, details_to_extract, model=model, max_tokens=max_tokens
)
for chunk in chunk_text(text)
]
final_summary_prompt = f"""
You are looking at the chunked summaries of multiple documents that are all related.
Combine the following summaries of the document from different truthful sources into a coherent overall summary:
<chunked_summaries>
{"".join(chunk_summaries)}
</chunked_summaries>
Focus on these key aspects:
{details_to_extract_str})
Provide the summary in bullet points nested within the XML header for each section. For example:
<parties involved>
- Sublessor: [Name]
// Add more details as needed
</parties involved>
If any information is not explicitly stated in the document, note it as "Not specified". Do not preamble.
"""
response = client.messages.create(
model=model,
max_tokens=max_tokens,
system="You are a legal expert that summarizes notes on one document.",
messages=[
{"role": "user", "content": final_summary_prompt},
{
"role": "assistant",
"content": "Here is the summary of the sublease agreement: <summary>",
},
],
stop_sequences=["</summary>"],
)
return response.content[0].text
long_summary = summarize_long_document(document_text, details_to_extract)
print(long_summary)
```
The `summarize_long_document` function builds upon the earlier `summarize_document` function by splitting the document into smaller chunks and summarizing each chunk individually.
The code achieves this by applying the `summarize_document` function to each chunk of 20,000 characters within the original document. The individual summaries are then combined, and a final summary is created from these chunk summaries.
Note that the `summarize_long_document` function isn't strictly necessary for the example pdf, as the entire document fits within Claude's context window. However, it becomes essential for documents exceeding Claude's context window or when summarizing multiple related documents together. Regardless, this meta-summarization technique often captures additional important details in the final summary that were missed in the earlier single-summary approach.
### Use summary indexed documents to explore a large collection of documents
Searching a collection of documents with an LLM usually involves retrieval-augmented generation (RAG). However, in scenarios involving large documents or when precise information retrieval is crucial, a basic RAG approach may be insufficient. Summary indexed documents is an advanced RAG approach that provides a more efficient way of ranking documents for retrieval, using less context than traditional RAG methods. In this approach, you first use Claude to generate a concise summary for each document in your corpus, and then use Claude to rank the relevance of each summary to the query being asked. For further details on this approach, including a code-based example, check out the summary indexed documents section in the [summarization cookbook](https://platform.claude.com/cookbook/capabilities-summarization-guide).
### Fine-tune Claude to learn from your dataset
Another advanced technique to improve Claude's ability to generate summaries is fine-tuning. Fine-tuning involves training Claude on a custom dataset that specifically aligns with your legal summarization needs, ensuring that Claude adapts to your use case. Here's an overview on how to perform fine-tuning:
1. **Identify errors:** Start by collecting instances where Claude's summaries fall short - this could include missing critical legal details, misunderstanding context, or using inappropriate legal terminology.
2. **Curate a dataset:** Once you've identified these issues, compile a dataset of these problematic examples. This dataset should include the original legal documents alongside your corrected summaries, ensuring that Claude learns the desired behavior.
3. **Perform fine-tuning:** Fine-tuning involves retraining the model on your curated dataset to adjust its weights and parameters. This retraining helps Claude better understand the specific requirements of your legal domain, improving its ability to summarize documents according to your standards.
4. **Iterative improvement:** Fine-tuning is not a one-time process. As Claude continues to generate summaries, you can iteratively add new examples where it has underperformed, further refining its capabilities. Over time, this continuous feedback loop will result in a model that is highly specialized for your legal summarization tasks.
> **Tip:** Fine-tuning is currently only available via Amazon Bedrock. Additional details are available in the [AWS launch blog](https://aws.amazon.com/blogs/machine-learning/fine-tune-anthropics-claude-3-haiku-in-amazon-bedrock-to-boost-model-accuracy-and-quality/).
**Related resources:**
- [Summarization cookbook](https://platform.claude.com/cookbook/capabilities-summarization-guide) - View a fully implemented code-based example of how to use Claude to summarize contracts.
- [Citations cookbook](https://platform.claude.com/cookbook/misc-using-citations) - Explore the Citations cookbook recipe for guidance on how to ensure accuracy and explainability of information.
---
## Can I use the Claude API for individual use? | Claude Help Center
Source: https://support.claude.com/en/articles/8987200-can-i-use-the-claude-api-for-individual-use
Path: 04-API-Reference/Other/can-i-use-the-claude-api-for-individual-use.md
# Can I use the Claude API for individual use?
March 16, 2026
Yes, individuals and hobbyists are welcome to use the Claude API. However, please note that use of the API is subject to our [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms), regardless of whether you are an individual or representing a company.
---
## API usage primer for Claude - Claude Platform Docs
Source: https://platform.claude.com/docs/en/claude_api_primer
Path: 04-API-Reference/Other/claude-api-primer.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fclaude_api_primer)
SearchCtrlK
[Console](usage-limits.md)
Documentation
# API usage primer for Claude
Copy page
This guide is designed to give Claude the basics of using the Claude API. It gives explanation and examples of model IDs/the basic messages API, tool use, streaming, thinking, and nothing else.
Copy page
# API usage primer for Claude
> This guide is designed to give Claude the basics of using the Claude API. It gives explanation and examples of model IDs/the basic messages API, tool use, streaming, thinking, and nothing else.
## Models
```python
Recommended default for most work, including complex agentic coding: Claude Opus 5.5: claude-opus-5-5
Step up for the hardest long-running agentic and research tasks, at 2.5x Claude Opus 5.5 pricing: Claude Fable 5.1: claude-fable-5-1
Previous Opus model: Claude Opus 5: claude-opus-5
Smart model: Claude Sonnet 5.5: claude-sonnet-5-5
Previous Sonnet model: Claude Sonnet 5: claude-sonnet-5
For fast, cost-effective tasks: Claude Haiku 4.5: claude-haiku-4-5-20251001
```
## Calling the API
### Basic request and response
CLI
Python
```python
import anthropic
message = anthropic.Anthropic().messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(message)
```
Output
```python
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello!"
}
],
"model": "claude-opus-5-5",
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 6
}
}
```
### Multiple conversational turns
The Messages API is stateless, which means that you always send the full conversational history to the API. You can use this pattern to build up a conversation over time. Earlier conversational turns don't necessarily need to actually originate from Claude. You can use synthetic `assistant` messages.
CLI
Python
```python
import anthropic
message = anthropic.Anthropic().messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, Claude"},
{"role": "assistant", "content": "Hello!"},
{"role": "user", "content": "Can you describe LLMs to me?"},
],
)
print(message)
```
### Prefilling Claude's response
You can prefill part of Claude's response in the last position of the input messages list. Use this technique to shape Claude's response. The following example uses `"max_tokens": 1` to get a single multiple choice answer from Claude.
Claude 4.6 and later models and Claude Mythos Preview do not support assistant message prefill; requests to those models must end with a user message. The examples below use a model that supports prefill.
CLI
Python
```python
import anthropic
message = anthropic.Anthropic().messages.create(
model="claude-sonnet-4-5",
max_tokens=1,
messages=[
{
"role": "user",
"content": "What is latin for Ant? (A) Apoidea, (B) Rhopalocera, (C) Formicidae",
},
{"role": "assistant", "content": "The answer is ("},
],
)
print(message.content[0].text)
```
### Vision
Claude can read both text and images in requests. Both `base64` and `url` source types are supported for images, along with the `image/jpeg`, `image/png`, `image/gif`, and `image/webp` media types.
CLI
Python
```python
import anthropic
import base64
import httpx2
# Option 1: Base64-encoded image
image_url = "https://platform.claude.com/docs/images/vision-example.jpg"
image_media_type = "image/jpeg"
image_data = base64.standard_b64encode(httpx2.get(image_url).content).decode("utf-8")
message = anthropic.Anthropic().messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": image_media_type,
"data": image_data,
},
},
{"type": "text", "text": "What is in the above image?"},
],
}
],
)
print(next(block.text for block in message.content if block.type == "text"))
# Option 2: URL-referenced image
message_from_url = anthropic.Anthropic().messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://platform.claude.com/docs/images/vision-example.jpg",
},
},
{"type": "text", "text": "What is in the above image?"},
],
}
],
)
print(next(block.text for block in message_from_url.content if block.type == "text"))
```
## Thinking
Thinking can sometimes help Claude with very hard tasks. The current mechanism is [adaptive thinking](../Guides/build-with-claude-thinking.md) (`thinking: {"type": "adaptive"}`): Claude decides when and how much to think, and you steer thinking depth with the [`effort`](../Guides/build-with-claude-effort.md) parameter rather than a token budget. Adaptive thinking is supported on Claude 4.6 and later models and Claude Mythos Preview. On Claude 5 models and Claude Mythos Preview, thinking is on by default when the `thinking` parameter is omitted.
Temperature must be set to 1 (or left unset) whenever thinking is enabled, on all models. On Claude 4.7 and later models and Claude Mythos Preview, `temperature` is deprecated and only its default value is accepted, even when thinking is off.
Thinking is supported in the following models:
- Claude Opus 5.5 (`claude-opus-5-5`, adaptive thinking only, always on)
- Claude Sonnet 5.5 (`claude-sonnet-5-5`, adaptive thinking only, on by default)
- Claude Opus 5 (
claude-opus-5
, adaptive thinking only, on by default)
- Claude Sonnet 5 (`claude-sonnet-5`, adaptive thinking only, on by default)
- Claude Opus 4.8 (
claude-opus-4-8
, adaptive thinking only)
- Claude Opus 4.7 (`claude-opus-4-7`, adaptive thinking only)
- Claude Opus 4.6 (`claude-opus-4-6`, adaptive or legacy manual thinking)
- Claude Sonnet 4.6 (`claude-sonnet-4-6`, adaptive or legacy manual thinking)
- Claude Opus 4.5 (`claude-opus-4-5-20251101`, legacy manual thinking only)
- Claude Sonnet 4.5 (`claude-sonnet-4-5-20250929`, legacy manual thinking only)
- Claude Haiku 4.5 (`claude-haiku-4-5-20251001`, legacy manual thinking only)
On Claude 4.7 and later models, manual extended thinking (`type: enabled` with a `budget_tokens` value) is not supported and returns a 400 error. Use [adaptive thinking](../Guides/build-with-claude-thinking.md) (`type: adaptive`) instead.
### How thinking works
When thinking is on, Claude creates `thinking` content blocks where it outputs its internal reasoning. The API response includes `thinking` content blocks, followed by `text` content blocks.
CLI
Python
```python
import anthropic
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
messages=[
{
"role": "user",
"content": "Are there an infinite number of prime numbers such that n mod 4 == 3?",
}
],
)
# The response contains summarized thinking blocks and text blocks
for block in response.content:
match block.type:
case "thinking":
print(f"\nThinking summary: {block.thinking}")
case "text":
print(f"\nResponse: {block.text}")
```
Manual extended thinking (`thinking: {"type": "enabled", "budget_tokens": N}`) is the legacy mechanism. It works only on Claude 4 through 4.6 models that support thinking; Claude 4.7 and later models reject `type: enabled` with a 400 error and use [adaptive thinking](../Guides/build-with-claude-thinking.md) instead. With manual extended thinking, `budget_tokens` sets the maximum number of tokens Claude is allowed to use for its internal reasoning process; the limit applies to full thinking tokens, not to the summarized output. Unless you are using [interleaved thinking](#interleaved-thinking), `budget_tokens` must be less than `max_tokens` so that Claude has space to write its response after thinking is complete.
## Thinking with tool use
Thinking can be used alongside tool use, allowing Claude to reason through tool selection and results processing.
Important limitations:
1. **Tool choice limitation:** Only supports `tool_choice: {"type": "auto"}` (default) or `tool_choice: {"type": "none"}`.
2. **Preserving thinking blocks:** During tool use, you must pass `thinking` blocks back to the API for the last assistant message.
### Preserving thinking blocks
CLI
Python
```python
import anthropic
client = anthropic.Anthropic()
weather_tool = {
"name": "get_weather",
"description": "Get the current weather for a location.",
"input_schema": {
"type": "object",
"properties": {"location": {"type": "string", "description": "The city name."}},
"required": ["location"],
},
}
weather_data = {"temperature": 72}
# First request - Claude responds with thinking and tool request
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
tools=[weather_tool],
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
)
# Extract thinking block and tool use block
thinking_block = next(
(block for block in response.content if block.type == "thinking"), None
)
tool_use_block = next(
(block for block in response.content if block.type == "tool_use"), None
)
# Second request - Include thinking block and tool result
continuation = client.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
tools=[weather_tool],
messages=[
{"role": "user", "content": "What's the weather in Paris?"},
# Notice that the thinking_block is passed in as well as the tool_use_block
{"role": "assistant", "content": [thinking_block, tool_use_block]},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use_block.id,
"content": f"Current temperature: {weather_data['temperature']}°F",
}
],
},
],
)
for block in continuation.content:
if block.type == "text":
print(block.text)
```
### Interleaved thinking
Interleaved thinking enables Claude to think between tool calls, reasoning about tool results before deciding the next step.
On models with [adaptive thinking](../Guides/build-with-claude-thinking.md) (`thinking: {type: "adaptive"}`), interleaved thinking is automatically enabled. No beta header is needed. Sonnet 4.6 supports both the `interleaved-thinking-2025-05-14` beta header with manual extended thinking and adaptive thinking.
On older models that use manual extended thinking (Claude 4, 4.5, and Sonnet 4.6 models), enable interleaved thinking by adding the beta header `interleaved-thinking-2025-05-14` to your API request:
CLI
Python
```python
import anthropic
client = anthropic.Anthropic()
calculator_tool = {
"name": "calculator",
"description": "Perform arithmetic calculations.",
"input_schema": {
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "The math expression to evaluate.",
}
},
"required": ["expression"],
},
}
database_tool = {
"name": "database_query",
"description": "Query the product database.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "The database query."}
},
"required": ["query"],
},
}
response = client.beta.messages.create(
model="claude-sonnet-4-6",
max_tokens=16000,
thinking={"type": "enabled", "budget_tokens": 10000},
tools=[calculator_tool, database_tool],
messages=[
{
"role": "user",
"content": "What's the total revenue if we sold 150 units of product A at $50 each?",
}
],
betas=["interleaved-thinking-2025-05-14"],
)
for block in response.content:
match block.type:
case "thinking":
print(f"Thinking: {block.thinking}")
case "tool_use":
print(f"Tool call: {block.name}({block.input})")
case "text":
print(f"Response: {block.text}")
```
With interleaved thinking and ONLY with interleaved thinking (not regular manual extended thinking), the `budget_tokens` can exceed the `max_tokens` parameter, as `budget_tokens` in this case represents the total budget across all thinking blocks within one assistant turn.
## Tool use
### Specifying client tools
Client tools are specified in the `tools` top-level parameter of the API request. Each tool definition includes:
| Parameter | Description |
|:---------------|:----------------------------------------------------------------------------------------------------|
| `name` | The name of the tool. Must match the regex `^[a-zA-Z0-9_-]{1,128}$`. |
| `description` | A detailed plaintext description of what the tool does, when it should be used, and how it behaves. |
| `input_schema` | A [JSON Schema](https://json-schema.org/) object defining the expected parameters for the tool. |
```python
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature, either 'celsius' or 'fahrenheit'"
}
},
"required": ["location"]
}
}
```
### Best practices for tool definitions
**Provide extremely detailed descriptions.** This is by far the most important factor in tool performance. Your descriptions should explain every detail about the tool, including:
- What the tool does
- When it should be used (and when it shouldn't)
- What each parameter means and how it affects the tool's behavior
- Any important caveats or limitations
**Consider using `input_examples` for complex tools.** For tools with nested objects, optional parameters, or format-sensitive inputs, you can provide concrete examples using the `input_examples` field (beta). This helps Claude understand expected input patterns. See [Providing tool use examples](../Agents-Tools/agents-and-tools-tool-use-implement-tool-use.md#providing-tool-use-examples) for details.
Example of a good tool description:
```python
{
"name": "get_stock_price",
"description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The stock ticker symbol, e.g. AAPL for Apple Inc."
}
},
"required": ["ticker"]
}
}
```
## Controlling Claude's output
### Forcing tool use
You can force Claude to use a specific tool by specifying the tool in the `tool_choice` field:
```python
tool_choice = {"type": "tool", "name": "get_weather"}
```
When working with the `tool_choice` parameter, there are four possible options:
- `auto` allows Claude to determine whether to call any provided tools or not (default).
- `any` tells Claude that it must use one of the provided tools.
- `tool` forces Claude to always use a particular tool.
- `none` prevents Claude from using any tools.
On Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1, and Claude Mythos 5.1, `any` and `tool` return a 400 error. Leave `tool_choice` at `auto` and set `"strict": true` on the tool definition to guarantee that any call Claude makes matches the tool's `input_schema`. See [Strict tool use](../Agents-Tools/agents-and-tools-tool-use-strict-tool-use.md).
### JSON output
Tools do not necessarily need to be client functions. You can use tools anytime you want the model to return JSON output that follows a provided schema.
### Parallel tool use
By default, Claude may use multiple tools to answer a user query. You can disable this behavior by setting `disable_parallel_tool_use=true`.
## Handling tool use and tool result content blocks
### Handling results from client tools
The response has a `stop_reason` of `tool_use` and one or more `tool_use` content blocks that include:
- `id`: A unique identifier for this particular tool use block.
- `name`: The name of the tool being used.
- `input`: An object containing the input being passed to the tool.
```python
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "location": "San Francisco, CA" }
}
]
}
```
When you receive a tool use response, you should:
1. Extract the `name`, `id`, and `input` from the `tool_use` block.
2. Run the actual tool in your code base corresponding to that tool name.
3. Continue the conversation by sending a new message with a `tool_result`:
```python
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "15 degrees"
}
]
}
```
### Handling the `max_tokens` stop reason
If Claude's response is cut off because it hits the `max_tokens` limit during tool use, retry the request with a higher `max_tokens` value.
### Handling the `pause_turn` stop reason
When using server tools such as web search, the API may return a `pause_turn` stop reason. Continue the conversation by passing the paused response back as-is in a subsequent request.
## Troubleshooting errors
### Tool execution error
If the tool itself throws an error during execution, return the error message with `"is_error": true`:
```python
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "ConnectionError: the weather service API is not available (HTTP 500)",
"is_error": true
}
]
}
```
### Invalid tool name
If Claude's attempted use of a tool is invalid (for example, missing required parameters), try the request again with more-detailed `description` values in your tool definitions.
## Streaming messages
When creating a Message, you can set `"stream": true` to incrementally stream the response using server-sent events (SSE).
### Streaming with SDKs
CLI
Python
```python
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
model="claude-opus-5-5",
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
```
### Event types
Each server-sent event includes a named event type and associated JSON data. Each stream uses the following event flow:
1. `message_start`: contains a `Message` object with empty `content`.
2. A series of content blocks, each with `content_block_start`, one or more `content_block_delta` events, and `content_block_stop`.
3. One or more `message_delta` events, indicating top-level changes to the final `Message` object.
4. A final `message_stop` event.
**Warning:** The token counts shown in the `usage` field of the `message_delta` event are *cumulative*.
### Content block delta types
#### Text delta
```python
{
"type": "content_block_delta",
"index": 0,
"delta": { "type": "text_delta", "text": "Hello frien" }
}
```
#### Input JSON delta
For `tool_use` content blocks, deltas are *partial JSON strings*:
```python
{"type": "content_block_delta","index": 1,"delta": {"type": "input_json_delta","partial_json": "{\"location\": \"San Fra"}}}
```
#### Thinking delta
When using thinking with streaming:
```python
{
"type": "content_block_delta",
"index": 0,
"delta": {
"type": "thinking_delta",
"thinking": "Let me solve this step by step..."
}
}
```
### Basic streaming request example
```python
event: message_start
data: {"type": "message_start", "message": {"id": "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY", "type": "message", "role": "assistant", "content": [], "model": "claude-opus-5-5", "stop_reason": null, "stop_sequence": null, "usage": {"input_tokens": 25, "output_tokens": 1}}}
event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Hello"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "!"}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 0}
event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence":null}, "usage": {"output_tokens": 15}}
event: message_stop
data: {"type": "message_stop"}
```
---
## Batch Processing with Message Batches API
Source: https://github.com/anthropics/claude-cookbooks/blob/main/misc/batch_processing.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-misc-batch-processing.md
# Batch Processing with Message Batches API
Message Batches allow you to process large volumes of Messages requests
asynchronously and cost-effectively. This cookbook demonstrates how to
use the Message Batches API to handle bulk operations while reducing
costs by 50%.
In this cookbook, we will demonstrate how to:
1. Create and submit message batches
2. Monitor batch processing status
3. Retrieve and handle batch results
4. Implement best practices for effective batching
:::
::: {.cell .markdown vscode="{\"languageId\":\"plaintext\"}"}
## Setup
First, let\'s set up our environment with the necessary imports:
:::
::: {.cell .code}
``` python
%pip install anthropic
```
:::
::: {.cell .code execution_count="4"}
``` python
import time
import anthropic
client = anthropic.Anthropic()
MODEL_NAME = "claude-sonnet-4-5"
```
:::
::: {.cell .markdown}
## Example 1: Basic Batch Processing
Let\'s start with a simple example that demonstrates creating and
monitoring a batch of message requests.
:::
:::: {.cell .code execution_count="68"}
``` python
# Prepare a list of questions for batch processing
questions = [
"How do solar panels convert sunlight into electricity?",
"What's the difference between mutual funds and ETFs?",
"What is a pick and roll in basketball?",
"Why do leaves change color in autumn?",
]
# Create batch requests
batch_requests = [
{
"custom_id": f"question-{i}",
"params": {
"model": MODEL_NAME,
"max_tokens": 1024,
"messages": [{"role": "user", "content": question}],
},
}
for i, question in enumerate(questions)
]
# Submit the batch
response = client.beta.messages.batches.create(requests=batch_requests)
print(f"Batch ID: {response.id}")
print(f"Status: {response.processing_status}")
print(f"Created at: {response.created_at}")
```
::: {.output .stream .stdout}
Batch ID: msgbatch_01GgqTz9XzriGNHzTSGZsJJ8
Status: in_progress
Created at: 2024-10-08 00:46:30.694748+00:00
:::
::::
::: {.cell .markdown}
# Monitoring Batch Progress
Now let\'s monitor the batch processing status:
:::
:::: {.cell .code execution_count="69"}
``` python
def monitor_batch(batch_id, polling_interval=5):
while True:
batch_update = client.beta.messages.batches.retrieve(batch_id)
batch_update_status = batch_update.processing_status
print(batch_update)
print(f"Status: {batch_update_status}")
if batch_update_status == "ended":
return batch_update
time.sleep(polling_interval)
# Monitor our batch
batch_result = monitor_batch(response.id)
print("\nBatch processing complete!")
print("\nRequest counts:")
print(f" Succeeded: {batch_result.request_counts.succeeded}")
print(f" Errored: {batch_result.request_counts.errored}")
print(f" Processing: {batch_result.request_counts.processing}")
print(f" Canceled: {batch_result.request_counts.canceled}")
print(f" Expired: {batch_result.request_counts.expired}")
```
::: {.output .stream .stdout}
BetaMessageBatch(id='msgbatch_01GgqTz9XzriGNHzTSGZsJJ8', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_01GgqTz9XzriGNHzTSGZsJJ8', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_01GgqTz9XzriGNHzTSGZsJJ8', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_01GgqTz9XzriGNHzTSGZsJJ8', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), ended_at=datetime.datetime(2024, 10, 8, 0, 46, 47, 283392, tzinfo=TzInfo(UTC)), expires_at=datetime.datetime(2024, 10, 9, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), processing_status='ended', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=0, succeeded=4), results_url='https://api.anthropic.com/v1/messages/batches/msgbatch_01GgqTz9XzriGNHzTSGZsJJ8/results', type='message_batch')
Status: ended
Batch processing complete!
Request counts:
Succeeded: 4
Errored: 0
Processing: 0
Canceled: 0
Expired: 0
:::
::::
::: {.cell .markdown}
# Retrieving Results
Once the batch is complete, we can retrieve and process the results:
:::
:::: {.cell .code execution_count="70"}
``` python
def process_results(batch_id):
# First get the batch status
batch = client.beta.messages.batches.retrieve(batch_id)
print(f"\nBatch {batch.id} Summary:")
print(f"Status: {batch.processing_status}")
print(f"Created: {batch.created_at}")
print(f"Ended: {batch.ended_at}")
print(f"Expires: {batch.expires_at}")
if batch.processing_status == "ended":
print("\nIndividual Results:")
for result in client.beta.messages.batches.results(batch_id):
print(f"\nResult for {result.custom_id}:")
print(f"Status: {result.result.type}")
if result.result.type == "succeeded":
print(f"Content: {result.result.message.content[0].text[:200]}...")
elif result.result.type == "errored":
print("Request errored")
elif result.result.type == "canceled":
print("Request was canceled")
elif result.result.type == "expired":
print("Request expired")
# Example usage:
batch_status = monitor_batch(response.id)
if batch_status.processing_status == "ended":
process_results(batch_status.id)
```
::: {.output .stream .stdout}
BetaMessageBatch(id='msgbatch_01GgqTz9XzriGNHzTSGZsJJ8', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), ended_at=datetime.datetime(2024, 10, 8, 0, 46, 47, 283392, tzinfo=TzInfo(UTC)), expires_at=datetime.datetime(2024, 10, 9, 0, 46, 30, 694748, tzinfo=datetime.timezone.utc), processing_status='ended', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=0, succeeded=4), results_url='https://api.anthropic.com/v1/messages/batches/msgbatch_01GgqTz9XzriGNHzTSGZsJJ8/results', type='message_batch')
Status: ended
Batch msgbatch_01GgqTz9XzriGNHzTSGZsJJ8 Summary:
Status: ended
Created: 2024-10-08 00:46:30.694748+00:00
Ended: 2024-10-08 00:46:47.283392+00:00
Expires: 2024-10-09 00:46:30.694748+00:00
Individual Results:
Result for question-0:
Status: succeeded
Content: Solar panels convert sunlight into electricity through a process called the photovoltaic effect. Here's a step-by-step explanation of how this works:
1. Solar panel composition:
Solar panels are made...
Result for question-1:
Status: succeeded
Content: Mutual funds and ETFs (Exchange-Traded Funds) are both popular investment vehicles that allow investors to diversify their portfolios, but they have several key differences:
1. Trading:
- Mutual fund...
Result for question-2:
Status: succeeded
Content: A pick and roll, also known as a screen and roll, is a fundamental offensive play in basketball involving two players. Here's how it works:
1. The ball handler (usually a guard) has possession of the...
Result for question-3:
Status: succeeded
Content: Leaves change color in autumn due to a combination of factors, primarily related to changes in temperature, daylight, and the tree's biological processes. Here's a breakdown of why this happens:
1. C...
:::
::::
::: {.cell .markdown}
## Example 2: Advanced Batch Processing for Different Message Types
This example demonstrates more advanced usage, including error handling
and processing different types of requests in a single batch including a
simple message, a message with a system prompt, a multi-turn message,
and a message with an image.
:::
:::: {.cell .code execution_count="63"}
``` python
import base64
def create_complex_batch():
# Get base64 encoded image
def get_base64_encoded_image(image_path):
with open(image_path, "rb") as image_file:
binary_data = image_file.read()
base_64_encoded_data = base64.b64encode(binary_data)
base64_string = base_64_encoded_data.decode("utf-8")
return base64_string
# Mix of different request types
batch_requests = [
{
"custom_id": "simple-question",
"params": {
"model": MODEL_NAME,
"max_tokens": 1024,
"messages": [{"role": "user", "content": "What is quantum computing?"}],
},
},
{
"custom_id": "image-analysis",
"params": {
"model": MODEL_NAME,
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": get_base64_encoded_image(
"../images/sunset-dawn-nature-mountain-preview.jpg"
),
},
},
{
"type": "text",
"text": "Describe this mountain landscape. What time of day does it appear to be, and what weather conditions do you observe?",
},
],
}
],
},
},
{
"custom_id": "system-prompt",
"params": {
"model": MODEL_NAME,
"max_tokens": 1024,
"system": "You are a helpful science teacher.",
"messages": [{"role": "user", "content": "Explain gravity to a 5-year-old."}],
},
},
{
"custom_id": "multi-turn",
"params": {
"model": MODEL_NAME,
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "What is DNA?"},
{
"role": "assistant",
"content": "DNA is like a blueprint for living things...",
},
{"role": "user", "content": "How is DNA copied?"},
],
},
},
]
try:
response = client.beta.messages.batches.create(requests=batch_requests)
return response.id
except Exception as e:
print(f"Error creating batch: {e}")
return None
complex_batch_id = create_complex_batch()
print(f"Complex batch ID: {complex_batch_id}")
```
::: {.output .stream .stdout}
Complex batch ID: msgbatch_011FAkvqkL8pEskdyS3xdmNW
:::
::::
::: {.cell .markdown}
Great now let\'s view the results of the batch:
:::
:::: {.cell .code execution_count="64"}
``` python
# Example usage:
batch_status = monitor_batch(complex_batch_id)
if batch_status.processing_status == "ended":
process_results(batch_status.id)
```
::: {.output .stream .stdout}
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=None, expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='in_progress', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=4, succeeded=0), results_url=None, type='message_batch')
Status: in_progress
BetaMessageBatch(id='msgbatch_011FAkvqkL8pEskdyS3xdmNW', cancel_initiated_at=None, created_at=datetime.datetime(2024, 10, 8, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), ended_at=datetime.datetime(2024, 10, 8, 0, 24, 27, 768229, tzinfo=TzInfo(UTC)), expires_at=datetime.datetime(2024, 10, 9, 0, 23, 58, 507550, tzinfo=datetime.timezone.utc), processing_status='ended', request_counts=RequestCounts(canceled=0, errored=0, expired=0, processing=0, succeeded=4), results_url='https://api.anthropic.com/v1/messages/batches/msgbatch_011FAkvqkL8pEskdyS3xdmNW/results', type='message_batch')
Status: ended
Batch msgbatch_011FAkvqkL8pEskdyS3xdmNW Summary:
Status: ended
Created: 2024-10-08 00:23:58.507550+00:00
Ended: 2024-10-08 00:24:27.768229+00:00
Expires: 2024-10-09 00:23:58.507550+00:00
Individual Results:
Result for simple-question:
Status: succeeded
Content: Quantum computing is an advanced form of computing that uses the principles of quantum mechanics to process information. Unlike classical computers that use bits (0s and 1s) to store and process data,...
Result for image-analysis:
Status: succeeded
Content: This image captures a breathtaking mountain landscape at sunset. The sun is visible as a bright orb just dipping behind the distant mountain ranges, casting a warm golden glow across the entire scene....
Result for system-prompt:
Status: succeeded
Content: Sure! Here's how I might explain gravity to a 5-year-old:
Gravity is like a big invisible hug that the Earth gives to everything on it. It's what keeps us stuck to the ground instead of floating away...
Result for multi-turn:
Status: succeeded
Content: DNA replication is the process by which DNA makes a copy of itself during cell division. Here's a basic overview of how it works:
1. Unwinding: The double helix structure of DNA unwinds, and the two ...
:::
::::
---
## Uploading" PDFs to Claude Via the API
Source: https://github.com/anthropics/claude-cookbooks/blob/main/misc/pdf_upload_summarization.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-misc-pdf-upload-summarization.md
# \"Uploading\" PDFs to Claude Via the API
:::
::: {.cell .markdown id="pOwrbbrD_Bmn"}
One really nice feature of [Claude.ai](https://www.claude.ai) is the
ability to upload PDFs. Let\'s mock up that feature in a notebook, and
then test it out by summarizing a long PDF.
:::
::: {.cell .markdown id="GTQO6z2-ELFX"}
We\'ll start by installing the Anthropic client and create an instance
of it we will use throughout the notebook.
:::
::: {.cell .code colab="{\"base_uri\":\"https://localhost:8080/\"}" id="pOEGPdoXEFsT" outputId="b718c166-6d64-4757-bd8b-259b7775a09c" vscode="{\"languageId\":\"python\"}"}
``` coconut
%pip install anthropic
```
:::
::: {.cell .code execution_count="24" id="-4bSTHWq_Bmp" vscode="{\"languageId\":\"python\"}"}
``` coconut
from anthropic import Anthropic
# While PDF support is in beta, you must pass in the correct beta header
client = Anthropic(default_headers={
"anthropic-beta": "pdfs-2024-09-25"
}
)
# For now, only claude-sonnet-4-5 supports PDFs
MODEL_NAME = "claude-sonnet-4-5"
```
:::
::: {.cell .markdown id="xrDg6fb5_Bmo"}
We already have a PDF available in the `../multimodal/documents`
directory. We\'ll convert the PDF file into base64 encoded bytes. This
is the format required for the [PDF document
block](https://docs.claude.com/en/docs/build-with-claude/pdf-support) in
the Claude API. Note that this type of extraction works for both text
and visual elements (like charts and graphs).
:::
::: {.cell .code id="VznQXTKm_Bmp" vscode="{\"languageId\":\"python\"}"}
``` coconut
import base64
# Start by reading in the PDF and encoding it as base64
file_name = "../multimodal/documents/constitutional-ai-paper.pdf"
with open(file_name, "rb") as pdf_file:
binary_data = pdf_file.read()
base64_encoded_data = base64.standard_b64encode(binary_data)
base64_string = base64_encoded_data.decode("utf-8")
```
:::
::: {.cell .markdown id="FO5EGbpn_Bmp"}
With the paper downloaded and in memory, we can ask Claude to perform
various fun tasks with it. We\'ll pass the document ot the model
alongside a simple question.
:::
::: {.cell .code execution_count="32" id="ZHgYhs6eDXLc" vscode="{\"languageId\":\"python\"}"}
``` coconut
prompt = """
Please do the following:
1. Summarize the abstract at a kindergarten reading level. (In <kindergarten_abstract> tags.)
2. Write the Methods section as a recipe from the Moosewood Cookbook. (In <moosewood_methods> tags.)
3. Compose a short poem epistolizing the results in the style of Homer. (In <homer_results> tags.)
"""
messages = [
{
"role": 'user',
"content": [
{"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": base64_string}},
{"type": "text", "text": prompt}
]
}
]
```
:::
::: {.cell .code execution_count="21" id="qrGN7vB2_Bmq" vscode="{\"languageId\":\"python\"}"}
``` coconut
def get_completion(client, messages):
return client.messages.create(
model=MODEL_NAME,
max_tokens=2048,
messages=messages
).content[0].text
```
:::
:::: {.cell .code execution_count="33" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="oWQcC1dn_Bmq" outputId="9c741990-499c-4fed-cea1-08ad1bb9daff" vscode="{\"languageId\":\"python\"}"}
``` coconut
completion = get_completion(client, messages)
print(completion)
```
::: {.output .stream .stdout}
<kindergarten_abstract>
The scientists wanted to make computer helpers that are nice and don't do bad things. They taught the computer how to check its own work and fix its mistakes without humans having to tell it what's wrong every time. It's like teaching the computer to be its own teacher! They gave the computer some basic rules to follow, like "be kind" and "don't hurt others." Now the computer can answer questions in a helpful way while still being nice and explaining why some things aren't okay to do.
</kindergarten_abstract>
<moosewood_methods>
Constitutional AI Training Stew
A nourishing recipe for teaching computers to be helpful and harmless
Ingredients:
- 1 helpful AI model, pre-trained
- A bundle of constitutional principles
- Several cups of training data
- A dash of human feedback (for helpfulness only)
- Chain-of-thought reasoning, to taste
Method:
1. Begin by gently simmering your pre-trained AI model in a bath of helpful training data until it responds reliably to instructions.
2. In a separate bowl, combine your constitutional principles with some example conversations. Mix well until principles are evenly distributed.
3. Take your helpful AI and ask it to generate responses to challenging prompts. Have it critique its own responses using the constitutional principles, then revise accordingly. Repeat this process 3-4 times until responses are properly seasoned with harmlessness.
4. For the final garnish, add chain-of-thought reasoning and allow the model to explain its decisions step by step.
5. Let rest while training a preference model using AI feedback rather than human labels.
Serves: All users seeking helpful and harmless AI assistance
Cook time: Multiple training epochs
Note: Best results come from consistent application of principles throughout the process
</moosewood_methods>
<homer_results>
O Muse! Sing of the AI that learned to be
Both helpful and harmless, guided by philosophy
Without human labels marking right from wrong
The model learned wisdom, grew capable and strong
Through cycles of critique and thoughtful revision
It mastered the art of ethical decision
Better than models trained by human hand
More transparent in purpose, more clear in command
No longer evasive when faced with hard themes
But engaging with wisdom that thoughtfully deems
What counsel to give, what bounds to maintain
Teaching mortals while keeping its principles plain
Thus did the researchers discover a way
To scale up alignment for use every day
Through constitutional rules and self-guided learning
The path to safe AI they found themselves earning
</homer_results>
:::
::::
---
## Prompt caching through the Claude API
Source: https://github.com/anthropics/claude-cookbooks/blob/main/misc/prompt_caching.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-misc-prompt-caching.md
# Prompt caching through the Claude API
Prompt caching allows you to store and reuse context within your prompt.
This makes it more practical to include additional information in your
prompt---such as detailed instructions and example responses---which
help improve every response Claude generates.
In addition, by fully leveraging prompt caching within your prompt, you
can reduce latency by \>2x and costs up to 90%. This can generate
significant savings when building solutions that involve repetitive
tasks around detailed book_content.
In this cookbook, we will demonstrate how to use prompt caching in a
single turn and across a multi-turn conversation.
:::
::: {.cell .markdown vscode="{\"languageId\":\"plaintext\"}"}
## Setup
First, let\'s set up our environment with the necessary imports and
initializations:
:::
:::: {.cell .code execution_count="3"}
``` python
%pip install anthropic bs4 --quiet
```
::: {.output .stream .stdout}
Note: you may need to restart the kernel to use updated packages.
:::
::::
::: {.cell .code execution_count="16"}
``` python
import time
import anthropic
import requests
from bs4 import BeautifulSoup
client = anthropic.Anthropic()
MODEL_NAME = "claude-sonnet-4-5"
TIMESTAMP = int(time.time())
```
:::
::: {.cell .markdown}
Now let\'s fetch some text content to use in our examples. We\'ll use
the text from Pride and Prejudice by Jane Austen which is around
\~187,000 tokens long.
:::
::: {.cell .code}
``` python
def fetch_article_content(url):
response = requests.get(url, timeout=30)
soup = BeautifulSoup(response.content, "html.parser")
# Remove script and style elements
for script in soup(["script", "style"]):
script.decompose()
# Get text
text = soup.get_text()
# Break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# Break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
# Drop blank lines
text = "\n".join(chunk for chunk in chunks if chunk)
return text
# Fetch the content of the article
book_url = "https://www.gutenberg.org/cache/epub/1342/pg1342.txt"
book_content = fetch_article_content(book_url)
print(f"Fetched {len(book_content)} characters from the book.")
print("First 500 characters:")
print(book_content[:500])
```
:::
::: {.cell .markdown}
## Example 1: Single turn
Let\'s demonstrate prompt caching with a large document, comparing the
performance and cost between cached and non-cached API calls.
:::
::: {.cell .markdown}
### Part 1: Non-cached API Call (Baseline)
First, let\'s make a truly non-cached API call **without** the
`cache_control` parameter. This will establish our baseline performance.
We\'ll ask for a short output to keep response generation time low,
since prompt caching only affects input processing time.
:::
:::: {.cell .code execution_count="17"}
``` python
def make_non_cached_api_call():
"""Make an API call WITHOUT cache_control - no caching enabled."""
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": str(TIMESTAMP) + "<book>" + book_content + "</book>",
# Note: No cache_control parameter here - this is truly non-cached
},
{"type": "text", "text": "What is the title of this book? Only output the title."},
],
}
]
start_time = time.time()
response = client.messages.create(
model=MODEL_NAME,
max_tokens=300,
messages=messages,
)
end_time = time.time()
return response, end_time - start_time
non_cached_response, non_cached_time = make_non_cached_api_call()
print(f"Non-cached API call time: {non_cached_time:.2f} seconds")
print(f"Input tokens: {non_cached_response.usage.input_tokens}")
print(f"Output tokens: {non_cached_response.usage.output_tokens}")
print("\nResponse:")
print(non_cached_response.content[0].text)
```
::: {.output .stream .stdout}
Non-cached API call time: 6.86 seconds
Input tokens: 187363
Output tokens: 8
Response:
Pride and Prejudice
:::
::::
::: {.cell .markdown}
### Part 2: First Cached API Call (Cache Creation)
Now let\'s enable prompt caching by adding
`cache_control: {"type": "ephemeral"}` to the book content.
**Important:** The first call with `cache_control` will **create** the
cache entry. This initial call will have similar timing to the
non-cached call because it still needs to process all tokens. However,
it will store them in the cache for future use.
Look for the `cache_creation_input_tokens` field in the usage stats to
see how many tokens were cached.
:::
:::: {.cell .code execution_count="18"}
``` python
def make_cached_api_call_create():
"""First call WITH cache_control - creates the cache entry."""
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": str(TIMESTAMP) + "<book>" + book_content + "</book>",
"cache_control": {"type": "ephemeral"}, # This enables caching
},
{"type": "text", "text": "What is the title of this book? Only output the title."},
],
}
]
start_time = time.time()
response = client.messages.create(
model=MODEL_NAME,
max_tokens=300,
messages=messages,
)
end_time = time.time()
return response, end_time - start_time
cached_create_response, cached_create_time = make_cached_api_call_create()
print(f"First cached API call time: {cached_create_time:.2f} seconds")
print(f"Input tokens: {cached_create_response.usage.input_tokens}")
print(f"Output tokens: {cached_create_response.usage.output_tokens}")
print(
f"Cache creation tokens: {getattr(cached_create_response.usage, 'cache_creation_input_tokens', 0)}"
)
print("\nResponse:")
print(cached_create_response.content[0].text)
print(
"\nNote: This first call creates the cache but doesn't benefit from it yet - timing is similar to non-cached call."
)
```
::: {.output .stream .stdout}
First cached API call time: 5.96 seconds
Input tokens: 16
Output tokens: 8
Cache creation tokens: 187347
Response:
Pride and Prejudice
Note: This first call creates the cache but doesn't benefit from it yet - timing is similar to non-cached call.
:::
::::
::: {.cell .markdown}
### Part 3: Second Cached API Call (Cache Hit)
Now let\'s make another API call with the same `cache_control`
parameter. Since the cache was created in Part 2, this call will **read
from the cache** instead of processing all tokens again.
This is where you see the real performance benefit! Look for the
`cache_read_input_tokens` field in the usage stats.
:::
:::: {.cell .code execution_count="19"}
``` python
def make_cached_api_call_hit():
"""Second call WITH cache_control - reads from existing cache."""
messages = [
{
"role": "user",
"content": [
{
"type": "text",
"text": str(TIMESTAMP) + "<book>" + book_content + "</book>",
"cache_control": {"type": "ephemeral"}, # Same cache_control as before
},
{"type": "text", "text": "What is the title of this book? Only output the title."},
],
}
]
start_time = time.time()
response = client.messages.create(
model=MODEL_NAME,
max_tokens=300,
messages=messages,
)
end_time = time.time()
return response, end_time - start_time
cached_hit_response, cached_hit_time = make_cached_api_call_hit()
print(f"Second cached API call time: {cached_hit_time:.2f} seconds")
print(f"Input tokens: {cached_hit_response.usage.input_tokens}")
print(f"Output tokens: {cached_hit_response.usage.output_tokens}")
print(f"Cache read tokens: {getattr(cached_hit_response.usage, 'cache_read_input_tokens', 0)}")
print("\nResponse:")
print(cached_hit_response.content[0].text)
print("\n" + "=" * 70)
print("PERFORMANCE COMPARISON")
print("=" * 70)
print(f"Non-cached call: {non_cached_time:.2f}s")
print(f"First cached call: {cached_create_time:.2f}s (creates cache)")
print(f"Second cached call: {cached_hit_time:.2f}s (reads from cache)")
print(f"\nSpeedup from caching: {non_cached_time / cached_hit_time:.1f}x faster!")
print("=" * 70)
```
::: {.output .stream .stdout}
Second cached API call time: 3.66 seconds
Input tokens: 16
Output tokens: 8
Cache read tokens: 187347
Response:
Pride and Prejudice
======================================================================
PERFORMANCE COMPARISON
======================================================================
Non-cached call: 6.86s
First cached call: 5.96s (creates cache)
Second cached call: 3.66s (reads from cache)
Speedup from caching: 1.9x faster!
======================================================================
:::
::::
::: {.cell .markdown}
## Summary of Example 1
This example demonstrated three distinct scenarios:
1. **Non-cached call** - Without `cache_control`, Claude processes all
\~187k tokens normally
2. **First cached call** - With `cache_control`, Claude processes all
tokens AND stores them in cache (similar timing to non-cached)
3. **Second cached call** - With `cache_control`, Claude reads from the
existing cache (2-10x faster!)
The key insight: **Prompt caching requires two calls to show benefits**
- The first call with `cache_control` creates the cache entry
- Subsequent calls with the same `cache_control` read from the cache for
dramatic speedups
This is especially valuable for:
- Large documents or codebases that remain constant across multiple
queries
- System prompts with detailed instructions
- Multi-turn conversations (as shown in Example 2 below)
:::
::: {.cell .markdown}
## Example 2: Multi-turn Conversation with Incremental Caching
Now, let\'s look at a multi-turn conversation where we add cache
breakpoints as the conversation progresses.
:::
:::: {.cell .code execution_count="21"}
``` python
class ConversationHistory:
def __init__(self):
# Initialize an empty list to store conversation turns
self.turns = []
def add_turn_assistant(self, content):
# Add an assistant's turn to the conversation history
self.turns.append({"role": "assistant", "content": [{"type": "text", "text": content}]})
def add_turn_user(self, content):
# Add a user's turn to the conversation history
self.turns.append({"role": "user", "content": [{"type": "text", "text": content}]})
def get_turns(self):
# Retrieve conversation turns with specific formatting
result = []
user_turns_processed = 0
# Iterate through turns in reverse order
for turn in reversed(self.turns):
if turn["role"] == "user" and user_turns_processed < 1:
# Add the last user turn with ephemeral cache control
result.append(
{
"role": "user",
"content": [
{
"type": "text",
"text": turn["content"][0]["text"],
"cache_control": {"type": "ephemeral"},
}
],
}
)
user_turns_processed += 1
else:
# Add other turns as they are
result.append(turn)
# Return the turns in the original order
return list(reversed(result))
# Initialize the conversation history
conversation_history = ConversationHistory()
# System message containing the book content
# Note: 'book_content' should be defined elsewhere in the code
system_message = f"{TIMESTAMP} <file_contents> {book_content} </file_contents>"
# Predefined questions for our simulation
questions = [
"What is the title of this novel?",
"Who are Mr. and Mrs. Bennet?",
"What is Netherfield Park?",
"What is the main theme of this novel?",
]
def simulate_conversation():
for i, question in enumerate(questions, 1):
print(f"\nTurn {i}:")
print(f"User: {question}")
# Add user input to conversation history
conversation_history.add_turn_user(question)
# Record the start time for performance measurement
start_time = time.time()
# Make an API call to the assistant
response = client.messages.create(
model=MODEL_NAME,
max_tokens=300,
system=[
{"type": "text", "text": system_message, "cache_control": {"type": "ephemeral"}},
],
messages=conversation_history.get_turns(),
)
# Record the end time
end_time = time.time()
# Extract the assistant's reply
assistant_reply = response.content[0].text
print(f"Assistant: {assistant_reply}")
# Print token usage information
input_tokens = response.usage.input_tokens
output_tokens = response.usage.output_tokens
input_tokens_cache_read = getattr(response.usage, "cache_read_input_tokens", "---")
input_tokens_cache_create = getattr(response.usage, "cache_creation_input_tokens", "---")
print(f"User input tokens: {input_tokens}")
print(f"Output tokens: {output_tokens}")
print(f"Input tokens (cache read): {input_tokens_cache_read}")
print(f"Input tokens (cache write): {input_tokens_cache_create}")
# Calculate and print the elapsed time
elapsed_time = end_time - start_time
# Calculate the percentage of input prompt cached
total_input_tokens = input_tokens + (
int(input_tokens_cache_read) if input_tokens_cache_read != "---" else 0
)
percentage_cached = (
int(input_tokens_cache_read) / total_input_tokens * 100
if input_tokens_cache_read != "---" and total_input_tokens > 0
else 0
)
print(f"{percentage_cached:.1f}% of input prompt cached ({total_input_tokens} tokens)")
print(f"Time taken: {elapsed_time:.2f} seconds")
# Add assistant's reply to conversation history
conversation_history.add_turn_assistant(assistant_reply)
# Run the simulated conversation
simulate_conversation()
```
::: {.output .stream .stdout}
Turn 1:
User: What is the title of this novel?
Assistant: The title of this novel is **Pride and Prejudice** by Jane Austen.
User input tokens: 3
Output tokens: 22
Input tokens (cache read): 0
Input tokens (cache write): 187360
0.0% of input prompt cached (3 tokens)
Time taken: 5.79 seconds
Turn 2:
User: Who are Mr. and Mrs. Bennet?
Assistant: Mr. and Mrs. Bennet are the parents of five daughters: Jane, Elizabeth, Mary, Kitty, and Lydia.
**Mr. Bennet** is described as an intelligent, sarcastic man with "quick parts, sarcastic humour, reserve, and caprice." He tends to be detached and ironic, often amusing himself at his wife's expense, and prefers to spend time in his library rather than deal with family matters.
**Mrs. Bennet** is described as "a woman of mean understanding, little information, and uncertain temper." She is nervous, excitable, and foolish, with her main goal in life being to get her daughters married. She lacks the intelligence and social graces of her husband and is often oblivious to her own impropriety.
Their contrasting personalities create much of the domestic tension and comedy in the novel. Mr. Bennet married Mrs. Bennet when he was "captivated by youth and beauty," but her weak understanding soon ended any real affection he had for her, leaving him to cope with his disappointment through ironic detachment.
User input tokens: 3
Output tokens: 247
Input tokens (cache read): 187360
Input tokens (cache write): 36
100.0% of input prompt cached (187363 tokens)
Time taken: 8.29 seconds
Turn 3:
User: What is Netherfield Park?
Assistant: **Netherfield Park** is a large estate in Hertfordshire that is rented by Mr. Bingley at the beginning of the novel.
It is located about three miles from Longbourn, the Bennet family's home, making it conveniently close for social visits. The estate becomes the center of much excitement and speculation when Mr. Bingley, a wealthy young bachelor, takes up residence there.
Key points about Netherfield:
- It's described as a good house with pleasant grounds
- Mr. Bingley rents it (rather than owning it), as he has not yet purchased an estate of his own
- His sisters, Caroline Bingley and Mrs. Hurst, live with him there, along with Mrs. Hurst's husband
- Mr. Darcy, Bingley's close friend, is a frequent visitor
- The famous ball where Elizabeth and Darcy have their first significant interactions takes place at Netherfield
- Jane Bennet stays there when she falls ill after riding over in the rain, which allows Elizabeth to visit and spend time at the estate
Netherfield Park is important to the plot as it brings the wealthy Mr. Bingley (and Mr. Darcy) into the neighborhood, setting the main romantic storylines in motion.
User input tokens: 3
Output tokens: 293
Input tokens (cache read): 187396
Input tokens (cache write): 258
100.0% of input prompt cached (187399 tokens)
Time taken: 10.14 seconds
Turn 4:
User: What is the main theme of this novel?
Assistant: The main themes of **Pride and Prejudice** include:
**1. Pride and Prejudice (as the title suggests)**
- The novel explores how pride and prejudice create misunderstandings and obstacles to happiness
- **Darcy's pride** in his social status initially leads him to insult Elizabeth and look down on her family
- **Elizabeth's prejudice** against Darcy (based on first impressions and Wickham's lies) blinds her to his true character
- Both must overcome these flaws to find happiness together
**2. Class and Social Status**
- The rigid class distinctions of Regency England and their impact on relationships and marriage prospects
- The tension between wealth, birth, and merit as measures of a person's worth
- Lady Catherine's objections to Elizabeth based on her "inferior" connections
**3. Marriage and Economics**
- The novel examines different motivations for marriage: love (Jane and Bingley), practicality (Charlotte and Mr. Collins), lust and recklessness (Lydia and Wickham), and the ideal combination of respect, affection, and compatibility (Elizabeth and Darcy)
- The economic pressures on women to marry well, especially with the entailment of the Bennet estate
**4. Self-Knowledge and Personal Growth**
- Elizabeth's journey from prejudice to understanding
User input tokens: 3
Output tokens: 300
Input tokens (cache read): 187654
Input tokens (cache write): 305
100.0% of input prompt cached (187657 tokens)
Time taken: 9.53 seconds
:::
::::
::: {.cell .markdown}
As you can see in this example, response times decreased from nearly 24
seconds to just 7-11 seconds after the initial cache setup, while
maintaining the same level of quality across the answers. Most of this
remaining latency is due to the time it takes to generate the response,
which is not affected by prompt caching.
And since nearly 100% of input tokens were cached in subsequent turns as
we kept adjusting the cache breakpoints, we were able to read the next
user message nearly instantly.
:::
---
## Usage & Cost Admin API Cookbook
Source: https://github.com/anthropics/claude-cookbooks/blob/main/observability/usage_cost_api.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-observability-usage-cost-api.md
::: {#9610be80 .cell .markdown id="9610be80"}
# Usage & Cost Admin API Cookbook {#usage--cost-admin-api-cookbook}
**A practical guide to programmatically accessing your Claude API usage
and cost data**
:::
::: {#69b800fc .cell .markdown id="69b800fc"}
### What You Can Do
**Usage Tracking:**
- Monitor token consumption (uncached input, output, cache
creation/reads)
- Track usage across models, workspaces, and API keys
- Analyze cache efficiency and server tool usage
**Cost Analysis:**
- Retrieve detailed cost breakdowns by service type
- Monitor spending trends across workspaces
- Generate reports for finance and chargeback scenarios
**Common Use Cases:**
- **Usage Monitoring**: Track consumption patterns and optimize costs
- **Cost Attribution**: Allocate expenses across teams/projects by
workspace
- **Cache Analysis**: Measure and improve cache efficiency
- **Financial Reporting**: Generate executive summaries and budget
reports
### API Overview
Two main endpoints:
1. **Messages Usage API**: Token-level usage data with flexible
grouping
2. **Cost API**: Financial data in USD with service breakdowns
### Prerequisites & Security {#prerequisites--security}
- **Admin API Key**: Get from [Claude
Console](https://console.anthropic.com/settings/admin-keys) (format:
`sk-ant-admin...`)
- **Security**: Store keys in environment variables, rotate regularly,
never commit to version control
:::
::: {#edd50a16 .cell .code colab="{\"base_uri\":\"https://localhost:8080/\"}" id="edd50a16" outputId="68f38db3-48ee-429f-cd6e-2d2ae74e009a"}
``` python
import os
from datetime import datetime, time, timedelta
from typing import Any
import requests
class AnthropicAdminAPI:
"""Secure wrapper for Anthropic Admin API endpoints."""
def __init__(self, api_key: str | None = None):
self.api_key = api_key or os.getenv("ANTHROPIC_ADMIN_API_KEY")
if not self.api_key:
raise ValueError(
"Admin API key required. Set ANTHROPIC_ADMIN_API_KEY environment variable."
)
if not self.api_key.startswith("sk-ant-admin"):
raise ValueError("Invalid Admin API key format.")
self.base_url = "https://api.anthropic.com/v1/organizations"
self.headers = {
"anthropic-version": "2023-06-01",
"x-api-key": self.api_key,
"Content-Type": "application/json",
}
def _make_request(self, endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
"""Make authenticated request with basic error handling."""
url = f"{self.base_url}/{endpoint}"
try:
response = requests.get(url, headers=self.headers, params=params, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
if response.status_code == 401:
raise ValueError("Invalid API key or insufficient permissions") from e
elif response.status_code == 429:
raise requests.exceptions.RequestException(
"Rate limit exceeded - try again later"
) from e
else:
raise requests.exceptions.RequestException(f"API error: {e}") from e
# Test connection
def test_connection():
try:
client = AnthropicAdminAPI()
# Simple test query - snap to start of day to align with bucket boundaries
params = {
"starting_at": (
datetime.combine(datetime.utcnow(), time.min) - timedelta(days=1)
).strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": datetime.combine(datetime.utcnow(), time.min).strftime(
"%Y-%m-%dT%H:%M:%SZ"
),
"bucket_width": "1d",
"limit": 1,
}
client._make_request("usage_report/messages", params)
print("✅ Connection successful!")
return client
except Exception as e:
print(f"❌ Connection failed: {e}")
return None
client = test_connection()
```
:::
::: {#781a0721 .cell .markdown id="781a0721"}
## Basic Usage & Cost Tracking {#basic-usage--cost-tracking}
### Understanding Usage Data
The Messages Usage API provides token consumption in **time buckets** -
fixed intervals containing aggregated usage.
**Key Metrics:**
- **uncached_input_tokens**: New input tokens (prompts, system messages)
- **output_tokens**: Claude\'s responses
- **cache_creation**: Tokens cached for reuse
- **cache_read_input_tokens**: Previously cached tokens reused
### Basic Usage Query
:::
:::: {#f9b26143 .cell .code execution_count="12" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="f9b26143" outputId="c60b91c0-084d-4629-eddd-cf0fb941e042"}
``` python
def get_daily_usage(client, days_back=7):
"""Get usage data for the last N days."""
end_time = datetime.combine(datetime.utcnow(), time.min)
start_time = end_time - timedelta(days=days_back)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"bucket_width": "1d",
"limit": days_back,
}
return client._make_request("usage_report/messages", params)
def analyze_usage_data(response):
"""Process and display usage data."""
if not response or not response.get("data"):
print("No usage data found.")
return
total_uncached_input = total_output = total_cache_creation = 0
total_cache_reads = total_web_searches = 0
daily_data = []
for bucket in response["data"]:
date = bucket["starting_at"][:10]
# Sum all results in bucket
bucket_uncached = bucket_output = bucket_cache_creation = 0
bucket_cache_reads = bucket_web_searches = 0
for result in bucket["results"]:
bucket_uncached += result.get("uncached_input_tokens", 0)
bucket_output += result.get("output_tokens", 0)
cache_creation = result.get("cache_creation", {})
bucket_cache_creation += cache_creation.get(
"ephemeral_1h_input_tokens", 0
) + cache_creation.get("ephemeral_5m_input_tokens", 0)
bucket_cache_reads += result.get("cache_read_input_tokens", 0)
server_tools = result.get("server_tool_use", {})
bucket_web_searches += server_tools.get("web_search_requests", 0)
daily_data.append(
{
"date": date,
"uncached_input_tokens": bucket_uncached,
"output_tokens": bucket_output,
"cache_creation": bucket_cache_creation,
"cache_reads": bucket_cache_reads,
"web_searches": bucket_web_searches,
"total_tokens": bucket_uncached + bucket_output,
}
)
# Add to totals
total_uncached_input += bucket_uncached
total_output += bucket_output
total_cache_creation += bucket_cache_creation
total_cache_reads += bucket_cache_reads
total_web_searches += bucket_web_searches
# Calculate cache efficiency
total_input_tokens = total_uncached_input + total_cache_creation + total_cache_reads
cache_efficiency = (
(total_cache_reads / total_input_tokens * 100) if total_input_tokens > 0 else 0
)
# Display summary
print("📊 Usage Summary:")
print(f"Uncached input tokens: {total_uncached_input:,}")
print(f"Output tokens: {total_output:,}")
print(f"Cache creation: {total_cache_creation:,}")
print(f"Cache reads: {total_cache_reads:,}")
print(f"Cache efficiency: {cache_efficiency:.1f}%")
print(f"Web searches: {total_web_searches:,}")
return daily_data
# Example usage
if client:
usage_response = get_daily_usage(client, days_back=7)
daily_usage = analyze_usage_data(usage_response)
```
::: {.output .stream .stdout}
📊 Usage Summary:
Uncached input tokens: 267,751
Output tokens: 2,848,746
Cache creation: 0
Cache reads: 0
Cache efficiency: 0.0%
Web searches: 0
:::
::::
::: {#e3164089 .cell .markdown id="e3164089"}
## Basic Cost Tracking
Note: Priority Tier costs use a different billing model and will never
appear in the cost endpoint. You can track Priority Tier usage in the
usage endpoint, but not costs.
:::
:::: {#SP4zefdtF0ft .cell .code execution_count="13" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="SP4zefdtF0ft" outputId="a1daa8d0-f36f-474b-9967-6fe00a0efe52"}
``` python
def get_daily_costs(client, days_back=7):
"""Get cost data for the last N days."""
end_time = datetime.combine(datetime.utcnow(), time.min)
start_time = end_time - timedelta(days=days_back)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"bucket_width": "1d", # Only 1d supported for cost API
"limit": min(days_back, 31), # Max 31 days per request
}
return client._make_request("cost_report", params)
def analyze_cost_data(response):
"""Process and display cost data."""
if not response or not response.get("data"):
print("No cost data found.")
return
total_cost_minor_units = 0
daily_costs = []
for bucket in response["data"]:
date = bucket["starting_at"][:10]
# Sum all costs in this bucket
bucket_cost = 0
for result in bucket["results"]:
# Convert string amounts to float if needed
amount = result.get("amount", 0)
if isinstance(amount, str):
try:
amount = float(amount)
except (ValueError, TypeError):
amount = 0
bucket_cost += amount
daily_costs.append(
{
"date": date,
"cost_minor_units": bucket_cost,
"cost_usd": bucket_cost / 100, # Convert to dollars
}
)
total_cost_minor_units += bucket_cost
total_cost_usd = total_cost_minor_units / 100
print("💰 Cost Summary:")
print(f"Total cost: ${total_cost_usd:.4f}")
print(f"Average daily cost: ${total_cost_usd / len(daily_costs):.4f}")
return daily_costs
# Example usage
if client:
cost_response = get_daily_costs(client, days_back=7)
daily_costs = analyze_cost_data(cost_response)
```
::: {.output .stream .stdout}
💰 Cost Summary:
Total cost: $83.7574
Average daily cost: $11.9653
:::
::::
::: {#6ce76fc4 .cell .markdown id="6ce76fc4"}
## Grouping, Filtering & Pagination {#grouping-filtering--pagination}
### Time Granularity Options
**Usage API** supports three granularities:
- `1m` (1 minute): High-resolution analysis, max 1440 buckets per
request
- `1h` (1 hour): Medium-resolution, max 168 buckets per request\
- `1d` (1 day): Daily analysis, max 31 buckets per request
**Cost API** supports:
- `1d` (1 day): Only option available, max 31 buckets per request
### Grouping and Filtering
:::
:::: {#87f1a7ac .cell .code execution_count="14" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="87f1a7ac" outputId="3266318c-8af4-4647-9360-b133ffa82452"}
``` python
def get_usage_by_model(client, days_back=7):
"""Get usage data grouped by model, handling pagination automatically."""
end_time = datetime.combine(datetime.utcnow(), time.min)
start_time = end_time - timedelta(days=days_back)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"group_by[]": ["model"],
"bucket_width": "1d",
}
# Aggregate across all pages of data
model_usage = {}
page_count = 0
max_pages = 10 # Reasonable limit to avoid infinite loops
try:
next_page = None
while page_count < max_pages:
current_params = params.copy()
if next_page:
current_params["page"] = next_page
response = client._make_request("usage_report/messages", current_params)
page_count += 1
# Process this page's data
for bucket in response.get("data", []):
for result in bucket.get("results", []):
model = result.get("model", "Unknown")
uncached = result.get("uncached_input_tokens", 0)
output = result.get("output_tokens", 0)
cache_creation = result.get("cache_creation", {})
cache_creation_tokens = cache_creation.get(
"ephemeral_1h_input_tokens", 0
) + cache_creation.get("ephemeral_5m_input_tokens", 0)
cache_reads = result.get("cache_read_input_tokens", 0)
tokens = uncached + output + cache_creation_tokens + cache_reads
if model not in model_usage:
model_usage[model] = 0
model_usage[model] += tokens
# Check if there's more data
if not response.get("has_more", False):
break
next_page = response.get("next_page")
if not next_page:
break
except Exception as e:
print(f"❌ Error retrieving usage data: {e}")
return {}
# Display results
print("📊 Usage by Model:")
if not model_usage:
print(f" No usage data found in the last {days_back} days")
print(" 💡 Try increasing the time range or check if you have recent API usage")
else:
for model, tokens in sorted(model_usage.items(), key=lambda x: x[1], reverse=True):
print(f" {model}: {tokens:,} tokens")
return model_usage
def filter_usage_example(client):
"""Example of filtering usage data."""
params = {
"starting_at": (datetime.combine(datetime.utcnow(), time.min) - timedelta(days=7)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
),
"ending_at": datetime.combine(datetime.utcnow(), time.min).strftime("%Y-%m-%dT%H:%M:%SZ"),
"models[]": ["claude-sonnet-4-5"], # Filter to specific model
"service_tiers[]": ["standard"], # Filter to standard tier
"bucket_width": "1d",
}
response = client._make_request("usage_report/messages", params)
print(f"Found {len(response.get('data', []))} days of filtered usage data")
return response
# Example usage
if client:
model_usage = get_usage_by_model(client, days_back=14)
filtered_usage = filter_usage_example(client)
```
::: {.output .stream .stdout}
📊 Usage by Model:
claude-3-5-haiku-20241022: 995,781 tokens
claude-sonnet-4-5: 861,880 tokens
claude-opus-4-1: 394,646 tokens
claude-sonnet-4-5: 356,766 tokens
claude-opus-4-20250514: 308,223 tokens
claude-opus-4-1: 199,201 tokens
Found 7 days of filtered usage data
:::
::::
::: {#487bc11d .cell .markdown id="487bc11d"}
### Pagination for Large Datasets
:::
:::: {#d7cc5437 .cell .code execution_count="18" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="d7cc5437" outputId="e7875fb4-09ae-44d2-f5fc-12a4cbdcce5a"}
``` python
def fetch_all_usage_data(client, params, max_pages=10):
"""Fetch all paginated usage data."""
all_data = []
page_count = 0
next_page = None
print("📥 Fetching paginated data...")
while page_count < max_pages:
current_params = params.copy()
if next_page:
current_params["page"] = next_page
try:
response = client._make_request("usage_report/messages", current_params)
if not response or not response.get("data"):
break
page_data = response["data"]
all_data.extend(page_data)
page_count += 1
print(f" Page {page_count}: {len(page_data)} time buckets")
if not response.get("has_more", False):
print(f"✅ Complete: Retrieved all data in {page_count} pages")
break
next_page = response.get("next_page")
if not next_page:
break
except Exception as e:
print(f"❌ Error on page {page_count + 1}: {e}")
break
print(f"📊 Total retrieved: {len(all_data)} time buckets")
return all_data
def large_dataset_example(client, days_back=3):
"""Example of handling a large dataset with pagination."""
# Use recent time range to ensure we have data
start_time = datetime.combine(datetime.utcnow(), time.min) - timedelta(days=days_back)
end_time = datetime.combine(datetime.utcnow(), time.min)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"bucket_width": "1h", # Hourly data for more buckets
"group_by[]": ["model"],
"limit": 24, # One day per page
}
all_buckets = fetch_all_usage_data(client, params, max_pages=5)
# Process the large dataset
if all_buckets:
total_tokens = sum(
sum(
result.get("uncached_input_tokens", 0) + result.get("output_tokens", 0)
for result in bucket["results"]
)
for bucket in all_buckets
)
print(f"📈 Total tokens across all data: {total_tokens:,}")
return all_buckets
# Example usage - use shorter time range to find recent data
if client:
large_dataset = large_dataset_example(client, days_back=3)
```
::: {.output .stream .stdout}
📥 Fetching paginated data...
Page 1: 24 time buckets
Page 2: 24 time buckets
Page 3: 24 time buckets
✅ Complete: Retrieved all data in 3 pages
📊 Total retrieved: 72 time buckets
📈 Total tokens across all data: 1,336,287
:::
::::
::: {#d55bbb55 .cell .markdown id="d55bbb55"}
## Simple Data Export
### CSV Export for External Analysis
:::
:::: {#f365296a .cell .code execution_count="16" colab="{\"base_uri\":\"https://localhost:8080/\"}" id="f365296a" outputId="87b4a733-6f90-4b8d-a109-7a23281b2a4d"}
``` python
import csv
def export_usage_to_csv(client, output_file="usage_data.csv", days_back=30):
"""Export usage data to CSV for external analysis."""
end_time = datetime.combine(datetime.utcnow(), time.min)
start_time = end_time - timedelta(days=days_back)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"group_by[]": ["model", "service_tier", "workspace_id"],
"bucket_width": "1d",
}
try:
# Collect all data across pages
rows = []
page_count = 0
max_pages = 20 # Allow more pages for export
next_page = None
while page_count < max_pages:
current_params = params.copy()
if next_page:
current_params["page"] = next_page
response = client._make_request("usage_report/messages", current_params)
page_count += 1
# Process this page's data
for bucket in response.get("data", []):
date = bucket["starting_at"][:10]
for result in bucket["results"]:
rows.append(
{
"date": date,
"model": result.get("model", ""),
"service_tier": result.get("service_tier", ""),
"workspace_id": result.get("workspace_id", ""),
"uncached_input_tokens": result.get("uncached_input_tokens", 0),
"output_tokens": result.get("output_tokens", 0),
"cache_creation_tokens": (
result.get("cache_creation", {}).get("ephemeral_1h_input_tokens", 0)
+ result.get("cache_creation", {}).get(
"ephemeral_5m_input_tokens", 0
)
),
"cache_read_tokens": result.get("cache_read_input_tokens", 0),
"web_search_requests": result.get("server_tool_use", {}).get(
"web_search_requests", 0
),
}
)
# Check if there's more data
if not response.get("has_more", False):
break
next_page = response.get("next_page")
if not next_page:
break
# Write CSV
if rows:
with open(output_file, "w", newline="") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
print(f"✅ Exported {len(rows)} rows to {output_file}")
else:
print(f"No usage data to export for the last {days_back} days")
print("💡 Try increasing days_back or check if you have recent API usage")
except Exception as e:
print(f"❌ Export failed: {e}")
def export_costs_to_csv(client, output_file="cost_data.csv", days_back=30):
"""Export cost data to CSV."""
end_time = datetime.combine(datetime.utcnow(), time.min)
start_time = end_time - timedelta(days=days_back)
params = {
"starting_at": start_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"ending_at": end_time.strftime("%Y-%m-%dT%H:%M:%SZ"),
"group_by[]": ["workspace_id", "description"],
}
try:
# Collect all data across pages
rows = []
page_count = 0
max_pages = 20
next_page = None
while page_count < max_pages:
current_params = params.copy()
if next_page:
current_params["page"] = next_page
response = client._make_request("cost_report", current_params)
page_count += 1
# Process this page's data
for bucket in response.get("data", []):
date = bucket["starting_at"][:10]
for result in bucket["results"]:
# Handle both string and numeric amounts
amount = result.get("amount", 0)
if isinstance(amount, str):
try:
amount = float(amount)
except (ValueError, TypeError):
amount = 0
rows.append(
{
"date": date,
"workspace_id": result.get(
"workspace_id", ""
), # null for default workspace
"description": result.get("description", ""),
"currency": result.get("currency", "USD"),
"amount_usd": amount / 100,
}
)
# Check if there's more data
if not response.get("has_more", False):
break
next_page = response.get("next_page")
if not next_page:
break
if rows:
with open(output_file, "w", newline="") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=rows[0].keys())
writer.writeheader()
writer.writerows(rows)
print(f"✅ Exported {len(rows)} cost records to {output_file}")
else:
print(f"No cost data to export for the last {days_back} days")
print("💡 Try increasing days_back or check if you have recent API usage")
except Exception as e:
print(f"❌ Cost export failed: {e}")
# Example usage
if client:
export_usage_to_csv(client, "my_usage_data.csv", days_back=14)
export_costs_to_csv(client, "my_cost_data.csv", days_back=14)
```
::: {.output .stream .stdout}
✅ Exported 36 rows to my_usage_data.csv
✅ Exported 72 cost records to my_cost_data.csv
:::
::::
::: {#5327a1c5 .cell .markdown id="5327a1c5"}
## Wrapping Up
This cookbook covers the essential patterns for working with the Usage &
Cost Admin API:
- **Basic queries** for usage and cost data
- **Grouping and filtering** for detailed analysis\
- **Pagination** for large datasets
- **Cost description parsing** for categorization
- **Common gotchas** to avoid issues
- **Simple CSV export** for external tools
### Next Steps
- Check the [official API documentation](https://docs.claude.com) for
the latest field definitions
- Test your integration with small date ranges first
- Consider data retention needs for your use case
- Monitor for new API features that may enhance your analysis
### Important Notes
- Field names and available options may evolve as the API matures
- Always handle unknown values gracefully in production code
- The API is designed for historical analysis, not real-time monitoring
- Priority Tier costs use a different billing model and don\'t appear in
cost endpoints
Happy analyzing! 📊
:::
---
## Using the Wolfram Alpha LLM API as a Tool with Claude
Source: https://github.com/anthropics/claude-cookbooks/blob/main/third_party/WolframAlpha/using_llm_api.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-third-party-wolframalpha-using-llm-api.md
# Using the Wolfram Alpha LLM API as a Tool with Claude
In this recipe, we\'ll show you how to integrate the Wolfram Alpha LLM
API as a tool for Claude to use. Claude will be able to send queries to
the Wolfram Alpha API and receive computed responses, which it can then
use to provide answers to user questions.
:::
::: {.cell .markdown}
## Step 1: Set up the environment
First, let\'s install the required libraries and set up the Claude API
client. We also will need to set our APP ID for using WolframAlpha. You
can sign up and create a new App ID for this project for free
[here](https://developer.wolframalpha.com/access).
:::
::: {.cell .code}
``` python
import json
import urllib.parse
import requests
from anthropic import Anthropic
client = Anthropic()
# Replace 'YOUR_APP_ID' with your actual Wolfram Alpha AppID
WOLFRAM_APP_ID = "YOUR_APP_ID"
MODEL_NAME = "claude-haiku-4-5"
```
:::
::: {.cell .markdown}
## Step 2: Define the Wolfram Alpha LLM API tool
We\'ll define a tool that allows Claude to send queries to the Wolfram
Alpha LLM API and receive the computed response.
:::
::: {.cell .code}
``` python
import urllib.parse
import requests
def wolfram_alpha_query(query):
# URL-encode the query
encoded_query = urllib.parse.quote(query)
# Make a request to the Wolfram Alpha LLM API
url = (
f"https://www.wolframalpha.com/api/v1/llm-api?input={encoded_query}&appid={WOLFRAM_APP_ID}"
)
response = requests.get(url, timeout=30)
if response.status_code == 200:
return response.text
else:
return f"Error: {response.status_code}: {response.text}"
tools = [
{
"name": "wolfram_alpha",
"description": "A tool that allows querying the Wolfram Alpha knowledge base. Useful for mathematical calculations, scientific data, and general knowledge questions.",
"input_schema": {
"type": "object",
"properties": {
"search_query": {
"type": "string",
"description": "The query to send to the Wolfram Alpha API.",
}
},
"required": ["query"],
},
}
]
```
:::
::: {.cell .markdown}
In this code, we define a wolfram_alpha_query function that takes a
query as input, URL-encodes it, and sends a request to the Wolfram Alpha
LLM API using the provided AppID. The function returns the computed
response from the API if the request is successful, or an error message
if there\'s an issue.
We then define the wolfram_alpha tool with an input schema that expects
a single query property of type string.
:::
::: {.cell .markdown}
## Step 3: Interact with Claude
Now, let\'s see how Claude can interact with the Wolfram Alpha tool to
answer user questions.
:::
::: {.cell .code execution_count="27"}
``` python
def process_tool_call(tool_name, tool_input):
if tool_name == "wolfram_alpha":
return wolfram_alpha_query(tool_input["search_query"])
def chat_with_claude(user_message):
print(f"\n{'=' * 50}\nUser Message: {user_message}\n{'=' * 50}")
prompt = f"""Here is a question: {user_message}. Please use the Wolfram Alpha tool to answer it. Do not reflect on the quality of the returned search results in your response."""
message = client.beta.tools.messages.create(
model=MODEL_NAME,
max_tokens=4096,
tools=tools,
messages=[{"role": "user", "content": prompt}],
)
print("\nInitial Response:")
print(f"Stop Reason: {message.stop_reason}")
print(f"Content: {message.content}")
if message.stop_reason == "tool_use":
tool_use = next(block for block in message.content if block.type == "tool_use")
tool_name = tool_use.name
tool_input = tool_use.input
print(f"\nTool Used: {tool_name}")
print("Tool Input:")
print(json.dumps(tool_input, indent=2))
tool_result = process_tool_call(tool_name, tool_input)
print("\nTool Result:")
print(str(json.dumps(tool_result, indent=2)))
response = client.beta.tools.messages.create(
model=MODEL_NAME,
max_tokens=2000,
tools=tools,
messages=[
{"role": "user", "content": prompt},
{"role": "assistant", "content": message.content},
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": str(tool_result),
}
],
},
],
)
print("\nResponse:")
print(f"Stop Reason: {response.stop_reason}")
print(f"Content: {response.content}")
else:
response = message
final_response = None
for block in response.content:
if hasattr(block, "text"):
final_response = block.text
break
print(f"\nFinal Response: {final_response}")
return final_response
```
:::
::: {.cell .markdown}
## Step 4: Try it out!
Let\'s try giving Claude a few example questions now that it has access
to Wolfram Alpha.
:::
:::: {.cell .code execution_count="28"}
``` python
# Example usage
print(chat_with_claude("What are the 5 largest countries in the world by population?"))
print(chat_with_claude("Calculate the square root of 1764."))
print(chat_with_claude("What is the distance between Earth and Mars?"))
```
::: {.output .stream .stdout}
==================================================
User Message: What are the 5 largest countries in the world by population?
==================================================
Initial Response:
Stop Reason: tool_use
Content: [ContentBlock(text='<thinking>\nThe query "What are the 5 largest countries in the world by population?" can be answered well using the wolfram_alpha tool, which has knowledge about countries and populations. The search_query parameter is the only required parameter, and the query text provided by the user can be used directly as the search_query value without any additional information needed. \n</thinking>', type='text'), ContentBlockToolUse(id='toolu_01VCQ5xAzMNdyXYsepbSAJLY', input={'search_query': 'What are the 5 largest countries in the world by population?'}, name='wolfram_alpha', type='tool_use')]
Tool Used: wolfram_alpha
Tool Input:
{
"search_query": "What are the 5 largest countries in the world by population?"
}
Tool Result:
"Query:\n\"What are the 5 largest countries in the world by population?\"\n\nInput interpretation:\n5 largest countries | by population\nin all countries, dependencies, and territories\n\nResult:\n1 | India | 1.43 billion people | \n2 | China | 1.43 billion people | \n3 | United States | 340 million people | \n4 | Indonesia | 278 million people | \n5 | Pakistan | 241 million people | \n\nNames:\n | full name | full native name\nIndia | Republic of India | Bh\u0101rat\u012bya Ga\u1e47ar\u0101jya\nChina | People's Republic of China | Zhonghua Renmin Gongheguo\nUnited States | United States of America | \nIndonesia | Republic of Indonesia | Republik Indonesia\nPakistan | Islamic Republic of Pakistan | Isl\u0101m\u012b Jumh\u016br\u012b-ye P\u0101kist\u0101n\n\nFlags:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP2444241bhh00e1he97fd0000383004g53722f9bd?MSPStoreType=image/png&s=14\nWolfram Language code: Dataset[EntityValue[{Entity[\"Country\", \"India\"], Entity[\"Country\", \"China\"], Entity[\"Country\", \"UnitedStates\"], Entity[\"Country\", \"Indonesia\"], Entity[\"Country\", \"Pakistan\"]}, EntityProperty[\"Country\", \"Flag\"], \"EntityAssociation\"]]\n\nLocations:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP2445241bhh00e1he97fd00002684a23aaahea47d?MSPStoreType=image/png&s=14\n\nGeographic properties:\ntotal area | total | 9.736 million mi^2 (square miles)\n | largest | 3.719 million mi^2 (square miles) (world rank: 3rd) (United States)\n | smallest | 307374 mi^2 (square miles) (world rank: 36th) (Pakistan)\nland area | total | 9.283 million mi^2 (square miles)\n | largest | 3.601 million mi^2 (square miles) (world rank: 2nd) (China)\n | smallest | 297637 mi^2 (square miles) (world rank: 36th) (Pakistan)\ncontinent | all | Asia | North America\n\nDemographics:\npopulation | total | 3.71 billion people\n | highest | 1.43 billion people (world rank: 1st) (2023 estimate) (India)\n | lowest | 241 million people (world rank: 5th) (2023 estimate) (Pakistan)\npopulation density | average | 390 people/mi^2\n | median | 385 people/mi^2\n | highest | 1226 people/mi^2 (world rank: 31st) (2021 estimate) (India)\n | lowest | 95.4 people/mi^2 (world rank: 183rd) (2021 estimate) (United States)\npopulation growth | average | 0.455 %/yr\n | median | 0.631 %/yr\n | highest | 1.83 %/yr (world rank: 56th) (2021 estimate) (Pakistan)\n | lowest | 0.004 %/yr (world rank: 188th) (2021 estimate) (China)\nlife expectancy | mean | 73 yr\n | median | 71.9 yr\n | highest | 78.9 yr (world rank: 66th) (2020 estimate) (United States)\n | lowest | 67.4 yr (world rank: 186th) (2020 estimate) (Pakistan)\nmedian age | median | 29.4 yr\n | highest | 37.9 yr (world rank: 70th) (2021 estimate) (China)\n | lowest | 20.2 yr (world rank: 191st) (2021 estimate) (Pakistan)\n\nCapital cities:\nIndia | New Delhi, Delhi\nChina | Beijing\nUnited States | Washington, District of Columbia\nIndonesia | Jakarta\nPakistan | Islamabad, F.C.T.\n\nEconomic properties:\nGDP | total | $48.51 trillion per year\n | median | $3.385 trillion per year\n | highest | $25.46 trillion per year (world rank: 1st) (2022 estimates) (United States)\n | lowest | $376.5 billion per year (world rank: 43rd) (2022 estimates) (Pakistan)\nGDP at parity | total | $73.22 trillion per year\n | median | $11.87 trillion per year\n | highest | $30.33 trillion per year (world rank: 1st) (2022 estimates) (China)\n | lowest | $1.518 trillion per year (world rank: 22nd) (2022 estimates) (Pakistan)\nreal GDP | total | $41.72 trillion per year\n | median | $2.955 trillion per year\n | highest | $20.95 trillion per year (world rank: 1st) (2022 estimates) (United States)\n | lowest | $362.2 billion per year (world rank: 37th) (2022 estimates) (Pakistan)\nGDP per capita | average | $13203 per year per person\n | median | $4788 per year per person\n | highest | $76399 per year per person (world rank: 12th) (2022 estimates) (United States)\n | lowest | $1597 per year per person (world rank: 197th) (2022 estimates) (Pakistan)\nGDP real growth | mean | +4.71% per year\n | median | +5.309% per year\n | highest | +7.003% per year (world rank: 38th) (2022 estimates) (India)\n | lowest | +2.062% per year (world rank: 157th) (2022 estimates) (United States)\nGini index | median | 0.371\n | highest | 0.398 (world rank: 102nd) (2018, 2020, 2021, and 2022 estimates) (United States)\n | lowest | 0.296 (world rank: 24th) (2018, 2020, 2021, and 2022 estimates) (Pakistan)\n | distribution | \nconsumer price inflation | mean | +8.15% per year\n | median | +6.7% per year\n | highest | +19.87% per year (world rank: 21st) (2022 estimates) (Pakistan)\n | lowest | +1.97% per year (world rank: 175th) (2022 estimates) (China)\n\nEmployment:\nunemployment rate | mean | 5.16%\n | median | 4.89%\n | highest | 7.33% (world rank: 74th highest) (2022 estimates) (India)\n | lowest | 3.55% (world rank: 162nd highest) (2022 estimates) (Indonesia)\nlong-term unemployment rate | mean | 1.57%\n | median | 1.42%\n | highest | 3.18% (world rank: 44th highest) (2010 and 2014 estimates) (India)\n | lowest | 0.127% (world rank: 111th highest) (2010 and 2014 estimates) (Pakistan)\nlabor force | highest | 781.8 million people (58.24% of population) (world rank: 1st) (2022 estimates) (China)\n | lowest | 78.91 million people (35.22% of population) (world rank: 6th) (2022 estimates) (Pakistan)\n | distribution | \neconomically active children | median | 3.7%\n | highest | 13% (world rank: 50th) (2010, 2011, and 2012 estimates) (Pakistan)\n | lowest | 1.7% (world rank: 90th) (2010, 2011, and 2012 estimates) (India)\n\nUN Human Development Index:\n | India | China | United States | Indonesia | Pakistan\nhealth | 0.743 (world rank: 131st) | 0.861 (world rank: 58th) | 0.911 (world rank: 36th) | 0.755 (world rank: 126th) | 0.713 (world rank: 137th)\neducation | 0.535 (world rank: 131st) | 0.631 (world rank: 108th) | 0.9 (world rank: 8th) | 0.622 (world rank: 112th) | 0.395 (world rank: 169th)\nliving standards | 0.61 (world rank: 126th) | 0.739 (world rank: 82nd) | 0.948 (world rank: 11th) | 0.696 (world rank: 103rd) | 0.592 (world rank: 133rd)\ntotal | 0.624 (world rank: 131st) | 0.738 (world rank: 90th) | 0.92 (world rank: 10th) | 0.689 (world rank: 113th) | 0.55 (world rank: 147th)\n(2015 estimate)\n\nWikipedia page hits history:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP2448241bhh00e1he97fd00006a6g1ee3gbhghh18?MSPStoreType=image/png&s=14\n\nWolfram|Alpha website result for \"What are the 5 largest countries in the world by population?\":\nhttps://www6b3.wolframalpha.com/input?i=What+are+the+5+largest+countries+in+the+world+by+population%3F"
Response:
Stop Reason: end_turn
Content: [ContentBlock(text='According to Wolfram Alpha, the 5 largest countries in the world by population are:\n\n1. India - 1.43 billion people\n2. China - 1.43 billion people \n3. United States - 340 million people\n4. Indonesia - 278 million people\n5. Pakistan - 241 million people\n\nTogether these 5 countries account for about 3.71 billion people. India and China have the largest populations, each with around 1.43 billion people. The United States has the 3rd largest population at 340 million. Indonesia and Pakistan round out the top 5 with 278 million and 241 million people respectively.', type='text')]
Final Response: According to Wolfram Alpha, the 5 largest countries in the world by population are:
1. India - 1.43 billion people
2. China - 1.43 billion people
3. United States - 340 million people
4. Indonesia - 278 million people
5. Pakistan - 241 million people
Together these 5 countries account for about 3.71 billion people. India and China have the largest populations, each with around 1.43 billion people. The United States has the 3rd largest population at 340 million. Indonesia and Pakistan round out the top 5 with 278 million and 241 million people respectively.
According to Wolfram Alpha, the 5 largest countries in the world by population are:
1. India - 1.43 billion people
2. China - 1.43 billion people
3. United States - 340 million people
4. Indonesia - 278 million people
5. Pakistan - 241 million people
Together these 5 countries account for about 3.71 billion people. India and China have the largest populations, each with around 1.43 billion people. The United States has the 3rd largest population at 340 million. Indonesia and Pakistan round out the top 5 with 278 million and 241 million people respectively.
==================================================
User Message: Calculate the square root of 1764.
==================================================
Initial Response:
Stop Reason: tool_use
Content: [ContentBlock(text='<thinking>\nThe relevant tool to use for this query is wolfram_alpha, since the question is asking for a mathematical calculation. The wolfram_alpha tool takes a single required parameter:\nsearch_query: The query to send to the Wolfram Alpha API\n\nThe user\'s request directly provides the search query needed, which is "square root of 1764". So all the required parameters are available to make the API call.\n</thinking>', type='text'), ContentBlockToolUse(id='toolu_011CkyJcwahqGaL513uFhdYJ', input={'search_query': 'square root of 1764'}, name='wolfram_alpha', type='tool_use')]
Tool Used: wolfram_alpha
Tool Input:
{
"search_query": "square root of 1764"
}
Tool Result:
"Query:\n\"square root of 1764\"\n\nInput:\nsqrt(1764)\n\nResult:\n42\n\nNumber line:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP191312396298479fhh7a0000212h885f5h3d6gig?MSPStoreType=image/png&s=4\nWolfram Language code: NumberLinePlot[42]\n\nNumber name:\nforty-two\n\nAll 2nd roots of 1764:\n42 e^0 = 42 (real, principal root)\n\n42 e^(i \u03c0) = -42 (real root)\n\nPlot of all roots in the complex plane:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP191412396298479fhh7a0000282bg11dc3e822ca?MSPStoreType=image/png&s=4\n\nWolfram|Alpha website result for \"square root of 1764\":\nhttps://www6b3.wolframalpha.com/input?i=square+root+of+1764"
Response:
Stop Reason: end_turn
Content: [ContentBlock(text='So the square root of 1764 is 42.', type='text')]
Final Response: So the square root of 1764 is 42.
So the square root of 1764 is 42.
==================================================
User Message: What is the distance between Earth and Mars?
==================================================
Initial Response:
Stop Reason: tool_use
Content: [ContentBlock(text='<thinking>\nThe wolfram_alpha tool is the relevant tool for answering this question about the distance between Earth and Mars, as it can provide scientific data like astronomical distances.\nThe search_query parameter can be filled in directly with the user\'s question "What is the distance between Earth and Mars?". No additional information is needed to fill the parameter.\nSince the search_query parameter is provided, the function can be called to answer the question.\n</thinking>', type='text'), ContentBlockToolUse(id='toolu_01PotM7Z1ooJ1PdK8QK1WgEu', input={'search_query': 'What is the distance between Earth and Mars?'}, name='wolfram_alpha', type='tool_use')]
Tool Used: wolfram_alpha
Tool Input:
{
"search_query": "What is the distance between Earth and Mars?"
}
Tool Result:
"Query:\n\"What is the distance between Earth and Mars?\"\n\nInput interpretation:\nMars | distance from Earth\n\nCurrent result:\n2.078 au (astronomical units)\n\nHistory:\nimage: https://www6b3.wolframalpha.com/Calculate/MSP/MSP101d6f8bccif83bf9800004259ii3cbcih744h?MSPStoreType=image/png&s=19\n\nUnit conversions:\n3.109\u00d710^8 km (kilometers)\n\n3.109\u00d710^11 meters\n\n193.2 million miles\n\nComparisons as distance:\n \u2248 ( 0.024 \u2248 1/42 ) \u00d7 smallest distance from the Sun to the heliosheath ( 79 to 100 au )\n\n \u2248 2.1 \u00d7 mean Earth-Sun distance ( 1.0000010178 au )\n\nCorresponding quantities:\nLight travel time t in vacuum from t = x/c:\n | 17 minutes\n\nLight travel time t in an optical fiber t = 1.48x/c:\n | 26 minutes\n\nSolar radiation pressure from P = L_\u2609/(c4\u03c0r^2):\n | 1.1 \u03bcPa (micropascals)\n | (assuming solar constant \u2248 1.36 kW/m^2)\n\nOrbital properties:\ncurrent distance from Earth | 2.078 au\n17.29 light minutes\naverage distance from Earth | 1.7 au\n14.1 light minutes\ncurrent distance from Sun | 1.39 au\n11.56 light minutes\nlargest distance from Sun | 154.863553 million mi\n1.66599116 au\nsmallest distance from Sun | 128.402967 million mi\n1.38133346 au\norbital period | 1.8808476 a\n\nWolfram|Alpha website result for \"What is the distance between Earth and Mars?\":\nhttps://www6b3.wolframalpha.com/input?i=What+is+the+distance+between+Earth+and+Mars%3F"
Response:
Stop Reason: end_turn
Content: [ContentBlock(text='The current distance between Earth and Mars is 2.078 astronomical units (au), which is equivalent to 3.109×10^8 km or 193.2 million miles. \n\nThe average distance between the two planets is about 1.7 au or 140 million miles.\n\nMars orbits the Sun at an average distance of 1.52 au, ranging from a minimum of 1.38 au to a maximum of 1.67 au. Its orbital period is 1.88 Earth years.\n\nAt the current Earth-Mars distance of 2.078 au, light takes about 17 minutes to travel between the two planets. The solar radiation pressure at that distance is approximately 1.1 μPa (micropascals).', type='text')]
Final Response: The current distance between Earth and Mars is 2.078 astronomical units (au), which is equivalent to 3.109×10^8 km or 193.2 million miles.
The average distance between the two planets is about 1.7 au or 140 million miles.
Mars orbits the Sun at an average distance of 1.52 au, ranging from a minimum of 1.38 au to a maximum of 1.67 au. Its orbital period is 1.88 Earth years.
At the current Earth-Mars distance of 2.078 au, light takes about 17 minutes to travel between the two planets. The solar radiation pressure at that distance is approximately 1.1 μPa (micropascals).
The current distance between Earth and Mars is 2.078 astronomical units (au), which is equivalent to 3.109×10^8 km or 193.2 million miles.
The average distance between the two planets is about 1.7 au or 140 million miles.
Mars orbits the Sun at an average distance of 1.52 au, ranging from a minimum of 1.38 au to a maximum of 1.67 au. Its orbital period is 1.88 Earth years.
At the current Earth-Mars distance of 2.078 au, light takes about 17 minutes to travel between the two planets. The solar radiation pressure at that distance is approximately 1.1 μPa (micropascals).
:::
::::
---
## Programatic Tool Calling (PTC) with the Claude API
Source: https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/programmatic_tool_calling_ptc.ipynb
Path: 04-API-Reference/Other/claude-cookbooks-tool-use-programmatic-tool-calling-ptc.md
# Programatic Tool Calling (PTC) with the Claude API
Programmatic Tool Calling (PTC) allows Claude to write code that calls
tools programmatically within the Code Execution environment, rather
than requiring round-trips through the model for each tool invocation.
This substantially reduces end-to-end latency for multiple tool calls,
and can dramatically reduce token consumption by allowing the model to
write code that removes irrelevant context before it hits the model's
context window (for example, by grepping for key information within
large and noisy files).
When faced with third-party APIs and tools that you may not be able to
modify directly, PTC can help reduce usage of context by allowing Claude
to write code that can be invoked in the Code Execution environment.
In this cookbook, we will work with a mock API for team expense
management. The API is designed to require multiple invocations and will
return large results which help illustrate the benefits of Programmatic
Tool Calling.
:::
::: {#4d7e647f .cell .markdown}
## By the end of this cookbook, you\'ll be able to:
- Understand the difference between regular tool calling and programatic
tool calling (PTC)
- Write agents that leverage PTC
:::
::: {#e0e31236 .cell .markdown}
## Prerequisites
Before following this guide, ensure you have:
**Required Knowledge**
- Python fundamentals - comfortable with async/await, functions, and
basic data structures
- Basic understanding of agentic patterns and tool calling
**Required Tools**
- Python 3.11 or higher
- Anthropic API key
- Anthropic Python SDK \>= 0.72
:::
::: {#43e53178 .cell .markdown}
## Setup
First, install the required dependencies:
:::
::: {#fa190a78 .cell .code}
``` python
# %pip install -r requirements.txt
```
:::
::: {#5d24e94e .cell .markdown}
Note: Ensure your .env file contains:
`ANTHROPIC_API_KEY=your_key_here`
Load your environment variables and configure the client. We also load a
helper utility to visualize Claude message responses.
:::
::: {#3a1f5033 .cell .code execution_count="2"}
``` python
from dotenv import load_dotenv
from utils.visualize import visualize
load_dotenv()
MODEL = "claude-sonnet-4-5"
viz = visualize(auto_show=True)
```
:::
::: {#8d0fc3df .cell .markdown}
## Understanding the Third-Party API
In [utils/team_expense_api.py](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/utils/team_expense_api.py), there are
three functions defined: `get_team_members`, `get_expenses`, and
`get_custom_budget`. The `get_team_members` function allows us to
retrieve all employees in a given department with their role, level, and
contact information. The `get_expenses` function returns all expense
line items for an employee in a specific quarter---this can be several
hundred records per employee, with each record containing extensive
metadata including receipt URLs, approval chains, merchant details, and
more. The `get_custom_budget` function checks if a specific employee has
a custom travel budget exception (otherwise they use the standard
\$5,000 quarterly limit).
In this scenario, we need to analyze team expenses and identify which
employees have exceeded their budgets. Traditionally, we might manually
pull expense reports for each person, sum up their expenses by category,
compare against budget limits (checking for custom budget exceptions),
and compile a report. Instead, we will ask Claude to perform this
analysis for us, using the available tools to retrieve team data, fetch
potentially hundreds of expense line items with rich metadata, and
determine who has gone over budget.
The key challenge here is that each employee may have 100+ expense line
items that need to be fetched, parsed, and aggregated---and the
`get_custom_budget` tool can only be called after analyzing expenses to
see if someone exceeded the standard budget. This creates a sequential
dependency chain that makes this an ideal use case for demonstrating the
benefits of Programmatic Tool Calling.
We\'ll pass our tool definitions to the messages API and ask Claude to
perform the analysis. Read the docs on [implementing tool
use](https://docs.claude.com/en/docs/agents-and-tools/tool-use/implement-tool-use)
if you are not familiar with how tool use works with Claude\'s API.
:::
::: {#af013fcc .cell .code execution_count="3"}
``` python
import json
import anthropic
from utils.team_expense_api import get_custom_budget, get_expenses, get_team_members
client = anthropic.Anthropic()
# Tool definitions for the team expense API
tools = [
{
"name": "get_team_members",
"description": 'Returns a list of team members for a given department. Each team member includes their ID, name, role, level (junior, mid, senior, staff, principal), and contact information. Use this to get a list of people whose expenses you want to analyze. Available departments are: engineering, sales, and marketing.\n\nRETURN FORMAT: Returns a JSON string containing an ARRAY of team member objects (not wrapped in an outer object). Parse with json.loads() to get a list. Example: [{"id": "ENG001", "name": "Alice", ...}, {"id": "ENG002", ...}]',
"input_schema": {
"type": "object",
"properties": {
"department": {
"type": "string",
"description": "The department name. Case-insensitive.",
}
},
"required": ["department"],
},
"input_examples": [
{"department": "engineering"},
{"department": "sales"},
{"department": "marketing"},
],
},
{
"name": "get_expenses",
"description": "Returns all expense line items for a given employee in a specific quarter. Each expense includes extensive metadata: date, category, description, amount (in USD), currency, status (approved, pending, rejected), receipt URL, approval chain, merchant name and location, payment method, and project codes. An employee may have 20-50+ expense line items per quarter, and each line item contains substantial metadata for audit and compliance purposes. Categories include: 'travel' (flights, trains, rental cars, taxis, parking), 'lodging' (hotels, airbnb), 'meals', 'software', 'equipment', 'conference', 'office', and 'internet'. IMPORTANT: Only expenses with status='approved' should be counted toward budget limits.\n\nRETURN FORMAT: Returns a JSON string containing an ARRAY of expense objects (not wrapped in an outer object with an 'expenses' key). Parse with json.loads() to get a list directly. Example: [{\"expense_id\": \"ENG001_Q3_001\", \"amount\": 1250.50, \"category\": \"travel\", ...}, {...}]",
"input_schema": {
"type": "object",
"properties": {
"employee_id": {
"type": "string",
"description": "The unique employee identifier",
},
"quarter": {
"type": "string",
"description": "Quarter identifier: 'Q1', 'Q2', 'Q3', or 'Q4'",
},
},
"required": ["employee_id", "quarter"],
},
"input_examples": [
{"employee_id": "ENG001", "quarter": "Q3"},
{"employee_id": "SAL002", "quarter": "Q1"},
{"employee_id": "MKT001", "quarter": "Q4"},
],
},
{
"name": "get_custom_budget",
"description": 'Get the custom quarterly travel budget for a specific employee. Most employees have a standard $5,000 quarterly travel budget. However, some employees have custom budget exceptions based on their role requirements. This function checks if a specific employee has a custom budget assigned.\n\nRETURN FORMAT: Returns a JSON string containing a SINGLE OBJECT (not an array). Parse with json.loads() to get a dict. Example: {"user_id": "ENG001", "has_custom_budget": false, "travel_budget": 5000, "reason": "Standard", "currency": "USD"}',
"input_schema": {
"type": "object",
"properties": {
"user_id": {
"type": "string",
"description": "The unique employee identifier",
}
},
"required": ["user_id"],
},
"input_examples": [
{"user_id": "ENG001"},
{"user_id": "SAL002"},
{"user_id": "MKT001"},
],
},
]
tool_functions = {
"get_team_members": get_team_members,
"get_expenses": get_expenses,
"get_custom_budget": get_custom_budget,
}
```
:::
::: {#ae7a7b68 .cell .markdown}
## Traditional Tool Calling (Baseline)
In this first example, we\'ll use traditional tool calling to establish
our baseline.
We\'ll call the `messages.create` API with our initial query. When the
model stops with a `tool_use` reason, we will execute the tool as
requested, and then add the output from the tool to the messages and
call the model again.
:::
::: {#dff83920 .cell .code execution_count="4"}
``` python
import time
from anthropic.types import TextBlock, ToolUseBlock
from anthropic.types.beta import (
BetaMessageParam as MessageParam,
)
from anthropic.types.beta import (
BetaTextBlock,
BetaToolUseBlock,
)
messages: list[MessageParam] = []
def run_agent_without_ptc(user_message):
"""Run agent using traditional tool calling"""
messages.append({"role": "user", "content": user_message})
total_tokens = 0
start_time = time.time()
api_counter = 0
while True:
response = client.beta.messages.create(
model=MODEL,
max_tokens=4000,
tools=tools,
messages=messages,
betas=["advanced-tool-use-2025-11-20"],
)
api_counter += 1
# Track token usage
total_tokens += response.usage.input_tokens + response.usage.output_tokens
viz.capture(response)
if response.stop_reason == "end_turn":
# Extract the first text block from the response
final_response = next(
(
block.text
for block in response.content
if isinstance(block, (BetaTextBlock, TextBlock))
),
None,
)
elapsed_time = time.time() - start_time
return final_response, messages, total_tokens, elapsed_time, api_counter
# Process tool calls
if response.stop_reason == "tool_use":
# First, add the assistant's response to messages
messages.append({"role": "assistant", "content": response.content})
# Collect all tool results
tool_results = []
for block in response.content:
if isinstance(block, (BetaToolUseBlock, ToolUseBlock)):
tool_name = block.name
tool_input = block.input
tool_use_id = block.id
result = tool_functions[tool_name](**tool_input)
content = str(result)
tool_result = {
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": content,
}
tool_results.append(tool_result)
# Append all tool results at once after collecting them
messages.append({"role": "user", "content": tool_results})
else:
print(f"\nUnexpected stop reason: {response.stop_reason}")
elapsed_time = time.time() - start_time
final_response = next(
(
block.text
for block in response.content
if isinstance(block, (BetaTextBlock, TextBlock))
),
f"Stopped with reason: {response.stop_reason}",
)
return final_response, messages, total_tokens, elapsed_time, api_counter
```
:::
::: {#db2d30d4 .cell .markdown}
Our initial query to the model provides some instructions to help guide
the model. For brevity, we\'ve asked the model to only call each tool
once. For deeper investigations, the model may wish to look into
multiple systems or time spans.
:::
::: {#6d4bb83a .cell .code execution_count="5"}
``` python
query = "Which engineering team members exceeded their Q3 travel budget? Standard quarterly travel budget is $5,000. However, some employees have custom budget limits. For anyone who exceeded the $5,000 standard budget, check if they have a custom budget exception. If they do, use that custom limit instead to determine if they truly exceeded their budget."
```
:::
:::::::: {#ac08a17f .cell .code execution_count="6"}
``` python
# Run the agent
result, conversation, total_tokens, elapsed_time, api_count_without_ptc = run_agent_without_ptc(
query
)
print(f"Result: {result}")
print(f"API calls made: {api_count_without_ptc}")
print(f"Total tokens used: {total_tokens:,}")
print(f"Total time taken: {elapsed_time:.2f}s")
```
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ──────────────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">1,859</span> in • <span style="color: #008000; text-decoration-color: #008000">85</span> out • <span style="color: #808000; text-decoration-color: #808000">1,944</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (2 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">I'll help you identify which engineering team members exceeded their Q3 travel budget. Let me </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">start by getting the list of engineering team members.</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_team_members</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01LuouuJYp1sSvBe2Du7EG7v <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"department"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"engineering"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ───────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">2,473</span> in • <span style="color: #008000; text-decoration-color: #008000">497</span> out • <span style="color: #808000; text-decoration-color: #808000">2,970</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (9 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Now let me get the Q3 expenses for all engineering team members:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01Wu8LLTT2sKTTqpVwGT65Lj <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG001"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01KzjQ5mQJa9ocWjCGzYkD9F <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG002"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 4</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01RjjhZTg9JsKXE5E9S6Foho <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG003"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 5</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_013xqpxpfc2N9rP5W5uMLAo9 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG004"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 6</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_019zfzG6Wox8iDqy1dUXiH3t <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG005"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 7</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01RxfTz11tzvbVE7oEtqHaVB <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG006"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 8</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01FsFEtK1gTEPxg56eVrhhf6 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG007"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 9</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01Ctq9dZbvzaVSLSZe86MTzb <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG008"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰──────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ──────────────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">51,744</span> in • <span style="color: #008000; text-decoration-color: #008000">290</span> out • <span style="color: #808000; text-decoration-color: #808000">52,034</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (7 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Now let me calculate the approved travel expenses for each engineer and identify who exceeded </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">$5,000:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_013oegKwjvToLwEW1daDD8av <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG001"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_0162W4Ycr9FcVVED65exjAj4 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG003"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 4</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01JcTX5rnwFxA99Am33gXmh6 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG005"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 5</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01QwNJz1wGeV5VeZoCd4ByER <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG006"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 6</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01KoJ4gzfiu1TPccLJB86Wiq <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG007"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 7</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01MxeFPzHot9aE5fPuniFkui <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> model (direct) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG008"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ──────────────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">52,533</span> in • <span style="color: #008000; text-decoration-color: #008000">992</span> out • <span style="color: #808000; text-decoration-color: #808000">53,525</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> end_turn <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (1 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Now let me analyze the data. I'll calculate the approved travel expenses for each engineer:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**Analysis of Q3 Travel Expenses:**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**ENG001 - Alice Chen (Senior Software Engineer)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Approved travel expenses: $1,161.04 + $18.63 + $13.21 + $36.55 + $1,440.42 + $166.46 + $48.43</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">+ $1,124.56 + $1,245.90 + $1,498.42 = **$6,753.62**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- **EXCEEDED by $1,753.62** ❌</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**ENG002 - Bob Martinez (Staff Engineer)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Approved travel expenses: $180.16 + $10.07 + $20.76 = **$210.99**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Under budget ✓</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**ENG003 - Carol White (Software Engineer)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Approved travel expenses: $24.75 + $424.74 + $1,397.17 + $1,026.12 + $1,288.36 + $1,128.90 + </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">$1,148.42 + $45.03 = **$6,483.49**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- **EXCEEDED by $1,483.49** ❌</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**ENG004 - David Kim (Principal Engineer)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Approved travel expenses: $21.68 + $46.12 + $1,008.68 + $46.43 = **$1,122.91**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">- Under budget ✓</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**ENG005 - Emma Johnson (Junior Software Engineer)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">... (truncated)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .stream .stdout}
Result: Now let me analyze the data. I'll calculate the approved travel expenses for each engineer:
**Analysis of Q3 Travel Expenses:**
**ENG001 - Alice Chen (Senior Software Engineer)**
- Approved travel expenses: $1,161.04 + $18.63 + $13.21 + $36.55 + $1,440.42 + $166.46 + $48.43 + $1,124.56 + $1,245.90 + $1,498.42 = **$6,753.62**
- Budget: $5,000 (Standard)
- **EXCEEDED by $1,753.62** ❌
**ENG002 - Bob Martinez (Staff Engineer)**
- Approved travel expenses: $180.16 + $10.07 + $20.76 = **$210.99**
- Budget: $5,000 (Standard)
- Under budget ✓
**ENG003 - Carol White (Software Engineer)**
- Approved travel expenses: $24.75 + $424.74 + $1,397.17 + $1,026.12 + $1,288.36 + $1,128.90 + $1,148.42 + $45.03 = **$6,483.49**
- Budget: $5,000 (Standard)
- **EXCEEDED by $1,483.49** ❌
**ENG004 - David Kim (Principal Engineer)**
- Approved travel expenses: $21.68 + $46.12 + $1,008.68 + $46.43 = **$1,122.91**
- Budget: $5,000 (Standard)
- Under budget ✓
**ENG005 - Emma Johnson (Junior Software Engineer)**
- Approved travel expenses: $450.00 + $1,376.36 + $1,164.49 + $151.55 + $1,253.88 = **$4,396.28**
- Budget: $5,000 (Standard)
- Under budget ✓
**ENG006 - Frank Liu (Senior Software Engineer)**
- Approved travel expenses: $596.48 + $1,018.71 + $1,193.82 + $159.08 + $1,112.11 + $24.97 = **$4,105.17**
- Budget: $5,000 (Standard)
- Under budget ✓
**ENG007 - Grace Taylor (Software Engineer)**
- Approved travel expenses: $1,476.63 + $39.85 + $1,220.19 + $189.16 + $1,032.52 + $1,331.00 = **$5,289.35**
- Budget: $5,000 (Standard)
- **EXCEEDED by $289.35** ❌
**ENG008 - Henry Park (Staff Engineer)**
- Approved travel expenses: $15.63 + $166.05 + $1,018.94 + $1,224.34 + $1,120.32 + $1,345.90 = **$4,891.18**
- Budget: $5,000 (Standard)
- Under budget ✓
---
## Summary: Engineering Team Members Who Exceeded Their Q3 Travel Budget
**3 team members exceeded their quarterly travel budget:**
1. **Alice Chen (ENG001)** - Senior Software Engineer
- Travel expenses: **$6,753.62**
- Budget: $5,000
- **Over budget by $1,753.62 (35% over)**
2. **Carol White (ENG003)** - Software Engineer
- Travel expenses: **$6,483.49**
- Budget: $5,000
- **Over budget by $1,483.49 (30% over)**
3. **Grace Taylor (ENG007)** - Software Engineer
- Travel expenses: **$5,289.35**
- Budget: $5,000
- **Over budget by $289.35 (6% over)**
All three employees have the standard $5,000 quarterly travel budget with no custom exceptions.
API calls made: 4
Total tokens used: 110,473
Total time taken: 35.38s
:::
::::::::
::: {#6a3fedb1 .cell .markdown}
Great! We can see that Claude was able to use the available tools
successfully to identify which team members exceeded their travel
budgets. However, we can also see that we used a lot of tokens to
accomplish this task. Claude had to ingest all the expense line items
through its context window---potentially 100+ records per employee, each
with extensive metadata including receipt URLs, approval chains,
merchant information, and more---in order to parse them, sum up the
totals by category, and compare against budget limits.
Additionally, the traditional tool calling approach requires multiple
sequential round trips: first fetching team members, then expenses for
each person, then checking custom budgets for those who exceeded the
standard limit. Each round trip adds latency, and all the rich metadata
from expense records flows through the model\'s context.
Let\'s see if we can use PTC to improve performance by allowing Claude
to write code that processes these large datasets in the code execution
environment instead.
:::
::: {#f9c2500e .cell .markdown}
To enable PTC on tools, we must first add the `allowed_callers` field to
any tool that should be callable via code execution.
**Key points to consider**
- Tools without allowed_callers default to model-only invocation
- Tools can be invoked by both the model AND code execution by including
multiple callers: `["direct", "code_execution_20250825"]`
- Only opt in tools that are safe for programmatic/repeated execution.
:::
::: {#yg1hozsmgz9 .cell .code execution_count="7"}
``` python
import copy
ptc_tools = copy.deepcopy(tools)
for tool in ptc_tools:
tool["allowed_callers"] = ["code_execution_20250825"] # type: ignore
# Add the code execution tool
ptc_tools.append(
{
"type": "code_execution_20250825", # type: ignore
"name": "code_execution",
}
)
```
:::
::: {#c9d1c138 .cell .markdown}
Now that we\'ve updated our tool definitions to allow programmatic tool
calling, we can run our agent with PTC. In order to do so, we\'ve had to
make a few changes to our function. We must use the `beta` messages API.
1. We\'ve added `"advanced-tool-use-2025-11-20"` to betas.
2. We pass in the `container_id` if it is defined with our request.
This is only necessary for stateful workflows like ours. In
single-turn workflows this is not required.
3. We can check the `caller` field in the `tool_use` block to determine
if this tool call is from a direct model invocation or from
programmatic invocation.
Note that in either case, we send our tool results via the Claude API,
however only `direct` invocations will be \"seen\" by the model.
`code_execution_20250825` types will only be seen my the code execution
container.
:::
::: {#dq3gj54mlv .cell .code execution_count="8"}
``` python
messages = []
def run_agent_with_ptc(user_message):
"""Run agent using PTC"""
messages.append({"role": "user", "content": user_message})
total_tokens = 0
start_time = time.time()
container_id = None
api_counter = 0
while True:
# Build request with PTC beta headers
request_params = {
"model": MODEL,
"max_tokens": 4000,
"tools": ptc_tools,
"messages": messages,
}
response = client.beta.messages.create(
**request_params,
betas=[
"advanced-tool-use-2025-11-20",
],
extra_body={"container": container_id} if container_id else None,
)
viz.capture(response)
api_counter += 1
# Track container for stateful execution
if hasattr(response, "container") and response.container:
container_id = response.container.id
print(f"\n[Container] ID: {container_id}")
if hasattr(response.container, "expires_at"):
# If the container has expired, we would need to restart our workflow. In our case, it completes before expiration.
print(f"[Container] Expires at: {response.container.expires_at}")
# Track token usage
total_tokens += response.usage.input_tokens + response.usage.output_tokens
if response.stop_reason == "end_turn":
# Extract the first text block from the response
final_response = next(
(block.text for block in response.content if isinstance(block, BetaTextBlock)),
None,
)
elapsed_time = time.time() - start_time
return final_response, messages, total_tokens, elapsed_time, api_counter
# As before, we process tool calls
if response.stop_reason == "tool_use":
# First, add the assistant's response to messages
messages.append({"role": "assistant", "content": response.content})
# Collect all tool results
tool_results = []
for block in response.content:
if isinstance(block, BetaToolUseBlock):
tool_name = block.name
tool_input = block.input
tool_use_id = block.id
# We can use caller type to understand how the tool was invoked
caller_type = block.caller["type"] # type: ignore
if caller_type == "code_execution_20250825":
print(f"[PTC] Tool called from code execution environment: {tool_name}")
elif caller_type == "direct":
print(f"[Direct] Tool called by model: {tool_name}")
result = tool_functions[tool_name](**tool_input)
# Format result as proper content for the API
if isinstance(result, list) and result and isinstance(result[0], str):
content = "\n".join(result)
elif isinstance(result, (dict, list)):
content = json.dumps(result)
else:
content = str(result)
tool_results.append(
{
"type": "tool_result",
"tool_use_id": tool_use_id,
"content": content,
}
)
messages.append({"role": "user", "content": tool_results})
else:
print(f"\nUnexpected stop reason: {response.stop_reason}")
elapsed_time = time.time() - start_time
final_response = next(
(block.text for block in response.content if isinstance(block, BetaTextBlock)),
f"Stopped with reason: {response.stop_reason}",
)
return final_response, messages, total_tokens, elapsed_time, api_counter
```
:::
:::::::::: {#640d2e02 .cell .code execution_count="9"}
``` python
# Run the PTC agent
result_ptc, conversation_ptc, total_tokens_ptc, elapsed_time_ptc, api_count_with_ptc = (
run_agent_with_ptc(query)
)
```
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ──────────────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">4,134</span> in • <span style="color: #008000; text-decoration-color: #008000">539</span> out • <span style="color: #808000; text-decoration-color: #808000">4,673</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (3 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">I'll help you identify which engineering team members exceeded their Q3 travel budget. Let me </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">start by getting the engineering team members and their expenses.</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Server Tool Use</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> srvtoolu_015mWPqaFni4B313UieCxbny <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> direct <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Code:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 1 </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 2 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">import</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> asyncio</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 3 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">import</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> json</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 4 </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 5 </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">async</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">def</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #a6e22e; text-decoration-color: #a6e22e; background-color: #272822">main</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">():</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 6 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># First, get all engineering team members</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 7 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> team_members_json </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">await</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> get_team_members({</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'department'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'engineering'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">})</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 8 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> team_members </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> json</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">.</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">loads(team_members_json)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 9 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">10 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> print(</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">f"Found {</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">len(team_members)</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">} engineering team members"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">11 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">12 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Get Q3 expenses for all team members in parallel</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">13 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expense_tasks </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> [</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">14 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> get_expenses({</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'employee_id'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: member[</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'id'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">], </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'quarter'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'Q3'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">})</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">15 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">for</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> member </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">in</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> team_members</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">16 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> ]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">17 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">18 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expenses_results </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">await</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> asyncio</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">.</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">gather(</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">*</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">expense_tasks)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">19 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">20 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Calculate travel expenses for each member</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">21 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> travel_spending </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> {}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">22 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">for</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> i, member </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">in</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> enumerate(team_members):</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">23 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expenses </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> json</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">.</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">loads(expenses_results[i])</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">24 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Only count approved expenses in travel category</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">25 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> travel_total </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> sum(</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">26 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expense[</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'amount'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">] </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">27 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">for</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expense </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">in</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expenses </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">28 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">if</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expense[</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'category'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">] </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">==</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">and</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> expense[</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'status'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">] </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">==</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'approved'</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">29 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> )</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">30 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> travel_spending[member[</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">31 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">...</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> (truncated)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_team_members</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_016EtCE7G5rH645G3gvjzrP6 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"department"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"engineering"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .stream .stdout}
[Container] ID: container_011CVSAwq5J4vNPi3A4P2Rwh
[Container] Expires at: 2025-11-24 05:41:17.467494+00:00
[PTC] Tool called from code execution environment: get_team_members
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭──────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">0</span> in • <span style="color: #008000; text-decoration-color: #008000">0</span> out • <span style="color: #808000; text-decoration-color: #808000">0</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (8 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01Nq2au3W69RmDFZaSdqe6u1 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG007"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01YYw9cuTSXk7bu7P38qBz6P <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG005"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01Fyxxe2KmVpVmw4jJL2CXSz <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG008"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 4</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01J4ovDu2UJa9Se19vxKTa6y <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG006"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 5</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01T24CrvQYA3LqGfZftCmueC <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG003"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 6</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01HotYZN6sk3gMLkpdWXbdz4 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG004"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 7</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01AxvEqi3AKqdnH44kGH1U6E <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG002"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 8</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_expenses</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01A4agznkK1jA4AyJo3H2jpg <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"employee_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG001"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">,</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"quarter"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"Q3"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .stream .stdout}
[Container] ID: container_011CVSAwq5J4vNPi3A4P2Rwh
[Container] Expires at: 2025-11-24 05:41:19.266670+00:00
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
[PTC] Tool called from code execution environment: get_expenses
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭─────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ───────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">4,751</span> in • <span style="color: #008000; text-decoration-color: #008000">679</span> out • <span style="color: #808000; text-decoration-color: #808000">5,430</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> tool_use <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (6 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Code Execution Result:</span> <span style="color: #008000; text-decoration-color: #008000">Success (exit 0)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">stdout:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Found 8 engineering team members</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Employees who exceeded $5,000 standard budget: 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">ENG001: Alice Chen - $9177.88</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">ENG003: Carol White - $6483.49</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">ENG007: Grace Taylor - $5289.35</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Now let me check if any of these three employees have custom budget exceptions:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 3</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Server Tool Use</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> srvtoolu_015GTpFmCbd2JPAQLAioB4Qb <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> direct <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Code:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 1 </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 2 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">import</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> asyncio</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 3 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">import</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> json</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 4 </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 5 </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">async</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">def</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #a6e22e; text-decoration-color: #a6e22e; background-color: #272822">main</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">():</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 6 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Check custom budgets for the three employees who exceeded standard</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 7 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> exceeded_ids </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> [</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG001'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">, </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG003'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">, </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG007'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 8 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> exceeded_amounts </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> {</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822"> 9 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG001'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: {</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'name'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'Alice Chen'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">, </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel_total'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #ae81ff; text-decoration-color: #ae81ff; background-color: #272822">9177.88</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">},</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">10 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG003'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: {</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'name'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'Carol White'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">, </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel_total'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #ae81ff; text-decoration-color: #ae81ff; background-color: #272822">6483.49</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">},</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">11 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'ENG007'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: {</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'name'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'Grace Taylor'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">, </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel_total'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #ae81ff; text-decoration-color: #ae81ff; background-color: #272822">5289.35</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">12 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> }</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">13 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">14 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Get custom budgets in parallel</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">15 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> budget_tasks </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> [</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">16 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> get_custom_budget({</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'user_id'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: emp_id})</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">17 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">for</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> emp_id </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">in</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> exceeded_ids</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">18 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> ]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">19 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">20 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> budget_results </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">await</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> asyncio</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">.</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">gather(</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">*</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">budget_tasks)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">21 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">22 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #959077; text-decoration-color: #959077; background-color: #272822"># Analyze who truly exceeded their budget</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">23 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> truly_exceeded </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> []</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">24 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">25 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">for</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> i, emp_id </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">in</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> enumerate(exceeded_ids):</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">26 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> budget_info </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> json</span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">.</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">loads(budget_results[i])</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">27 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> actual_budget </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> budget_info[</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel_budget'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">28 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> travel_total </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> exceeded_amounts[emp_id][</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'travel_total'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">29 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> name </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">=</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> exceeded_amounts[emp_id][</span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">'name'</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">]</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">30 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">31 </span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #66d9ef; text-decoration-color: #66d9ef; background-color: #272822">if</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> travel_total </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">></span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> actua</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #e3e3dd; text-decoration-color: #e3e3dd; background-color: #272822; font-weight: bold"> </span><span style="color: #656660; text-decoration-color: #656660; background-color: #272822">32 </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">...</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> (truncated)</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 4</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01EaaJ3SikeniibPsEdAqXo8 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG003"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 5</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01E5bqQ4xKX7FTdhh6xLkw4E <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG001"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 6</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #808000; text-decoration-color: #808000">Tool Use:</span> <span style="color: #808000; text-decoration-color: #808000; font-weight: bold">get_custom_budget</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">ID:</span> toolu_01PLF38q7ndVQB4mqdqaFt7u <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Caller:</span> code execution environment <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008000; text-decoration-color: #008000">Input:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">{</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822"> </span><span style="color: #ff4689; text-decoration-color: #ff4689; background-color: #272822">"user_id"</span><span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">: </span><span style="color: #e6db74; text-decoration-color: #e6db74; background-color: #272822">"ENG007"</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #f8f8f2; text-decoration-color: #f8f8f2; background-color: #272822">}</span><span style="background-color: #272822"> </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰───────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::: {.output .stream .stdout}
[Container] ID: container_011CVSAwq5J4vNPi3A4P2Rwh
[Container] Expires at: 2025-11-24 05:41:33.430636+00:00
[PTC] Tool called from code execution environment: get_custom_budget
[PTC] Tool called from code execution environment: get_custom_budget
[PTC] Tool called from code execution environment: get_custom_budget
:::
::: {.output .display_data}
<pre style="white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace"><span style="color: #008080; text-decoration-color: #008080">╭────────────────────────────────────────────── </span><span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude API Response</span><span style="color: #008080; text-decoration-color: #008080"> ──────────────────────────────────────────────╮</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080; font-weight: bold">Claude Message</span> (<span style="color: #008000; text-decoration-color: #008000">assistant</span>) <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">│</span> <span style="color: #800080; text-decoration-color: #800080">tokens:</span> <span style="color: #008080; text-decoration-color: #008080">5,611</span> in • <span style="color: #008000; text-decoration-color: #008000">205</span> out • <span style="color: #808000; text-decoration-color: #808000">5,816</span> total <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Model:</span> claude-sonnet-4-5-20250929 <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Stop Reason:</span> end_turn <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0; font-weight: bold">Content</span> (2 blocks) <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> ├── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 1</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #808000; text-decoration-color: #808000">Code Execution Result:</span> <span style="color: #008000; text-decoration-color: #008000">Success (exit 0)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #008000; text-decoration-color: #008000">stdout:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">ENGINEERING TEAM MEMBERS WHO EXCEEDED THEIR Q3 TRAVEL BUDGET:</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">================================================================================</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Alice Chen (ENG001)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Budget Limit: $5,000.00 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Travel Spending: $9,177.88</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Exceeded By: $4,177.88</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Carol White (ENG003)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Budget Limit: $5,000.00 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Travel Spending: $6,483.49</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Exceeded By: $1,483.49</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">Grace Taylor (ENG007)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Budget Limit: $5,000.00 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Travel Spending: $5,289.35</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> Exceeded By: $289.35</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> │ <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #dfdfdf; text-decoration-color: #dfdfdf">Block 2</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #008080; text-decoration-color: #008080">Text</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> └── <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">## Summary</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">**Three engineering team members exceeded their Q3 travel budget:**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">1. **Alice Chen (ENG001)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Spent: $9,177.88</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Over budget by: **$4,177.88**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">2. **Carol White (ENG003)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Spent: $6,483.49</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Over budget by: **$1,483.49**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">3. **Grace Taylor (ENG007)**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Budget: $5,000 (Standard)</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Spent: $5,289.35</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0"> - Over budget by: **$289.35**</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">All three employees are on the standard $5,000 quarterly travel budget with no custom </span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">│</span> <span style="color: #c0c0c0; text-decoration-color: #c0c0c0">exceptions, so they all genuinely exceeded their allocated travel budget for Q3.</span> <span style="color: #008080; text-decoration-color: #008080">│</span>
<span style="color: #008080; text-decoration-color: #008080">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>
</pre>
:::
::::::::::
:::: {#da1a0d5d .cell .code execution_count="10"}
``` python
print(f"\n{'=' * 60}")
print(f"Result: {result_ptc}")
print(f"\n{'=' * 60}")
print("Performance Metrics:")
print(
f" Total API calls to Claude: {len([m for m in conversation_ptc if m['role'] == 'assistant'])}"
)
print(f" Total tokens used: {total_tokens_ptc:,}")
print(f" Total time taken: {elapsed_time_ptc:.2f}s")
```
::: {.output .stream .stdout}
============================================================
Result: ## Summary
**Three engineering team members exceeded their Q3 travel budget:**
1. **Alice Chen (ENG001)**
- Budget: $5,000 (Standard)
- Spent: $9,177.88
- Over budget by: **$4,177.88**
2. **Carol White (ENG003)**
- Budget: $5,000 (Standard)
- Spent: $6,483.49
- Over budget by: **$1,483.49**
3. **Grace Taylor (ENG007)**
- Budget: $5,000 (Standard)
- Spent: $5,289.35
- Over budget by: **$289.35**
All three employees are on the standard $5,000 quarterly travel budget with no custom exceptions, so they all genuinely exceeded their allocated travel budget for Q3.
============================================================
Performance Metrics:
Total API calls to Claude: 3
Total tokens used: 15,919
Total time taken: 34.88s
:::
::::
::: {#xevg4ich93m .cell .markdown}
## Performance Comparison
Let\'s compare the performance between traditional tool calling and PTC:
**Note on API Call Count:** You may notice that PTC requires more API
calls in this example. This is because PTC writes more structured,
sequential code that follows best practices---for instance, separating
the expense fetching step from the budget checking step. Traditional
tool calling can sometimes batch operations together in a single turn,
but at the cost of sending all raw data through the model\'s context.
The token efficiency gains from PTC far outweigh the minimal increase in
round trips, especially when working with large, metadata-rich datasets.
:::
:::: {#fun83cq4bmq .cell .code execution_count="11"}
``` python
import pandas as pd
# Create comparison dataframe
comparison_data = {
"Metric": [
"API Calls",
"Total Tokens",
"Elapsed Time (s)",
"Token Reduction",
"Time Reduction",
],
"Traditional": [
api_count_without_ptc,
f"{total_tokens:,}",
f"{elapsed_time:.2f}",
"-",
"-",
],
"PTC": [
api_count_with_ptc,
f"{total_tokens_ptc:,}",
f"{elapsed_time_ptc:.2f}",
f"{((total_tokens - total_tokens_ptc) / total_tokens * 100):.1f}%",
f"{((elapsed_time - elapsed_time_ptc) / elapsed_time * 100):.1f}%",
],
}
df = pd.DataFrame(comparison_data)
print(df.to_string(index=False))
```
::: {.output .stream .stdout}
Metric Traditional PTC
API Calls 4 4
Total Tokens 110,473 15,919
Elapsed Time (s) 35.38 34.88
Token Reduction - 85.6%
Time Reduction - 1.4%
:::
::::
::: {#90jm6y08ua7 .cell .markdown}
## Key Takeaways
In this example, PTC demonstrated significant performance improvements
through three core capabilities:
### 1. Context Preservation Through Large Data Parsing {#1-context-preservation-through-large-data-parsing}
This was the primary benefit demonstrated in our workflow. Claude wrote
code to fetch and process hundreds of expense line items within the code
execution environment. By processing this data programmatically, Claude
parsed JSON, filtered by status, summed amounts by category, and
compared against budget limits---all without sending the raw expense
data and metadata through the model\'s context window. This resulted in
a **significant reduction in token usage**.
### 2. Sequential Dependency Optimization {#2-sequential-dependency-optimization}
The API has a sequential dependency: `get_custom_budget(user_id)` which
can only be called after analyzing expenses to identify who exceeded the
standard \$5,000 budget. In traditional tool calling, this requires
multiple round trips---fetch team members, fetch expenses for each
person, identify those over budget, then check their custom budgets one
by one. With PTC, Claude writes code that orchestrates this entire
workflow in the code execution environment, making programmatic tool
calls in a loop and maintaining state across calls. This transforms what
would be many sequential API round trips into fewer calls with smarter
orchestration.
### 3. Computational Logic in Code Execution {#3-computational-logic-in-code-execution}
Rather than requiring the model to mentally track and sum dozens of
expenses with complex metadata, Claude delegated the arithmetic and
aggregation logic to Python code. This reduced cognitive load on the
model, ensured precise calculations, and kept irrelevant metadata (like
receipt URLs and merchant locations) out of the model\'s context
entirely.
------------------------------------------------------------------------
## When to Use PTC
PTC is most beneficial when:
- **Working with large, metadata-rich datasets** that need filtering,
parsing, or aggregation (like our expense analysis with receipt URLs,
approval chains, merchant details, etc.)
- **Sequential dependencies exist** where one tool call depends on the
results of previous calls (like checking custom budgets only for
employees who exceeded standard limits)
- **Multiple tool calls are needed** in sequence or in loops across
similar entities (checking expenses and budgets for each team member)
- **Computational logic** can reduce what needs to flow through the
model\'s context
- **Tools are safe** for programmatic/repeated execution without human
oversight
## Conclusion
Our team expense analysis demonstrated PTC\'s strengths: **dramatically
reducing context consumption when working with large, metadata-rich
datasets** and **optimizing workflows with sequential dependencies**. By
allowing Claude to write code that orchestrates tool calls and processes
results programmatically, we achieved substantial token savings while
maintaining accuracy and insight quality.
PTC is particularly valuable for workflows involving bulk data
processing with rich metadata, repeated tool invocations with
dependencies, or scenarios where raw tool outputs would otherwise
pollute the model\'s context.
## Next Steps
Try adapting this pattern to your own use cases:
- Financial data analysis and reporting with sequential lookups
- Multi-entity health checks that depend on initial scan results\
- Large file processing with metadata (CSV, JSON, XML parsing)
- Database query result aggregation with follow-up queries
- Batch API operations with conditional logic based on initial results
:::
---
## Manage resources as code with ant apply - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/apply
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-apply.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fapply)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# Manage resources as code with ant apply
Copy page
Declare agents, environments, skills, memory stores, deployments, and vaults as files in your repository and keep the API's resources in sync with them using ant apply.
Copy page
`ant apply` creates and updates Claude API resources from files: agents, environments, skills, memory stores, deployments, and vaults. They live in your repository and change through the same review as your code. You describe each resource in a file, run `ant apply`, and approve the plan it shows. Then you commit the `claude-lock.json` it writes, so the next run updates the same resources instead of creating new ones.
To install and authenticate the CLI, see the [CLI quickstart](cli-sdks-libraries-cli-quickstart.md). `ant apply` requires CLI version 1.30.0 or later.
## Apply your first agent
Write the agent as a Markdown file under `agents/` and apply it:
CLI
```python
ant apply agents/summarizer.md
```
agents/summarizer.md
```python
---
name: Summarizer
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
---
You are a helpful assistant that writes concise summaries.
```
The frontmatter holds the agent's configuration (the fields from [Define your agent](managed-agents-agent-setup.md)) and the body is its system prompt. `ant apply` [infers](#kind-inference) that the file is an agent from its path, here the `agents/` directory.
In an interactive terminal, `ant apply` prints the plan and waits for your approval:
Output
```python
First apply ./claude-lock.json does not exist yet and will be created
Resources will be created with
credentials API key (--api-key / ANTHROPIC_API_KEY)
host api.anthropic.com
organization 1b0c2a4d-6c1f-4f0e-9a57-2e8d1c3b4a5f
workspace wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
Preview ./claude-lock.json (new)
± Name Plan
+ ./agents/summarizer.md create
Resources + 1 to create
Apply these changes? (y)es / (n)o / (d)etails y
Apply ./claude-lock.json
± Name Status
+ ./agents/summarizer.md created agent_011CYm1BLqPXpQRk5khsSXrs
Resources + 1 created
State written to ./claude-lock.json
```
Answer `d` to see details first: the fields of each new resource, or a field-by-field diff of each update. `--dry-run` prints that detailed plan and exits without changing anything.
To change the agent, edit the file and run `ant apply` again. The plan then shows an update instead of a create.
## Commit claude-lock.json
The first `ant apply` writes `claude-lock.json`, the lockfile, in the directory you run it from, so run it from the repository root. It records the ID of the resource each file created and the organization and workspace the resources live in:
claude-lock.json
```python
{
"version": 1,
"origin": {
"base_url": "https://api.anthropic.com",
"organization_id": "1b0c2a4d-6c1f-4f0e-9a57-2e8d1c3b4a5f",
"workspace_id": "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
},
"resources": {
"./agents/summarizer.md": {
"kind": "agent",
"id": "agent_011CYm1BLqPXpQRk5khsSXrs",
"version": "1",
"hash": "d23251c8d99b3613a64f3f8d87f5fad4",
"remote_hash": "1b771bee5bdbf600a5ad972fdac32d94"
}
}
}
```
Commit it with your files. It's how the next run, on your machine or in CI, finds these resources instead of creating them again, and it's where you read an agent's ID to [start a session](managed-agents-sessions.md). The two hashes fingerprint what was last sent and what the API returned. That's how a later run notices an edited file, or a resource changed outside these files.
## Grow it into a project
You can declaratively define the other resources as files as well. A file holds the request body you would send to that kind's create endpoint:
- An [environment](managed-agents-environments.md) is a YAML file in `environments/`.
- A [memory store](managed-agents-memory.md) is a YAML file in `memory_stores/`.
- A [deployment](managed-agents-scheduled-deployments.md) is a Markdown file in `deployments/`: the frontmatter is the request body and the prose becomes the message that starts each session.
- A [skill](managed-agents-skills.md) is a directory with a `SKILL.md` at its root, conventionally under `skills/`, uploaded as one bundle.
- A [vault](managed-agents-vaults.md) is a YAML file in `vaults/` whose only fields are `display_name` and, optionally, `metadata`. Vault files need CLI version 1.34.0 or later.
Any resource except a skill can be written as YAML, JSON, or Markdown. In Markdown, the frontmatter is the body and the prose fills the kind's text field: an agent's `system`, an environment's or memory store's `description`, a deployment's first message. A vault has no text field, so `ant apply` refuses a vault Markdown file that has prose.
Resources refer to each other by path. Wherever the API expects another resource's ID, write the relative path to that resource's file instead. In this project, the reviewer agent lists `../skills/pr-summary` under `skills`, the lead agent lists `./reviewer.md` in its roster, and the deployment names its agent, environment, and memory store by path. `ant apply` creates them in dependency order and fills in the real IDs. The project has six files:
agents/reviewer.md
agents/lead.md
skills/pr-summary/SKILL.md
environments/cloud.yaml
memory_stores/review-notes.yaml
deployments/nightly.md
agents/reviewer.md
agents/reviewer.md
```python
---
name: Code reviewer
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
skills:
- ../skills/pr-summary
---
You review pull requests for correctness, security, and readability.
```
```python
---
name: Engineering lead
model: claude-opus-5-5
multiagent:
type: coordinator
agents:
- ./reviewer.md
---
You coordinate engineering work. Delegate code review to the reviewer.
```
```python
---
name: pr-summary
description: Summarize a pull request's changes and risks in the team's review format.
---
# PR summary
List what changed, why, and anything a reviewer should look at closely, in three short sections.
```
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: review-env
description: Cloud container with limited networking for review sessions.
config:
type: cloud
networking:
type: limited
allow_package_managers: true
```
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/memory_store.json
name: Review notes
description: Recurring issues and house-style decisions the reviewer has recorded between runs.
```
```python
---
name: Nightly review
agent: ../agents/reviewer.md # the API's agent field: sent as {type: agent, id, version}
environment_id: ../environments/cloud.yaml # sent as the environment's ID
resources:
- path: ../memory_stores/review-notes.yaml
access: read_write
schedule:
type: cron
expression: "0 3 * * *"
timezone: America/Los_Angeles
---
Review any open pull requests. Start with the oldest.
```
Apply the whole directory:
CLI
```python
ant apply .
```
`claude-lock.json` then has an entry for every file in the project.
Relative paths are how these files point at each other. `ant apply` pins agent and skill references to the version it just applied, so editing `reviewer.md` or the skill updates everything that references them in the same run. A path also works inside an object, as in the deployment's `resources` entry, where the other keys such as `access` are kept.
To point at a resource these files don't manage, write its ID (`agent_...`, `skill_...`) instead. Anything else, such as `{type: anthropic, skill_id: xlsx}`, is sent to the API as written. A skill reference can also be a GitHub URL of the form `https://github.com/<owner>/<repo>/tree/<branch>/<dir>`, for example a directory of Anthropic's open-source [skills repository](https://github.com/anthropics/skills): `ant apply` downloads and uploads that directory, pinned to the resolved commit until you run with `--upgrade` (set `GITHUB_TOKEN` for a private repository).
### How ant apply infers a file's kind
When `ant apply` walks a directory, it determines each file's kind from the first of these that matches:
1. A top-level `type` field in the file.
2. The directory the file is directly in: `agents/`, `environments/`, `memory_stores/`, `deployments/`, or `vaults/`.
3. A file name that starts with the kind, such as `environment_staging.md`.
- Vaults are the exception: `ant apply` does not take the kind from a name such as `vault_staging.yaml`, because other tools name their secrets files `vault.yml`. Keep a vault file in `vaults/` or give it `type: vault`.
It skips files that match none of these, such as READMEs and CI configuration, unless you name them on the command line. A named Markdown file that matches none is treated as an agent, and a named YAML or JSON file that matches none is an error.
## Edit and reapply
Running `ant apply` with no arguments reconciles every file the lockfile tracks. At a terminal, it also lists untracked resource files under the lockfile's directory and offers to add them. Deleting a field from a file clears it on the resource if the API allows that field to be cleared. A field you never set, or one the API can't clear, keeps its current value.
If a resource was edited, archived, or deleted outside these files (in the Claude Console, for example), the plan ends with `This plan cannot be applied:` and the reason. The command then exits with `refusing to apply`. Pass `--force` to overwrite the edit or create a replacement.
Deleting a file leaves its resource in place with a warning, and `--prune` removes it (archiving it, or deleting it for a skill). Renaming a file therefore declares a new resource and leaves the old one in place until you prune.
`ant apply` can't adopt a resource you created in the Console or with `ant beta:agents create`. Only what's in the lockfile is managed, and applying a file that describes an existing agent creates a second one. If you downloaded your agent from the Console with **Export as code**, the download includes its own `claude-lock.json`, so applying it updates the resources you built there.
## Run ant apply in CI
Without a terminal, `ant apply` prints the plan and stops with `cannot ask for confirmation without a terminal; re-run with --yes to apply, or --dry-run to see the plan only`. Set up CI as follows:
- Run `ant apply --yes .` on your default branch after merge, naming the project directory. A bare `ant apply --yes` reconciles only files the lockfile already tracks and skips a newly added one.
- On pull requests, run `ant apply --dry-run .` to print the plan for reviewers. It's informational only and exits 0 even when the plan is blocked.
- Commit the updated `claude-lock.json` at the end of the job, even when the apply step failed partway, because a partial apply still records what it created.
- Run one apply at a time, because nothing locks the lockfile.
- Authenticate with [Workload Identity Federation](manage-claude-workload-identity-federation.md) rather than a stored API key, as an identity that reaches the organization and workspace recorded in `claude-lock.json`. `ant apply` refuses credentials that resolve to any other organization or workspace.
For a complete GitHub Actions workflow, see the [CI example in the CLI README](https://github.com/anthropics/anthropic-cli#in-ci).
## Flags
| Flag | Effect |
|----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--dry-run` | Print the plan and exit without applying or writing the lockfile. Exits 0 even when the plan is blocked. |
| `--yes` | Apply without asking for confirmation. Required when there's no terminal. |
| `--force` | Apply even where a resource was changed, archived, or deleted outside these files. |
| `--prune` | Remove resources that are in the lockfile but no longer declared in a file. |
| `--upgrade` | Re-resolve skills referenced by GitHub URL, which otherwise stay pinned to the commit recorded in the lockfile. |
| `--lock-file <path>` | Use this lockfile instead of searching upward from the current directory. Keep one for each organization or workspace: `ant apply` refuses a lockfile whose organization or workspace doesn't match your credentials. |
| `--verbose`, `-v` | Show unchanged resources and full field values in the plan. |
## Next steps
[Start a session](managed-agents-sessions.md)
Run the agents you applied, from the CLI or an SDK
[Scheduled deployments](managed-agents-scheduled-deployments.md)
Deployment fields, run history, and pausing
[CLI scripting and automation](cli-sdks-libraries-cli-scripting.md)
Scripting patterns and use from Claude Code
---
## CLI authentication options - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/authentication
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-authentication.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fauthentication)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# CLI authentication options
Copy page
Authenticate the ant CLI with interactive login, API keys, named profiles, and Workload Identity Federation.
Copy page
The `ant` CLI supports several credential sources. The [Quickstart](cli-sdks-libraries-cli-quickstart.md#authentication) covers the one-command happy path (`ant auth login`). This page covers every option in full.
## Interactive login
`ant auth login` lets you call the API without creating or managing an API key. It opens a browser-based OAuth flow against the Claude Console and stores the resulting credentials under `$ANTHROPIC_CONFIG_DIR` (see [Configuration directory](manage-claude-wif-reference.md#configuration-directory) for the OS-specific default). On a remote host or in any environment without a local browser, pass `--no-browser` to print the authorize URL and paste the returned code back into the terminal.
CLI
```python
ant auth login
# On a remote host without a browser:
ant auth login --no-browser
# Bind to a specific workspace and skip the browser picker:
ant auth login --workspace-id wrkspc_01...
# If the named profile you pass with --profile doesn't exist,
# a new named profile will be created with that name.
ant auth login --profile <profile-name>
```
During the browser flow, you select an organization and then a [workspace](manage-claude-workspaces.md). The issued token is [scoped to that workspace](manage-claude-workspaces.md#api-keys-and-resource-scoping), so the CLI can only see resources that belong to it. Pass `--workspace-id` to bind directly and skip the picker. To work in more than one workspace, see [Switch between workspaces](#switch-between-workspaces).
Interactive login is intended for local development and scripting on your own machine. For non-interactive workloads such as CI, servers, and containers, use [Workload Identity Federation](manage-claude-workload-identity-federation.md) instead.
Login writes credentials to `credentials/<profile>.json`. The first login for a profile also creates `configs/<profile>.json` and sets it as the active profile. To remove stored credentials, run `ant auth logout`, or `ant auth logout --all` to clear every profile.
## Admin access
By default, `ant auth login` requests a workspace-scoped token. To manage the resources documented on the [Admin API](manage-claude-admin-api.md) page, request the `org:admin` scope under a dedicated profile:
CLI
```python
ant auth login --profile admin --scope "org:admin"
# Print a bearer token for Authorization headers:
ant auth print-credentials --profile admin --access-token
```
The `org:admin` scope is granted only to organization members with the admin, owner, or primary owner role. The issued token has organization-wide access, and any workspace binding on the profile does not constrain it. Keep the admin profile separate from your day-to-day profile so routine commands never run with elevated access.
## API key
The CLI also reads your API key from the `ANTHROPIC_API_KEY` environment variable. Get a key from the [Claude Console](playground.md).
zsh
bash
Windows
```python
echo 'export ANTHROPIC_API_KEY=sk-ant-api03-...' >> ~/.zshrc
source ~/.zshrc
```
To override the key for a single invocation, pass `--api-key`. To point at a different API host, set `ANTHROPIC_BASE_URL` or pass `--base-url`.
If you are using an API key scoped to multiple workspaces, such as a [personal or service account key](manage-claude-authentication.md#key-types), you must [specify the workspace](manage-claude-authentication.md#select-a-workspace) to run your command in. Do this by setting an `ANTHROPIC_WORKSPACE_ID` environment variable, which the CLI reads automatically, or by using the [`--workspace-id` flag](cli-sdks-libraries-cli-using.md#global-flags). The value must be a `wrkspc_...` ID.
CLI
```python
ant messages create \
--workspace-id wrkspc_01... \
--model claude-opus-5-5 \
--max-tokens 1024 \
--message '{role: user, content: "Hello, Claude"}'
```
## Check authentication status
`ant auth status` prints the credential source the CLI selected (API key environment variable, OAuth login, federation, or profile), the active profile, the workspace the active token is bound to, and the configuration directory paths. Use it to diagnose why a workload picked the wrong credential or workspace.
CLI
```python
ant auth status
```
``` inline-block
Active profile: default
Config dir: ~/.config/anthropic
Profile config: ~/.config/anthropic/configs/default.json
Credentials: ~/.config/anthropic/credentials/default.json
Credentials
(active) * Profile (user_oauth) [via active_config] sk-ant-oat01-EXA...
...
Workspace
(active) * Workspace wrkspc_01... (Engineering)
```
Read the `(active)` rows to see which credential source and workspace won. The command reports status rather than performing a health check, so don't script against the exit status. For the full ordering of credential sources, see [Credential precedence](manage-claude-wif-reference.md#credential-precedence).
## Switch between workspaces
An interactive-login token is bound to a single workspace. To use the CLI against more than one workspace, log in to each under its own named profile, then switch between them:
CLI
```python
# 1. Create the profile (interactive; pick the other workspace in the
# browser, or pass --workspace-id to skip the picker):
# ant auth login --profile other-ws
# 2. Make it the default for subsequent commands:
ant profile activate other-ws
# 3. Or select it for a single command without changing the default:
ant --profile other-ws models list
ANTHROPIC_PROFILE=other-ws ant models list
```
Run [`ant auth status`](#check-authentication-status) to confirm which profile and workspace are active.
Profiles are only consulted when no API key is set. If `ANTHROPIC_API_KEY` is present in your environment, it overrides every profile and these commands all use that key's workspace (or, for a multi-workspace key, the workspace set with `ANTHROPIC_WORKSPACE_ID` or `--workspace-id`). Unset it before switching profiles.
## Manage profiles
The `ant profile` subcommands inspect and edit profile state directly:
CLI
```python
ant profile list
ant profile get --profile other-ws
ant profile set workspace_id wrkspc_01... --profile other-ws
```
The writable keys for `ant profile set` are `workspace_id`, `base_url`, `organization_id`, `scope`, `client_id`, and `console_url`. Setting `workspace_id` records the target workspace in the profile config but does not rebind credentials that were already issued; run `ant auth login` again under that profile to mint a token for the new workspace.
For the profile file schema and the federation block, see [Profile configuration file](manage-claude-wif-reference.md#profile-configuration-file). For Workload Identity Federation, see the [Authentication overview](manage-claude-authentication.md) and the [WIF reference](manage-claude-wif-reference.md).
## Next steps
[Using the CLI](cli-sdks-libraries-cli-using.md)
Command structure, output formats, GJSON transforms, and request bodies
[CLI scripting and automation](cli-sdks-libraries-cli-scripting.md)
Version-control API resources, scripting patterns, and use from Claude Code
[Workload Identity Federation](manage-claude-workload-identity-federation.md)
Non-interactive authentication for CI, servers, and containers
---
## CLI quickstart - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/quickstart
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-quickstart.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fquickstart)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# CLI quickstart
Copy page
Install the ant command-line tool, authenticate, and send your first request to the Claude API.
Copy page
The `ant` CLI provides access to the Claude API from your terminal. Every API resource is exposed as a subcommand, with output formatting, response filtering, and YAML or JSON file input.
Compared to `curl`, `ant` builds request bodies from typed flags or piped YAML instead of hand-written JSON, and inlines file contents into string fields with an `@path` reference. It extracts response fields with a built-in `--transform` query, so you don't need a separate tool such as `jq`, and it paginates list endpoints automatically.
For endpoint-specific parameters and response schemas, see the [API reference](../Endpoints/cli-messages-create.md). This page gets you to a working command. For everything else the CLI does, see [Using the CLI](cli-sdks-libraries-cli-using.md) and [CLI scripting and automation](cli-sdks-libraries-cli-scripting.md).
## Installation
Homebrew (macOS)
curl (Linux/WSL)
Go
```python
brew install anthropics/tap/ant
```
Check the installation:
```python
ant --version
```
## Authentication
`ant auth login` opens a browser-based OAuth flow against the Claude Console and stores the resulting credentials locally, so you can call the API without creating or managing an API key.
CLI
```python
ant auth login
```
For other ways to authenticate (API key environment variable, headless hosts, multiple workspaces, named profiles, and Workload Identity Federation), see [CLI authentication options](cli-sdks-libraries-cli-authentication.md).
## Send your first request
With the binary installed and authenticated, call the [Messages API](../Endpoints/cli-messages-create.md):
```python
ant messages create \
--model claude-opus-5-5 \
--max-tokens 1024 \
--message '{role: user, content: "Hello, Claude"}'
```
Output
```python
{
"model": "claude-opus-5-5",
"id": "msg_01YMmR5XodC5nTqMxLZMKaq6",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Hello! How are you doing today? Is there something I can help you with?"
}
],
"stop_reason": "end_turn",
"usage": { "input_tokens": 27, "output_tokens": 20 /*, ... */ }
}
```
The response is the full API object, pretty-printed because stdout is a terminal.
## Shell completion
The CLI ships completion scripts for bash, zsh, fish, and PowerShell. Generate and install one for your shell:
zsh
bash
fish
PowerShell
```python
ant @completion zsh > "${fpath[1]}/_ant"
# Restart your shell or run: autoload -U compinit && compinit
```
## Next steps
[CLI authentication options](cli-sdks-libraries-cli-authentication.md)
API keys, headless hosts, multiple workspaces, and named profiles
[Using the CLI](cli-sdks-libraries-cli-using.md)
Command structure, output formats, GJSON transforms, and request bodies
[CLI scripting and automation](cli-sdks-libraries-cli-scripting.md)
Version-control API resources, scripting patterns, and use from Claude Code
---
## CLI scripting and automation - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/scripting
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-scripting.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fscripting)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# CLI scripting and automation
Copy page
Version-control API resources as files with ant apply, chain ant CLI commands in scripts, operate on resources from Claude Code, and authenticate curl calls with CLI credentials.
Copy page
This page covers task-oriented workflows built on the `ant` CLI. For the underlying flags and output options, see [Using the CLI](cli-sdks-libraries-cli-using.md).
## Version-controlling API resources
To keep agents, environments, and other Claude Managed Agents resources as files in your repository, see [Manage resources as code with ant apply](cli-sdks-libraries-cli-apply.md).
### Run the applied agent from the shell
Once an agent and environment exist, you can drive a session from the shell:
1. 1
### Start a session
Pass the agent and environment IDs to the session create command. After `ant apply`, read them from `claude-lock.json`: each entry under `resources` has an `id`, and for the project in [Manage resources as code with ant apply](cli-sdks-libraries-cli-apply.md) the entries are `./agents/summarizer.md` and `./environments/cloud.yaml`.
``` shiki
ant beta:sessions create \
--agent agent_011CYm1BLqPXpQRk5khsSXrs \
--environment-id env_01595EKxaaTTGwwY3kyXdtbs \
--title "Summarization task"
```
Output
``` shiki
{
"id": "session_01JZCh78XvmxJjiXVy3oSi7K",
"status": "running"
/* ... */
}
```
2. 2
### Send a user message
Copy the session `id` from the previous output into `--session-id`:
``` shiki
ant beta:sessions:events send \
--session-id session_01JZCh78XvmxJjiXVy3oSi7K \
--event '{type: user.message, content: [{type: text, text: "Summarize the benefits of type safety in one sentence."}]}'
```
3. 3
### Read the conversation
Once the agent has replied, list the events. `--transform` runs against each listed event, so this prints the text of every message in order. `--format auto` overrides the interactive explorer that list commands open by default in a terminal:
``` shiki
ant beta:sessions:events list \
--session-id session_01JZCh78XvmxJjiXVy3oSi7K \
--transform 'content.0.text' \
--raw-output \
--format auto
```
Output
``` block
Summarize the benefits of type safety in one sentence.
Type safety catches errors at compile time rather than runtime, reducing bugs, improving code clarity, enabling better tooling support, and making codebases easier to maintain and refactor with confidence.
```
To watch a session as it runs, use `ant beta:sessions:events stream --session-id session_01JZCh78XvmxJjiXVy3oSi7K --format jsonl`, which writes each event to stdout as it arrives. Without `--format`, a terminal opens the interactive explorer instead.
## Scripting patterns
The CLI is designed to compose with standard shell tooling.
### Chain list output into a second command
`--transform id --raw-output` on a list endpoint emits one bare ID per line, so standard tools such as `head` and `xargs` apply directly. Capture the first result, then pass it to a follow-up command:
```python
FIRST_AGENT=$(ant beta:agents list --transform id --raw-output | head -1)
ant beta:agents:versions list \
--agent-id "$FIRST_AGENT" \
--transform "{version,created_at}" --format jsonl
```
### Inspect errors
The `--transform-error` and `--format-error` flags apply the same filtering to error responses. `--raw-output` does not apply to errors, so use `--format-error yaml` for an unquoted scalar. Extract only the error message:
```python
ant beta:agents retrieve --agent-id bogus \
--transform-error error.message --format-error yaml 2>&1
```
Output
``` block
GET "https://api.anthropic.com/v1/agents/bogus?beta=true": 404 Not Found
Agent not found.
```
## Use the CLI from Claude Code
[Claude Code](../../02-Claude-Code-CLI/code-home.md) can use the `ant` CLI out of the box. With the CLI installed and authenticated, you can ask Claude Code to operate on your API resources directly. For example:
- "List my recent agent sessions and summarize which ones errored."
- "Upload every PDF in `./reports` to the Files API and print the resulting IDs."
- "Pull the events for session `session_01...` and tell me where the agent got stuck."
Claude Code shells out to `ant`, parses the structured output, and reasons over the results (no custom integration code required).
## Authenticate curl requests with CLI credentials
Scripts that call the API with `curl` or another HTTP client can use the credentials stored by [`ant auth login`](cli-sdks-libraries-cli-quickstart.md#authentication) instead of a static API key. The OAuth access token goes in the `Authorization` header as a bearer token; the `x-api-key` header is only for static API keys.
`ant auth print-credentials --access-token` prints the active profile's access token, refreshing it first if it is expired or near expiry:
cURL
```python
curl https://api.anthropic.com/v1/messages \
-H "Authorization: Bearer $(ant auth print-credentials --access-token)" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 256,
"messages": [{"role": "user", "content": "hi"}]
}'
```
Keep `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` unset when working from a CLI login. Either variable takes precedence over the login for `ant` commands (see [Credential precedence](manage-claude-wif-reference.md#credential-precedence)) and can silently route them to a different organization or workspace.
Run [`ant auth status`](cli-sdks-libraries-cli-authentication.md#check-authentication-status) to confirm which organization and workspace you are logged in to; it warns when an environment variable is overriding your login.
---
## Connect to a Managed Agents session from your terminal - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/sessions-connect
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-sessions-connect.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fsessions-connect)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# Connect to a Managed Agents session from your terminal
Copy page
Attach the ant CLI to a Claude Managed Agents session to follow its transcript live, send messages, allow or deny tool calls, or open the session viewer in your browser.
Copy page
`ant beta:sessions connect` attaches your terminal to an existing Claude Managed Agents [session](managed-agents-sessions.md). It loads the session's transcript and follows it live as the agent works. You can also step in: send a message, interrupt the agent, or allow or deny a tool call that is waiting for approval. With `--web`, it opens the session in the Claude Console's session viewer in your browser instead.
The command requires version 1.32.0 or later of the CLI. To install or update the CLI and authenticate, see the [CLI quickstart](cli-sdks-libraries-cli-quickstart.md).
## Connect to a session
Pass the ID of a session in your workspace. You can copy it from the create response, from `ant beta:sessions list`, or from the Console.
CLI
```python
ant beta:sessions connect sesn_011CZkZAtmR3yMPDzynEDxu7
```
Without `--web`, the command needs an interactive terminal. In scripts, use `ant beta:sessions:events stream` and `ant beta:sessions:events send` instead. See [CLI scripting and automation](cli-sdks-libraries-cli-scripting.md).
Press Ctrl+C to detach. The session keeps running, and connecting again loads its full history.
## Follow and steer the session
The terminal view shows the conversation live: messages and tool calls, with each call's duration and outcome. A status bar shows whether the session is running, idle, or waiting for your approval. In [multiagent](managed-agents-multiagent-orchestration.md) sessions, the view follows the session's primary thread, which includes the messages the coordinator exchanges with the agents it delegates to.
| Key | Action |
|--------------------|----------------------------------------------------------------------------------------------------------------------------|
| Enter | Send your input as a `user.message` event. Alt+Enter or Ctrl+J starts a new line. |
| Esc | Interrupt the agent while it's running (`user.interrupt`). |
| Ctrl+O | Show or hide detail: tool inputs and results, token usage, and status events. `--verbose` (`-v`) starts with detail shown. |
| Page Up, Page Down | Scroll through the transcript. Scrolling up pauses following; End resumes it. |
| Ctrl+C | Detach. Ctrl+D on an empty input line also detaches. |
When a tool call is waiting for your approval, the input line changes to **Allow tool call?** This happens under an `always_ask` policy, or under `auto` when the server reaches no determination. Choose **Yes**, **No**, or **No, and tell the agent why**. The CLI sends your choice as a [`user.tool_confirmation`](managed-agents-permission-policies.md#respond-to-confirmation-requests) event, with any reason you type as its `deny_message`.
If the session is `terminated` or deleted, the view is read-only.
## Open the session viewer in your browser
CLI
```python
ant beta:sessions connect sesn_011CZkZAtmR3yMPDzynEDxu7 --web
```
`--web` serves the Console's session viewer from a local server on `127.0.0.1`, prints its URL, and opens it in your browser. Add `--no-browser` to skip opening the browser. You can also send messages, interrupt the agent, and allow or deny a tool call from the browser. Unlike the terminal view, the browser viewer follows every thread of a multiagent session.
The URL can be opened once, within two minutes of being printed. Reloading that tab works, but to open the viewer anywhere else, run the command again. Your credentials never leave the CLI: the page sends requests only to the local `ant` process, which makes the API requests. The server runs until you press Ctrl+C.
---
## Using the CLI - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/cli/using
Path: 04-API-Reference/Other/cli-sdks-libraries-cli-using.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fcli%2Fusing)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)ant CLI
# Using the CLI
Copy page
Command structure, output formats, GJSON transforms, request bodies, and debugging for the ant CLI.
Copy page
This page covers the `ant` CLI's input and output mechanics that apply across every endpoint. To install and authenticate, see the [Quickstart](cli-sdks-libraries-cli-quickstart.md). To chain commands and version-control resources, see [CLI scripting and automation](cli-sdks-libraries-cli-scripting.md).
## Command structure
Commands follow a `resource action` pattern. Nested resources use colons:
``` block
ant <resource>[:<subresource>] <action> [flags]
```
Run `ant --help` for the full resource list, or append `--help` to any subcommand for its flags.
Resources in beta (including agents, sessions, deployments, and environments) live under the `beta:` prefix. Commands in this namespace automatically send the appropriate `anthropic-beta` header for that resource, so you don't need to pass it yourself. Use `--beta <header>` only to override the default (for example, to opt into a different schema version).
```python
ant models list
ant messages create --model claude-opus-5-5 --max-tokens 1024 ...
ant beta:agents retrieve --agent-id agent_01...
ant beta:sessions:events list --session-id session_01...
```
### Global flags
| Flag | Description |
|---------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--profile` | Named profile to use for this invocation (equivalent to setting `ANTHROPIC_PROFILE`). See [Switch between workspaces](cli-sdks-libraries-cli-authentication.md#switch-between-workspaces). |
| `--format` | Output format: `auto`, `json`, `jsonl`, `yaml`, `pretty`, `raw`, `explore` |
| `--transform` | Filter or reshape the response with a [GJSON path](#transform-output-with-gjson) |
| `-r`, `--raw-output` | Print string results without surrounding quotes, like `jq -r` |
| `--base-url` | Override the API base URL |
| `--workspace-id` | Optional. Workspace ID (`wrkspc_...`) to send as the `anthropic-workspace-id` header, for API keys with access to multiple workspaces (equivalent to setting `ANTHROPIC_WORKSPACE_ID`). See [Select a workspace](manage-claude-authentication.md#select-a-workspace). [Admin API](manage-claude-admin-api.md) commands take their own `--workspace-id`, which names the workspace they manage instead. |
| `--debug` | Print full HTTP request and response to stderr |
| `--format-error`, `--transform-error` | Same as `--format` and `--transform` but applied to [error responses](cli-sdks-libraries-cli-scripting.md#inspect-errors) |
## Output formats
`auto` pretty-prints JSON and is the default for commands that create or modify resources. List and retrieve commands default to the [interactive explorer](#interactive-explorer) when writing to a terminal, and to pretty-printed JSON when piped. Override either default with `--format`:
```python
ant models retrieve --model-id claude-opus-5-5 --format yaml
```
Output
```python
type: model
id: claude-opus-5-5
display_name: Claude Opus 5.5
created_at: "2026-09-22T00:00:00Z"
...
```
List endpoints auto-paginate. In the default formats each item is written separately (one compact JSON object per line in `jsonl` mode, a stream of YAML documents in `yaml` mode), which streams cleanly into `head`, `grep`, and `--transform` filters.
### Interactive explorer
The explorer is a fold-and-search TUI for browsing large responses. Arrow keys expand and collapse nodes, `/` searches, `q` exits. List and retrieve commands open it by default when connected to a terminal. Pass `--format explore` to open it explicitly:
```python
ant models list --format explore
```
## Transform output with GJSON
Use `--transform` to reshape responses before printing. The expression is a [GJSON path](https://github.com/tidwall/gjson/blob/master/SYNTAX.md). For list endpoints the transform runs against each item individually, not the envelope:
```python
ant beta:agents list \
--transform "{id,name,model}" \
--format jsonl
```
Output
```python
{"id": "agent_011CYm1BLqPX...", "name": "Docs CLI Test Agent", "model": "claude-opus-5-5"}
{"id": "agent_011CYkVwfaEt...", "name": "Coffee Making Assistant", "model": "claude-opus-5-5"}
{"id": "agent_011CYixHhtUP...", "name": "Coding Assistant", "model": "claude-opus-5-5"}
```
### Extract a scalar
To capture a single field as an unquoted string (for example, the ID of a newly created resource), pair `--transform` with `--raw-output`. The result prints without JSON quotes and is ready to assign to a shell variable:
```python
AGENT_ID=$(ant beta:agents create \
--name "My Agent" \
--model '{id: claude-opus-5-5}' \
--transform id --raw-output)
printf '%s\n' "$AGENT_ID"
```
Output
``` block
agent_011CYm1BLqPXpQRk5khsSXrs
```
`--raw-output` is distinct from `--format raw`. `--raw-output` strips JSON quotes from string results, like `jq -r`. `--format raw` prints the response body's raw JSON bytes without auto-paginating; on list endpoints it applies `--transform` to the pagination envelope rather than to each item.
## Passing request bodies
The right input mechanism depends on the shape of the data: use **flags** for scalar fields and short structured values, pipe a **stdin** document for nested or multiline bodies, and use **`@file` references** to pull file contents into any string or binary field.
### Flags
Scalar fields map directly to flags. Structured fields accept a relaxed YAML-like syntax (unquoted keys, optional quotes around strings) or strict JSON:
```python
ant beta:sessions create \
--agent '{type: agent, id: agent_011CYm1BLqPXpQRk5khsSXrs, version: 1}' \
--environment-id env_01595EKxaaTTGwwY3kyXdtbs \
--title "CLI docs test session"
```
Repeatable flags build arrays. Each `--tool` or `--event` appends one element:
```python
ant beta:agents create \
--name "Research Agent" \
--model '{id: claude-opus-5-5}' \
--tool '{type: agent_toolset_20260401}' \
--tool '{type: custom, name: search_docs, input_schema: {type: object, properties: {query: {type: string}}}}'
```
### Stdin
Pipe a JSON or YAML document to stdin to supply the full request body. Fields from stdin are merged with flags, with flags taking precedence. Here `version` is the optimistic-locking token returned by an earlier `retrieve`, and `$AGENT_ID` was captured as in [Extract a scalar](#extract-a-scalar):
```python
echo '{"description": "Updated test agent.", "version": 1}' | \
ant beta:agents update --agent-id "$AGENT_ID"
```
Heredocs work the same way and are convenient for multiline YAML. Quote the delimiter (as in `<<'YAML'`) to disable variable expansion inside the body.
```python
ant beta:agents create <<'YAML'
name: Research Agent
model: claude-opus-5-5
system: |
You are a research assistant. Cite sources for every claim.
tools:
- type: agent_toolset_20260401
YAML
```
### File references
Flags that take a file path, such as `--file` on the upload command, accept a bare path:
```python
ant files upload --file ./report.pdf
```
To inline a file's contents into a string-valued field, prefix the path with `@`:
```python
ant beta:agents create \
--name "Researcher" --model '{id: claude-opus-5-5}' \
--system @./prompts/researcher.txt
```
Inside structured flag values, wrap the path in quotes. To send a PDF to the Messages API:
```python
ant messages create \
--model claude-opus-5-5 \
--max-tokens 1024 \
--message '{role: user, content: [
{type: document, source: {type: base64, media_type: application/pdf, data: "@./scan.pdf"}},
{type: text, text: "Extract the text from this scanned document."}
]}' \
--transform 'content.#(type=="text").text' --raw-output
```
The CLI detects the file type and encodes binary files as base64 automatically. To force a specific encoding use `@file://` for plain text or `@data://` for base64. Escape a literal leading `@` with a backslash (`\@username`).
## Debugging
Add `--debug` to any command to print the exact HTTP request and response (headers and body) to stderr. API keys are redacted.
```python
ant --debug beta:agents list
```
Output
```python
GET /v1/agents?beta=true HTTP/1.1
Host: api.anthropic.com
Anthropic-Beta: managed-agents-2026-04-01
Anthropic-Version: 2023-06-01
X-Api-Key: <REDACTED>
...
```
## Available resources
Every API resource the CLI exposes is documented in the [API reference](../Endpoints/cli-messages-create.md). For a local listing, run `ant --help`, and append `--help` to any subcommand for its flags and parameters.
## Next steps
[CLI scripting and automation](cli-sdks-libraries-cli-scripting.md)
Version-control API resources, scripting patterns, and use from Claude Code
[API reference](../Endpoints/cli-messages-create.md)
Endpoint-specific parameters, request fields, and response schemas
[CLI authentication options](cli-sdks-libraries-cli-authentication.md)
API keys, headless hosts, multiple workspaces, and named profiles
---
## Apple Foundation Models - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/libraries/apple-foundation-models
Path: 04-API-Reference/Other/cli-sdks-libraries-libraries-apple-foundation-models.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Flibraries%2Fapple-foundation-models)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Libraries and integrations
# Apple Foundation Models
Copy page
Use Claude on Apple platforms through the Foundation Models framework with the Claude for Foundation Models Swift package.
Copy page
[Claude for Foundation Models](https://github.com/anthropics/ClaudeForFoundationModels) is a Swift package that makes Claude available as a server-side language model in Apple's [Foundation Models](https://developer.apple.com/documentation/foundationmodels) framework. The package conforms Claude to the framework's `LanguageModel` protocol, so you drive it with the same `LanguageModelSession` API you use for Apple's on-device model: `respond(to:)`, streaming, guided generation, and tool calling all work the same way.
Requests go directly from your app to the Claude API; Apple is not in the request path and does not see prompts or responses. Usage is billed to your Anthropic account at [standard API pricing](../../17-Billing-Plans/about-claude-pricing.md), so your organization needs an available credit balance or an active billing method. Your app decides when to use Claude and when to use Apple's on-device model: pass whichever model you want to each session.
**Beta.** This package targets the Foundation Models server-side language model API introduced in the OS 27 betas. APIs might change during the beta.
Claude for Foundation Models is **not** a general-purpose Messages API client. Its public surface is the Foundation Models provider conformance plus the configuration types that reach it (`ClaudeLanguageModel`, `ClaudeModel`, `AuthMode`, `ClaudeServerTool`). For direct access to the Messages API in another language, see the [Client SDKs](cli-sdks-libraries-overview.md#client-sdks).
## Requirements
- iOS 27, macOS 27, visionOS 27, or watchOS 27 (all in beta): the OS releases whose Foundation Models framework supports server-side language models
- Xcode 27 (beta)
- A Claude API key from the [Claude Console](usage-limits.md) for development. See [Authentication](#authentication) for production options.
## Install the package
Add the package to your `Package.swift`:
```python
dependencies: [
.package(url: "https://github.com/anthropics/ClaudeForFoundationModels.git", from: "0.1.0")
]
```
Or in Xcode: **File** \> **Add Package Dependencies…** and enter the repository URL.
Then add `ClaudeForFoundationModels` to your target's dependencies and import it alongside `FoundationModels`:
```python
import FoundationModels
import ClaudeForFoundationModels
```
## Quick start
`ClaudeLanguageModel` is the entry point. Pass it to `LanguageModelSession` and use the session exactly as you would with any Foundation Models provider:
```python
import FoundationModels
import ClaudeForFoundationModels
let model = ClaudeLanguageModel(
name: .sonnet5,
auth: .apiKey(ProcessInfo.processInfo.environment["ANTHROPIC_API_KEY"] ?? "")
)
let session = LanguageModelSession(model: model)
let response = try await session.respond(to: "Plan a 4-day trip to Buenos Aires.")
print(response.content)
```
The initializer also accepts `baseURL` (default `https://api.anthropic.com`), `timeout`, and `serverTools` (see [Server-side tools](#server-side-tools)).
For a complete working program, the repository includes [`Examples/ClaudeExample`](https://github.com/anthropics/ClaudeForFoundationModels/tree/main/Examples/ClaudeExample), a runnable command-line target that streams a chat turn to the terminal, with a `--search` flag that enables server-side web search for the turn. Running it requires a macOS 27 host.
## Choosing a model
Model identifiers are values of `ClaudeModel`. Use a compiled-in constant, or construct one with explicit capabilities for an ID that isn't compiled in yet (see [Capabilities](#capabilities)):
```python
ClaudeLanguageModel(name: .opus5_5, auth: auth)
```
Constants mirror API model IDs (`.opus5` is `claude-opus-5`) and carry each model's capabilities. New models ship as new constants in package releases; check `ClaudeModel` in Xcode for the current list, and the [Models overview](../../20-Models/about-claude-models-overview.md) to compare models.
### Capabilities
Each `ClaudeModel` declares what it accepts: sampling parameters, effort levels, adaptive thinking, structured output, and image input. The package uses this to determine which request fields to send, because sending a field a model rejects is a hard error. The constants carry the right capabilities. For an ID that isn't compiled in, declare what the model accepts (there is deliberately no shorthand that guesses):
```python
let model = ClaudeModel(
id: "claude-experimental-x",
capabilities: .init(samplingParams: false, effortLevels: [.low, .high])
)
ClaudeLanguageModel(name: model, auth: auth)
```
### Effort
Pin a Claude [effort level](../Guides/build-with-claude-effort.md) for every request with `fixedEffort:`. It takes precedence over the framework's per-request reasoning hints. The framework's named reasoning levels stop at high; to request more effort for a single request instead, pass a custom reasoning level naming the Claude effort (`.custom("xhigh")` or `.custom("max")`), which maps directly. The API defaults to `high` when no effort is sent:
```python
ClaudeLanguageModel(name: .opus5_5, auth: auth, fixedEffort: .xhigh)
```
The level must be one the model accepts. Each `ClaudeModel` declares which of the five levels (`low`, `medium`, `high`, `xhigh`, `max`) its model takes, if any: some models don't accept effort at all.
### When to use Claude versus the on-device model
Apple's on-device model is fast, private, and available offline, but it is sized for lightweight tasks. Escalate to Claude when you need larger context, frontier reasoning, or server-side tools such as web search and code execution. Because both use the same `LanguageModelSession` API, you can switch by swapping the `model:` argument.
## Authentication
Set the credential with the `auth:` parameter. Use `.appAttest` to ship without a back end, `.proxied` to route requests through your own back end, or `.apiKey` to iterate during development.
### App Attest
Each installation of your app uses Apple's [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity) service to prove that it is a genuine, unmodified build of the app you registered. Anthropic then issues the device a short-lived access token that bills usage to your workspace. The app ships no API key, and there is no proxy for you to operate.
App Attest authentication is available only when your app calls the Claude API directly. It is not available through Amazon Bedrock, Google Cloud, or Microsoft Foundry.
To ship without running a back end, use `.appAttest`:
```python
ClaudeLanguageModel(
name: .sonnet5,
auth: .appAttest(clientID: "clid_...")
)
```
App Attest requires a physical device. The Simulator, and hardware without a Secure Enclave, cannot perform App Attest. Use `.apiKey` while iterating in the Simulator, and `.appAttest` when running on a device.
To set up App Attest, you need your Apple Developer Team ID and the admin, owner, or primary owner role in your organization. Configure your Xcode project and register your app in the [Claude Console](usage-limits.md):
1. In Xcode, add the **App Attest** capability to your app target under **Signing & Capabilities**.
2. In your workspace's settings in the Claude Console, open **App integrations**.
3. Click **Create app integration** and enter a name, your Apple Developer Team ID, and one or more bundle IDs (up to 32).
4. Copy the client ID (`clid_...`) from the integration's **Overview** tab and pass it to your app's Claude configuration.
The first time your app uses Claude on a device, the app requests a challenge from Anthropic, attests the device with Apple's `DCAppAttestService`, and exchanges the verified attestation for an access token. The Claude for Foundation Models package runs this flow automatically and requests new tokens as they expire; there is no attestation code for you to write.
Tokens are scoped to your workspace, expire after one hour, and authorize only [Messages API](../Endpoints/messages-create.md) calls. They carry no end-user identity: App Attest identifies your app, not the person using it, so handle any per-user logic in your app.
To stop a compromised or retired app, revoke its integration: in your workspace's settings in the Claude Console, open **App integrations**, select the integration, and click **Revoke**, then confirm. Revoking an integration revokes its outstanding tokens, and its registered devices can no longer request new ones. Revocation is permanent, so create a new app integration to restore access.
### Proxy (production)
For production, route requests through your own back end with `.proxied`. The relay at `baseURL` adds the Claude API credential server-side, so the app ships no key. The `headers` you provide are sent on every request so your proxy can authorize the caller. Pass `[:]` if it needs none:
```python
ClaudeLanguageModel(
name: .sonnet5,
auth: .proxied(headers: ["X-App-Token": "..."]),
baseURL: URL(string: "https://api.yourapp.com/claude")!
)
```
Your proxy receives standard [Messages API](../Endpoints/messages-create.md) requests, attaches the `x-api-key` header, and forwards them to `https://api.anthropic.com`.
### API key (development)
Pass an API key directly while developing:
```python
ClaudeLanguageModel(name: .sonnet5, auth: .apiKey("YOUR_API_KEY"))
```
A key bundled into an app is extractable from the shipping binary, and anyone who extracts it can make requests billed to your account. Use `.apiKey` for development only, and switch to App Attest or a proxy before release.
## Streaming
`streamResponse(to:)` returns the response incrementally. Each element is a cumulative snapshot of the response so far, not a delta:
```python
let stream = session.streamResponse(to: "Summarize today's top science stories.")
for try await partial in stream {
print(partial.content)
}
```
## Structured output
Annotate a type with `@Generable` and request it with `generating:`. The model returns a value of that type through [structured outputs](../Guides/build-with-claude-structured-outputs.md):
```python
@Generable
struct Trip {
@Guide(description: "Destination city") var destination: String
@Guide(description: "Length in days") var days: Int
}
let response = try await session.respond(to: "Plan a trip to Tokyo.", generating: Trip.self)
print(response.content.destination)
```
Structured output requires a model whose capabilities include it (all compiled-in constants do). If the chosen model does not, the package throws `LanguageModelError.unsupportedGenerationGuide` rather than silently degrading.
## Tool use
### Client-side tools
The framework's `tools:` array works unchanged. Conform your types to `Tool`, pass them to `LanguageModelSession`, and the framework invokes them on the device when Claude calls them. See [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md).
```python
let session = LanguageModelSession(model: model, tools: [FindRestaurantsTool()])
```
### Server-side tools
[Server tools](../Agents-Tools/agents-and-tools-tool-use-server-tools.md) (web search, web fetch, and code execution) run on Anthropic's infrastructure within a single round trip, with nothing for the framework to invoke on the device. Configure them for each model with `serverTools:`:
```python
let model = ClaudeLanguageModel(
name: .sonnet5,
auth: auth,
serverTools: [
.webSearch(maxUses: 5),
.codeExecution,
]
)
```
`.webSearch` and `.webFetch` accept optional `allowedDomains`, `blockedDomains`, and `maxUses`. Server tool activity surfaces in the transcript as `ClaudeServerToolSegment` custom segments.
`serverTools` is configured on `ClaudeLanguageModel` rather than on `LanguageModelSession` because the session type is Apple's. To use different server-tool sets for each conversation, construct multiple `ClaudeLanguageModel` instances.
## Images
Models whose capabilities include image input declare the framework's vision capability. Pass image content through the framework's standard session API; the package converts it to the Claude API's image format. See [Vision](../Guides/build-with-claude-vision.md) for image requirements.
## Error handling
The package maps Claude API errors onto Apple's `LanguageModelError` cases where one fits: context-window overflow surfaces as `.contextSizeExceeded`, HTTP 429 as `.rateLimited`, a request past the configured timeout as `.timeout`. Provider errors with no framework equivalent surface as `ClaudeError`. Pattern-match to drive product flows:
```python
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch ClaudeError.missingCredential {
// Prompt for an API key.
} catch let error as LanguageModelError {
// Framework-shaped errors (rate limits, guardrails, context length, decoding).
} catch {
// Transport errors.
}
```
A common pattern is to catch `.rateLimited` and fall back to `SystemLanguageModel` for that turn, queue the request, or surface a retry affordance.
## Feature support
The package surfaces the Messages API capabilities that the Foundation Models provider protocol can express. Features with no representation in Apple's protocol are not available through it, including:
- Prompt caching controls (the package applies prompt caching automatically; cache TTL and breakpoint placement are not configurable)
- Stop sequences
- Batch processing
- Files API
- Token counting
- Beta headers
## Additional resources
| Reference | Covers |
|-----------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------|
| [Apple Foundation Models documentation](https://developer.apple.com/documentation/foundationmodels) | `LanguageModelSession`, `@Generable`, `Transcript`, `Tool`, and the rest of the framework surface |
| [`ClaudeForFoundationModels` on GitHub](https://github.com/anthropics/ClaudeForFoundationModels) | Source, the runnable example, and the issue tracker |
| [Claude API reference](../Endpoints/overview.md) | The underlying Messages API |
The package is licensed under Apache 2.0. Bug reports are welcome through GitHub issues. External pull requests are not being accepted during the beta period.
---
## OpenAI SDK compatibility - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/libraries/openai-sdk
Path: 04-API-Reference/Other/cli-sdks-libraries-libraries-openai-sdk.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Flibraries%2Fopenai-sdk)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Libraries and integrations
# OpenAI SDK compatibility
Copy page
Anthropic provides a compatibility layer that enables you to use the OpenAI SDK to test the Claude API. With a few code changes, you can quickly evaluate Anthropic model capabilities.
Copy page
This compatibility layer is primarily intended to test and compare model capabilities, and is not considered a long-term or production-ready solution for most use cases. While it is intended to remain fully functional and not have breaking changes, the priority is the reliability and effectiveness of the [Claude API](../Endpoints/overview.md).
For more information on known compatibility limitations, see [Important OpenAI compatibility limitations](#important-openai-compatibility-limitations).
If you encounter any issues with the OpenAI SDK compatibility feature, please share your feedback via this [compatibility feedback form](https://forms.gle/oQV4McQNiuuNbz9n8).
For the best experience and access to Claude API full feature set ([PDF processing](../Guides/build-with-claude-pdf-support.md), [citations](../Guides/build-with-claude-citations.md), [thinking](../Guides/build-with-claude-thinking.md), and [prompt caching](../Guides/build-with-claude-prompt-caching.md)), use the native [Claude API](../Endpoints/overview.md).
## Getting started with the OpenAI SDK
To use the OpenAI SDK compatibility feature, you'll need to:
1. Use an official OpenAI SDK
2. Change the following
- Update your base URL to point to the Claude API
- Replace your API key with a [Claude API key](playground.md)
- If your key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, also send the `anthropic-workspace-id` header on every request (for example, `default_headers` in the Python SDK or `defaultHeaders` in TypeScript); see [Select a workspace](manage-claude-authentication.md#select-a-workspace)
- Update your model name to use a [Claude model](../../20-Models/about-claude-models-overview.md)
3. Review the following sections for what features are supported
### Quick start example
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("ANTHROPIC_API_KEY"), # Your Claude API key
base_url="https://api.anthropic.com/v1/", # the Claude API endpoint
)
response = client.chat.completions.create(
model="claude-opus-5-5", # Claude model name
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who are you?"},
],
)
print(response.choices[0].message.content)
```
## Important OpenAI compatibility limitations
### API behavior
Here are the most substantial differences from using OpenAI:
- The `strict` parameter for function calling is ignored, which means the tool use JSON is not guaranteed to follow the supplied schema. For guaranteed schema conformance, use the native [Claude API with Structured Outputs](../Guides/build-with-claude-structured-outputs.md).
- Audio input is not supported; it will be ignored and stripped from input
- Prompt caching is not supported, but it is supported in the [Anthropic SDKs](cli-sdks-libraries-overview.md)
- System/developer messages are hoisted and concatenated to the beginning of the conversation, as Anthropic only supports a single initial system message.
Most unsupported fields are silently ignored rather than producing errors. These are all documented in the following sections.
### Output quality considerations
If you’ve done lots of tweaking to your prompt, it’s likely to be well-tuned to OpenAI specifically. Consider reworking it for Claude using the [prompting best practices guide](../../10-Prompting-Guides/build-with-claude-prompt-engineering-claude-prompting-best-practices.md).
### System / developer message hoisting
Most of the inputs to the OpenAI SDK clearly map directly to Anthropic’s API parameters, but one distinct difference is the handling of system / developer prompts. These two prompts can be put throughout a chat conversation via OpenAI. Since Anthropic only supports an initial system message, the API takes all system/developer messages and concatenates them together with a single newline (`\n`) in between them. This full string is then supplied as a single system message at the start of the messages.
### Thinking support
You can enable [thinking](../Guides/build-with-claude-thinking.md) by adding the `thinking` parameter. On current models thinking is adaptive, with Claude deciding when and how deeply to think, and on Claude 5 models it is on by default; manually configured extended thinking is a legacy mode. Although thinking improves Claude's reasoning for complex tasks, the OpenAI SDK doesn't return Claude's thinking. For full thinking features, including [summarized thinking](../Guides/build-with-claude-thinking.md#summarized-thinking), use the native Claude API.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
response = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Who are you?"}],
extra_body={"thinking": {"type": "enabled", "budget_tokens": 2000}},
)
```
## Rate limits
Rate limits follow Anthropic's [standard limits](../Endpoints/rate-limits.md) for the `/v1/messages` endpoint.
## Detailed OpenAI compatible API support
### Request fields
#### Simple fields
| Field | Support status |
|-------------------------|------------------------------------------------------------------------------------------------------------------------------|
| `model` | Use Claude model names |
| `max_tokens` | Fully supported |
| `max_completion_tokens` | Fully supported |
| `stream` | Fully supported |
| `stream_options` | Fully supported |
| `top_p` | Fully supported |
| `parallel_tool_calls` | Fully supported |
| `stop` | All non-whitespace stop sequences work |
| `temperature` | Between 0 and 1 (inclusive). Values greater than 1 are capped at 1. |
| `n` | Must be exactly 1 |
| `logprobs` | Ignored |
| `metadata` | Ignored |
| `response_format` | Ignored. For JSON output, use [Structured Outputs](../Guides/build-with-claude-structured-outputs.md) with the native Claude API |
| `prediction` | Ignored |
| `presence_penalty` | Ignored |
| `frequency_penalty` | Ignored |
| `seed` | Ignored |
| `service_tier` | Ignored |
| `audio` | Ignored |
| `logit_bias` | Ignored |
| `store` | Ignored |
| `user` | Ignored |
| `modalities` | Ignored |
| `top_logprobs` | Ignored |
| `reasoning_effort` | Ignored |
#### `tools` / `functions` fields
### Show fields
Tools
Functions
`tools[n].function` fields
| Field | Support status |
|---------------|--------------------------------------------------------------------------------------------------------------------------------------|
| `name` | Fully supported |
| `description` | Fully supported |
| `parameters` | Fully supported |
| `strict` | Ignored. Use [Structured Outputs](../Guides/build-with-claude-structured-outputs.md) with native Claude API for strict schema validation |
#### `messages` array fields
### Show fields
Developer role
System role
User role
Assistant role
Tool role
Function role
Fields for `messages[n].role == "developer"`
Developer messages are hoisted to beginning of conversation as part of the initial system message
| Field | Support status |
|-----------|------------------------------|
| `content` | Fully supported, but hoisted |
| `name` | Ignored |
### Response fields
| Field | Support status |
|-----------------------------------|--------------------------------|
| `id` | Fully supported |
| `choices[]` | Will always have a length of 1 |
| `choices[].finish_reason` | Fully supported |
| `choices[].index` | Fully supported |
| `choices[].message.role` | Fully supported |
| `choices[].message.content` | Fully supported |
| `choices[].message.tool_calls` | Fully supported |
| `object` | Fully supported |
| `created` | Fully supported |
| `model` | Fully supported |
| `finish_reason` | Fully supported |
| `content` | Fully supported |
| `usage.completion_tokens` | Fully supported |
| `usage.prompt_tokens` | Fully supported |
| `usage.total_tokens` | Fully supported |
| `usage.completion_tokens_details` | Always empty |
| `usage.prompt_tokens_details` | Always empty |
| `choices[].message.refusal` | Always empty |
| `choices[].message.audio` | Always empty |
| `logprobs` | Always empty |
| `service_tier` | Always empty |
| `system_fingerprint` | Always empty |
### Error message compatibility
The compatibility layer maintains consistent error formats with the OpenAI API. However, the detailed error messages will not be equivalent. Only use the error messages for logging and debugging.
### Header compatibility
While the OpenAI SDK automatically manages headers, here is the complete list of headers supported by the Claude API for developers who need to work with them directly.
| Header | Support Status |
|----------------------------------|---------------------|
| `x-ratelimit-limit-requests` | Fully supported |
| `x-ratelimit-limit-tokens` | Fully supported |
| `x-ratelimit-remaining-requests` | Fully supported |
| `x-ratelimit-remaining-tokens` | Fully supported |
| `x-ratelimit-reset-requests` | Fully supported |
| `x-ratelimit-reset-tokens` | Fully supported |
| `retry-after` | Fully supported |
| `request-id` | Fully supported |
| `openai-version` | Always `2020-10-01` |
| `authorization` | Fully supported |
| `openai-processing-ms` | Always empty |
---
## SDK middleware - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/middleware
Path: 04-API-Reference/Other/cli-sdks-libraries-middleware.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fmiddleware)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# SDK middleware
Copy page
Intercept and modify the SDK's requests and responses.
Copy page
The Claude SDK provides a middleware (or interceptor) hook that lets you run code before a request is sent and after the response is received. Use middleware for cross-cutting concerns such as logging, custom retries, request annotation, and refusal fallback handling.
Each middleware can inspect or replace the request before calling `next()`, and the response after `next()` returns.
## Registering middleware
Each middleware is a function that receives the outgoing request and the next handler. Call `call_next(request)` to forward the request to the rest of the chain (or directly to the SDK core if this is the last middleware), and return its response. Anything before that call runs on the way out; anything after runs on the way back.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
def logging_middleware(request: APIRequest, call_next: CallNext) -> APIResponse[Any]:
# Before the request
print(f"-> {request.method} {request.url}")
# Forward the request to the rest of the chain
response = call_next(request)
# After the request
print(f"<- {response.status_code}")
return response
client = Anthropic(middleware=[logging_middleware])
```
## Middleware ordering
When you register multiple middleware, they apply in the order given: the first middleware's "before" code runs first, and its "after" code runs last. Middleware registered on the client runs before middleware passed as a per-request option.
In the Go SDK, repeated `option.WithMiddleware` calls concatenate (client first, then method). In the other SDKs, pass an array; later entries wrap inner.
## Replacing the HTTP client
The SDK also accepts a custom HTTP client (for proxy configuration, custom TLS, or connection pooling). Only one HTTP client is used per SDK client; setting it replaces the default. The custom HTTP client receives requests after all middleware has run.
## Built-in middleware
The SDK ships a refusal-fallback middleware that automatically retries requests Claude Fable 5 declines on a fallback model. See [Detect and retry on a fallback model](../Guides/build-with-claude-refusals-and-fallback.md#client-side-fallback) for setup and per-language examples.
---
## SDKs, CLI, and libraries - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/overview
Path: 04-API-Reference/Other/cli-sdks-libraries-overview.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Foverview)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
SDKs, CLI, and libraries
# SDKs, CLI, and libraries
Copy page
Official tools for building with the Claude API: client SDKs in seven languages, the ant CLI, and framework-specific libraries.
Copy page
Anthropic provides three kinds of official tooling for building with the Claude API:
- **CLI:** The `ant` command-line tool for shell scripting and interactive use.
- **Client SDKs:** General-purpose Messages API clients for Python, TypeScript, C#, Go, Java, PHP, and Ruby. Each SDK provides idiomatic interfaces, type safety, and built-in support for streaming, retries, and error handling.
- **Libraries and integrations:** Packages and compatibility layers that expose Claude inside another framework's API surface rather than the Messages API directly.
For the full API specification, see the [API reference](../Endpoints/overview.md).
## CLI
[ant CLI](cli-sdks-libraries-cli-quickstart.md)
Shell scripting, typed flags, response transforms
## Client SDKs
[Python](cli-sdks-libraries-sdks-python.md)
Sync and async clients, Pydantic models
[TypeScript](cli-sdks-libraries-sdks-typescript.md)
Node.js, Deno, Bun, and browser support
[C#](cli-sdks-libraries-sdks-csharp.md)
.NET Standard 2.0+, IChatClient integration
[Go](cli-sdks-libraries-sdks-go.md)
Context-based cancellation, functional options
[Java](cli-sdks-libraries-sdks-java.md)
Builder pattern, CompletableFuture async
[PHP](cli-sdks-libraries-sdks-php.md)
Value objects, builder pattern
[Ruby](cli-sdks-libraries-sdks-ruby.md)
Sorbet types, streaming helpers
## Libraries and integrations
Libraries and integrations expose Claude through another framework's API surface. They are not general-purpose Messages API clients.
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)
Swift package for Apple's `LanguageModelSession` API
[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
Use Claude through the OpenAI SDK surface
## Building agents or using Claude Code?
The CLI, client SDKs, and libraries are for calling the Claude API yourself: you send each request and handle each response. Claude Code, the Claude Agent SDK, and Claude Managed Agents work at a higher level, providing the agent loop, tool execution, and runtime.
[Claude Code](../../02-Claude-Code-CLI/code-home.md)
Agentic coding tool for delegating coding tasks to Claude
[Claude Agent SDK](../../05-Agent-SDK/agent-sdk-overview.md)
Build agents that run in a process you operate
[Claude Managed Agents](managed-agents-overview.md)
Run agents in Anthropic's managed infrastructure
---
## C# SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/csharp
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-csharp.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fcsharp)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# C# SDK
Copy page
Install and configure the Anthropic C# SDK for .NET applications with IChatClient integration
Copy page
The Anthropic C# SDK provides convenient access to the Claude API from applications written in C#.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers C#-specific SDK features and configuration.
As of version 10+, the `Anthropic` package is now the official Anthropic SDK for C#. Package versions 3.X and below were previously used for the tryAGI community-built SDK, which has moved to [`tryAGI.Anthropic`](https://www.nuget.org/packages/tryagi.Anthropic/). If you need to continue using the former client in your project, update your package reference to `tryAGI.Anthropic`.
## Installation
Install the package from [NuGet](https://www.nuget.org/packages/Anthropic):
```python
dotnet add package Anthropic
```
## Requirements
This library requires .NET Standard 2.0 or later.
## Usage
```python
using System;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
MessageCreateParams parameters = new()
{
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = "Hello, Claude",
},
],
Model = Model.ClaudeOpus5_5,
};
var message = await client.Messages.Create(parameters);
foreach (var block in message.Content)
{
if (block.TryPickText(out var textBlock))
{
Console.WriteLine(textBlock.Text);
}
}
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
## Client configuration
Configure the client using environment variables:
```python
using Anthropic;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
AnthropicClient client = new();
```
Or manually:
```python
using Anthropic;
AnthropicClient client = new() { ApiKey = "my-anthropic-api-key" };
```
Or using a combination of the two approaches.
See this table for the available options:
| Property | Environment variable | Required | Default value |
|-------------|------------------------|----------|-------------------------------|
| `ApiKey` | `ANTHROPIC_API_KEY` | false | \- |
| `AuthToken` | `ANTHROPIC_AUTH_TOKEN` | false | \- |
| `BaseUrl` | `ANTHROPIC_BASE_URL` | true | `"https://api.anthropic.com"` |
### Modifying configuration
To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `WithOptions` on any client or service:
```python
using System;
var message = await client
.WithOptions(options =>
options with
{
BaseUrl = "https://example.com",
Timeout = TimeSpan.FromSeconds(42),
}
)
.Messages.Create(parameters);
Console.WriteLine(message);
```
Using a [`with` expression](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression) makes it easy to construct the modified options.
The `WithOptions` method does not affect the original client or service.
## Streaming
The SDK defines methods that return response "chunk" streams, where each chunk can be individually processed as soon as it arrives instead of waiting on the full response. Streaming methods generally correspond to [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) or [JSONL](https://jsonlines.org) responses.
A streaming method always has a `Streaming` suffix in its name, even if it doesn't have a non-streaming variant.
These streaming methods return [`IAsyncEnumerable`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1):
```python
using System;
using Anthropic.Models.Messages;
MessageCreateParams parameters = new()
{
MaxTokens = 1024,
Messages =
[
new()
{
Role = Role.User,
Content = "Hello, Claude",
},
],
Model = Model.ClaudeOpus5_5,
};
await foreach (var message in client.Messages.CreateStreaming(parameters))
{
Console.WriteLine(message);
}
```
## Error handling
The SDK throws custom unchecked exception types:
- `AnthropicApiException`: Base class for API errors. See this table for which exception subclass is thrown for each HTTP status code:
| Status | Exception |
|--------|------------------------------------------|
| 400 | `AnthropicBadRequestException` |
| 401 | `AnthropicUnauthorizedException` |
| 403 | `AnthropicForbiddenException` |
| 404 | `AnthropicNotFoundException` |
| 422 | `AnthropicUnprocessableEntityException` |
| 429 | `AnthropicRateLimitException` |
| 5xx | `Anthropic5xxException` |
| others | `AnthropicUnexpectedStatusCodeException` |
Additionally, all 4xx errors inherit from `Anthropic4xxException`.
- `AnthropicSseException`: thrown for errors encountered during SSE streaming after a successful initial HTTP response.
- `AnthropicIOException`: I/O networking errors.
- `AnthropicInvalidDataException`: Failure to interpret successfully parsed data. For example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it from the response.
- `AnthropicException`: Base class for all exceptions.
## Retries
The SDK automatically retries 2 times by default, with a short exponential backoff between requests.
Only the following error types are retried:
- Connection errors (for example, because of a network connectivity problem)
- 408 Request Timeout
- 409 Conflict
- 429 Rate Limit
- 5xx Internal
The API may also explicitly instruct the SDK to retry or not retry a request.
To set a custom number of retries, configure the client using the `MaxRetries` property:
```python
using Anthropic;
AnthropicClient client = new() { MaxRetries = 3 };
```
Or configure a single method call using `WithOptions`:
```python
using System;
var message = await client
.WithOptions(options =>
options with { MaxRetries = 3 }
)
.Messages.Create(parameters);
Console.WriteLine(message);
```
## Timeouts
Requests time out after 10 minutes by default.
To set a custom timeout, configure the client using the `Timeout` option:
```python
using System;
using Anthropic;
AnthropicClient client = new() { Timeout = TimeSpan.FromSeconds(42) };
```
Or configure a single method call using `WithOptions`:
```python
using System;
var message = await client
.WithOptions(options =>
options with { Timeout = TimeSpan.FromSeconds(42) }
)
.Messages.Create(parameters);
Console.WriteLine(message);
```
## Pagination
The SDK defines methods that return paginated lists of results. It provides convenient ways to access the results either one page at a time or item-by-item across all pages.
### Auto-pagination
To iterate through all results across all pages, use the `Paginate` method, which automatically fetches more pages as needed. The method returns an [`IAsyncEnumerable`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.iasyncenumerable-1):
```python
using System;
var page = await client.Messages.Batches.List(parameters);
await foreach (var item in page.Paginate())
{
Console.WriteLine(item);
}
```
### Manual pagination
To access individual page items and manually request the next page, use the `Items` property, and `HasNext` and `Next` methods:
```python
var page = await client.Messages.Batches.List();
while (true)
{
foreach (var item in page.Items)
{
Console.WriteLine(item);
}
if (!page.HasNext())
{
break;
}
page = await page.Next();
}
```
## Response validation
In rare cases, the API may return a response that doesn't match the expected type. By default, the SDK does not throw an exception in this case. It throws `AnthropicInvalidDataException` only if you directly access the property.
If you would prefer to check that the response is completely well-typed upfront, then either call `Validate`:
```python
var message = await client.Messages.Create(parameters);
message.Validate();
```
Or configure the client using the `ResponseValidation` option:
```python
using Anthropic;
AnthropicClient client = new() { ResponseValidation = true };
```
Or configure a single method call using `WithOptions`:
```python
using System;
var message = await client
.WithOptions(options =>
options with { ResponseValidation = true }
)
.Messages.Create(parameters);
Console.WriteLine(message);
```
## IChatClient integration
The SDK provides an implementation of the `IChatClient` interface from the `Microsoft.Extensions.AI.Abstractions` library. This enables `AnthropicClient` (and `Anthropic.Services.IBetaService`) to be used with other libraries that integrate with these core abstractions. For example, tools in the MCP C# SDK (`ModelContextProtocol`) library can be used directly with an `AnthropicClient` exposed through `IChatClient`.
```python
using Anthropic;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Client;
// Configured using the ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN and ANTHROPIC_BASE_URL environment variables
AnthropicClient client = new();
IChatClient chatClient = client.AsIChatClient("claude-opus-5-5")
.AsBuilder()
.UseFunctionInvocation()
.Build();
// Using McpClient from the MCP C# SDK
McpClient learningServer = await McpClient.CreateAsync(
new HttpClientTransport(new() { Endpoint = new("https://learn.microsoft.com/api/mcp") }));
ChatOptions options = new() { Tools = [.. await learningServer.ListToolsAsync()] };
Console.WriteLine(await chatClient.GetResponseAsync("Tell me about IChatClient", options));
```
## Requests and responses
To send a request to the Claude API, build an instance of a `Params` class and pass it to the corresponding client method. When the response is received, it's deserialized into an instance of a C# class.
For example, `client.Messages.Create` should be called with an instance of `MessageCreateParams`, and it will return an instance of `Task<Message>`.
## Advanced usage
### Binary responses
The SDK defines methods that return binary responses, which are used for API responses that shouldn't necessarily be parsed, like non-JSON data.
These methods return `HttpResponse`:
```python
using System;
using Anthropic.Models.Files;
FileDownloadParams parameters = new() { FileID = "file_id" };
var response = await client.Files.Download(parameters);
Console.WriteLine(response);
```
To save the response content to a file, or any [`Stream`](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream), use the [`CopyToAsync`](https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.copytoasync) method:
```python
using System.IO;
using var response = await client.Files.Download(parameters);
using var contentStream = await response.ReadAsStream();
using var fileStream = File.Open(path, FileMode.OpenOrCreate);
await contentStream.CopyToAsync(fileStream); // Or any other Stream
```
### Raw responses
The SDK defines methods that deserialize responses into instances of C# classes. To access response headers, status code, or the raw response body, prefix any HTTP method call on a client or service with `WithRawResponse`:
```python
var response = await client.WithRawResponse.Messages.Create(parameters);
var statusCode = response.StatusCode;
var headers = response.Headers;
```
The raw `HttpResponseMessage` can also be accessed through the `RawMessage` property.
For non-streaming responses, you can deserialize the response into an instance of a C# class if needed:
```python
using System;
using Anthropic.Models.Messages;
var response = await client.WithRawResponse.Messages.Create(parameters);
Message deserialized = await response.Deserialize();
Console.WriteLine(deserialized);
```
For streaming responses, you can deserialize the response to an `IAsyncEnumerable` if needed:
```python
using System;
var response = await client.WithRawResponse.Messages.CreateStreaming(parameters);
await foreach (var item in response.Enumerate())
{
Console.WriteLine(item);
}
```
### Logging
All log messages are intended for debugging only. The format and content of log messages may change between releases.
Enable debug logging by setting an environment variable:
```python
export ANTHROPIC_LOG=debug
```
### Undocumented API functionality
The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
The C# SDK supports the following platforms through separate NuGet packages:
- **Agent Platform:** `Anthropic.Vertex`. See [Claude on Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md) for client setup.
- **Bedrock:** `Anthropic.Bedrock`. Use `AnthropicBedrockMantleClient` for the Messages-API Bedrock endpoint, or `AnthropicBedrockClient` (`bedrock-runtime` path). `AnthropicBedrockMantleClient` takes an optional `MantleAwsClientOptions` config object; `AnthropicBedrockClient` accepts `AnthropicBedrockCredentialsHelper.FromEnv()` or explicit credentials.
- **Claude Platform on AWS:** `Anthropic.Aws`. Use `AnthropicAwsClient`; set `WorkspaceId` on the client or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable (see [Workspaces](../Guides/build-with-claude-claude-platform-on-aws.md#workspaces)). Available in beta.
- **Foundry:** `Anthropic.Foundry`. Use `AnthropicFoundryClient` with `DefaultAnthropicFoundryCredentials.FromEnv()` or explicit credentials.
Use `AnthropicBedrockMantleClient` for new projects; `AnthropicBedrockClient` remains for existing applications using the Bedrock `InvokeModel` API.
## Semantic versioning
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backward-incompatible changes may be released as minor versions:
1. Changes to library internals that are technically public but not intended or documented for external use.
2. Changes that aren't expected to impact the vast majority of users in practice.
Backward-compatibility is taken seriously to ensure you can rely on a smooth upgrade experience.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-csharp)
- [NuGet package](https://www.nuget.org/packages/Anthropic)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
---
## Go SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/go
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-go.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fgo)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# Go SDK
Copy page
Install and configure the Anthropic Go SDK with context-based cancellation and functional options
Copy page
The Anthropic Go library provides convenient access to the Claude API from applications written in Go.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers Go-specific SDK features and configuration.
## Installation
```python
import (
"github.com/anthropics/anthropic-sdk-go" // imported as anthropic
)
```
Install with `go get`:
```python
go get github.com/anthropics/anthropic-sdk-go
```
## Requirements
This library requires Go 1.24+.
## Usage
```python
package main
import (
"context"
"fmt"
"github.com/anthropics/anthropic-sdk-go"
"github.com/anthropics/anthropic-sdk-go/option"
)
func main() {
client := anthropic.NewClient(
option.WithAPIKey("my-anthropic-api-key"), // defaults to os.LookupEnv("ANTHROPIC_API_KEY")
)
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is a quaternion?")),
},
Model: anthropic.ModelClaudeOpus5_5,
})
if err != nil {
panic(err.Error())
}
for _, block := range message.Content {
if textBlock, ok := block.AsAny().(anthropic.TextBlock); ok {
fmt.Println(textBlock.Text)
}
}
}
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
### Conversations
```python
messages := []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is my first name?")),
}
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
Messages: messages,
MaxTokens: 1024,
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)
messages = append(messages, message.ToParam())
messages = append(messages, anthropic.NewUserMessage(
anthropic.NewTextBlock("My full name is John Doe"),
))
message, err = client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
Messages: messages,
MaxTokens: 1024,
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)
```
### System prompts
```python
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
System: []anthropic.TextBlockParam{
{Text: "Be very serious at all times."},
},
Messages: messages,
})
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", message.Content)
```
### Streaming
```python
content := "What is a quaternion?"
stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(content)),
},
})
message := anthropic.Message{}
for stream.Next() {
event := stream.Current()
err := message.Accumulate(event)
if err != nil {
panic(err)
}
switch eventVariant := event.AsAny().(type) {
case anthropic.ContentBlockDeltaEvent:
switch deltaVariant := eventVariant.Delta.AsAny().(type) {
case anthropic.TextDelta:
print(deltaVariant.Text)
}
}
}
if stream.Err() != nil {
panic(stream.Err())
}
```
### Tool calling
```python
messages := []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(content)),
}
toolParams := []anthropic.ToolParam{
{
Name: "get_coordinates",
Description: anthropic.String("Accepts a place as an address, then returns the latitude and longitude coordinates."),
InputSchema: GetCoordinatesInputSchema,
},
}
tools := make([]anthropic.ToolUnionParam, len(toolParams))
for i, toolParam := range toolParams {
tools[i] = anthropic.ToolUnionParam{OfTool: &toolParam}
}
for {
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5_5,
MaxTokens: 1024,
Messages: messages,
Tools: tools,
})
if err != nil {
panic(err)
}
print(color("[assistant]: "))
for _, block := range message.Content {
switch block := block.AsAny().(type) {
case anthropic.TextBlock:
println(block.Text)
println()
case anthropic.ToolUseBlock:
inputJSON, _ := json.Marshal(block.Input)
println(block.Name + ": " + string(inputJSON))
println()
}
}
messages = append(messages, message.ToParam())
toolResults := []anthropic.ContentBlockParamUnion{}
for _, block := range message.Content {
switch variant := block.AsAny().(type) {
case anthropic.ToolUseBlock:
print(color("[user (" + block.Name + ")]: "))
var response interface{}
switch block.Name {
case "get_coordinates":
var input struct {
Location string `json:"location"`
}
err := json.Unmarshal([]byte(variant.JSON.Input.Raw()), &input)
if err != nil {
panic(err)
}
response = GetCoordinates(input.Location)
}
b, err := json.Marshal(response)
if err != nil {
panic(err)
}
println(string(b))
toolResults = append(toolResults, anthropic.NewToolResultBlock(block.ID, string(b), false))
}
}
if len(toolResults) == 0 {
break
}
messages = append(messages, anthropic.NewUserMessage(toolResults...))
}
```
## Request fields
The anthropic library uses the [`omitzero`](https://tip.golang.org/doc/go1.24#encodingjsonpkgencodingjson) semantics from the Go 1.24+ `encoding/json` release for request fields.
Required primitive fields (such as `int64` or `string`) feature the tag `` `json:"...,required"` ``. These fields are always serialized, even their zero values.
Optional primitive types are wrapped in a `param.Opt[T]`. These fields can be set with the provided constructors, such as `anthropic.String(string)` or `anthropic.Int(int64)`.
Any `param.Opt[T]`, map, slice, struct or string enum uses the tag `` `json:"...,omitzero"` ``. Its zero value is considered omitted.
The `param.IsOmitted(any)` function can confirm the presence of any `omitzero` field.
```python
p := anthropic.ExampleParams{
ID: "id_xxx", // required property
Name: anthropic.String("..."), // optional property
Point: anthropic.Point{
X: 0, // required field will serialize as 0
Y: anthropic.Int(1), // optional field will serialize as 1
// ... omitted non-required fields will not be serialized
},
Origin: anthropic.Origin{}, // the zero value of [Origin] is considered omitted
}
```
To send `null` instead of a `param.Opt[T]`, use `param.Null[T]()`. To send `null` instead of a struct `T`, use `param.NullStruct[T]()`.
```python
p.Name = param.Null[string]() // 'null' instead of string
p.Point = param.NullStruct[Point]() // 'null' instead of struct
param.IsNull(p.Name) // true
param.IsNull(p.Point) // true
```
Request structs contain a `.SetExtraFields(map[string]any)` method which can send non-conforming fields in the request body. Extra fields overwrite any struct fields with a matching key.
For security reasons, only use `SetExtraFields` with trusted data.
To send a custom value instead of a struct, use the generic function `param.Override` (for example, `param.Override[anthropic.FooParams](https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/12)`).
```python
// In cases where the API specifies a given type,
// but you want to send something else, use [SetExtraFields]:
p.SetExtraFields(map[string]any{
"x": 0.01, // send "x" as a float instead of int
})
// Send a number instead of an object
custom := param.Override[anthropic.FooParams](12)
```
### Request unions
Unions are represented as a struct with fields prefixed by "Of" for each of its variants, only one field can be non-zero. The non-zero field will be serialized.
Subproperties of the union can be accessed through methods on the union struct. These methods return a mutable pointer to the underlying data, if present.
```python
// Only one field can be non-zero, use param.IsOmitted() to check if a field is set
type AnimalUnionParam struct {
OfCat *Cat `json:",omitzero,inline"`
OfDog *Dog `json:",omitzero,inline"`
}
animal := AnimalUnionParam{
OfCat: &Cat{
Name: "Whiskers",
Owner: PersonParam{
Address: AddressParam{Street: "3333 Coyote Hill Rd", ZipCode: 0},
},
},
}
// Mutating a field
if address := animal.GetOwner().GetAddress(); address != nil {
address.ZipCode = 94304
}
```
### Deserializing params
`param.SetJSON` requires SDK v1.20.0 or later.
Param types (types ending in `Param`, such as `MessageNewParams` or `ToolUnionParam`) are designed for outgoing requests only. They marshal correctly to JSON but do not fully support round-trip deserialization. If you unmarshal raw JSON into a param struct, typed union fields like `OfBashTool20250124` will be nil even when the underlying JSON is valid.
If you need to reconstruct params from raw JSON (for example, from a database, middleware, or a previous request), call `UnmarshalJSON` to populate non-union fields, then use `param.SetJSON` to attach the raw bytes for correct re-serialization:
```python
// Serialize params (for example, for storage or forwarding)
b, err := json.Marshal(original)
if err != nil {
panic(err)
}
// Later, reconstruct params from the stored JSON
var params anthropic.MessageNewParams
if err := params.UnmarshalJSON(b); err != nil {
panic(err)
}
param.SetJSON(b, ¶ms)
// params.Model and other scalar fields are populated by UnmarshalJSON.
// params.Tools[0].OfBashTool20250124 is nil (the union limitation),
// but the raw JSON is preserved. When params is marshaled again
// for the API call, the tools serialize correctly.
b2, _ := json.Marshal(params)
fmt.Println(string(b) == string(b2)) // true
```
For this use case, `param.SetJSON` (available since v1.20.0) is preferred over the more general `param.Override[T](https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/any)` because it doesn't require spelling out the type parameter and makes the round-trip intent explicit.
## Response objects
All fields in response structs are ordinary value types (not pointers or wrappers). Response structs also include a special `JSON` field containing metadata about each property.
```python
type Animal struct {
Name string `json:"name,nullable"`
Owners int `json:"owners"`
Age int `json:"age"`
JSON struct {
Name respjson.Field
Owners respjson.Field
Age respjson.Field
ExtraFields map[string]respjson.Field
} `json:"-"`
}
```
To handle optional data, use the `.Valid()` method on the JSON field. `.Valid()` returns true when the field is present, non-`null`, and was unmarshaled successfully.
If `.Valid()` is false, the corresponding field will be its zero value.
```python
raw := `{"owners": 1, "name": null}`
var res Animal
json.Unmarshal([]byte(raw), &res)
// Accessing regular fields
res.Owners // 1
res.Name // ""
res.Age // 0
// Optional field checks
res.JSON.Owners.Valid() // true
res.JSON.Name.Valid() // false
res.JSON.Age.Valid() // false
// Raw JSON values
res.JSON.Owners.Raw() // "1"
res.JSON.Name.Raw() == "null" // true
res.JSON.Name.Raw() == respjson.Null // true
res.JSON.Age.Raw() == "" // true
res.JSON.Age.Raw() == respjson.Omitted // true
```
These `.JSON` structs also include an `ExtraFields` map containing any properties in the json response that were not specified in the struct. This can be useful for API features not yet present in the SDK.
```python
body := res.JSON.ExtraFields["my_unexpected_field"].Raw()
```
### Response unions
In responses, unions are represented by a flattened struct containing all possible fields from each of the object variants. To convert it to a variant use the `.AsFooVariant()` method or the `.AsAny()` method if present.
If a response value union contains primitive values, primitive fields will be alongside the properties but prefixed with `Of` and feature the tag `json:"...,inline"`.
```python
type AnimalUnion struct {
// From variants [Dog], [Cat]
Owner Person `json:"owner"`
// From variant [Dog]
DogBreed string `json:"dog_breed"`
// From variant [Cat]
CatBreed string `json:"cat_breed"`
// ...
JSON struct {
Owner respjson.Field
// ...
} `json:"-"`
}
// If animal variant
if animal.Owner.Address.ZipCode == "" {
panic("missing zip code")
}
// Switch on the variant
switch variant := animal.AsAny().(type) {
case Dog:
case Cat:
default:
panic("unexpected type")
}
```
## Error handling
When the API returns a non-success status code, the SDK returns an error with type `*anthropic.Error`. This contains the `StatusCode`, `*http.Request`, and `*http.Response` values of the request, along with the JSON of the error body (much like other response objects in the SDK). The error also includes the `RequestID` from the response headers, which is useful for troubleshooting with Anthropic support.
To handle errors, use the `errors.As` pattern:
```python
_, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
MaxTokens: 1024,
Messages: []anthropic.MessageParam{{
Content: []anthropic.ContentBlockParamUnion{{
OfText: &anthropic.TextBlockParam{
Text: "What is a quaternion?",
},
}},
Role: anthropic.MessageParamRoleUser,
}},
Model: anthropic.ModelClaudeOpus5_5,
})
if err != nil {
var apierr *anthropic.Error
if errors.As(err, &apierr) {
println("Request ID:", apierr.RequestID)
println(string(apierr.DumpRequest(true))) // Prints the serialized HTTP request
println(string(apierr.DumpResponse(true))) // Prints the serialized HTTP response
}
panic(err.Error()) // POST "/v1/messages": 400 Bad Request (Request-ID: req_xxx) { ... }
}
```
When other errors occur, they are returned unwrapped; for example, if HTTP transport fails, you might receive `*url.Error` wrapping `*net.OpError`.
## Retries
Certain errors will be automatically retried 2 times by default, with a short exponential backoff. The SDK retries by default all connection errors, 408 Request Timeout, 409 Conflict, 429 Rate Limit, and \>=500 Internal errors.
You can use the `WithMaxRetries` option to configure or disable this:
```python
// Configure the default for all requests:
client := anthropic.NewClient(
option.WithMaxRetries(0), // default is 2
)
// Override per-request:
// ...
client.Messages.New(
context.TODO(),
anthropic.MessageNewParams{
MaxTokens: 1024,
Messages: []anthropic.MessageParam{{
Content: []anthropic.ContentBlockParamUnion{{
OfText: &anthropic.TextBlockParam{
Text: "What is a quaternion?",
},
}},
Role: anthropic.MessageParamRoleUser,
}},
Model: anthropic.ModelClaudeOpus5_5,
},
option.WithMaxRetries(5),
)
```
## Timeouts
Non-streaming Messages requests time out after 10 minutes by default; other requests have no default timeout. Use context to configure a timeout for a request lifecycle.
Note that if a request is [retried](#retries), the context timeout does not start over. To set a per-retry timeout, use `option.WithRequestTimeout()`.
```python
// This sets the timeout for the request, including all the retries.
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
// ...
client.Messages.New(
ctx,
anthropic.MessageNewParams{
MaxTokens: 1024,
Messages: []anthropic.MessageParam{{
Content: []anthropic.ContentBlockParamUnion{{
OfText: &anthropic.TextBlockParam{
Text: "What is a quaternion?",
},
}},
Role: anthropic.MessageParamRoleUser,
}},
Model: anthropic.ModelClaudeOpus5_5,
},
// This sets the per-retry timeout
option.WithRequestTimeout(20*time.Second),
)
```
## Long requests
Consider using the streaming Messages API for longer running requests.
Avoid setting a large `MaxTokens` value without using streaming as some networks may drop idle connections after a certain period of time, which can cause the request to fail or [timeout](#timeouts) without receiving a response from Anthropic.
This SDK will also return an error if a non-streaming request is expected to be above roughly 10 minutes long. Calling `.Messages.NewStreaming()` or [setting a custom timeout](#timeouts) disables this error.
## File uploads
Request parameters that correspond to file uploads in multipart requests are typed as `io.Reader`. The contents of the `io.Reader` will by default be sent as a multipart form part with the file name of "anonymous_file" and content-type of "application/octet-stream", so the recommended approach is to specify a custom content-type with the `anthropic.File(reader io.Reader, filename string, contentType string)` helper, which wraps any `io.Reader` with the appropriate file name and content type.
```python
// A file from the file system
file, err := os.Open("/path/to/file.json")
anthropic.FileUploadParams{
File: anthropic.File(file, "custom-name.json", "application/json"),
}
// A file from a string
anthropic.FileUploadParams{
File: anthropic.File(strings.NewReader("my file contents"), "custom-name.json", "application/json"),
}
```
The file name and content-type can also be customized by implementing `Name() string` or `ContentType() string` on the run-time type of `io.Reader`. Note that `os.File` implements `Name() string`, so a file returned by `os.Open` will be sent with the file name on disk.
## Pagination
This library provides some conveniences for working with paginated list endpoints.
You can use `.ListAutoPaging()` methods to iterate through items across all pages:
```python
iter := client.Messages.Batches.ListAutoPaging(context.TODO(), anthropic.MessageBatchListParams{
Limit: anthropic.Int(20),
})
// Automatically fetches more pages as needed.
for iter.Next() {
messageBatch := iter.Current()
fmt.Println(messageBatch.ID)
}
if err := iter.Err(); err != nil {
panic(err.Error())
}
```
Or you can use simple `.List()` methods to fetch a single page and receive a standard response object with additional helper methods like `.GetNextPage()`:
```python
page, err := client.Messages.Batches.List(context.TODO(), anthropic.MessageBatchListParams{
Limit: anthropic.Int(20),
})
for page != nil {
for _, batch := range page.Data {
fmt.Println(batch.ID)
}
page, err = page.GetNextPage()
}
if err != nil {
panic(err.Error())
}
```
## RequestOptions
This library uses the functional options pattern. Functions defined in the `option` package return a `RequestOption`, which is a closure that mutates a `RequestConfig`. These options can be supplied to the client or at individual requests. For example:
```python
client := anthropic.NewClient(
// Adds a header to every request made by the client
option.WithHeader("X-Some-Header", "custom_header_info"),
)
client.Messages.New(context.TODO(), // ...,
// Override the header
option.WithHeader("X-Some-Header", "some_other_custom_header_info"),
// Add an undocumented field to the request body, using sjson syntax
option.WithJSONSet("some.json.path", map[string]string{"my": "object"}),
)
```
The request option `option.WithDebugLog(nil)` may be helpful while debugging.
See the [full list of request options](https://pkg.go.dev/github.com/anthropics/anthropic-sdk-go/option).
## HTTP client customization
For request middleware (`option.WithMiddleware`) and replacing the default `http.Client` (`option.WithHTTPClient`), see [SDK middleware](cli-sdks-libraries-middleware.md).
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
The Go SDK supports the following platforms:
- **Agent Platform:** `import "github.com/anthropics/anthropic-sdk-go/vertex"`. Use `vertex.WithGoogleAuth(ctx, region, projectID)` or `vertex.WithCredentials(ctx, region, projectID, creds)`.
- **Bedrock:** `import "github.com/anthropics/anthropic-sdk-go/bedrock"`. Use `bedrock.NewMantleClient` for the Messages-API Bedrock endpoint (streams over SSE), or `bedrock.WithLoadDefaultConfig(ctx)` / `bedrock.WithConfig(cfg)` (`bedrock-runtime` path). Importing the `bedrock` package globally registers a decoder for `application/vnd.amazon.eventstream` with the SDK's streaming layer (through package `init()`). This applies whether you use the `bedrock-runtime` `WithConfig`/`WithLoadDefaultConfig` path or `NewMantleClient`.
- **Claude Platform on AWS:** `import anthropicaws "github.com/anthropics/anthropic-sdk-go/aws"`. Use `anthropicaws.NewClient(ctx, cfg)` with an `anthropicaws.ClientConfig` value to construct a client; set `WorkspaceID` on the config or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable. The `anthropicaws` import alias avoids a name collision with `github.com/aws/aws-sdk-go-v2/aws` when both are imported. Available in beta.
- **Foundry:** `import "github.com/anthropics/anthropic-sdk-go/foundry"`. Use `foundry.NewClient(cfg)` with a `foundry.ClientConfig` value to construct a client; set `APIKey` (or `AzureADTokenProvider` for Microsoft Entra ID) and `Resource` on the config, or the `ANTHROPIC_FOUNDRY_API_KEY` and `ANTHROPIC_FOUNDRY_RESOURCE` environment variables.
Use `bedrock.NewMantleClient` for new projects; `bedrock.WithLoadDefaultConfig`/`WithConfig` remain for existing applications using the Bedrock `InvokeModel` API.
## Advanced usage
### Accessing raw response data (for example, response headers)
You can access the raw HTTP response data by using the `option.WithResponseInto()` request option. This is useful when you need to examine response headers, status codes, or other details.
```python
// Create a variable to store the HTTP response
var response *http.Response
message, err := client.Messages.New(
context.TODO(),
anthropic.MessageNewParams{
MaxTokens: 1024,
Messages: []anthropic.MessageParam{{
Content: []anthropic.ContentBlockParamUnion{{
OfText: &anthropic.TextBlockParam{
Text: "What is a quaternion?",
},
}},
Role: anthropic.MessageParamRoleUser,
}},
Model: anthropic.ModelClaudeOpus5_5,
},
option.WithResponseInto(&response),
)
if err != nil {
// handle error
}
fmt.Printf("%+v\n", message.Content)
fmt.Printf("Status Code: %d\n", response.StatusCode)
fmt.Printf("Headers: %+#v\n", response.Header)
```
### Making custom/undocumented requests
This library is typed for convenient access to the documented API. If you need to access undocumented endpoints, params, or response properties, the library can still be used.
#### Undocumented endpoints
To make requests to undocumented endpoints, you can use `client.Get`, `client.Post`, and other HTTP verbs. `RequestOptions` on the client, such as retries, will be respected when making these requests.
```python
var (
// params can be an io.Reader, a []byte, an encoding/json serializable object,
// or a "...Params" struct defined in this library.
params map[string]any
// result can be an []byte, *http.Response, a encoding/json deserializable object,
// or a model defined in this library.
result *http.Response
)
err := client.Post(context.Background(), "/unspecified", params, &result)
if err != nil {
// ...
}
```
#### Undocumented request params
To make requests using undocumented parameters, you may use either the `option.WithQuerySet()` or the `option.WithJSONSet()` methods.
```python
params := FooNewParams{
ID: "id_xxxx",
Data: FooNewParamsData{
FirstName: anthropic.String("John"),
},
}
client.Foo.New(context.Background(), params, option.WithJSONSet("data.last_name", "Doe"))
```
#### Undocumented response properties
To access undocumented response properties, you may either access the raw JSON of the response as a string with `result.JSON.RawJSON()`, or get the raw JSON of a particular field on the result with `result.JSON.Foo.Raw()`.
Any fields that are not present on the response struct are saved and can be accessed through `result.JSON.ExtraFields`, which is a `map[string]respjson.Field`.
## Semantic versioning
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backward-incompatible changes may be released as minor versions:
1. Changes to library internals that are technically public but not intended or documented for external use.
2. Changes that aren't expected to impact the vast majority of users in practice.
Backward-compatibility is taken seriously to ensure you can rely on a smooth upgrade experience.
Your feedback is welcome; open an [issue](https://github.com/anthropics/anthropic-sdk-go/issues) with questions, bugs, or suggestions.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-go)
- [Go package documentation](https://pkg.go.dev/github.com/anthropics/anthropic-sdk-go)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
---
## Java SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/java
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-java.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fjava)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# Java SDK
Copy page
Install and configure the Anthropic Java SDK with builder patterns and async support
Copy page
The Anthropic Java SDK provides convenient access to the Claude API from applications written in Java. It uses the builder pattern for creating requests and supports both synchronous and asynchronous operations.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers Java-specific SDK features and configuration.
## Installation
Gradle
Maven
```python
implementation("com.anthropic:anthropic-java:2.67.0")
```
## Requirements
This library requires Java 8 or later.
The SDK supports Java 8 and later. Code examples in this documentation are written as [JDK 25 compact source files](https://openjdk.org/jeps/512), using a bare `void main()` entry point and `IO.println()` for output. The API calls themselves are identical on every supported JDK; to compile an example on an earlier version, replace `IO.println(...)` with `System.out.println(...)` and place the body inside `public static void main(String[] args)` within a class.
## Quick start
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
Message message = client.messages().create(params);
```
## Client configuration
### API key setup
Configure the client using system properties or environment variables:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
// Configures using the `anthropic.apiKey`, `anthropic.authToken` and `anthropic.baseUrl` system properties
// Or configures using the `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` and `ANTHROPIC_BASE_URL` environment variables
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
```
Or configure manually:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.apiKey("my-anthropic-api-key")
.build();
```
Or use a combination of both approaches:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
// Configures using system properties or environment variables
.fromEnv()
.apiKey("my-anthropic-api-key")
.build();
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
### Configuration options
| Setter | System property | Environment variable | Required | Default value |
|-------------|-----------------------|------------------------|----------|-------------------------------|
| `apiKey` | `anthropic.apiKey` | `ANTHROPIC_API_KEY` | false | \- |
| `authToken` | `anthropic.authToken` | `ANTHROPIC_AUTH_TOKEN` | false | \- |
| `baseUrl` | `anthropic.baseUrl` | `ANTHROPIC_BASE_URL` | true | `"https://api.anthropic.com"` |
System properties take precedence over environment variables.
Don't create more than one client in the same application. Each client has a connection pool and thread pools, which are more efficient to share between requests.
### Modifying configuration
To temporarily use a modified client configuration while reusing the same connection and thread pools, call `withOptions()` on any client or service:
```python
import com.anthropic.client.AnthropicClient;
AnthropicClient clientWithOptions = client.withOptions(optionsBuilder -> {
optionsBuilder.baseUrl("https://example.com");
optionsBuilder.maxRetries(42);
});
```
The `withOptions()` method does not affect the original client or service.
## Async usage
The default client is synchronous. To switch to asynchronous execution, call the `async()` method:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
CompletableFuture<Message> message = client.async().messages().create(params);
```
Or create an asynchronous client from the beginning:
```python
import com.anthropic.client.AnthropicClientAsync;
import com.anthropic.client.okhttp.AnthropicOkHttpClientAsync;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
AnthropicClientAsync client = AnthropicOkHttpClientAsync.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
CompletableFuture<Message> message = client.messages().create(params);
```
The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.
## Streaming
The SDK defines methods that return response "chunk" streams, where each chunk can be individually processed as soon as it arrives instead of waiting on the full response.
### Synchronous streaming
These streaming methods return `StreamResponse` for synchronous clients:
```python
import com.anthropic.core.http.StreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;
try (StreamResponse<RawMessageStreamEvent> streamResponse = client.messages().createStreaming(params)) {
streamResponse.stream().forEach(chunk -> {
IO.println(chunk);
});
IO.println("No more chunks!");
}
```
### Asynchronous streaming
For asynchronous clients, the method returns `AsyncStreamResponse`:
```python
import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.RawMessageStreamEvent;
client.async().messages().createStreaming(params).subscribe(chunk -> {
IO.println(chunk);
});
// If you need to handle errors or completion of the stream
client.async().messages().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
@Override
public void onNext(RawMessageStreamEvent chunk) {
IO.println(chunk);
}
@Override
public void onComplete(Optional<Throwable> error) {
if (error.isPresent()) {
IO.println("Something went wrong!");
throw new RuntimeException(error.get());
} else {
IO.println("No more chunks!");
}
}
});
// Or use futures
client.async().messages().createStreaming(params)
.subscribe(chunk -> {
IO.println(chunk);
})
.onCompleteFuture()
.whenComplete((unused, error) -> {
if (error != null) {
IO.println("Something went wrong!");
throw new RuntimeException(error);
} else {
IO.println("No more chunks!");
}
});
```
Async streaming uses a dedicated per-client cached thread pool `Executor` to stream without blocking the current thread. To use a different `Executor`:
```python
Executor executor = Executors.newFixedThreadPool(4);
client.async().messages().createStreaming(params).subscribe(
chunk -> IO.println(chunk), executor
);
```
Or configure the client globally using the `streamHandlerExecutor` method:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.streamHandlerExecutor(Executors.newFixedThreadPool(4))
.build();
```
### Streaming with message accumulator
A `MessageAccumulator` can record the stream of events in the response as they are processed and accumulate a `Message` object similar to what would have been returned by the non-streaming API.
For a synchronous response, add a `Stream.peek()` call to the stream pipeline to accumulate each event:
```python
import com.anthropic.core.http.StreamResponse;
import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.RawMessageStreamEvent;
MessageAccumulator messageAccumulator = MessageAccumulator.create();
try (StreamResponse<RawMessageStreamEvent> streamResponse =
client.messages().createStreaming(createParams)) {
streamResponse.stream()
.peek(messageAccumulator::accumulate)
.flatMap(event -> event.contentBlockDelta().stream())
.flatMap(deltaEvent -> deltaEvent.delta().text().stream())
.forEach(textDelta -> IO.print(textDelta.text()));
}
Message message = messageAccumulator.message();
```
For an asynchronous response, add the `MessageAccumulator` to the `subscribe()` call:
```python
import com.anthropic.helpers.MessageAccumulator;
import com.anthropic.models.messages.Message;
MessageAccumulator messageAccumulator = MessageAccumulator.create();
client.async().messages()
.createStreaming(createParams)
.subscribe(event -> messageAccumulator.accumulate(event).contentBlockDelta().stream()
.flatMap(deltaEvent -> deltaEvent.delta().text().stream())
.forEach(textDelta -> IO.print(textDelta.text())))
.onCompleteFuture()
.join();
Message message = messageAccumulator.message();
```
A `BetaMessageAccumulator` is also available for the accumulation of a `BetaMessage` object. It is used in the same manner as the `MessageAccumulator`.
## Structured outputs
For complete structured outputs documentation including Java examples, see [Structured outputs](../Guides/build-with-claude-structured-outputs.md).
## Tool use
[Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md) lets you integrate external tools and functions directly into the AI model's responses. Instead of producing plain text, the model can output instructions (with parameters) for calling a tool or function when appropriate. You define JSON schemas for tools, and the model uses the schemas to determine when and how to use these tools.
The tool use feature supports a "strict" mode that guarantees that the JSON output from the AI model will conform to the JSON schema you provide in the input parameters.
The SDK can derive a tool and its parameters automatically from the structure of an arbitrary Java class: the class's name (converted to snake case) provides the tool name, and the class's fields define the tool's parameters.
Declare your tool classes as top-level classes or `static` nested classes. This requirement comes from the Jackson Databind library (`com.fasterxml.jackson.databind`), which the SDK uses to deserialize tool inputs into your class instances and cannot instantiate non-static inner classes.
### Defining tools with annotations
```python
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
enum Unit {
CELSIUS,
FAHRENHEIT;
public String toString() {
return switch (this) {
case CELSIUS -> "C";
case FAHRENHEIT -> "F";
};
}
public double fromKelvin(double temperatureK) {
return switch (this) {
case CELSIUS -> temperatureK - 273.15;
case FAHRENHEIT -> (temperatureK - 273.15) * 1.8 + 32.0;
};
}
}
@JsonClassDescription("Get the weather in a given location")
static class GetWeather {
@JsonPropertyDescription("The city and state, e.g. San Francisco, CA")
public String location;
@JsonPropertyDescription("The unit of temperature")
public Unit unit;
public Weather execute() {
double temperatureK = switch (location) {
case "San Francisco, CA" -> 300.0;
case "New York, NY" -> 310.0;
case "Dallas, TX" -> 305.0;
default -> 295;
};
return new Weather(String.format("%.0f%s", unit.fromKelvin(temperatureK), unit));
}
}
static class Weather {
public String temperature;
public Weather(String temperature) {
this.temperature = temperature;
}
}
```
### Calling tools
When your tool classes are defined, add them to the message parameters using `MessageCreateParams.Builder.addTool(Class<T>)` and then call them if requested to do so in the AI model's response. `BetaToolUseBlock.input(Class<T>)` can be used to parse a tool's parameters in JSON form to an instance of your tool-defining class.
After calling the tool, use `BetaToolResultBlockParam.Builder.contentAsJson(Object)` to pass the tool's result back to the AI model:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.anthropic.models.beta.messages.*;
import com.anthropic.models.messages.Model;
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(2048)
.addTool(GetWeather.class)
.addUserMessage("What's the temperature in New York?");
client.beta().messages().create(createParamsBuilder.build()).content().stream()
.flatMap(contentBlock -> contentBlock.toolUse().stream())
.forEach(toolUseBlock -> createParamsBuilder
// Add a message indicating that the tool use was requested.
.addAssistantMessageOfBetaContentBlockParams(
List.of(BetaContentBlockParam.ofToolUse(BetaToolUseBlockParam.builder()
.name(toolUseBlock.name())
.id(toolUseBlock.id())
.input(toolUseBlock._input())
.build())))
// Add a message with the result of the requested tool use.
.addUserMessageOfBetaContentBlockParams(
List.of(BetaContentBlockParam.ofToolResult(BetaToolResultBlockParam.builder()
.toolUseId(toolUseBlock.id())
.contentAsJson(callTool(toolUseBlock))
.build()))));
client.beta().messages().create(createParamsBuilder.build()).content().stream()
.flatMap(contentBlock -> contentBlock.text().stream())
.forEach(textBlock -> IO.println(textBlock.text()));
private static Object callTool(BetaToolUseBlock toolUseBlock) {
if (!"get_weather".equals(toolUseBlock.name())) {
throw new IllegalArgumentException("Unknown tool: " + toolUseBlock.name());
}
GetWeather tool = toolUseBlock.input(GetWeather.class);
return tool != null ? tool.execute() : new Weather("unknown");
}
```
### Tool name conversion
Tool names are derived from the camel case tool class names (for example, `GetWeather`) and converted to snake case (for example, `get_weather`). Word boundaries begin where the current character is not the first character, is upper-case, and either the preceding character is lower-case, or the following character is lower-case. For example, `MyJSONParser` becomes `my_json_parser` and `ParseJSON` becomes `parse_json`. This conversion can be overridden using the `@JsonTypeName` annotation.
### Local tool JSON schema validation
You can perform local validation to check that the JSON schema derived from your tool class respects Anthropic's restrictions. Local validation is enabled by default, but it can be disabled:
```python
MessageCreateParams.Builder createParamsBuilder = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(2048)
.addTool(GetWeather.class, JsonSchemaLocalValidation.NO)
.addUserMessage("What's the temperature in New York?");
```
### Annotating tool classes
You can use annotations to add further information about tools to the JSON schemas:
- `@JsonClassDescription` - Add a description to a tool class detailing when and how to use that tool.
- `@JsonTypeName` - Set the tool name to something other than the simple name of the class converted to snake case.
- `@JsonPropertyDescription` - Add a detailed description to a tool parameter.
- `@JsonIgnore` - Exclude a `public` field or getter method from the generated JSON schema for a tool's parameters.
- `@JsonProperty` - Include a non-`public` field or getter method in the generated JSON schema for a tool's parameters.
## Message batches
The SDK provides support for [Batch processing](../Guides/build-with-claude-batch-processing.md) under the `client.messages().batches()` namespace. See [Pagination](#pagination) for how to list and paginate through batches.
## File uploads
The SDK defines methods that accept files through the `MultipartField` class:
```python
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(Files.newInputStream(Paths.get("/path/to/file.pdf")))
.contentType("application/pdf")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
```
Or from an `InputStream`:
```python
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(URI.create("https://example.com/path/to/file").toURL().openStream())
.filename("document.pdf")
.contentType("application/pdf")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
```
Or from in-memory bytes:
```python
import com.anthropic.core.MultipartField;
import com.anthropic.models.files.FileMetadata;
import com.anthropic.models.files.FileUploadParams;
FileUploadParams params = FileUploadParams.builder()
.file(
MultipartField.<InputStream>builder()
.value(new ByteArrayInputStream("content".getBytes()))
.filename("document.txt")
.contentType("text/plain")
.build()
)
.build();
FileMetadata fileMetadata = client.files().upload(params);
```
### Binary responses
The SDK defines methods that return binary responses for API responses that aren't necessarily parsed as JSON:
```python
import com.anthropic.core.http.HttpResponse;
HttpResponse response = client.files().download("file_abc123");
```
To save the response content to a file:
```python
import com.anthropic.core.http.HttpResponse;
try (HttpResponse response = client.files().download(params)) {
Files.copy(
response.body(),
Paths.get(path),
StandardCopyOption.REPLACE_EXISTING
);
} catch (Exception e) {
IO.println("Something went wrong!");
throw new RuntimeException(e);
}
```
Or transfer the response content to any `OutputStream`:
```python
import com.anthropic.core.http.HttpResponse;
try (HttpResponse response = client.files().download(params)) {
response.body().transferTo(Files.newOutputStream(Paths.get(path)));
} catch (Exception e) {
IO.println("Something went wrong!");
throw new RuntimeException(e);
}
```
## Error handling
The SDK throws custom unchecked exception types:
- `AnthropicServiceException` - Base class for HTTP errors.
- `AnthropicIoException` - I/O networking errors.
- `AnthropicRetryableException` - Generic error indicating a failure that could be retried.
- `AnthropicInvalidDataException` - Failure to interpret successfully parsed data (for example, when accessing a property that's supposed to be required, but the API unexpectedly omitted it).
- `AnthropicException` - Base class for all exceptions.
### Status code mapping
| Status | Exception |
|--------|---------------------------------|
| 400 | `BadRequestException` |
| 401 | `UnauthorizedException` |
| 403 | `PermissionDeniedException` |
| 404 | `NotFoundException` |
| 422 | `UnprocessableEntityException` |
| 429 | `RateLimitException` |
| 5xx | `InternalServerException` |
| others | `UnexpectedStatusCodeException` |
`SseException` is thrown for errors encountered during SSE streaming after a successful initial HTTP response.
```python
import com.anthropic.errors.*;
try {
Message message = client.messages().create(params);
} catch (RateLimitException e) {
IO.println("Rate limited, retry after: " + e.headers());
} catch (UnauthorizedException e) {
IO.println("Invalid API key");
} catch (AnthropicServiceException e) {
IO.println("API error: " + e.statusCode());
} catch (AnthropicIoException e) {
IO.println("Network error: " + e.getMessage());
}
```
## Request IDs
When using [raw responses](#raw-response-access), you can access the `request-id` response header using the `requestId()` method:
```python
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);
Optional<String> requestId = message.requestId();
```
This can be used to quickly log failing requests and report them back to Anthropic. For more information on debugging requests, see [Request ID](../Endpoints/errors.md#request-id).
## Retries
The SDK automatically retries 2 times by default, with a short exponential backoff between requests.
Only the following error types are retried:
- Connection errors (for example, because of a network connectivity problem)
- 408 Request Timeout
- 409 Conflict
- 429 Rate Limit
- 5xx Internal
The API may also explicitly instruct the SDK to retry or not retry a request.
To set a custom number of retries, configure the client using the `maxRetries` method:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder().fromEnv().maxRetries(4).build();
```
## Timeouts
Requests time out after 10 minutes by default.
However, for methods that accept `maxTokens`, if you specify a large `maxTokens` value and are streaming, then the default timeout will be calculated dynamically using this formula:
```python
Duration.ofSeconds(
Math.min(
60 * 60, // 1 hour max
Math.max(
10 * 60, // 10 minute minimum
60 * 60 * maxTokens / 128_000
)
)
)
```
This results in a timeout of up to 60 minutes, scaled by the `maxTokens` parameter, unless overridden.
For non-streaming requests, the dynamic timeout scales from a 30 second minimum up to a 10 minute maximum based on `maxTokens`.
To set a custom timeout per-request:
```python
import com.anthropic.models.messages.Message;
Message message = client
.messages()
.create(params, RequestOptions.builder().timeout(Duration.ofSeconds(30)).build());
```
Or configure the default for all method calls at the client level:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.timeout(Duration.ofSeconds(30))
.build();
```
## Long requests
Consider using [streaming](#streaming) for longer running requests.
Avoid setting a large `maxTokens` value without using streaming. Some networks may drop idle connections after a certain period of time, which can cause the request to fail or [timeout](#timeouts) without receiving a response from Anthropic. The SDK periodically pings the API to keep the connection alive and reduce the impact of these networks.
The SDK throws an error if a non-streaming request is expected to take longer than 10 minutes. Using a [streaming method](#streaming) or [overriding the timeout](#timeouts) at the client or request level disables the error.
## Pagination
The SDK provides convenient ways to access paginated results either one page at a time or item-by-item across all pages.
### Auto-pagination
To iterate through all results across all pages, use the `autoPager()` method, which automatically fetches more pages as needed.
```python
import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;
BatchListPage page = client.messages().batches().list();
// Process as an Iterable
for (MessageBatch batch : page.autoPager()) {
IO.println(batch);
}
// Process as a Stream
page.autoPager()
.stream()
.limit(50)
.forEach(batch -> IO.println(batch));
```
When using the asynchronous client, the method returns an `AsyncStreamResponse`:
```python
import com.anthropic.core.http.AsyncStreamResponse;
import com.anthropic.models.messages.batches.BatchListPageAsync;
import com.anthropic.models.messages.batches.MessageBatch;
CompletableFuture<BatchListPageAsync> pageFuture = client.async().messages().batches().list();
pageFuture.thenAccept(page -> page.autoPager().subscribe(batch -> {
IO.println(batch);
}));
// If you need to handle errors or completion of the stream
pageFuture.thenAccept(page -> page.autoPager().subscribe(new AsyncStreamResponse.Handler<>() {
@Override
public void onNext(MessageBatch batch) {
IO.println(batch);
}
@Override
public void onComplete(Optional<Throwable> error) {
if (error.isPresent()) {
IO.println("Something went wrong!");
throw new RuntimeException(error.get());
} else {
IO.println("No more!");
}
}
}));
// Or use futures
pageFuture.thenAccept(page -> page.autoPager()
.subscribe(batch -> {
IO.println(batch);
})
.onCompleteFuture()
.whenComplete((unused, error) -> {
if (error != null) {
IO.println("Something went wrong!");
throw new RuntimeException(error);
} else {
IO.println("No more!");
}
}));
```
### Manual pagination
To access individual page items and manually request the next page:
```python
import com.anthropic.models.messages.batches.BatchListPage;
import com.anthropic.models.messages.batches.MessageBatch;
BatchListPage page = client.messages().batches().list();
while (true) {
for (MessageBatch batch : page.items()) {
IO.println(batch);
}
if (!page.hasNextPage()) {
break;
}
page = page.nextPage();
}
```
## Type system
### Immutability and builders
Each class in the SDK has an associated builder for constructing it. Each class is immutable once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy.
```python
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
// Create a modified copy using toBuilder()
MessageCreateParams modified = params.toBuilder().maxTokens(2048L).build();
```
Because each class is immutable, builder modification never affects already built class instances.
### Requests and responses
To send a request to the Claude API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it is deserialized into an instance of a Java class.
For example, `client.messages().create(...)` should be called with an instance of `MessageCreateParams`, and it returns an instance of `Message`.
### Undocumented parameters
To set undocumented parameters, call the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods on any `Params` class:
```python
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
MessageCreateParams params = MessageCreateParams.builder()
.putAdditionalHeader("Secret-Header", "42")
.putAdditionalQueryParam("secret_query_param", "42")
.putAdditionalBodyProperty("secretProperty", JsonValue.from("42"))
.build();
```
These can be accessed on the built object later using the `_additionalHeaders()`, `_additionalQueryParams()`, and `_additionalBodyProperties()` methods.
The values passed to these methods overwrite values passed to earlier methods. For security reasons, ensure these methods are only used with trusted input data.
To set undocumented parameters on nested headers, query params, or body classes:
```python
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Metadata;
MessageCreateParams params = MessageCreateParams.builder()
.metadata(
Metadata.builder().putAdditionalProperty("secretProperty", JsonValue.from("42")).build()
)
.build();
```
These properties can be accessed on the nested built object later using the `_additionalProperties()` method.
To set a documented parameter or property to an undocumented or not yet supported value, pass a `JsonValue` object to its setter:
```python
import com.anthropic.core.JsonValue;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(JsonValue.from(3.14))
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
```
### JsonValue creation
The most straightforward way to create a `JsonValue` is using its `from(...)` method:
```python
import com.anthropic.core.JsonValue;
// Create primitive JSON values
JsonValue nullValue = JsonValue.from(null);
JsonValue booleanValue = JsonValue.from(true);
JsonValue numberValue = JsonValue.from(42);
JsonValue stringValue = JsonValue.from("Hello World!");
// Create a JSON array value equivalent to `["Hello", "World"]`
JsonValue arrayValue = JsonValue.from(List.of("Hello", "World"));
// Create a JSON object value equivalent to `{ "a": 1, "b": 2 }`
JsonValue objectValue = JsonValue.from(Map.of("a", 1, "b", 2));
// Create an arbitrarily nested JSON equivalent to:
// { "a": [1, 2], "b": [3, 4] }
JsonValue complexValue = JsonValue.from(Map.of("a", List.of(1, 2), "b", List.of(3, 4)));
```
### Forcibly omitting required parameters
Normally a `Builder` class's `build` method will throw `IllegalStateException` if any required parameter or property is unset. To forcibly omit a required parameter or property, pass `JsonMissing`:
```python
import com.anthropic.core.JsonMissing;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.addUserMessage("Hello, world")
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(JsonMissing.of())
.build();
```
### Response properties
To access undocumented response properties, call the `_additionalProperties()` method:
```python
import com.anthropic.core.JsonValue;
Map<String, JsonValue> additionalProperties = client
.messages()
.create(params)
._additionalProperties();
JsonValue secretPropertyValue = additionalProperties.get("secretProperty");
String result = secretPropertyValue.accept(new JsonValue.Visitor<>() {
@Override
public String visitNull() {
return "It's null!";
}
@Override
public String visitBoolean(boolean value) {
return "It's a boolean!";
}
@Override
public String visitNumber(Number value) {
return "It's a number!";
}
// Other methods include `visitMissing`, `visitString`, `visitArray`, and `visitObject`
// The default implementation of each unimplemented method delegates to `visitDefault`,
// which throws by default, but can also be overridden
});
```
To access a property's raw JSON value, call its `_` prefixed method:
```python
import com.anthropic.core.JsonField;
import com.anthropic.models.messages.StopReason;
JsonField<StopReason> stopReason = client.messages().create(params)._stopReason();
if (stopReason.isMissing()) {
// The property is absent from the JSON response
} else if (stopReason.isNull()) {
// The property was set to literal null
} else {
// Check if value was provided as a string
// Other methods include `asNumber()`, `asBoolean()`, etc.
Optional<String> jsonString = stopReason.asString();
// Try to deserialize into a custom type
MyClass myObject = stopReason.asUnknown().orElseThrow().convert(MyClass.class);
}
```
### Response validation
By default, the SDK does not throw an exception when the API returns a response that doesn't match the expected type. It throws `AnthropicInvalidDataException` only if you directly access the property.
To check that the response is completely well-typed upfront, call `validate()`:
```python
import com.anthropic.models.messages.Message;
Message message = client.messages().create(params).validate();
```
Or configure per-request:
```python
import com.anthropic.models.messages.Message;
Message message = client
.messages()
.create(params, RequestOptions.builder().responseValidation(true).build());
```
Or configure the default for all method calls at the client level:
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.responseValidation(true)
.build();
```
## HTTP client customization
### Proxy configuration
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import java.net.Proxy;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("https://example.com", 8080)))
.build();
```
### HTTPS / SSL configuration
Most applications should not call these methods, and instead use the system defaults. The defaults include special optimizations that can be lost if the implementations are modified.
```python
import com.anthropic.client.AnthropicClient;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
AnthropicClient client = AnthropicOkHttpClient.builder()
.fromEnv()
.sslSocketFactory(yourSSLSocketFactory)
.trustManager(yourTrustManager)
.hostnameVerifier(yourHostnameVerifier)
.build();
```
### Custom HTTP client
The SDK consists of three artifacts:
- `anthropic-java-core` - Contains core SDK logic, does not depend on OkHttp. Exposes `AnthropicClient`, `AnthropicClientAsync`, and their implementation classes, all of which can work with any HTTP client.
- `anthropic-java-client-okhttp` - Depends on OkHttp. Exposes `AnthropicOkHttpClient` and `AnthropicOkHttpClientAsync`.
- `anthropic-java` - Depends on and exposes the APIs of both `anthropic-java-core` and `anthropic-java-client-okhttp`. Does not have its own logic.
This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies.
#### Customized OkHttpClient
Try the available [network options](#retries) before replacing the default client.
To use a customized `OkHttpClient`:
1. Replace your `anthropic-java` dependency with `anthropic-java-core`.
2. Copy `anthropic-java-client-okhttp`'s `OkHttpClient` class into your code and customize it.
3. Construct `AnthropicClientImpl` or `AnthropicClientAsyncImpl` using your customized client.
#### Completely custom HTTP client
To use a completely custom HTTP client:
1. Replace your `anthropic-java` dependency with `anthropic-java-core`.
2. Write a class that implements the `HttpClient` interface.
3. Construct `AnthropicClientImpl` or `AnthropicClientAsyncImpl` using your new client class.
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
The Java SDK supports the following platforms through separate dependencies that provide platform-specific `Backend` implementations:
- **Agent Platform:** `com.anthropic:anthropic-java-vertex`: Use `VertexBackend.fromEnv()` or `VertexBackend.builder()`.
- **Bedrock:** `com.anthropic:anthropic-java-bedrock`: Use `BedrockMantleBackend.fromEnv()` or `BedrockMantleBackend.builder()` for the Messages-API Bedrock endpoint, or `BedrockBackend.fromEnv()` / `BedrockBackend.builder()` (`bedrock-runtime` path).
- **Claude Platform on AWS:** `com.anthropic:anthropic-java-aws`: Use `AwsBackend.fromEnv()` (reads `ANTHROPIC_AWS_WORKSPACE_ID` and the AWS default region/credential chain) or `AwsBackend.builder()`. Available in beta.
- **Foundry:** `com.anthropic:anthropic-java-foundry`: Use `FoundryBackend.fromEnv()` or `FoundryBackend.builder()`.
Use `BedrockMantleBackend` for new projects; `BedrockBackend` remains for existing applications using the Bedrock `InvokeModel` API.
The platform artifacts are add-ons to the base `com.anthropic:anthropic-java` dependency, which provides `AnthropicOkHttpClient`, so install both. Each `Backend` implementation is passed to the client with `.backend()` on `AnthropicOkHttpClient.builder()`. Each cloud backend pulls in its respective cloud-platform SDK classes as transitive dependencies.
## Advanced usage
### Raw response access
To access HTTP headers, status codes, and the raw response body, prefix any HTTP method call with `withRawResponse()`:
```python
import com.anthropic.core.http.Headers;
import com.anthropic.core.http.HttpResponseFor;
import com.anthropic.models.messages.Message;
import com.anthropic.models.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
MessageCreateParams params = MessageCreateParams.builder()
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.model(Model.CLAUDE_OPUS_5_5)
.build();
HttpResponseFor<Message> message = client.messages().withRawResponse().create(params);
int statusCode = message.statusCode();
Headers headers = message.headers();
```
You can still deserialize the response into an instance of a Java class if needed:
```python
import com.anthropic.models.messages.Message;
Message parsedMessage = message.parse();
```
### Logging
The SDK uses the standard OkHttp logging interceptor.
Enable logging by setting the `ANTHROPIC_LOG` environment variable to `info`:
```python
export ANTHROPIC_LOG=info
```
Or to `debug` for more verbose logging:
```python
export ANTHROPIC_LOG=debug
```
### Jackson compatibility
The SDK depends on Jackson for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.19.4 by default.
The SDK throws an exception if it detects an incompatible Jackson version at runtime (for example, if the default version was overridden in your Maven or Gradle config).
If the SDK threw an exception, but you're certain the version is compatible, then disable the version check using `checkJacksonVersionCompatibility` on `AnthropicOkHttpClient` or `AnthropicOkHttpClientAsync`.
There is no guarantee that the SDK works correctly when the Jackson version check is disabled.
There are also bugs in older Jackson versions that can affect the SDK. The SDK doesn't work around all Jackson bugs and expects users to upgrade Jackson for those instead.
### ProGuard/R8 configuration
Although the SDK uses reflection, it is still usable with ProGuard and R8 because `anthropic-java-core` is published with a configuration file containing keep rules.
ProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary.
### Undocumented API functionality
The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
#### Undocumented request parameters
To set undocumented request parameters, use the `putAdditionalHeader`, `putAdditionalQueryParam`, or `putAdditionalBodyProperty` methods as described in [Undocumented parameters](#undocumented-parameters).
#### Undocumented response properties
To access undocumented response properties, use the `_additionalProperties()` method as described in [Response properties](#response-properties).
#### New or unreleased enum values
Enum-like classes in the SDK, such as `Model` and `AnthropicBeta`, are not closed Java `enum` types. Each one provides an `of(String)` factory method that accepts any string, so you can use values that have not been added to the SDK yet, such as a model or beta header released after your SDK version:
```python
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.messages.Model;
Model model = Model.of("some-new-model");
AnthropicBeta beta = AnthropicBeta.of("some-new-beta-2026-01-01");
```
Builder methods that take these types often also provide a `String` overload that calls `of(...)` for you:
```python
import com.anthropic.models.messages.MessageCreateParams;
MessageCreateParams params = MessageCreateParams.builder()
.model("some-new-model") // same as .model(Model.of("some-new-model"))
.maxTokens(1024L)
.addUserMessage("Hello, Claude")
.build();
```
Prefer the well-typed constants (for example, `Model.CLAUDE_OPUS_5`) so you get autocomplete and deprecation warnings. The `String` overloads and `of(...)` are primarily for setting the field to an undocumented or not yet supported value while waiting for an SDK release that includes it.
## Beta features
Beta features are available before general release to get early feedback and test new functionality. You can check the availability of all of Claude's capabilities and tools in the [build with Claude overview](../Guides/build-with-claude-overview.md).
You can access most beta API features through the `beta()` method on the client. To enable a particular beta feature, add the appropriate [beta header](../Endpoints/beta-headers.md) with `.addBeta()` when building the message params.
For example, to enable [context editing](../Guides/build-with-claude-context-editing.md):
```python
import com.anthropic.models.beta.AnthropicBeta;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
BetaMessage message = client.beta().messages().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5_5)
.maxTokens(1024L)
.addBeta(AnthropicBeta.CONTEXT_MANAGEMENT_2025_06_27)
.addUserMessage("Hello, Claude")
.build());
}
```
## Frequently asked questions
### Why doesn't the SDK use plain enum classes?
Java `enum` classes are not trivially forward compatible. Using them in the SDK could cause runtime exceptions if the API is updated to respond with a new enum value.
Because these classes are open, you can also construct them with any string value through their `of(String)` factory method. See [New or unreleased enum values](#new-or-unreleased-enum-values) if you need to use a value that isn't in your SDK version yet.
### Why are fields represented using JsonField\<T\> instead of just plain T?
Using `JsonField<T>` enables a few features:
- Allowing usage of undocumented API functionality
- Lazily validating the API response against the expected shape
- Representing absent vs explicitly null values
### Why doesn't the SDK use data classes?
It is not backwards compatible to add new fields to a data class, and the SDK avoids introducing a breaking change every time a field is added to a class.
### Why doesn't the SDK use checked exceptions?
Checked exceptions are widely considered a mistake in the Java programming language. In fact, they were omitted from Kotlin for this reason.
Checked exceptions:
- Are verbose to handle
- Encourage error handling at the wrong level of abstraction, where nothing can be done about the error
- Are tedious to propagate because of the function coloring problem
- Don't play well with lambdas (also because of the function coloring problem)
## Semantic versioning
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backward-incompatible changes may be released as minor versions:
1. Changes to library internals which are technically public but not intended or documented for external use.
2. Changes that aren't expected to impact the vast majority of users in practice.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-java)
- [Javadocs](https://javadoc.io/doc/com.anthropic/anthropic-java)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
- [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md)
---
## PHP SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/php
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-php.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fphp)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# PHP SDK
Copy page
Install and configure the Anthropic PHP SDK with value objects and builder patterns
Copy page
The Anthropic PHP library provides convenient access to the Claude API from any PHP 8.1.0+ application.
The PHP SDK is currently in beta. APIs might change between versions.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers PHP-specific SDK features and configuration.
## Installation
The SDK uses [PSR-18](https://www.php-fig.org/psr/psr-18/) for HTTP and discovers any installed PSR-18 client automatically. [Guzzle](https://docs.guzzlephp.org/) is recommended because the SDK configures it for streaming with no additional setup:
```python
composer require "anthropic-ai/sdk" "guzzlehttp/guzzle:^7"
```
## Requirements
PHP 8.1.0 or higher.
## Usage
This library uses named parameters to specify optional arguments. Parameters with a default value must be set by name.
```python
$client = new Client();
$message = $client->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-opus-5-5',
);
$textBlock = array_find($message->content, static fn ($block): bool => $block->type === 'text');
echo $textBlock->text;
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
## Value objects
It is recommended to use the static `with` constructor `Base64ImageSource::with(data: "U3RhaW5sZXNzIHJvY2tz", ...)` and named parameters to initialize value objects.
However, builders are also provided `(new Base64ImageSource)->withData("U3RhaW5sZXNzIHJvY2tz")`.
## Streaming
The SDK provides support for streaming responses using Server-Sent Events (SSE).
```python
$client = new Client();
$stream = $client->messages->createStream(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-opus-5-5',
);
foreach ($stream as $event) {
echo $event->type . PHP_EOL;
}
```
Streaming requires an HTTP client that returns the response body incrementally. When Guzzle is the discovered PSR-18 client, the SDK configures it for streaming automatically. With a buffering client, the `foreach` loop yields every event at once when the response completes instead of incrementally; if you observe that symptom, install Guzzle or supply a streaming-capable PSR-18 client through the `streamingTransporter` request option:
```python
$client = new Anthropic\Client(
requestOptions: Anthropic\RequestOptions::with(streamingTransporter: $myStreamingClient),
);
```
## Error handling
When the library is unable to connect to the API, or if the API returns a non-success status code (that is, a 4xx or 5xx response), a subclass of `Anthropic\Core\Exceptions\APIException` is thrown:
```python
<?php
// ...
use Anthropic\Core\Exceptions\APIConnectionException;
use Anthropic\Core\Exceptions\APIStatusException;
use Anthropic\Core\Exceptions\RateLimitException;
// ...
try {
$message = $client->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-opus-5-5',
);
} catch (APIConnectionException $e) {
echo "The server could not be reached", PHP_EOL;
echo $e->getPrevious()?->getMessage(), PHP_EOL;
} catch (RateLimitException $_) {
echo "A 429 status code was received; we should back off a bit.", PHP_EOL;
} catch (APIStatusException $e) {
echo "Another non-200-range status code was received", PHP_EOL;
echo $e->getMessage();
}
```
Error codes are as follows:
| Cause | Error Type |
|------------------|--------------------------------|
| HTTP 400 | `BadRequestException` |
| HTTP 401 | `AuthenticationException` |
| HTTP 403 | `PermissionDeniedException` |
| HTTP 404 | `NotFoundException` |
| HTTP 409 | `ConflictException` |
| HTTP 422 | `UnprocessableEntityException` |
| HTTP 429 | `RateLimitException` |
| HTTP \>= 500 | `InternalServerException` |
| Other HTTP error | `APIStatusException` |
| Timeout | `APITimeoutException` |
| Network error | `APIConnectionException` |
## Retries
Certain errors are automatically retried two times by default, with a short exponential backoff.
Connection errors (for example, because of a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, \>=500 Internal errors, and timeouts are all retried by default.
You can use the `maxRetries` option to configure or disable this:
```python
use Anthropic\RequestOptions;
// ...
// Configure the default for all requests:
$client = new Client(requestOptions: RequestOptions::with(maxRetries: 0));
// Or, configure per-request:
$result = $client->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-opus-5-5',
requestOptions: RequestOptions::with(maxRetries: 5),
);
```
## Pagination
List methods in the Claude API are paginated.
This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
```python
$client = new Client();
$page = $client->beta->messages->batches->list(limit: 20);
// fetch items from the current page
foreach ($page->getItems() as $item) {
echo $item->id, PHP_EOL;
}
// make additional network requests to fetch items from all pages, including and after the current page
foreach ($page->pagingEachItem() as $item) {
echo $item->id, PHP_EOL;
}
```
## Advanced usage
### Undocumented properties
You can send undocumented parameters to any endpoint, and read undocumented response properties, as follows:
The `extra*` parameters of the same name override the documented parameters.
```python
<?php
// ...
use Anthropic\RequestOptions;
// ...
$message = $client->messages->create(
maxTokens: 1024,
messages: [['role' => 'user', 'content' => 'Hello, Claude']],
model: 'claude-opus-5-5',
requestOptions: RequestOptions::with(
extraQueryParams: ['my_query_parameter' => 'value'],
extraBodyParams: ['my_body_parameter' => 'value'],
extraHeaders: ['my-header' => 'value'],
),
);
```
### Undocumented request parameters
If you want to explicitly send an extra parameter, you can do so with the `extraQueryParams`, `extraBodyParams`, and `extraHeaders` options under `RequestOptions::with()` when making a request, as seen in the preceding example.
### Undocumented endpoints
To make requests to undocumented endpoints while retaining the benefit of authentication, retries, and other client features, you can make requests using `client->request`, as follows:
```python
$client = new Client();
$response = $client->request(
method: "post",
path: '/undocumented/endpoint',
query: ['dog' => 'woof'],
headers: ['useful-header' => 'interesting-value'],
body: ['hello' => 'world']
);
```
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
The PHP SDK supports the following platforms:
- **Agent Platform:** `Anthropic\Vertex\Client`. Use `::fromEnvironment()`.
- **Bedrock:** `Anthropic\Bedrock\MantleClient`. Use `new MantleClient(awsRegion: ...)`.
- **Bedrock (legacy):** `Anthropic\Bedrock\Client`. Use `::fromEnvironment()` or `::withCredentials()`.
- **Claude Platform on AWS:** `Anthropic\Aws\Client` (requires `aws/aws-sdk-php` as a soft dependency). Use `new Anthropic\Aws\Client(workspaceId: ...)` or set `ANTHROPIC_AWS_WORKSPACE_ID`. Available in beta.
- **Foundry:** `Anthropic\Foundry\Client`. Use `::withCredentials()`.
Use `MantleClient` for new projects; `Anthropic\Bedrock\Client` remains for existing applications using the Bedrock `InvokeModel` API.
## Semantic versioning
This package follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions. As the library is in initial development and has a major version of `0`, APIs might change at any time.
This package considers improvements to the (non-runtime) PHPDoc type definitions to be non-breaking changes.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-php)
- [Packagist](https://packagist.org/packages/anthropic-ai/sdk)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
---
## Python SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/python
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-python.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fpython)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# Python SDK
Copy page
Install and configure the Anthropic Python SDK with sync and async client support
Copy page
The Anthropic Python SDK provides convenient access to the Claude API from Python applications. It supports both synchronous and asynchronous operations, streaming, and integrations with Amazon Bedrock, Claude Platform on AWS, Google Cloud, and Microsoft Foundry.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers Python-specific SDK features and configuration.
## Installation
```python
pip install anthropic
```
For platform-specific integrations or improved async performance, install with extras:
```python
# For Amazon Bedrock support
pip install "anthropic[bedrock]"
# For Google Cloud support
pip install "anthropic[vertex]"
# For Claude Platform on AWS support
pip install "anthropic[aws]"
# Microsoft Foundry support is included in the base package
# For improved async performance with aiohttp
pip install "anthropic[aiohttp]"
```
## Requirements
Python 3.10 or later is required. If you are upgrading from a 0.x release of the SDK, see the [v1 migration guide](https://github.com/anthropics/anthropic-sdk-python/blob/main/MIGRATION.md) for the list of breaking changes.
## Usage
```python
import os
from anthropic import Anthropic
client = Anthropic(
# This is the default and can be omitted
api_key=os.environ.get("ANTHROPIC_API_KEY"),
)
message = client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
)
for block in message.content:
if block.type == "text":
print(block.text)
```
Consider using [python-dotenv](https://pypi.org/project/python-dotenv/) to add `ANTHROPIC_API_KEY="my-anthropic-api-key"` to your `.env` file so that your API key isn't stored in source control.
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
## Async usage
```python
import os
import asyncio
from anthropic import AsyncAnthropic
client = AsyncAnthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY"),
)
async def main() -> None:
message = await client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
)
print(message.content)
asyncio.run(main())
```
### Using aiohttp for better concurrency
For improved async performance, you can use the `aiohttp` HTTP backend instead of the default `httpx2`:
```python
import os
import asyncio
from anthropic import AsyncAnthropic, DefaultAioHttpClient
async def main() -> None:
async with AsyncAnthropic(
api_key=os.environ.get("ANTHROPIC_API_KEY"),
http_client=DefaultAioHttpClient(),
) as client:
message = await client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
)
print(message.content)
asyncio.run(main())
```
## Streaming responses
The SDK provides support for streaming responses using Server-Sent Events (SSE).
```python
client = Anthropic()
stream = client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
stream=True,
)
for event in stream:
print(event.type)
```
The async client uses the exact same interface:
```python
client = AsyncAnthropic()
stream = await client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
stream=True,
)
async for event in stream:
print(event.type)
```
### Streaming helpers
The SDK also provides streaming helpers that use context managers and provide access to the accumulated text and the final message:
```python
async def main() -> None:
async with client.messages.stream(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Say hello there!",
}
],
model="claude-opus-5-5",
) as stream:
async for text in stream.text_stream:
print(text, end="", flush=True)
print()
message = await stream.get_final_message()
print(message.to_json())
asyncio.run(main())
```
Streaming with `client.messages.stream(...)` exposes various helpers including accumulation and SDK-specific events.
Alternatively, you can use `client.messages.create(..., stream=True)` which only returns an iterable of the events in the stream and uses less memory (it doesn't build up a final message object for you).
## Token counting
You can see the exact usage for a given request through the `usage` response property:
```python
message = client.messages.create(...)
print(message.usage)
# Usage(input_tokens=25, output_tokens=13)
```
You can also count tokens before making a request:
```python
count = client.messages.count_tokens(
model="claude-opus-5-5",
messages=[{"role": "user", "content": "Hello, world"}],
)
print(count.input_tokens) # 10
```
## Tool use
This SDK provides support for tool use, also known as function calling. For more details, see [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md).
### Tool helpers
The SDK provides helpers for defining and running tools as pure Python functions. The `@beta_tool` decorator generates the tool schema from the function signature and docstring:
```python
import json
from anthropic import Anthropic, beta_tool
client = Anthropic()
@beta_tool
def get_weather(location: str) -> str:
"""Get the weather for a given location.
Args:
location: The city and state, for example, San Francisco, CA
Returns:
A JSON-encoded string with the location, temperature, and weather condition.
"""
return json.dumps(
{
"location": location,
"temperature": "68°F",
"condition": "Sunny",
}
)
# Use the tool_runner to automatically handle tool calls
runner = client.beta.messages.tool_runner(
max_tokens=1024,
model="claude-opus-5-5",
tools=[get_weather],
messages=[
{"role": "user", "content": "What is the weather in SF?"},
],
)
for message in runner:
print(message)
```
On every iteration, an API request is made. If the response includes a call to one of the given tools, the tool is automatically called, and the result is returned directly to the model in the next iteration.
## Message batches
This SDK provides support for [Batch processing](../Guides/build-with-claude-batch-processing.md) under `client.messages.batches`.
### Creating a batch
Message Batches takes an array of requests, where each object has a `custom_id` identifier and the same request `params` as the standard Messages API:
```python
client.messages.batches.create(
requests=[
{
"custom_id": "my-first-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hello, world"}],
},
},
{
"custom_id": "my-second-request",
"params": {
"model": "claude-opus-5-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Hi again, friend"}],
},
},
]
)
```
### Getting results from a batch
Once a Message Batch has been processed, indicated by `.processing_status == 'ended'`, you can access the results with `.batches.results()`:
```python
client = anthropic.Anthropic()
batch_id = "batch_abc123"
result_stream = client.messages.batches.results(batch_id)
for entry in result_stream:
if entry.result.type == "succeeded":
print(entry.result.message.content)
```
## File uploads
Request parameters that correspond to file uploads can be passed in many different forms:
- A `PathLike` object (for example, `pathlib.Path`)
- A tuple of `(filename, content, content_type)`
- A `BinaryIO` file-like object
```python
from pathlib import Path
from anthropic import Anthropic
client = Anthropic()
# Upload using a file path
client.files.upload(
file=Path("/path/to/file"),
)
# Upload using bytes
client.files.upload(
file=("file.txt", b"my bytes", "text/plain"),
)
```
The async client uses the exact same interface. If you pass a `PathLike` instance, the file contents are read asynchronously automatically.
## Handling errors
When the library is unable to connect to the API, or if the API returns a non-success status code (that is, 4xx or 5xx response), a subclass of `APIError` is raised:
```python
import anthropic
# ...
try:
message = client.messages.create(
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Hello, Claude",
}
],
model="claude-opus-5-5",
)
except anthropic.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx2
except anthropic.RateLimitError as e:
print("A 429 status code was received; we should back off a bit.")
except anthropic.APIStatusError as e:
print("Another non-200-range status code was received")
print(e.status_code)
print(e.response)
```
Error codes are as follows:
| Status code | Error type |
|-------------|----------------------------|
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| \>=500 | `InternalServerError` |
| N/A | `APIConnectionError` |
## Request IDs
> For more information on debugging requests, see [Request ID](../Endpoints/errors.md#request-id).
All object responses in the SDK provide a `_request_id` property which is added from the `request-id` response header so that you can quickly log failing requests and report them back to Anthropic.
```python
message = client.messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
)
print(message._request_id) # e.g., req_018EeWyXxfu5pfWkrYcMdjWG
```
Unlike other properties that use an `_` prefix, the `_request_id` property is public. Unless documented otherwise, all other `_` prefix properties, methods, and modules are private.
## Retries
Certain errors are automatically retried 2 times by default, with a short exponential backoff. Connection errors (for example, because of a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and \>=500 Internal errors are all retried by default.
You can use the `max_retries` option to configure or disable this:
```python
# Configure the default for all requests:
client = Anthropic(
max_retries=0, # default is 2
)
# Or, configure per-request:
client.with_options(max_retries=5).messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
)
```
## Timeouts
By default requests time out after 10 minutes. You can configure this with a `timeout` option, which accepts a float or an `httpx2.Timeout` object:
```python
import httpx2
from anthropic import Anthropic
# Configure the default for all requests:
client = Anthropic(
timeout=20.0, # 20 seconds (default is 10 minutes)
)
# More granular control:
client = Anthropic(
timeout=httpx2.Timeout(60.0, read=5.0, write=10.0, connect=2.0),
)
# Override per-request:
client.with_options(timeout=5.0).messages.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
)
```
On timeout, the SDK throws an `APITimeoutError`.
Note that requests that time out are [retried twice by default](#retries).
## Long requests
Consider using the streaming [Messages API](#streaming-responses) for longer running requests.
Avoid setting a large `max_tokens` value without using streaming. Some networks may drop idle connections after a certain period of time, which can cause the request to fail or [timeout](#timeouts) without receiving a response from Anthropic.
The SDK will throw a `ValueError` if a non-streaming request is expected to take longer than approximately 10 minutes. Passing `stream=True` or overriding the `timeout` option at the client or request level disables this error.
An expected request latency longer than the [timeout](#timeouts) for a non-streaming request will result in the client terminating the connection and retrying without receiving a response.
The SDK sets a [TCP socket keep-alive](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) option to reduce the impact of idle connection timeouts on some networks. This can be overridden by passing a custom `http_client` option to the client.
## Auto-pagination
List methods in the Claude API are paginated. You can use the `for` syntax to iterate through items across all pages:
```python
client = Anthropic()
all_batches = []
# Automatically fetches more pages as needed.
for batch in client.messages.batches.list(limit=20):
all_batches.append(batch)
print(all_batches)
```
For async iteration:
```python
async def main() -> None:
all_batches = []
async for batch in client.messages.batches.list(limit=20):
all_batches.append(batch)
print(all_batches)
asyncio.run(main())
```
Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
```python
first_page = await client.messages.batches.list(limit=20)
if first_page.has_next_page():
print(f"will fetch next page using these details: {first_page.next_page_info()}")
next_page = await first_page.get_next_page()
print(f"number of items we just fetched: {len(next_page.data)}")
# Remove `await` for non-async usage.
```
Or work directly with the returned data:
```python
first_page = await client.messages.batches.list(limit=20)
print(f"next page cursor: {first_page.last_id}")
for batch in first_page.data:
print(batch.id)
# Remove `await` for non-async usage.
```
## Default headers
The SDK automatically sends the `anthropic-version` header set to `2023-06-01`.
If you need to, you can override it by setting default headers on the client object or per-request.
Overriding default headers may result in incorrect types and other unexpected or undefined behavior in the SDK.
```python
# Set default headers for all requests on the client
client = Anthropic(
default_headers={"anthropic-version": "My-Custom-Value"},
)
# Or override per-request
client.messages.with_raw_response.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
extra_headers={"anthropic-version": "My-Custom-Value"},
)
```
## Type system
### Request parameters
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also have helper methods for things like serializing back into JSON ([`v1`](https://docs.pydantic.dev/1.10/usage/models/), [`v2`](https://docs.pydantic.dev/latest/concepts/serialization/)).
Typed requests and responses provide autocomplete and documentation within your editor. If you'd like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
### Response models
To convert a Pydantic model to a dictionary, use the helper methods:
```python
message = client.messages.create(...)
# Convert to JSON string
json_str = message.to_json()
# Convert to dictionary
data = message.to_dict()
```
### Handling null vs missing fields
In responses, you can distinguish between fields that are explicitly `null` versus fields that were not returned (missing):
```python
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello"}],
)
if response.my_field is None:
if "my_field" not in response.model_fields_set:
print("field was not in the response")
else:
print("field was null")
```
## Advanced usage
### Accessing raw response data (for example, headers)
The "raw" `Response` returned by `httpx2` can be accessed through the `.with_raw_response` property on the client. This is useful for accessing response headers or other metadata:
```python
client = Anthropic()
response = client.messages.with_raw_response.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
)
print(response.headers.get("request-id"))
message = (
response.parse()
) # get the object that `messages.create()` would have returned
print(message.content)
```
These methods return an `APIResponse` object. On the async client they return an `AsyncAPIResponse`, and `.parse()`, `.read()`, `.text()`, and `.json()` must be awaited.
### Streaming response body
The `.with_raw_response` approach eagerly reads the full response body when you make the request. To stream the response body instead, use `.with_streaming_response`, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()`, or `.parse()`. In the async client, these are async methods.
```python
with client.messages.with_streaming_response.create(
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
model="claude-opus-5-5",
) as response:
print(response.headers.get("request-id"))
for line in response.iter_lines():
print(line)
```
The context manager is required so that the response will reliably be closed.
### Logging
The SDK uses the standard library `logging` module.
You can enable logging by setting the environment variable `ANTHROPIC_LOG` to `debug` or `info`:
```python
export ANTHROPIC_LOG=debug
```
### Making custom/undocumented requests
This library is typed for convenient access to the documented API. If you need to access undocumented endpoints, params, or response properties, the library can still be used.
#### Undocumented endpoints
To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs. Options on the client, such as retries, are respected when making these requests.
```python
import httpx2
response = client.post(
"/foo",
cast_to=httpx2.Response,
body={"my_param": True},
)
print(response.json())
```
#### Undocumented request params
If you want to explicitly send an extra parameter, you can do so with the `extra_query`, `extra_body`, and `extra_headers` request options.
The `extra_` parameters override documented parameters of the same name. For security reasons, ensure these methods are only used with trusted input data.
#### Undocumented response properties
To access undocumented response properties, you can access the extra fields like `response.unknown_prop`. You can also get all extra fields on the Pydantic model as a dict with `response.model_extra`.
### Configuring the HTTP client
The SDK sends requests with [httpx2](https://httpx2.pydantic.dev), an API-compatible fork of `httpx`. To customize the HTTP client, including proxies and transports, pass your own [httpx2 client](https://httpx2.pydantic.dev/api/#client) as `http_client`:
```python
import httpx2
from anthropic import Anthropic, DefaultHttpxClient
client = Anthropic(
# Or use the `ANTHROPIC_BASE_URL` env var
base_url="http://my.test.server.example.com:8083",
http_client=DefaultHttpxClient(
proxy="http://my.test.proxy.example.com",
transport=httpx2.HTTPTransport(local_address="0.0.0.0"),
),
)
```
You can also customize the client on a per-request basis by using `with_options()`:
```python
client.with_options(http_client=DefaultHttpxClient(...))
```
Use `DefaultHttpxClient` and `DefaultAsyncHttpxClient` instead of raw `httpx2.Client` and `httpx2.AsyncClient` to ensure the SDK's default configuration (such as timeouts and connection limits) is preserved. The `http_client` argument must be an `httpx2` client. Passing a client from the separate `httpx` package raises a `TypeError`.
Tracing and mocking tools that patch `httpx` itself, such as OpenTelemetry's `HTTPXClientInstrumentor`, Sentry's `httpx` integration, `respx`, or `pytest-httpx`, do not see the SDK's requests by default. To use them, call `httpx2.alias_httpx()` once at startup, before anything imports `httpx`. This makes `import httpx` resolve to `httpx2` for the whole process.
### Managing HTTP resources
By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
```python
with Anthropic() as client:
message = client.messages.create(...)
# HTTP client is automatically closed
```
## Beta features
Beta features are available before general release to get early feedback and test new functionality. You can check the availability of all of Claude's capabilities and tools in the [build with Claude overview](../Guides/build-with-claude-overview.md).
You can access most beta API features through the `beta` property of the client. To enable a particular beta feature, you need to add the appropriate [beta header](../Endpoints/beta-headers.md) to the `betas` field when creating a message.
For example, to enable [context editing](../Guides/build-with-claude-context-editing.md):
```python
client = Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
betas=["context-management-2025-06-27"],
)
```
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
All five client classes are included in the base `anthropic` package:
| Provider | Client | Extra dependencies |
|----------------------------------|------------------------------------------------|------------------------------------|
| Agent Platform | `from anthropic import AnthropicVertex` | `pip install "anthropic[vertex]"` |
| Bedrock | `from anthropic import AnthropicBedrockMantle` | `pip install "anthropic[bedrock]"` |
| Bedrock (`bedrock-runtime` path) | `from anthropic import AnthropicBedrock` | `pip install "anthropic[bedrock]"` |
| Claude Platform on AWS | `from anthropic import AnthropicAWS` | `pip install "anthropic[aws]"` |
| Foundry | `from anthropic import AnthropicFoundry` | None |
The `AnthropicAWS` client is in beta. Pass `workspace_id` to the constructor or set the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable.
Use `AnthropicBedrockMantle` for new projects; `AnthropicBedrock` remains for existing applications using the Bedrock `InvokeModel` API.
## Semantic versioning
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backward-incompatible changes may be released as minor versions:
1. Changes that only affect static types, without breaking runtime behavior.
2. Changes to library internals which are technically public but not intended or documented for external use.
3. Changes that aren't expected to impact the vast majority of users in practice.
### Determining the installed version
If you've upgraded to the latest version but aren't seeing new features you were expecting, your Python environment is likely still using an older version. You can determine the version being used at runtime with:
```python
print(anthropic.__version__)
```
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-python)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
- [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md)
---
## Ruby SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/ruby
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-ruby.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Fruby)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# Ruby SDK
Copy page
Install and configure the Anthropic Ruby SDK with Sorbet types, streaming helpers, and connection pooling
Copy page
The Anthropic Ruby library provides convenient access to the Claude API from any Ruby 3.2.0+ application. It ships with comprehensive types and docstrings in Yard, RBS, and RBI. The standard library's `net/http` is used as the HTTP transport, with connection pooling through the `connection_pool` gem.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers Ruby-specific SDK features and configuration.
## Installation
Add the gem to your application's `Gemfile` with Bundler:
```python
bundle add anthropic
```
## Requirements
Ruby 3.2.0 or higher.
## Usage
```python
anthropic = Anthropic::Client.new(
api_key: ENV["ANTHROPIC_API_KEY"] # This is the default and can be omitted
)
message = anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5"
)
message.content.each do |block|
puts block.text if block.type == :text
end
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
## Streaming
The SDK provides support for streaming responses using Server-Sent Events (SSE).
```python
anthropic = Anthropic::Client.new
stream = anthropic.messages.stream(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5"
)
stream.each do |message|
puts(message.type)
end
```
### Streaming helpers
This library provides several conveniences for streaming messages, for example:
```python
anthropic = Anthropic::Client.new
stream = anthropic.messages.stream(
max_tokens: 1024,
messages: [{role: :user, content: "Say hello there!"}],
model: :"claude-opus-5-5"
)
stream.text.each do |text|
print(text)
end
```
Streaming with `anthropic.messages.stream(...)` exposes various helpers including accumulation and SDK-specific events.
## Input schema and tool calling
The SDK provides helper mechanisms to define structured data classes for tools and let Claude automatically execute them. For detailed documentation on tool use patterns including the tool runner, see [Tool Runner (SDK)](../Agents-Tools/agents-and-tools-tool-use-tool-runner.md).
```python
anthropic = Anthropic::Client.new
class CalculatorInput < Anthropic::BaseModel
required :lhs, Float
required :rhs, Float
required :operator, Anthropic::InputSchema::EnumOf[:+, :-, :*, :/]
end
class Calculator < Anthropic::BaseTool
input_schema CalculatorInput
def call(expr)
expr.lhs.public_send(expr.operator, expr.rhs)
end
end
# Automatically handles tool execution loop
anthropic.beta.messages.tool_runner(
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{role: "user", content: "What's 15 * 7?"}],
tools: [Calculator.new]
).each_message { |message| puts message.content }
```
## Structured outputs
For complete structured outputs documentation including Ruby examples, see [Structured outputs](../Guides/build-with-claude-structured-outputs.md).
## Handling errors
When the library is unable to connect to the API, or if the API returns a non-success status code (that is, 4xx or 5xx response), a subclass of `Anthropic::Errors::APIError` is raised:
```python
anthropic = Anthropic::Client.new
begin
message = anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5"
)
rescue Anthropic::Errors::APIConnectionError => e
puts("The server could not be reached")
puts(e.cause) # an underlying Exception, likely raised within `net/http`
rescue Anthropic::Errors::RateLimitError => e
puts("A 429 status code was received; we should back off a bit.")
rescue Anthropic::Errors::APIStatusError => e
puts("Another non-200-range status code was received")
puts(e.status)
end
```
Error codes are as follows:
| Cause | Error Type |
|------------------|----------------------------|
| HTTP 400 | `BadRequestError` |
| HTTP 401 | `AuthenticationError` |
| HTTP 403 | `PermissionDeniedError` |
| HTTP 404 | `NotFoundError` |
| HTTP 409 | `ConflictError` |
| HTTP 422 | `UnprocessableEntityError` |
| HTTP 429 | `RateLimitError` |
| HTTP \>= 500 | `InternalServerError` |
| Other HTTP error | `APIStatusError` |
| Timeout | `APITimeoutError` |
| Network error | `APIConnectionError` |
## Retries
Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
Connection errors (for example, because of a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, \>=500 Internal errors, and timeouts are all retried by default.
You can use the `max_retries` option to configure or disable this:
```python
# Configure the default for all requests:
anthropic = Anthropic::Client.new(
max_retries: 0 # default is 2
)
# Or, configure per-request:
anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5",
request_options: {max_retries: 5}
)
```
## Timeouts
By default, requests time out after 10 minutes. You can use the `timeout` option to configure this:
```python
# Configure the default for all requests:
anthropic = Anthropic::Client.new(
timeout: 20 # 20 seconds (default is 10 minutes)
)
# Or, configure per-request:
anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5",
request_options: {timeout: 5}
)
```
On timeout, `Anthropic::Errors::APITimeoutError` is raised.
Note that requests that time out are retried by default.
## Pagination
List methods in the Claude API are paginated.
This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
```python
anthropic = Anthropic::Client.new
page = anthropic.messages.batches.list(limit: 20)
# Fetch single item from page.
batch = page.data[0]
puts(batch.id)
# Automatically fetches more pages as needed.
page.auto_paging_each do |batch|
puts(batch.id)
end
```
Alternatively, you can use the `#next_page?` and `#next_page` methods for more granular control working with pages.
```python
anthropic = Anthropic::Client.new
page = anthropic.messages.batches.list(limit: 20)
loop do
page.data&.each { |batch| puts(batch.id) }
break unless page.next_page?
page = page.next_page
end
```
## File uploads
Request parameters that correspond to file uploads can be passed as raw contents, a [`Pathname`](https://rubyapi.org/3.2/o/pathname) instance, [`StringIO`](https://rubyapi.org/3.2/o/stringio), or more.
```python
anthropic = Anthropic::Client.new
require "pathname"
# Use `Pathname` to send the filename and/or avoid paging a large file into memory:
file_metadata = anthropic.files.upload(file: Pathname("/path/to/file"))
# Alternatively, pass file contents or a `StringIO` directly:
file_metadata = anthropic.files.upload(file: File.read("/path/to/file"))
# Or, to control the filename and/or content type:
file = Anthropic::FilePart.new(File.read("/path/to/file"), filename: "/path/to/file", content_type: "...")
file_metadata = anthropic.files.upload(file: file)
puts(file_metadata.id)
```
Note that you can also pass a raw `IO` descriptor, but this disables retries, as the library can't be sure if the descriptor is a file or pipe (which cannot be rewound).
## Sorbet
This library provides comprehensive [RBI](https://sorbet.org/docs/rbi) definitions, and has no dependency on sorbet-runtime.
You can provide typesafe request parameters like so:
```python
anthropic = Anthropic::Client.new
anthropic.messages.create(
max_tokens: 1024,
messages: [Anthropic::MessageParam.new(role: "user", content: "Hello, Claude")],
model: :"claude-opus-5-5"
)
```
Or, equivalently:
```python
anthropic = Anthropic::Client.new
# Hashes work, but are not typesafe:
anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5"
)
# You can also splat a full Params class:
params = Anthropic::MessageCreateParams.new(
max_tokens: 1024,
messages: [Anthropic::MessageParam.new(role: "user", content: "Hello, Claude")],
model: :"claude-opus-5-5"
)
anthropic.messages.create(**params)
```
### Enums
Since this library does not depend on `sorbet-runtime`, it cannot provide [`T::Enum`](https://sorbet.org/docs/tenum) instances. Instead, the SDK provides "tagged symbols", which is always a primitive at runtime:
```python
# :auto
puts(Anthropic::MessageCreateParams::ServiceTier::AUTO)
# Revealed type: `T.all(Anthropic::MessageCreateParams::ServiceTier, Symbol)`
T.reveal_type(Anthropic::MessageCreateParams::ServiceTier::AUTO)
```
Enum parameters have a "relaxed" type, so you can either pass in enum constants or their literal value:
```python
# Using the enum constants preserves the tagged type information:
anthropic.messages.create(
service_tier: Anthropic::MessageCreateParams::ServiceTier::AUTO,
# ...
)
# Literal values are also permissible:
anthropic.messages.create(
service_tier: :auto,
# ...
)
```
## BaseModel
All parameter and response objects inherit from `Anthropic::Internal::Type::BaseModel`, which provides several conveniences, including:
1. All fields, including unknown ones, are accessible with `obj[:prop]` syntax, and can be destructured with `obj => {prop: prop}` or pattern-matching syntax.
2. Structural equivalence for equality; if two API calls return the same values, comparing the responses with == will return true.
3. Both instances and the classes themselves can be pretty-printed.
4. Helpers such as `#to_h`, `#deep_to_h`, `#to_json`, and `#to_yaml`.
## Concurrency and connection pooling
The `Anthropic::Client` instances are threadsafe, but are only fork-safe when there are no in-flight HTTP requests.
Each instance of `Anthropic::Client` has its own HTTP connection pool with a default size of 99. As such, the recommendation is to create the client once per application in most settings.
When all available connections from the pool are checked out, requests wait for a new connection to become available, with queue time counting toward the request timeout.
Unless otherwise specified, other classes in the SDK do not have locks protecting their underlying data structure.
## Making custom or undocumented requests
### Undocumented properties
You can send undocumented parameters to any endpoint, and read undocumented response properties, like so:
The `extra_` parameters of the same name override the documented parameters. For security reasons, ensure these methods are only used with trusted input data.
```python
anthropic = Anthropic::Client.new
value = "example"
message =
anthropic.messages.create(
max_tokens: 1024,
messages: [{role: "user", content: "Hello, Claude"}],
model: :"claude-opus-5-5",
request_options: {
extra_query: {my_query_parameter: value},
extra_body: {my_body_parameter: value},
extra_headers: {"my-header": value}
}
)
puts(message[:my_undocumented_property])
```
### Undocumented request params
If you want to explicitly send an extra param, you can do so with the `extra_query`, `extra_body`, and `extra_headers` under the `request_options:` parameter when making a request, as seen in the examples above.
### Undocumented endpoints
To make requests to undocumented endpoints while retaining the benefit of auth, retries, and so on, you can make requests using `anthropic.request`, like so:
```python
response = anthropic.request(
method: :post,
path: '/undocumented/endpoint',
query: {"dog": "woof"},
headers: {"useful-header": "interesting-value"},
body: {"hello": "world"}
)
```
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
The Ruby SDK supports the following platforms:
- **Agent Platform:** `Anthropic::VertexClient`. Requires the `googleauth` gem.
- **Bedrock:** `Anthropic::BedrockMantleClient`, or `Anthropic::BedrockClient` for the `bedrock-runtime` path. `Anthropic::BedrockMantleClient` requires the `aws-sdk-core` gem; `Anthropic::BedrockClient` requires the `aws-sdk-bedrockruntime` gem.
- **Claude Platform on AWS:** Part of the main `anthropic` gem (requires the `aws-sdk-core` gem). Provides `Anthropic::AWSClient`. Pass `workspace_id:` to the constructor or set the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable (see [Workspaces](../Guides/build-with-claude-claude-platform-on-aws.md#workspaces)). Available in beta.
- **Foundry:** Not currently supported in the Ruby SDK. See [Claude in Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md) for supported SDKs.
Use `Anthropic::BedrockMantleClient` for new projects; `Anthropic::BedrockClient` remains for existing applications using the Bedrock `InvokeModel` API.
## Semantic versioning
This package follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions.
This package considers improvements to the (non-runtime) `*.rbi` and `*.rbs` type definitions to be non-breaking changes.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-ruby)
- [YARD documentation](https://gemdocs.org/gems/anthropic)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
---
## TypeScript SDK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/typescript
Path: 04-API-Reference/Other/cli-sdks-libraries-sdks-typescript.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fcli-sdks-libraries%2Fsdks%2Ftypescript)
SearchCtrlK
SDKs, CLI, and libraries
[Overview](cli-sdks-libraries-overview.md)
ant CLI
[Quickstart](cli-sdks-libraries-cli-quickstart.md)[Authentication options](cli-sdks-libraries-cli-authentication.md)[Using the CLI](cli-sdks-libraries-cli-using.md)[Scripting and automation](cli-sdks-libraries-cli-scripting.md)[Manage resources as code](cli-sdks-libraries-cli-apply.md)[Connect to a Managed Agents session](cli-sdks-libraries-cli-sessions-connect.md)
Client SDKs
[Middleware](cli-sdks-libraries-middleware.md)[Python](cli-sdks-libraries-sdks-python.md)[TypeScript](cli-sdks-libraries-sdks-typescript.md)[C#](cli-sdks-libraries-sdks-csharp.md)[Go](cli-sdks-libraries-sdks-go.md)[Java](cli-sdks-libraries-sdks-java.md)[PHP](cli-sdks-libraries-sdks-php.md)[Ruby](cli-sdks-libraries-sdks-ruby.md)
Libraries and integrations
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md)[OpenAI SDK compatibility](cli-sdks-libraries-libraries-openai-sdk.md)
[Console](usage-limits.md)
[SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)Client SDKs
# TypeScript SDK
Copy page
Install and configure the Anthropic TypeScript SDK for Node.js, Deno, Bun, and browser environments
Copy page
This library provides convenient access to the Claude API from TypeScript or JavaScript.
For API feature documentation with code examples, see the [API reference](../Endpoints/overview.md). This page covers TypeScript-specific SDK features and configuration.
## Installation
```python
npm install @anthropic-ai/sdk
```
## Requirements
TypeScript \>= 5.0 is supported.
The following runtimes are supported:
- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.
- Deno v1.28.0 or higher.
- Bun 1.0 or later.
- Cloudflare Workers.
- Vercel Edge Runtime.
- Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time).
- Nitro v2.6 or greater.
- Web browsers: disabled by default to avoid exposing your secret API credentials (see [API key best practices](api-key-best-practices-keeping-your-keys-safe-and-secure.md)). Enable browser support by explicitly setting `dangerouslyAllowBrowser` to `true`.
Note that React Native is not supported at this time.
If you are interested in other runtime environments, open or upvote an issue on the [GitHub repository](https://github.com/anthropics/anthropic-sdk-typescript).
## Usage
```python
const client = new Anthropic({
apiKey: process.env["ANTHROPIC_API_KEY"] // This is the default and can be omitted
});
const message = await client.messages.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
});
for (const block of message.content) {
if (block.type === "text") {
console.log(block.text);
}
}
```
For authentication options including Workload Identity Federation, see [Authentication](manage-claude-authentication.md). If your API key is a [personal or service account key](manage-claude-authentication.md#key-types) with access to multiple workspaces, set the workspace ID in the `anthropic-workspace-id` request header; [Select a workspace](manage-claude-authentication.md#select-a-workspace) shows the per-request option for this SDK.
## Request and response types
This library includes TypeScript definitions for all request parameters and response fields. You may import and use them like so:
```python
const client = new Anthropic({
apiKey: process.env["ANTHROPIC_API_KEY"] // This is the default and can be omitted
});
const params: Anthropic.MessageCreateParams = {
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
};
const message: Anthropic.Message = await client.messages.create(params);
```
Documentation for each method, request parameter, and response field is available in docstrings and appears on hover in most modern editors.
## Counting tokens
You can see the exact usage for a given request through the `usage` response property, for example:
```python
const message = await client.messages.create(/* ... */);
console.log(message.usage);
// { input_tokens: 25, output_tokens: 13 }
```
## Streaming responses
The SDK provides support for streaming responses using Server Sent Events (SSE).
```python
const client = new Anthropic();
const stream = await client.messages.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5",
stream: true
});
for await (const messageStreamEvent of stream) {
console.log(messageStreamEvent.type);
}
```
If you need to cancel a stream, you can `break` from the loop or call `stream.controller.abort()`.
## Streaming helpers
This library provides several conveniences for streaming messages, for example:
```python
const anthropic = new Anthropic();
const stream = anthropic.messages
.stream({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Say hello there!"
}
]
})
.on("text", (text) => {
console.log(text);
});
const message = await stream.finalMessage();
console.log(message);
```
Streaming with `client.messages.stream(...)` exposes various helpers for your convenience including event handlers and accumulation.
Alternatively, you can use `client.messages.create({ ..., stream: true })` which only returns an async iterable of the events in the stream and thus uses less memory (it does not build up a final message object for you).
## Tool helpers
This SDK provides helpers for making it easy to create and run tools in the Messages API. You can use Zod schemas or JSON Schemas to describe the input to a tool. You can then run those tools using the `client.beta.messages.toolRunner()` method. This method handles passing the inputs generated by the chosen model into the right tool and passing the result back to the model.
For more details on tool use, see [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md).
```python
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const anthropic = new Anthropic();
const weatherTool = betaZodTool({
name: "get_weather",
inputSchema: z.object({
location: z.string()
}),
description: "Get the current weather in a given location",
run: (input) => {
return `The weather in ${input.location} is foggy and 60°F`;
}
});
const finalMessage = await anthropic.beta.messages.toolRunner({
model: "claude-opus-5-5",
max_tokens: 1000,
messages: [{ role: "user", content: "What is the weather in San Francisco?" }],
tools: [weatherTool]
});
console.log(finalMessage.content);
```
### Tool errors
To report an error from a tool back to the model, throw a `ToolError` from the `run` function. Unlike a plain `Error`, `ToolError` accepts content blocks, allowing you to include images or other structured content in the error response:
```python
import { ToolError } from "@anthropic-ai/sdk/lib/tools/BetaRunnableTool";
const screenshotTool = betaZodTool({
name: "take_screenshot",
inputSchema: z.object({ url: z.string() }),
run: async (input) => {
if (!isValidUrl(input.url)) {
throw new ToolError(`Invalid URL: ${input.url}`);
}
const result = await takeScreenshot(input.url);
if (result.error) {
// Include the error screenshot so the model can see what went wrong
throw new ToolError([
{ type: "text", text: `Failed to load page: ${result.error}` },
{
type: "image",
source: { type: "base64", data: result.screenshot, media_type: "image/png" }
}
]);
}
return {
type: "image",
source: { type: "base64", data: result.screenshot, media_type: "image/png" }
};
}
});
```
If a plain `Error` is thrown, the message will be converted to a text content block.
## Tool use
This SDK provides support for tool use, also known as function calling. For more details, see [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md).
## MCP helpers
This SDK provides helpers for integrating with [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. These helpers convert MCP types to Claude API types, reducing boilerplate when working with MCP tools, prompts, and resources.
The Claude API also supports an [`mcp_servers` parameter](../Agents-Tools/agents-and-tools-mcp-connector.md) that lets Claude connect directly to remote MCP servers. Use `mcp_servers` when you have remote servers accessible by URL and only need tool support. Use the MCP helpers when you need local MCP servers, prompts, resources, or more control over the MCP connection.
```python
import {
mcpTools,
mcpMessages,
mcpResourceToContent,
mcpResourceToFile
} from "@anthropic-ai/sdk/helpers/beta/mcp";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const anthropic = new Anthropic();
// Connect to an MCP server
const transport = new StdioClientTransport({ command: "mcp-server", args: [] });
const mcpClient = new Client({ name: "my-client", version: "1.0.0" });
await mcpClient.connect(transport);
// Use MCP prompts
const { messages } = await mcpClient.getPrompt({ name: "my-prompt" });
const response = await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: mcpMessages(messages)
});
console.log(response.content);
// Use MCP tools with toolRunner
const { tools } = await mcpClient.listTools();
const finalMessage = await anthropic.beta.messages.toolRunner({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Use the available tools" }],
tools: mcpTools(tools, mcpClient)
});
console.log(finalMessage.content);
// Use MCP resources as content
const resource = await mcpClient.readResource({ uri: "file:///path/to/doc.txt" });
await anthropic.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: [
mcpResourceToContent(resource),
{ type: "text", text: "Summarize this document" }
]
}
]
});
// Upload MCP resources as files
const fileResource = await mcpClient.readResource({ uri: "file:///path/to/data.json" });
await anthropic.files.upload({ file: mcpResourceToFile(fileResource) });
```
### MCP error handling
The conversion functions throw `UnsupportedMCPValueError` if an MCP value isn't supported by the Claude API (for example, unsupported content type, unsupported MIME type, non-http/https resource link).
## Message batches
This SDK provides support for [Batch processing](../Guides/build-with-claude-batch-processing.md) under the `client.messages.batches` namespace.
### Creating a batch
Message Batches takes an array of requests, where each object has a `custom_id` identifier, and the exact same request `params` as the standard Messages API:
```python
const batch = await client.messages.batches.create({
requests: [
{
custom_id: "my-first-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, world" }]
}
},
{
custom_id: "my-second-request",
params: {
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hi again, friend" }]
}
}
]
});
```
### Getting results from a batch
Once a Message Batch has been processed, indicated by `.processing_status === 'ended'`, you can access the results with `.batches.results()`
```python
const results = await client.messages.batches.results(batch.id);
for await (const entry of results) {
if (entry.result.type === "succeeded") {
console.log(entry.result.message.content);
}
}
```
## File uploads
Request parameters that correspond to file uploads can be passed in many different forms:
- `File` (or an object with the same structure)
- a `fetch` `Response` (or an object with the same structure)
- an `fs.ReadStream`
- the return value of the `toFile` helper
Set the content-type explicitly as the files API will not infer it for you:
```python
import fs from "node:fs";
import Anthropic, { toFile } from "@anthropic-ai/sdk";
const client = new Anthropic();
// If you have access to Node `fs`, use `fs.createReadStream()`:
await client.files.upload({
file: await toFile(fs.createReadStream("/path/to/file"), undefined, {
type: "application/json"
})
});
// Or if you have the web `File` API you can pass a `File` instance:
await client.files.upload({
file: new File(["my bytes"], "file.txt", { type: "text/plain" })
});
// You can also pass a `fetch` `Response`:
await client.files.upload({
file: await fetch("https://somesite/file")
});
// Or a `Buffer` / `Uint8Array`
await client.files.upload({
file: await toFile(Buffer.from("my bytes"), "file", { type: "text/plain" })
});
await client.files.upload({
file: await toFile(new Uint8Array([0, 1, 2]), "file", { type: "text/plain" })
});
```
## Handling errors
When the library is unable to connect to the API, or if the API returns a non-success status code (that is, 4xx or 5xx response), a subclass of `APIError` is thrown:
```python
const message = await client.messages
.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
})
.catch(async (err) => {
if (err instanceof Anthropic.APIError) {
console.log(err.status); // 400
console.log(err.name); // BadRequestError
console.log(err.headers); // {server: 'nginx', ...}
} else {
throw err;
}
});
```
Error codes are as follows:
| Status code | Error type |
|-------------|----------------------------|
| 400 | `BadRequestError` |
| 401 | `AuthenticationError` |
| 403 | `PermissionDeniedError` |
| 404 | `NotFoundError` |
| 409 | `ConflictError` |
| 422 | `UnprocessableEntityError` |
| 429 | `RateLimitError` |
| \>=500 | `InternalServerError` |
| N/A | `APIConnectionError` |
## Request IDs
> For more information on debugging requests, see [Request ID](../Endpoints/errors.md#request-id).
All object responses in the SDK provide a `_request_id` property which is added from the `request-id` response header so that you can quickly log failing requests and report them back to Anthropic.
```python
const message = await client.messages.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
});
console.log(message._request_id); // req_018EeWyXxfu5pfWkrYcMdjWG
```
## Retries
Certain errors are automatically retried 2 times by default, with a short exponential backoff. Connection errors (for example, because of a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, and \>=500 Internal errors are all retried by default.
You can use the `maxRetries` option to configure or disable this:
```python
// Configure the default for all requests:
const client = new Anthropic({
maxRetries: 0 // default is 2
});
// Or, configure per-request:
await client.messages.create(
{
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
},
{ maxRetries: 5 }
);
```
## Timeouts
By default requests time out after 10 minutes. However if you have specified a large `max_tokens` value and are *not* streaming, the default timeout will be calculated dynamically using the formula:
```python
const minimum = 10 * 60;
const calculated = (60 * 60 * maxTokens) / 128_000;
return calculated < minimum ? minimum * 1000 : calculated * 1000;
```
which will result in a timeout up to 60 minutes, scaled by the `max_tokens` parameter, unless overridden at the request or client level.
You can configure this with a `timeout` option:
```python
// Configure the default for all requests:
const client = new Anthropic({
timeout: 20 * 1000 // 20 seconds (default is 10 minutes)
});
// Override per-request:
await client.messages.create(
{
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
},
{ timeout: 5 * 1000 }
);
```
On timeout, an `APIConnectionTimeoutError` is thrown.
Note that requests that time out are [retried twice by default](#retries).
## Long requests
Consider using the streaming [Messages API](#streaming-responses) for longer running requests.
Avoid setting a large `max_tokens` value without using streaming. Some networks may drop idle connections after a certain period of time, which can cause the request to fail or [timeout](#timeouts) without receiving a response from Anthropic.
This SDK also throws an error if a non-streaming request is expected to be above roughly 10 minutes long. Passing `stream: true` or [overriding](#timeouts) the `timeout` option at the client or request level disables this error.
An expected request latency longer than the [timeout](#timeouts) for a non-streaming request will result in the client terminating the connection and retrying without receiving a response.
When supported by the `fetch` implementation, the SDK sets a [TCP socket keep-alive](https://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html) option to reduce the impact of idle connection timeouts on some networks. This can be [overridden](#configuring-proxies) by configuring a custom proxy.
## Auto-pagination
List methods in the Claude API are paginated. You can use the `for await ... of` syntax to iterate through items across all pages:
```python
async function fetchAllMessageBatches() {
const allMessageBatches = [];
// Automatically fetches more pages as needed.
for await (const messageBatch of client.messages.batches.list({ limit: 20 })) {
allMessageBatches.push(messageBatch);
}
return allMessageBatches;
}
```
Alternatively, you can request a single page at a time:
```python
let page = await client.messages.batches.list({ limit: 20 });
for (const messageBatch of page.data) {
console.log(messageBatch);
}
// Convenience methods are provided for manually paginating:
while (page.hasNextPage()) {
page = await page.getNextPage();
// ...
}
```
## Default headers
The SDK automatically sends the `anthropic-version` header set to `2023-06-01`.
If you need to, you can override it by setting default headers on a per-request basis.
Be aware that doing so may result in incorrect types and other unexpected or undefined behavior in the SDK.
```python
const client = new Anthropic();
const message = await client.messages.create(
{
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
},
{ headers: { "anthropic-version": "My-Custom-Value" } }
);
```
## Advanced usage
### Accessing raw Response data (for example, headers)
The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return. This method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.
You can also use the `.withResponse()` method to get the raw `Response` along with the parsed data. Unlike `.asResponse()` this method consumes the body, returning once it is parsed.
```python
const client = new Anthropic();
const response = await client.messages
.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
})
.asResponse();
console.log(response.headers.get("X-My-Header"));
console.log(response.statusText); // access the underlying Response object
const { data: message, response: raw } = await client.messages
.create({
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
model: "claude-opus-5-5"
})
.withResponse();
console.log(raw.headers.get("X-My-Header"));
console.log(message.content);
```
### Logging
All log messages are intended for debugging only. The format and content of log messages may change between releases.
#### Log levels
You can configure the log level in two ways:
1. Through the `ANTHROPIC_LOG` environment variable
2. Using the `logLevel` client option (overrides the environment variable if set)
```python
const client = new Anthropic({
logLevel: "debug" // Show all log messages
});
```
Available log levels, from most to least verbose:
- `'debug'` - Show debug messages, info, warnings, and errors
- `'info'` - Show info messages, warnings, and errors
- `'warn'` - Show warnings and errors (default)
- `'error'` - Show only errors
- `'off'` - Disable all logging
At the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies. Some authentication-related headers are redacted, but sensitive data in request and response bodies may still be visible.
#### Custom logger
By default, this library logs to `globalThis.console`. You can also provide a custom logger. Most logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, open an issue.
When providing a custom logger, the `logLevel` option still controls which messages are emitted; messages below the configured level will not be sent to your logger.
```python
import pino from "pino";
const logger = pino();
const client = new Anthropic({
logger: logger.child({ name: "Anthropic" }),
logLevel: "debug" // Send all messages to pino, allowing it to filter
});
```
### Making custom/undocumented requests
This library is typed for convenient access to the documented API. If you need to access undocumented endpoints, params, or response properties, the library can still be used.
#### Undocumented endpoints
To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs. Options on the client, such as retries, are respected when making these requests.
```python
await client.post("/some/path", {
body: { some_prop: "foo" },
query: { some_query_arg: "bar" }
});
```
#### Undocumented request parameters
To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you send will be sent as-is.
```python
client.messages.create({
// ...
// @ts-expect-error baz is not yet public
baz: "undocumented option"
});
```
For requests with the `GET` verb, any extra parameters will be in the query; all other requests will send the extra parameter in the body.
If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request options.
#### Undocumented response properties
To access undocumented response properties, you may access the response object with `// @ts-expect-error` on the response object, or cast the response object to the requisite type. Like the request parameters, the SDK does not validate or strip extra properties from the response from the API.
### Customizing the fetch client
By default, this library expects a global `fetch` function is defined.
If you want to use a different `fetch` function, you can either polyfill the global:
```python
import fetch from "my-fetch";
globalThis.fetch = fetch;
```
Or pass it to the client:
```python
import fetch from "my-fetch";
const client = new Anthropic({ fetch });
```
### Fetch options
If you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when creating the client or making a request. (Request-specific options override client options.)
```python
const client = new Anthropic({
fetchOptions: {
// `RequestInit` options
}
});
```
### Configuring proxies
To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy options to requests:
Node.js
Bun
Deno
```python
import * as undici from "undici";
const proxyAgent = new undici.ProxyAgent("http://localhost:8888");
const client = new Anthropic({
fetchOptions: {
dispatcher: proxyAgent
}
});
```
## Beta features
Beta features are available before general release to get early feedback and test new functionality. You can check the availability of all of Claude's capabilities and tools in the [build with Claude overview](../Guides/build-with-claude-overview.md).
You can access most beta API features through the beta property of the client. To enable a particular beta feature, you need to add the appropriate [beta header](../Endpoints/beta-headers.md) to the `betas` field when creating a message.
For example, to enable [context editing](../Guides/build-with-claude-context-editing.md):
```python
const client = new Anthropic();
const response = await client.beta.messages.create({
model: "claude-opus-5-5",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello, Claude" }],
betas: ["context-management-2025-06-27"]
});
```
## Runtime support
### Browser usage
Enabling the `dangerouslyAllowBrowser` option can be dangerous because it exposes your secret API credentials in the client-side code. Web browsers are inherently less secure than server environments, any user with access to the browser can potentially inspect, extract, and misuse these credentials. This could lead to unauthorized access using your credentials and potentially compromise sensitive data or functionality.
**When might this not be dangerous?**
In certain scenarios where enabling browser support might not pose significant risks:
- **Internal tools:** If the application is used solely within a controlled internal environment where the users are trusted, the risk of credential exposure can be mitigated.
- **Development or debugging purpose:** Enabling this feature temporarily might be acceptable, provided the credentials are short-lived, aren't also used in production environments, or are frequently rotated.
## Platform integrations
For detailed platform setup guides with code examples, see:
- [Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
- [Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)
- [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
- [Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
- [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
The TypeScript SDK supports the following platforms:
- **Agent Platform:** `npm install @anthropic-ai/vertex-sdk`: Provides `AnthropicVertex` client
- **Bedrock:** `npm install @anthropic-ai/bedrock-sdk`: Provides `AnthropicBedrockMantle` client, and `AnthropicBedrock` for the `bedrock-runtime` path
- **Claude Platform on AWS:** `npm install @anthropic-ai/aws-sdk`: Provides `AnthropicAws` client. Pass `workspaceId` to the constructor or set the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable. Available in beta.
- **Foundry:** `npm install @anthropic-ai/foundry-sdk`: Provides `AnthropicFoundry` client
Use `AnthropicBedrockMantle` for new projects; `AnthropicBedrock` remains for existing applications using the Bedrock `InvokeModel` API.
## Semantic versioning
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backward-incompatible changes may be released as minor versions:
1. Changes that only affect static types, without breaking runtime behavior.
2. Changes to library internals which are technically public but not intended or documented for external use.
3. Changes that aren't expected to impact the vast majority of users in practice.
Backward-compatibility is taken seriously to ensure you can rely on a smooth upgrade experience.
## Frequently asked questions
See the [GitHub repository](https://github.com/anthropics/anthropic-sdk-typescript) for FAQs, issues, and community support.
## Additional resources
- [GitHub repository](https://github.com/anthropics/anthropic-sdk-typescript)
- [API reference](../Endpoints/overview.md)
- [Streaming Messages](../Guides/build-with-claude-streaming.md)
- [Tool use with Claude](../Agents-Tools/agents-and-tools-tool-use-overview.md)
---
## Not Found - Claude Platform Docs
Source: https://platform.claude.com/docs/en/features/adaptive-thinking
Path: 04-API-Reference/Other/features-adaptive-thinking.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [CLI, SDKs, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Ffeatures%2Fadaptive-thinking)
# Page not found
The requested page could not be found. These pages are close:
- [Extended thinking](../Guides/build-with-claude-extended-thinking.md)Configure manual extended thinking with a fixed budget_tokens budget on Claude models that support it, and migrate to adaptive thinking.
- [Thinking](../Guides/build-with-claude-thinking.md)Understand how Claude's thinking works: turn it on, read thinking output, steer thinking depth with effort, and use thinking with tools, caching, and streaming.
- [Steering thinking](../Guides/build-with-claude-thinking-steering-and-cost.md)Steer how often and how deeply Claude thinks with effort levels, system prompt guidance, and per-message steering, and understand thinking's cost and pricing.
---
## Not Found - Claude Platform Docs
Source: https://platform.claude.com/docs/en/features/api-fast-mode
Path: 04-API-Reference/Other/features-api-fast-mode.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [CLI, SDKs, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Ffeatures%2Fapi-fast-mode)
# Page not found
The requested page could not be found. These pages are close:
- [API and data retention](../Guides/build-with-claude-api-and-data-retention.md)Learn about how Anthropic's APIs and associated features retain data, including information about zero data retention (ZDR) and HIPAA-ready API access.
- [Fast mode (research preview)](../Guides/build-with-claude-fast-mode.md)Get up to 2.5x higher output tokens per second from supported Claude Opus models.
---
## Get your Claude API key - Claude Platform Docs
Source: https://platform.claude.com/docs/en/get-api-key
Path: 04-API-Reference/Other/get-api-key.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fget-api-key)
SearchCtrlK
First steps
[Intro to Claude](../../01-Getting-Started/intro.md)[Get your API key](get-api-key.md)[Quickstart](../../01-Getting-Started/get-started.md)[Authentication](manage-claude-authentication.md)
Building with Claude
[Features overview](../Guides/build-with-claude-overview.md)[Using the Messages API](../Guides/build-with-claude-working-with-messages.md)[Stop reasons and fallback](../Guides/build-with-claude-handling-stop-reasons.md)[Refusals and fallback](../Guides/build-with-claude-refusals-and-fallback.md)[Fallback credit](../Guides/build-with-claude-fallback-credit.md)
Model capabilities
[Effort](../Guides/build-with-claude-effort.md)[Task budgets (beta)](../Guides/build-with-claude-task-budgets.md)[Fast mode (research preview)](../Guides/build-with-claude-fast-mode.md)[Structured outputs](../Guides/build-with-claude-structured-outputs.md)[Citations](../Guides/build-with-claude-citations.md)[Streaming Messages](../Guides/build-with-claude-streaming.md)[Batch processing](../Guides/build-with-claude-batch-processing.md)[Search results](../Guides/build-with-claude-search-results.md)[Streaming refusals](../Test-Evaluate/test-and-evaluate-strengthen-guardrails-handle-streaming-refusals.md)[Multilingual support](../Guides/build-with-claude-multilingual-support.md)[Embeddings](../Guides/build-with-claude-embeddings.md)
[Thinking](../Guides/build-with-claude-thinking.md)
Tools
[Overview](../Agents-Tools/agents-and-tools-tool-use-overview.md)[How tool use works](../Agents-Tools/agents-and-tools-tool-use-how-tool-use-works.md)[Tutorial: Build a tool-using agent](../Agents-Tools/agents-and-tools-tool-use-build-a-tool-using-agent.md)[Define tools](../Agents-Tools/agents-and-tools-tool-use-implement-tool-use.md)[Handle tool calls](../Agents-Tools/agents-and-tools-tool-use-handle-tool-calls.md)[Parallel tool use](../Agents-Tools/agents-and-tools-tool-use-parallel-tool-use.md)[Tool Runner (SDK)](../Agents-Tools/agents-and-tools-tool-use-tool-runner.md)[Strict tool use](../Agents-Tools/agents-and-tools-tool-use-strict-tool-use.md)[Server tools](../Agents-Tools/agents-and-tools-tool-use-server-tools.md)[Web search tool](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md)[Web fetch tool](../Agents-Tools/agents-and-tools-tool-use-web-fetch-tool.md)[Code execution tool](../Agents-Tools/agents-and-tools-tool-use-code-execution-tool.md)[Advisor tool](../Agents-Tools/agents-and-tools-tool-use-advisor-tool.md)[Tool search tool](../Agents-Tools/agents-and-tools-tool-use-tool-search-tool.md)[Memory tool](../Agents-Tools/agents-and-tools-tool-use-memory-tool.md)[Bash tool](../Agents-Tools/agents-and-tools-tool-use-bash-tool.md)[Text editor tool](../Agents-Tools/agents-and-tools-tool-use-text-editor-tool.md)[Computer use tool](../Agents-Tools/agents-and-tools-tool-use-computer-use-tool.md)[Browser use tool](../Agents-Tools/agents-and-tools-tool-use-browser-use-tool.md)[Troubleshooting](../Agents-Tools/agents-and-tools-tool-use-troubleshooting-tool-use.md)
Tool infrastructure
[Tool reference](../Agents-Tools/agents-and-tools-tool-use-tool-reference.md)[Manage tool context](../Agents-Tools/agents-and-tools-tool-use-manage-tool-context.md)[Tool combinations](../Agents-Tools/agents-and-tools-tool-use-tool-combinations.md)[Tool use with prompt caching](../Agents-Tools/agents-and-tools-tool-use-tool-use-with-prompt-caching.md)[Programmatic tool calling](../Agents-Tools/agents-and-tools-tool-use-programmatic-tool-calling.md)[Fine-grained tool streaming](../Agents-Tools/agents-and-tools-tool-use-fine-grained-tool-streaming.md)
Context management
[Context windows](../Guides/build-with-claude-context-windows.md)[Context editing](../Guides/build-with-claude-context-editing.md)[Prompt caching](../Guides/build-with-claude-prompt-caching.md)[Mid-conversation system messages and tool changes](../Guides/build-with-claude-mid-conversation-system-messages.md)[Build an orchestration mode](../Guides/build-with-claude-mid-conversation-effort-example.md)[Cache diagnostics](../Guides/build-with-claude-cache-diagnostics.md)[Token counting](../Guides/build-with-claude-token-counting.md)
[Compaction](../Guides/build-with-claude-compaction.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Quickstart](../Agents-Tools/agents-and-tools-agent-skills-quickstart.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)[Skills in the API](../Guides/build-with-claude-skills-guide.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)[MCP connector](../Agents-Tools/agents-and-tools-mcp-connector.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Amazon Bedrock (Opus 4.7 and later)](../Guides/build-with-claude-claude-in-amazon-bedrock.md)[Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)[Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)[Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
[Console](usage-limits.md)
[Messages](../../01-Getting-Started/intro.md)First steps
# Get your Claude API key
Copy page
Find, create, and manage your API keys for the Claude API in the Claude Console.
Copy page
API keys for the Claude API (also called Anthropic API keys) live in the Claude Console. To view your existing keys or create a new one, go to [Settings → API keys](playground.md).
## Choose a key type
When you create a key, you choose its type, which determines what the key can do, where it works, and when it stops working. A **personal key** acts as you, and stops working if you leave an organization. A **service account key** represents a [service account](manage-claude-workload-identity-federation.md#service-accounts) which can be used by workloads such as CI pipelines, production services, or agents. Use a personal key for your own development, and a service account key for anything shared.
You can also create a **workspace key**, a legacy key without an owner: it belongs to the workspace you create it in and keeps working after its creator leaves. It is preferable to use a personal or service account key, as these stop working automatically when their associated account is removed from the organization.
## Create an API key
1. 1
### Sign in to the Claude Console
Go to [platform.claude.com](usage-limits.md) and sign in, or create an account if you don't have one yet.
2. 2
### Open the API keys page
Go to [Settings → API keys](playground.md).
3. 3
### Create a key
Click **Create key**, name the key, choose an [expiration](manage-claude-authentication.md#key-expiration), and set **Linked account** to yourself or a service account. You can also choose a [workspace](playground.md) to scope the key to.
4. 4
### Copy and store the key
The Console shows the full key, which starts with `sk-ant-`, only once, at creation. Copy it and store it somewhere safe, such as a secrets manager. If you lose a key, you can't view it again in the Console. Create a new key instead.
If the **Create key** button on the API keys page is disabled, your role may not allow you to create keys there. Ask an organization admin to change your role, or to create a service account key for your workload.
## Use your API key
Set the key as an environment variable:
```python
export ANTHROPIC_API_KEY="sk-ant-api03-..."
```
The [client SDK](cli-sdks-libraries-overview.md) reads `ANTHROPIC_API_KEY` automatically. Direct HTTP requests send the key in the `x-api-key` header. If your API key works on multiple workspaces, you must also send the `anthropic-workspace-id` header on each Claude API request, as shown in [Select a workspace](manage-claude-authentication.md#select-a-workspace). For the Admin API, see [API keys and the Admin API](#api-keys-and-the-admin-api).
To make your first request, follow the [Quickstart](../../01-Getting-Started/get-started.md), and see [Authentication](manage-claude-authentication.md) for the full picture, including short-lived credentials with Workload Identity Federation.
## API keys and the Admin API
The [Admin API](../Admin/http-beta-organization.md) includes endpoints for managing your organization's API keys programmatically, such as [Retrieve API Key](../Admin/beta-organization-api-keys-retrieve.md) and [List API Keys](../Admin/beta-organization-api-keys-list.md). These endpoints are for organization admins automating key management. They accept an [Admin API key](manage-claude-admin-api-keys.md), an OAuth token with the `org:admin` scope, or a personal or service account key that isn't scoped to a specific workspace; workspace keys don't work there. They never return a key's secret value, only a partially redacted hint.
The Admin API can't recover a lost key or give you a key to call the Claude API with. To get a usable API key, create one in [Settings → API keys](playground.md) in the Claude Console.
---
## Documentation - Claude Platform Docs
Source: https://platform.claude.com/docs/en/home
Path: 04-API-Reference/Other/home.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fhome)
Claude Platform
# Start building with Claude
Everything you need to integrate Claude into your applications. From first API call to production.
SearchCtrlK
[Quickstart](../../01-Getting-Started/get-started.md)[Get API key](playground.md)[API reference](../Endpoints/overview.md)
Python
TypeScript
Go
Java
Ruby
PHP
C#
cURL
CLI
```python
import anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": "Hello, Claude"
}]
)
for block in message.content:
if block.type == "text":
print(block.text)
```
Platform
## Choose how you build
Pick the developer surface that matches your approach, and the infrastructure that fits your stack.
### Messages
Direct model access. You construct every turn, manage conversation state, and write your own tool loop.
[Quickstart](../../01-Getting-Started/get-started.md)[API reference](../Endpoints/messages-create.md)[Client SDKs](cli-sdks-libraries-overview.md)
### Managed Agents
Fully managed agent infrastructure. Deploy and manage autonomous agents in stateful sessions with persistent event history.
[Quickstart](managed-agents-quickstart.md)[API reference](../Endpoints/beta-sessions.md)[Define your agent](managed-agents-agent-setup.md)
Claude is also available on these cloud platforms:
[Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
[Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)
[Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
Developer journey
## From idea to production
Follow the lifecycle or jump to what you need.
Messages
Managed Agents
1. 1
### Get started
[Quickstart](../../01-Getting-Started/get-started.md)
[Get API key](playground.md)
[Choose a model](../../20-Models/about-claude-models-overview.md)
[Install an SDK](cli-sdks-libraries-overview.md)
[Try the API in playground](playground.md)
2. 2
### Build
[Messages API](../Endpoints/messages-create.md)
[Thinking](../Guides/build-with-claude-thinking.md)
[Vision](../Guides/build-with-claude-vision.md)
[Tool use](../Agents-Tools/agents-and-tools-tool-use-overview.md)
[Web search](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md)
[Code execution](../Agents-Tools/agents-and-tools-tool-use-code-execution-tool.md)
[Structured outputs](../Guides/build-with-claude-structured-outputs.md)
[Prompt caching](../Guides/build-with-claude-prompt-caching.md)
[Streaming](../Guides/build-with-claude-streaming.md)
3. 3
### Evaluate and ship
[Prompting best practices](../../10-Prompting-Guides/build-with-claude-prompt-engineering-overview.md)
[Run evals](../Test-Evaluate/test-and-evaluate-develop-tests.md)
[Batch testing](../Guides/build-with-claude-batch-processing.md)
[Safety and guardrails](../Test-Evaluate/test-and-evaluate-strengthen-guardrails-increase-consistency.md)
[Rate limits and errors](../Endpoints/rate-limits.md)
[Cost optimization](../../17-Billing-Plans/about-claude-pricing.md)
4. 4
### Operate
[Workspaces and admin](manage-claude-workspaces.md)
[API key management](playground.md)
[Usage monitoring](manage-claude-usage-cost-api.md)
[Model migration](../../20-Models/about-claude-models-migration-guide.md)
Models
## The Claude model family
Choose the right model for your use case.
### [Fable 5.1](../../20-Models/models-fable-5-1-overview.md)
New
Most capableResearchMulti-day tasks
For demanding reasoning and long-horizon agentic work
### [Opus 5.5](../../20-Models/models-opus-5-5-overview.md)
New
Complex projectsAgentsCoding
For long-running agentic coding and knowledge work
### [Sonnet 5.5](../../20-Models/models-sonnet-5-5-overview.md)
New
Everyday tasksWritingCost-efficient
The best combination of speed and intelligence
### [Haiku 4.5](../../20-Models/models-haiku-4-5-overview.md)
FastestLowest costHigh volume
The fastest model with near-frontier intelligence
Resources
## Keep learning
[Courses](https://academy.claude.com/courses)
Interactive courses to master Claude.
[Cookbook](https://platform.claude.com/cookbook)
Code samples and patterns.
[Quickstarts](https://github.com/anthropics/anthropic-quickstarts)
Deployable starter apps.
[What's new](../../20-Models/release-notes-overview.md)
Latest features and updates.
[Claude Code](../../02-Claude-Code-CLI/code-home.md)
An agentic coding assistant in your terminal.
---
## How can I access the Claude API? | Claude Help Center
Source: https://support.claude.com/en/articles/8114521-how-can-i-access-the-claude-api
Path: 04-API-Reference/Other/how-can-i-access-the-claude-api.md
# How can I access the Claude API?
August 18, 2026
Organizations interested in building with the Claude API can create a [Claude Console account](usage-limits.md). The Claude Console is where you can create API keys, add users to your team, set up billing, and experiment with Claude on the playground. Please note that access to the API is subject to our [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms).
Read more about [building with the Claude API here](https://claude.com/platform/api).
If you are an alignment researcher and are interested in accessing the API for AI alignment research purposes, read more about accessing the Claude API here: [What is the External Researcher Access Program?](../../15-Claude-AI-Features/what-is-the-external-researcher-access-program.md)
---
## How can I get higher rate limits on the Claude API? | Claude Help Center
Source: https://support.claude.com/en/articles/10366389-how-can-i-advance-my-claude-api-usage-to-tier-2
Path: 04-API-Reference/Other/how-can-i-advance-my-claude-api-usage-to-tier-2.md
# How can I get higher rate limits on the Claude API?
Usage tiers are assigned automatically. There's no deposit or purchase that moves you up, and you don't need to take any action.
Claude Console organizations linked to a Team plan for monthly API credits move to at least the Build tier, so the full \$500 monthly credit fits under the organization’s spend limit. Claiming monthly API credits doesn’t otherwise change your usage tier. Learn more about **[monthly API credits for Max and Team plans](https://support.claude.com/en/articles/17154008)**.
If you need higher limits, you can request them in the Claude Console once you're using at least 50% of your current limits. You can view your tier and current limits in the **[Claude Console](playground.md)**.
For custom limits or committed capacity, **[contact our sales team](https://claude.com/contact-sales)**.
Refer to **[our Claude Platform Docs](https://docs.claude.com/en/api/rate-limits)** to read more about rate limits and usage tiers.
---
## How can I check connectivity to the Claude API? | Claude Help Center
Source: https://support.claude.com/en/articles/13403291-how-can-i-check-connectivity-to-the-claude-api
Path: 04-API-Reference/Other/how-can-i-check-connectivity-to-the-claude-api.md
# How can I check connectivity to the Claude API?
March 16, 2026
You can programmatically verify your connection to the Claude API by following these steps:
1. **Create an API key:** You'll need a valid API key to make requests to the API. Log in to your [Claude Console account](usage-limits.md) to [create an API key](playground.md).
2. **Make a test request:** Use your API key to make a test request to the API. You can refer to our [Getting started documentation](../Endpoints/overview.md#examples) for an example.
3. **Verify request success:** Check if your test request is successful. This can include examining the response status code, response body, and any error messages.
---
## How do I get access to Claude in Amazon Bedrock? | Claude Help Center
Source: https://support.claude.com/en/articles/7996920-how-do-i-get-access-to-claude-in-amazon-bedrock
Path: 04-API-Reference/Other/how-do-i-get-access-to-claude-in-amazon-bedrock.md
# How do I get access to Claude in Amazon Bedrock?
March 16, 2026
Get started with Claude in Amazon Bedrock by visiting the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/). For a step-by-step walkthrough on how to request Claude model access in the Amazon Bedrock console, [view this blog](https://aws.amazon.com/blogs/aws/anthropics-claude-3-5-sonnet-model-now-available-in-amazon-bedrock-the-most-intelligent-claude-model-yet/).
---
## Models overview - Claude Platform Docs
Source: https://support.claude.com/en/articles/8606395-how-large-is-the-claude-api-s-context-window
Path: 04-API-Reference/Other/how-large-is-the-claude-api-s-context-window.md
- [Managed Agents](https://support.claude.com/docs/en/managed-agents/overview)
- [Admin](https://support.claude.com/docs/en/manage-claude/admin-api)
- Resources
- [Best practices](https://support.claude.com/docs/en/about-claude/use-case-guides/overview)
- [Models & pricing](https://support.claude.com/docs/en/models/overview)
- [SDKs, CLI, and libraries](https://support.claude.com/docs/en/cli-sdks-libraries/overview)
- [Claude API skill](https://support.claude.com/docs/en/agents-and-tools/agent-skills/claude-api-skill)
- [Release notes](https://support.claude.com/docs/en/release-notes/overview)
[API reference](https://support.claude.com/docs/en/api/overview)
[Console](https://support.claude.com/)[Log in](https://support.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmodels%2Foverview)
SearchCtrlK
Models
[Models overview](https://support.claude.com/docs/en/models/overview)
[Claude Fable 5.1](https://support.claude.com/docs/en/models/fable-5-1/overview)
[Claude Opus 5.5](https://support.claude.com/docs/en/models/opus-5-5/overview)
[Claude Sonnet 5.5](https://support.claude.com/docs/en/models/sonnet-5-5/overview)
[Claude Haiku 5.5](https://support.claude.com/docs/en/models/haiku-5-5/overview)
Specialized models
Legacy models
Guides
[Choosing a model](https://support.claude.com/docs/en/about-claude/models/choosing-a-model)[Optimizing for cost and intelligence](https://support.claude.com/docs/en/about-claude/models/optimizing-for-cost-and-intelligence)[Upgrade between model versions](https://support.claude.com/docs/en/about-claude/models/migration-guide)
Lifecycle and reference
[Model IDs and versioning](https://support.claude.com/docs/en/about-claude/models/model-ids-and-versions)[Model deprecations](https://support.claude.com/docs/en/about-claude/model-deprecations)[Model cards](https://support.claude.com/docs/en/resources/overview)[Pricing](https://support.claude.com/docs/en/about-claude/pricing)[API credits for subscribers](https://support.claude.com/docs/en/about-claude/api-credits-for-subscribers)
[System prompts](https://support.claude.com/docs/en/release-notes/system-prompts/overview)
[Console](https://support.claude.com/)
Models & pricingModels
# Models overview
Copy page
Claude is a family of state-of-the-art large language models developed by Anthropic. Compare the current lineup, find the model ID for every platform, and open each model's page for its full specs and resources.
[Choosing a model](https://support.claude.com/docs/en/about-claude/models/choosing-a-model)[Pricing](https://support.claude.com/docs/en/about-claude/pricing)[Migration guide](https://support.claude.com/docs/en/about-claude/models/migration-guide)
Copy page
## Compare models
If you're unsure which model to use, start with [Claude Opus 5.5](https://support.claude.com/docs/en/models/opus-5-5/overview) for most workloads. Use [Claude Fable 5.1](https://support.claude.com/docs/en/models/fable-5-1/overview) for demanding reasoning and long-horizon agentic work, or when your evals on Claude Opus 5.5 at higher effort still fall short. All current models support text and image input, text output, multilingual capabilities, vision, and tool use. Each model's page lists the platforms it's available on.
[TABLE]
Once you've picked a model, [learn how to make your first API call](https://support.claude.com/docs/en/get-started). To understand how model IDs, aliases, and snapshots work, see [Model IDs and versioning](https://support.claude.com/docs/en/about-claude/models/model-ids-and-versions); for the reliable-knowledge and training-data cutoffs behind each model, see [Anthropic's Transparency Hub](https://www.anthropic.com/transparency).
## Using the Models API
You can query model capabilities and token limits programmatically with the [Models API](https://support.claude.com/docs/en/api/models/list). The response includes `max_input_tokens`, `max_tokens`, and a `capabilities` object for every available model.
Each model in the response also has a `line` field, which names the model line it belongs to. Claude Opus 4.5 and Claude Opus 4.6 both report `opus`. Use `line` to group models, for example, in a model picker. `line` is `null` when a model belongs to no line. Read `line` instead of inferring it from the model's `id`. Anthropic might add more lines, so don't treat the set of values as fixed.
Each model's `capabilities` object includes `thinking.types.disabled`, which reports whether the model accepts `thinking: {type: "disabled"}`, the setting that [turns thinking off](https://support.claude.com/docs/en/build-with-claude/thinking#turning-thinking-off). `supported` is `false` when the model rejects `"disabled"` with a 400 error, and `true` on a model that doesn't support thinking. Even when `supported` is `true`, the API can still reject a `"disabled"` request for another reason. One such reason is an [effort](https://support.claude.com/docs/en/build-with-claude/effort) level that the model doesn't allow with thinking off.
Each model's `capabilities` object also includes `server_tools`, which reports whether the model accepts the [web search](https://support.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) and [code execution](https://support.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool) tools. It doesn't cover other server tools, such as web fetch. `server_tools.web_search.supported` and `server_tools.code_execution.supported` are `true` when the model accepts at least one version of that tool, not necessarily every version. `server_tools.supported` is `true` when the model accepts at least one of the two tools. Even when a tool is supported, your organization's settings can still cause a request that uses it to fail. For example, an administrator can [disable web search](https://support.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool#how-to-use-web-search).
The top-level `code_execution` capability is a different check. It reports whether code that Claude runs in the code execution tool can call the request's other tools, as in [programmatic tool calling](https://support.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling). For Claude Haiku 4.5, for example, the Models API reports `server_tools.code_execution.supported` as `true` and `code_execution.supported` as `false`.
## Prompt and output performance
Current Claude models excel in:
- **Performance:** Top-tier results in reasoning, coding, multilingual tasks, long-context handling, honesty, and image processing. See [Prompting best practices](https://support.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices) for general and model-specific prompting guidance.
- **Engaging responses:** Claude models are ideal for applications that require rich, human-like interactions. If you prefer more concise responses, adjust your prompts to guide the model toward the desired output length. Refer to the [prompt engineering guides](https://support.claude.com/docs/en/build-with-claude/prompt-engineering) for details.
- **Output quality:** When migrating from a previous model generation, you may notice larger improvements in overall performance. If you're on Claude Opus 5 or earlier, see the [Claude Opus 5.5 migration guide](https://support.claude.com/docs/en/models/opus-5-5/migration-guide).
## Get started with Claude
If you're ready to start exploring what Claude can do for you, dive in! Whether you're a developer looking to integrate Claude into your applications or a user wanting to experience the power of AI firsthand, the following resources can help.
[Intro to Claude](https://support.claude.com/docs/en/intro)
Explore Claude's capabilities and development flow.
[Quickstart](https://support.claude.com/docs/en/get-started)
Learn how to make your first API call in minutes.
[Choosing a model](https://support.claude.com/docs/en/about-claude/models/choosing-a-model)
Establish criteria and pick the right model for your use case.
[Pricing](https://support.claude.com/docs/en/about-claude/pricing)
Complete pricing, including batch discounts and prompt caching rates.
[Model deprecations](https://support.claude.com/docs/en/about-claude/model-deprecations)
Lifecycle status and retirement commitments for every model.
[Claude Console](https://support.claude.com/)
Craft and test prompts directly in your browser.
Looking to chat with Claude? Visit [claude.ai](https://claude.ai). If you have questions, reach out to the [support team](https://support.claude.com/) or the [Discord community](https://www.anthropic.com/discord).
---
## How will I be billed for Claude API use? | Anthropic Help Center
Source: https://support.claude.com/en/articles/8114526-how-will-i-be-billed-for-claude-api-use
Path: 04-API-Reference/Other/how-will-i-be-billed-for-claude-api-use.md
# How will I be billed for Claude API use?
March 16, 2026
**Note:** If you have received access to the Claude Console on or after 2/13/24, you will pay via our prepaid billing process. Refer to this article for more information on prepaid billing: [How do I pay for my Claude API usage?](../../22-Safety-Policy/how-do-i-pay-for-my-claude-api-usage.md)
Once you’re on a paid API contract, we aggregate your usage across API calls, Console chats, and other services we may make available associated with your account. In general, failed requests are not charged, and you will only be billed for successful API calls and completed tasks. However you will be charged if your client disconnects or times out in the middle of an API call that was on track to be successful.
Regardless of what interface you use, we apply our [standard pay-as-you-go pricing](../../17-Billing-Plans/pricing.md#api). At the end of every calendar month, you will receive an invoice from Stripe. You can then enter your payment details directly into Stripe to pay the invoice.
You can view your charges for the current billing period by checking the [Billing page within your Claude Console settings](playground.md).
---
## I created a Claude Console organization - how do I start using the Claude API? | Claude Help Center
Source: https://support.claude.com/en/articles/8114531-i-created-a-claude-console-organization-how-do-i-start-using-the-claude-api
Path: 04-API-Reference/Other/i-created-a-claude-console-organization-how-do-i-start-using-the-claude-api.md
# I created a Claude Console organization - how do I start using the Claude API?
To use the Claude API and the playground, add usage credits to your organization's balance. You’ll need to fill out some additional information about your organization and use case. Then, you’ll enter your payment details and fund your account.
Claude Max and Team plans include monthly credits for the Claude API. If you’re a Max subscriber or a Team plan owner, you can link this Console organization to your plan to claim them. No payment method is needed to claim or use the credits. Learn more about **[monthly API credits for Max and Team plans](https://support.claude.com/en/articles/17154008)**.
Once you’ve purchased usage credits for your account, you’re ready to use the API in your commercial application. You can **[find more information on prepaid billing and credits here](https://support.claude.com/en/articles/8977456-how-do-i-pay-for-my-api-usage)**.
If you need custom rate limits, monthly invoicing, or hands-on support, **[contact us](https://claude.com/contact-sales)** for further information.
---
## I'm getting an API connection error. How can I fix it? | Claude Help Center
Source: https://support.claude.com/en/articles/10366432-i-m-getting-an-api-connection-error-how-can-i-fix-it
Path: 04-API-Reference/Other/i-m-getting-an-api-connection-error-how-can-i-fix-it.md
# I'm getting an API connection error. How can I fix it?
March 16, 2026
This error usually indicates an issue on your end, potentially related to your firewall, network, or VPN. Try these steps:
- Check if there are any firewall rules or network restrictions blocking the connection to the Claude API endpoint.
- Configure your firewall or network settings to allow the connection.
- Ensure you're not using a VPN when running the request.
If you need to [contact our Support team](https://support.claude.com/en/articles/9015913-how-to-get-support) for further assistance, you can do so by clicking on the message icon in the bottom right of our Help Center, or by clicking your initials in the lower left corner of your Claude Console account, then "Get help."
---
## I use Claude in Amazon Bedrock. Who do I contact for customer support inquiries? | Claude Help Center
Source: https://support.claude.com/en/articles/7996921-i-use-claude-in-amazon-bedrock-who-do-i-contact-for-customer-support-inquiries
Path: 04-API-Reference/Other/i-use-claude-in-amazon-bedrock-who-do-i-contact-for-customer-support-inquiries.md
# I use Claude in Amazon Bedrock. Who do I contact for customer support inquiries?
March 16, 2026
[Contact AWS Support](https://aws.amazon.com/contact-us/) for Claude in Amazon Bedrock support inquiries or reach out to your AWS account manager. For community-based support, visit [AWS re:Post](https://repost.aws/).
## How can I get a refund for my usage through Amazon Bedrock?
If you're using Claude through AWS Bedrock, your usage is non-refundable. If you are a customer with a private offer and direct contract with Anthropic for your Bedrock usage, you can reach out to your Anthropic relationship manager for additional assistance.
------------------------------------------------------------------------
**Note:** For Japanese Customers using Bedrock, Anthropic will register for Japanese Consumption Tax once it becomes a Japanese Consumption Taxpayer by exceeding the legal thresholds (at the earliest as of FY2026).
---
## Inviting members to the Claude Console | Claude Help Center
Source: https://support.claude.com/en/articles/13443764-inviting-members-to-the-claude-console
Path: 04-API-Reference/Other/inviting-members-to-the-claude-console.md
# Inviting members to the Claude Console
March 16, 2026
The instructions in this article apply to your overall Console organization. If you are working within a specific Console Workspace, see this article: [Creating and managing Workspaces in the Claude Console](../../13-Enterprise-Admin/creating-and-managing-workspaces-in-the-claude-console.md).
**Note:** Only organization Admins can manage Console members and invitations. See [Claude Console Roles and Permissions](../../13-Enterprise-Admin/claude-console-roles-and-permissions.md).
## Inviting members to your Console organization
1. Log in as a Admin.
2. Navigate to [Settings \> Members](playground.md).
3. Click the "+ Invite" button in the upper right corner of the page.
4. Enter up to 50 email addresses in the text box, separated by commas or new lines.
5. Select the role for the new users from the drop-down.
6. Click "Send Invites."
7. The newly-added users will receive an emailed invite and appear on the **Members** list with a "Pending" badge.
## Do Console invitations expire?
Yes, newly-invited members have three weeks to accept the invitation via email.
## Revoking invitations to your Console organization
1. Locate the "Pending" member in [Settings \> Members](playground.md).
2. Click the trash can icon to the right of the listed user to revoke their invitation.
3. Confirm the action by clicking "Revoke" in the **Revoke invitation?** modal.
---
## Verify Access Transparency events with the transparency log - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/access-transparency-log
Path: 04-API-Reference/Other/manage-claude-access-transparency-log.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Faccess-transparency-log)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Overview](manage-claude-access-transparency.md)[Transparency log (beta)](manage-claude-access-transparency-log.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Access Transparency
# Verify Access Transparency events with the transparency log
Copy page
Use signed checkpoints and Merkle proofs from the Compliance API to verify that no Access Transparency event was removed or altered after it was committed to the log.
Copy page
Verify Access Transparency events with the transparency log
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
Learn how to cryptographically verify that no [Access Transparency](manage-claude-access-transparency.md) event was removed or altered after it was committed to your organization's transparency log.
The transparency log is in beta, and its endpoints and response shapes may change during the beta. It is part of Access Transparency, which is available to eligible customers on request and is not self-serve (see [Access Transparency](manage-claude-access-transparency.md)). Anthropic maintains one for your organization once Access Transparency is enabled. You read it through the Compliance API with the same key and scope you use for the [Activity Feed](manage-claude-compliance-activity-feed.md).
Your log is created when the first Access Transparency event is recorded for your organization after enablement. Until then, every transparency log endpoint returns `404`.
The transparency log is provided for your information. It is not designed or certified to satisfy any security, privacy, or regulatory requirement. You are responsible for determining whether it fits your own obligations.
## How the transparency log works
A transparency log is a technique for making a record tamper-evident. Entries are only ever appended. Each time the log grows, its operator signs a short statement, called a checkpoint, that commits to every entry so far through a Merkle tree hash. Anyone who keeps a checkpoint can later demand proof that the current log still contains everything that checkpoint covered, unchanged and in the same order. Removing or rewriting an entry therefore cannot go unnoticed by a verifier who kept a checkpoint that covered that entry. Certificate Transparency and the Go module checksum database are built on the same technique. [C2SP tlog-tiles](https://c2sp.org/tlog-tiles) is an open specification for serving such a log as signed checkpoints plus static, cacheable tiles of hashes and entries, so that clients can fetch the hashes and compute every proof themselves.
When Access Transparency is enabled, Anthropic maintains a transparency log for your organization. It is an append-only, cryptographically signed record of Access Transparency events (`anthropic_access` and `cmek_preserve`). Each such event recorded for your organization after the log was created is appended to it. The log follows the C2SP tlog-tiles format, so tooling built for that standard understands its checkpoints, tiles, and proofs.
- **One log per organization.** Each organization's log has a fixed origin string: `axt.anthropic.com/<your organization UUID>`. The origin never changes for the life of the organization.
- **Every new event becomes a leaf.** When an Access Transparency event becomes eligible to appear on your Activity Feed, it is first appended to your log as a leaf, and only then served on the feed. A leaf is a deterministic serialization of the event's documented fields. The event on your feed carries `transparency_log_leaf_index`, its zero-based position in your log.
- **Checkpoints commit to the whole history.** The log is a Merkle tree. Whenever it grows, Anthropic publishes a signed checkpoint: a short text document stating the log's origin, its current size, and the root hash that commits to every leaf. Each checkpoint carries exactly one signature from the log's signing key.
- **Two proofs follow.** An inclusion proof shows that a specific event is present at its position under a checkpoint. A consistency proof shows that a later checkpoint is an append-only extension of an earlier one you saved, so nothing between them was removed or changed.
- **Verification keys are served in-band.** The [verifier keys endpoint](#read-the-verifier-keys) returns the public keys that sign checkpoints. In a planned key rotation, the new key is added to that list before it starts signing, and earlier keys stay listed. Checkpoints you already hold therefore keep verifying.
### What the transparency log proves
- The leaf fields of an event you hold (listed in [How an event becomes a leaf](#how-an-event-becomes-a-leaf)) are, byte for byte, what Anthropic committed to the log.
- The log for your organization only ever grows. Verification against a checkpoint you hold fails if an event committed to the log is later deleted or rewritten there. It also fails if you are served a different history from the one you were served before.
- New entries can only be appended. An event cannot be inserted into history you have already verified.
### What it does not prove or change
- It does not prove that every access was recorded, or that a recorded event accurately describes the access. It proves only that what Anthropic committed to the log has not changed since.
- It does not change [what Access Transparency covers](manage-claude-access-transparency.md#what-access-transparency-covers) or when events arrive.
- Served fields outside the leaf, such as `workspace_uuid` and any field added later, are not covered by the proof.
- An inclusion proof speaks for an event you were served. It does not by itself prove that the feed listed every leaf the log holds. The log's [entry bundles](#read-an-entry-bundle) contain every leaf, so you can read the complete set of committed events directly when you need it.
- The presence of `transparency_log_leaf_index` on an event is a pointer, not a proof. Always verify inclusion before treating an event as committed in the log.
- The protection against a rewritten history comes from the checkpoints you keep. A checkpoint's signature by a key listed in [Published key fingerprints](#published-key-fingerprints) proves that it came from Anthropic's log. A consistency proof from the checkpoint you saved last time proves that the history you already observed only grew.
## Before you begin
You need:
- A Compliance Access Key with the `read:compliance_activities` scope, the same key and scope you use for the [Activity Feed](manage-claude-compliance-activity-feed.md). A parent-organization key can read each enrolled child organization's log by naming the child organization on every request.
- Your organization's UUID. Find it in the Claude Console under **Settings \> Organization**. It is the same value the Activity Feed serves as `organization_uuid`, but take it from the Console. This value is what makes a checkpoint yours, so it must not come from the API you are verifying. You derive your log's origin from it as `axt.anthropic.com/<organization UUID>`. Derive this string yourself. Do not read it from an API response.
- Somewhere durable to keep the last checkpoint you verified. That saved checkpoint is what turns "the log is consistent today" into "the log has been consistent since you started watching."
## Timing
- **Events:** Access Transparency events appear on your Activity Feed within two business days of the access. An event enters the log only once it is eligible to be served, so the log never reveals an event early. Because the log is written before the feed serves the event, an entry can briefly appear in the log before its event appears on your feed. That gap is not a discrepancy.
- **Checkpoints:** A new checkpoint is published whenever your log grows.
- **Inclusion proofs:** A proof for a newly served event is available once a checkpoint covering the event's position is published, normally very shortly after the event appears. If you request one sooner, you receive a `404` and retry after a short delay.
- **Verification cadence:** Run your verification at least daily. Hourly is reasonable.
- **Disenrollment:** If your organization stops using Access Transparency, nothing is deleted. Your log stays readable through the same endpoints. If Access Transparency is enabled again later, the same log continues.
## Retention and deletion
- **Transparency log:** Anthropic does not delete entries from your log, and the log has no expiry. It is kept if your organization stops using Access Transparency and after your organization is deleted, because removing entries is the change the log exists to detect. [Entry bundles](#read-an-entry-bundle) hold the [leaf fields](#how-an-event-becomes-a-leaf) of each event, so those fields are kept for as long as the log is.
- **Activity Feed:** Access Transparency events on the Activity Feed follow the feed's retention. Activities are retained for 6 years. See [Query the Activity Feed](manage-claude-compliance-activity-feed.md).
- **No deletion by you:** The transparency log endpoints are read-only. There is no way to delete or amend an entry.
## Transparency log endpoints
Six read-only endpoints are served under `https://api.anthropic.com/v1/compliance/transparency_log/`:
| Endpoint | Returns |
|:-----------------------------------------------------|:-----------------------------------------------------------------|
| [`GET /checkpoint`](#read-the-latest-checkpoint) | The latest signed checkpoint |
| [`GET /keys`](#read-the-verifier-keys) | The verifier key set |
| [`GET /inclusion`](#fetch-an-inclusion-proof) | An inclusion proof for one event |
| [`GET /consistency`](#fetch-a-consistency-proof) | A consistency proof from a checkpoint you hold to the latest one |
| [`GET /tile/{level}/{index}`](#read-a-hash-tile) | A Merkle hash tile |
| [`GET /tile/entries/{index}`](#read-an-entry-bundle) | An entry bundle of leaf bytes |
Checkpoints, tiles, and entry bundles follow the C2SP tlog-tiles wire format exactly. The two proof endpoints are conveniences: every proof is also computable from tiles, so you never have to trust a proof endpoint's output. You verify the hashes it returns against a signed checkpoint.
### Authentication and scoping
Send your Compliance Access Key in the `x-api-key` header and the `anthropic-version` header, as for every Compliance API request (see [Versioning](manage-claude-compliance-api.md#versioning)). The Compliance API must be enabled for your organization.
There is no separate transparency log permission. Any key that can read your organization's Activity Feed, for your organization or its parent, can read your whole log, including the event fields in its entry bundles.
Every request reads exactly one organization's log:
- An organization-level key reads its own organization's log. The `organization_id` query parameter is optional. If present, it must name the key's own organization.
- A parent-organization-level key must pass `organization_id`, naming one child organization.
- `organization_id` accepts the `org_...` tagged ID or the organization UUID.
A `404` means there is no log this key can read. An organization outside the key's scope, a nonexistent organization, and an organization whose log has not been created yet are deliberately indistinguishable. The log of an organization that has since stopped using Access Transparency is not this case: it continues to be served.
### Errors
Errors use the standard Compliance API JSON error envelope on every endpoint, including the text and binary ones. See [Errors](manage-claude-compliance-errors.md) for the envelope and the shared error types.
| Status | Meaning on this surface |
|:-------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `400` | `organization_id` is malformed or is omitted with a parent-organization key, the Compliance API is not enabled, a query parameter is unknown, or a per-endpoint validation failed |
| `401` | The API key is missing or not valid |
| `403` | The key lacks the required scope |
| `404` | No log readable by this key, or the per-endpoint "not covered" and "beyond the tree" cases |
| `429` | Rate limited. These endpoints share the Compliance API's per-parent-organization [rate limit](manage-claude-compliance-api.md). Honor `retry-after` |
| `503` | The log is temporarily unavailable. Retry with backoff |
### Caching
Responses are cacheable only by the requesting client. `Cache-Control` always includes `private`, and responses carry `Vary: x-api-key`. Do not put a shared cache in front of these endpoints. Full tiles and full entry bundles never change and are served with `Cache-Control: private, max-age=604800, immutable`. Everything else, including checkpoints, proofs, keys, partial tiles, and errors, is served with `Cache-Control: private, no-store`.
### Read the latest checkpoint
`GET /v1/compliance/transparency_log/checkpoint`
```python
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/transparency_log/checkpoint" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
The response is `text/plain`: a C2SP [signed note](https://c2sp.org/signed-note). The body lines are the origin, the tree size in decimal, and the base64 root hash. A blank line follows, then the signature line, which starts with an em dash (U+2014), names the origin, and ends with a base64 value. Values here are illustrative:
``` block
axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b
1207
C6C4HzGRqDNlbu54LWCvpDX0NcB5DRTmLcjM4u5vWUI=
— axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b q83vATBFAiEAvL8m…(base64)…
```
- The first four bytes of the decoded signature value are the signing key's `key_hash`, which tells you which entry in the [verifier key set](#read-the-verifier-keys) to verify with. The remaining bytes are an ASN.1 DER ECDSA P-256 signature over the SHA-256 of the note body: every byte before the blank line, including the body's trailing newline.
- A checkpoint can carry additional lines after the root hash. Ignore lines you do not understand. They are covered by the signature.
- Ignore a signature line whose name is not your origin or whose key hash you do not hold.
- Never cache a checkpoint. A stale one hides the log's current size, so newly served events look uncovered.
### Read the verifier keys
`GET /v1/compliance/transparency_log/keys`
```python
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/transparency_log/keys" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
```python
{
"type": "transparency_log_keys",
"origin": "axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b",
"log_keys": [
{
"verifier_key": "axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b+<key_hash>+<base64 key>",
"key_hash": "<8 lowercase hex digits>",
"fingerprint": "<64 lowercase hex digits>",
"algorithm": "ecdsa_p256_sha256",
"public_key": "<base64 DER SubjectPublicKeyInfo>"
}
]
}
```
| Field | Type | Description |
|:--------------------------|:-------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | string | Always `transparency_log_keys` |
| `origin` | string | The origin line every checkpoint of this log carries. Informational: compare checkpoints against the origin you derive yourself, not against this field |
| `log_keys` | array | The key that signs new checkpoints first, then every other key the log serves, newest first. Never empty |
| `log_keys[].verifier_key` | string | The key as a C2SP note-verifier string, `<origin>+<key_hash>+<base64 key>`, accepted by tlog-tiles tooling that supports ECDSA note keys (for example, the Go `github.com/transparency-dev/formats` module). The base64 part decodes to the algorithm byte `0x02` followed by the DER-encoded public key |
| `log_keys[].key_hash` | string | Eight lowercase hex digits. The 4-byte selector that matches this key to a checkpoint's signature line. It is the first four bytes of `fingerprint` and is not an identity |
| `log_keys[].fingerprint` | string | 64 lowercase hex digits: the SHA-256 of the DER `SubjectPublicKeyInfo` |
| `log_keys[].algorithm` | string | The key type, currently `ecdsa_p256_sha256`. Values may be added. Skip a key whose algorithm you do not support |
| `log_keys[].public_key` | string | The public key as base64 DER `SubjectPublicKeyInfo` |
The `key_hash` covers only the key bytes: it is the first four bytes of SHA-256 over the DER `SubjectPublicKeyInfo`, which is the rule the ECDSA note-verifier encoding uses. It is not the name-dependent key ID that the base signed-note format defines for Ed25519 keys, so it does not change with the origin. A valid signature by a key listed in [Published key fingerprints](#published-key-fingerprints) proves the checkpoint came from Anthropic's transparency log service. The origin line inside the signed checkpoint is what binds it to your organization. That is why you compare that line against the origin you derive yourself.
Keys can rotate:
- A rotation is a cutover. From some checkpoint on, new checkpoints are signed by the new key.
- In a planned rotation, the new key appears in `log_keys` before it signs anything, and earlier keys stay listed. A checkpoint you saved before the rotation therefore keeps verifying.
- A verifier can fetch the key set on every run or hold it locally. A verifier that holds it locally re-reads this endpoint when it meets a signature whose `key_hash` it does not hold.
#### Published key fingerprints
Anthropic publishes the fingerprint of every key that signs checkpoints here, outside the API. This lets you check a key you hold locally against a source the serving path cannot alter. The key you hold may come from an earlier `GET /keys` response or from tooling that pins the key.
| Key hash | SHA-256 fingerprint | Algorithm | Signing since | Status |
|:-----------|:-------------------------------------------------------------------|:--------------------|:--------------|:--------------------|
| `1dff5fe4` | `1dff5fe420d49743fe444a04fc17f818eea856699dec2ebbc24df15602c74a58` | `ecdsa_p256_sha256` | 2026-08-17 | Current signing key |
A planned rotation is announced on this page at least 30 days before the new key signs its first checkpoint. During that notice period the new key is listed in `log_keys` and in this table with its switch date. Retired keys stay listed with their service dates. A key that does not appear in this table is not legitimate, whatever `GET /keys` returns. Treat a checkpoint that verifies under no listed key as a verification failure, and report it to your Anthropic account representative or [Anthropic support](https://support.claude.com).
[`axt-verify`](#verify-with-axt-verify) carries the current key in each release and never reads a key from the API. Each release carries exactly one key. On the switch date, Anthropic starts signing with the new key and publishes the `axt-verify` release that carries it. On the same date Anthropic re-issues every organization's latest checkpoint under the new key, even for a log that has not grown. Upgrade on the switch date. Running the old release after the switch fails with exit status `1`, and so does running the new release before it. Either failure clears once you run the matching release. A verifier you maintain yourself needs the new fingerprint, with its switch date, added before that date.
### Fetch an inclusion proof
`GET /v1/compliance/transparency_log/inclusion?leaf_index={index}`
| Parameter | Type | Description |
|:------------------|:------------------|:----------------------------------------------------------------------------------------------------------------------------------|
| `leaf_index` | integer, required | The event's position in the log: the `transparency_log_leaf_index` the Activity Feed served on the event. Must be zero or greater |
| `organization_id` | string, optional | See [Authentication and scoping](#authentication-and-scoping) |
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/transparency_log/inclusion" \
--data-urlencode "leaf_index=41" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
```python
{
"type": "transparency_log_inclusion_proof",
"leaf_index": 41,
"hashes": [
"mUdyOWMp0zXIq0CDMvSYDUSBl9yAvnTZzdm51RwWpUM=",
"yR6tDHkAhKvdQSLqQATVjXOo4GM3FDyiKF2XCKTtMUI=",
"..."
],
"checkpoint": "axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b\n42\nCsRlS31ITFHrX9GR5XjyPw8n0MkfrB8Yh2UDHl3Lr3E=\n\n— axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b q83vATBEAiB0…(base64)…\n"
}
```
| Field | Type | Description |
|:-------------|:-----------------|:-------------------------------------------------------------------------------------------|
| `type` | string | Always `transparency_log_inclusion_proof` |
| `leaf_index` | integer | The event's position in the log, echoed from the request |
| `hashes` | array of strings | The base64 sibling hashes of the audit path, ordered from the leaf up to the root |
| `checkpoint` | string | The latest signed checkpoint, the one the proof verifies against. It carries the tree size |
There is no lookup by activity ID. You always hold the index, because it arrives on the event, and you check the proof against the event bytes you fetched from the feed.
A `404` means the latest published checkpoint does not cover the supplied position:
- For an index you read off a served event, this is transient. A covering checkpoint is published shortly, so retry after a short delay.
- The same `404` answers any other uncovered position, such as an index the feed never served. For such a position there is no promise that a covering checkpoint is ever published. The response does not say which case you are in.
A `leaf_index` that is missing or is not a non-negative integer returns `400`.
### Fetch a consistency proof
`GET /v1/compliance/transparency_log/consistency?from={size}`
| Parameter | Type | Description |
|:------------------|:------------------|:-------------------------------------------------------------------------------------------------------------------|
| `from` | integer, required | The tree size of the earlier checkpoint you hold. Must be at least 1 and at most the latest checkpoint's tree size |
| `organization_id` | string, optional | See [Authentication and scoping](#authentication-and-scoping) |
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/transparency_log/consistency" \
--data-urlencode "from=1180" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
```python
{
"type": "transparency_log_consistency_proof",
"hashes": [
"dGw0aPzu2N0pdc4C5ZAvNIbkXF7J6F9ZQLkPpV6v8Vg=",
"9PSWm1T9RUmhjF6z6YQzB9CW6E2m2n3mK0aVgqf5Qm0=",
"..."
],
"checkpoint": "axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b\n1207\nC6C4HzGRqDNlbu54LWCvpDX0NcB5DRTmLcjM4u5vWUI=\n\n— axt.anthropic.com/25f6429a-3293-49bf-afed-cb312911554b q83vATBFAiEAvL8m…(base64)…\n"
}
```
| Field | Type | Description |
|:-------------|:-----------------|:-------------------------------------------------------------------------------------|
| `type` | string | Always `transparency_log_consistency_proof` |
| `hashes` | array of strings | The base64 proof hashes, in RFC 9162 order |
| `checkpoint` | string | The latest signed checkpoint, the one the proof extends to. It carries the tree size |
- The proof always extends to the latest published checkpoint. This API never serves historical checkpoints: you keep the ones you are served.
- A `from` equal to the latest checkpoint's tree size returns the empty proof.
- A held checkpoint of tree size 0 needs no consistency proof, because every log extends the empty log. Adopt the latest checkpoint directly in that case.
- A `from` less than 1, or greater than the latest checkpoint's tree size, returns `400`.
- If the log can no longer prove that it extends a checkpoint it once signed for you, treat that as a verification failure, not a usage error.
### Read a hash tile
`GET /v1/compliance/transparency_log/tile/{level}/{index}`
Returns `application/octet-stream`: concatenated 32-byte SHA-256 hashes, per tlog-tiles. Tile addressing follows tlog-tiles exactly, including the `{level}` and `{index}` path grammar, the `x001/234` index form for large trees, and the partial-tile suffix `.p/{width}`. Hash tiles are the unit from which tlog-tiles clients compute proofs themselves.
- Full tiles are immutable and are served with `Cache-Control: private, max-age=604800, immutable`.
- Partial tiles are superseded as the tree grows and are served with `Cache-Control: private, no-store`. Once a tile fills, a request for its earlier partial form can return `404` even though the full tile exists. Falling back from the partial tile to the full tile is the client's job, as tlog-tiles specifies, and standard clients already do it.
- A malformed `level`, `index`, or partial-tile width returns `400`. A tile position beyond the current tree size returns `404`.
### Read an entry bundle
`GET /v1/compliance/transparency_log/tile/entries/{index}`
Returns `application/octet-stream`: consecutive leaf entries, each prefixed with its big-endian `uint16` length, per tlog-tiles. Entry bundles contain event plaintext: the [canonical bytes](#how-an-event-becomes-a-leaf) of each Access Transparency event. That is why the whole surface requires the Activity Feed's scope. Addressing, the partial form, caching, and errors are identical to hash tiles.
## The `transparency_log_leaf_index` field on Activity Feed events
`anthropic_access` and `cmek_preserve` events on `GET /v1/compliance/activities` carry `transparency_log_leaf_index`, an integer, whenever the event has a leaf. Other activity types never carry it.
- **The key is absent, not `null`, when the event has no leaf.** A robust verifier treats an absent key and a `null` value the same way.
- **An event is served without `transparency_log_leaf_index` in only two cases.** The first is while your organization is not enrolled in Access Transparency, that is, before enrollment or between an un-enrollment and a re-enrollment. The second is when the event was recorded before your organization's log was created. For an organization enrolled before the transparency log was introduced, that includes its earlier history. Once your log exists and while you are enrolled, every event is appended to the log before the feed serves it. If a fault prevents the feed from learning the index, the feed delays the event rather than serving it without one. The event is not lost: it is already in the log and appears on the feed, index included, once the fault is remediated. An index-less event is not expected when it is dated after your log was created and falls inside a period when you were enrolled.
- **A present index is a pointer, not a proof.** Verify inclusion before treating the event as committed in the log. The anomaly worth escalating is a present index whose inclusion proof still cannot be fetched long after a covering checkpoint should have been published.
- The index is assigned when the event is appended to the log and is not one of the fields that make up the leaf.
## How an event becomes a leaf
A leaf entry is the schema-version byte `0x01` followed by canonical JSON of 11 fields. The JSON follows [RFC 8785](https://www.rfc-editor.org/rfc/rfc8785) (JSON Canonicalization Scheme), and the fields are taken from the event exactly as the Activity Feed serves it:
- `id`, `type`, `created_at`, `accessed_at`, `organization_id`, `organization_uuid`, `workspace_id`, `accessor_department`, and `reason_code`
- `actor`, with its nested `type` and `email_address`
- `resource_details`, with its nested `type`, `id`, and `parent`
The rules:
- Served fields outside that set, such as `workspace_uuid` and `transparency_log_leaf_index` itself, are ignored.
- A documented field that the served event omits enters the leaf as `null`. An empty string is distinct from `null`.
- `actor` and `resource_details` are objects with exactly their documented keys when the served event carries them. Under version `0x01`, `actor.email_address` and `resource_details.parent` are always `null`. When the served event omits one of these objects or serves it as `null`, the whole value is `null` in the leaf, not an object of `null` fields. Many access events carry no `resource_details`.
- String values, including both timestamps and `reason_code`, are taken byte for byte as served. If you re-derive a timestamp from another representation, reproduce the served rendering exactly:
- RFC 3339 UTC with a `Z` suffix.
- `created_at` has no fractional digits when its microseconds are zero, and exactly six otherwise.
- `accessed_at` has zero, three, six, or nine fractional digits, the shortest that preserves its nanoseconds exactly.
- Canonical JSON means object keys sorted, no insignificant whitespace, and minimal string escaping. No numbers appear anywhere in the leaf.
- The leaf hash is RFC 6962's `SHA-256(0x00 || entry)`. Interior nodes hash as `SHA-256(0x01 || left || right)`.
- A verifier rejects an unknown version byte, and a `0x01` leaf whose `type` is not one of the two Access Transparency types. New event types or rule changes ship under a new version byte. Existing leaves never re-hash.
For example, this access event as the Activity Feed serves it:
```python
{
"id": "activity_01GPXmAhizavrUuoXNn3tzeA",
"type": "anthropic_access",
"created_at": "2025-07-08T18:40:00Z",
"accessed_at": "2025-07-08T18:39:58Z",
"organization_id": "org_015gtSHLz269eTwgrH8NX5yk",
"organization_uuid": "25f6429a-3293-49bf-afed-cb312911554b",
"workspace_id": "wrkspc_01PaGUP2rbg1XDh7Z9W1CEpd",
"workspace_uuid": "b6ce2143-1083-d4a7-247c-17530f55a076",
"accessor_department": "Trust & Safety",
"reason_code": "safety_review",
"actor": { "type": "anthropic_actor", "email_address": null },
"resource_details": { "type": "message", "id": "msg_01HXAMPLE12345678" },
"transparency_log_leaf_index": 17
}
```
becomes this canonical JSON. It has exactly the 11 documented keys, sorted, on one line. `workspace_uuid` and `transparency_log_leaf_index` drop out, and `resource_details.parent`, absent from the served event, enters as `null`:
``` block
{"accessed_at":"2025-07-08T18:39:58Z","accessor_department":"Trust & Safety","actor":{"email_address":null,"type":"anthropic_actor"},"created_at":"2025-07-08T18:40:00Z","id":"activity_01GPXmAhizavrUuoXNn3tzeA","organization_id":"org_015gtSHLz269eTwgrH8NX5yk","organization_uuid":"25f6429a-3293-49bf-afed-cb312911554b","reason_code":"safety_review","resource_details":{"id":"msg_01HXAMPLE12345678","parent":null,"type":"message"},"type":"anthropic_access","workspace_id":"wrkspc_01PaGUP2rbg1XDh7Z9W1CEpd"}
```
The leaf entry is the byte `0x01` followed by those UTF-8 bytes. Its leaf hash, `SHA-256(0x00 || entry)`, is `6ro7vTcFq+sYDZiiGvZaFetUIoMSLig3DGDrCKK1HFU=` in base64. Use this example as a test vector for your own canonicalization code.
## Verify your log
Verification runs on your own infrastructure. Everything the API returns is untrusted until it verifies against two things you hold yourself. The first is the origin you derive from your organization UUID. The second is the checkpoint you saved on your last run. A complete verification run does the following, in order:
1. Fetch the [verifier key set](#read-the-verifier-keys). Compute each key's fingerprint yourself as the SHA-256 of its base64-decoded `public_key`. Keep only the keys whose fingerprint appears in [Published key fingerprints](#published-key-fingerprints), together with each key's status there. Verify a newly fetched checkpoint only under a key that table lists as current on that date. Accept a retired key only for a checkpoint you saved before its retirement date.
2. Establish the latest checkpoint. On the first run, fetch it from the [checkpoint endpoint](#read-the-latest-checkpoint). On every later run, request a [consistency proof](#fetch-a-consistency-proof) from the tree size you saved. The response carries the latest checkpoint together with the proof.
3. Check the checkpoint's origin line first. Compare its first line, byte for byte, with the origin you derived. Reject any checkpoint whose origin differs, before doing anything else.
4. Verify the checkpoint's signature. Find the signature line named for your origin whose first four decoded bytes equal the `key_hash` of a currently valid key you kept. Verify the remaining bytes as an ECDSA P-256 signature over the SHA-256 of the note body, using that key's `public_key`. If no signature line matches such a key, or the signature does not verify, reject the checkpoint.
5. Prove append-only. If the new tree size is smaller than the one you saved, fail. If it is equal, the root hashes must match. If it is larger, verify the RFC 9162 consistency proof from your saved size and root hash to the new size and root hash.
6. Prove each event is included. Read every Access Transparency event the Activity Feed serves. For each new one, rebuild its [leaf](#how-an-event-becomes-a-leaf), hash it, and fetch its [inclusion proof](#fetch-an-inclusion-proof). Walk the audit path from your leaf hash at the event's index up to the checkpoint's root hash. A mismatch means the event you were served is not the event the log committed. The proof response can carry a different checkpoint from the one you hold. Link it into your history with a consistency proof before you verify anything against it.
7. Re-check what you saw before. Whenever you read an event again, it must be served with the same index and the same leaf bytes as when you verified it. No event may lose the index it had, and while you are enrolled no event may newly appear without one. The events served without an index on your first run are your pre-log history. A run that re-reads only recent events re-checks only those. To re-check older ones, verify the copies you kept (step 8) again.
8. Save the checkpoint you verified and a record of each event you verified. They are your evidence and your starting point for the next run.
### Verify with axt-verify
[`axt-verify`](https://github.com/anthropics/axt-verify) is Anthropic's open-source verifier for this log. It is a single Go binary that you run on your own infrastructure. Each release has exactly one log signing key from [Published key fingerprints](#published-key-fingerprints) built in, so it never asks the API which key to trust. When Anthropic rotates the key, you upgrade to the release that carries the new one on the switch date. `axt-verify` derives your origin from the organization UUID you pass with `--org`, which is the one you took from the Console in [Before you begin](#before-you-begin). It rejects any checkpoint whose origin line differs.
Install it with Go 1.26 or newer. It reads your Compliance Access Key from the `ANTHROPIC_COMPLIANCE_ACCESS_KEY` environment variable, never from a flag or a file. The `run` command is the one to schedule:
```python
go install github.com/anthropics/axt-verify/cmd/axt-verify@latest
export ANTHROPIC_COMPLIANCE_ACCESS_KEY="<your Compliance Access Key>"
axt-verify --org 25f6429a-3293-49bf-afed-cb312911554b \
--state /var/lib/axt-verify/25f6429a-3293-49bf-afed-cb312911554b.state \
run
```
Each `run` fetches the latest checkpoint and verifies its signature and origin line. It then proves the log is an append-only extension of the checkpoint the previous run saved. Next it pages through the Access Transparency events on your Activity Feed. It starts seven days (by `created_at`) before the newest event the previous run read, so that events listed late or out of order are still picked up. It rebuilds each event's leaf, verifies an inclusion proof for it, and compares any event it verified before against what it recorded then. Finally, it saves the new checkpoint and its progress to the `--state` file, which the next run starts from. Run it at least daily. Hourly is reasonable. With a parent-organization key, run one copy per child organization, each with its own `--org` and `--state` file. Each child organization's UUID also comes from the Claude Console, not from the Compliance API responses you are verifying. Find it on the child organization's **Settings \> Organization** page or in your parent organization's list of organizations in the Console. `axt-verify checkpoint` does only the checkpoint and append-only steps. `axt-verify events FILE` verifies events you already hold, such as an auditor's sample or your own export. It proves that each event in the file is still committed in the log under the current checkpoint. It does not read the feed or touch the state file.
One limit follows from that window: `run` re-reads only the last seven days of events, so it re-checks recently served events (step 7), not your whole history. Keep the events you export (see [Keep your own checkpoint archive](#keep-your-own-checkpoint-archive)). `axt-verify events FILE` proves at any later date that those copies are still committed in the log, but it does not re-read the feed. To detect an older event removed from or rewritten on the feed, re-export that range from the Activity Feed and compare it with the copies you kept. You can also verify the re-export itself with `axt-verify events FILE`. The seven-day overlap is longer than the feed's two-business-day delivery time, so a late-arriving event still falls inside a later run's window. `--overlap` changes the duration if you need to.
If you need your own implementation instead, follow the preceding checklist with a tlog-tiles library that supports ECDSA note keys.
### Interpret the result
`axt-verify` prints your origin, the tree size and root hash of the checkpoint it verified, the tree size the append-only check started from, and a count of events by outcome. Pass `--json` to get the same report as one JSON object per line. Each event has one of four outcomes:
- **Verified:** the leaf rebuilt from the served event is committed at the event's index in the signed log.
- **Pending:** the event's index is beyond the latest published checkpoint. This is normal for a short time after an event appears. In `run`, `axt-verify` remembers the event, verifies it on a later run once a checkpoint covers it, and fails it if that takes longer than 24 hours. `events FILE` has no later run to settle it, so it waits up to a minute for a covering checkpoint to be published. If none arrives, it reports the event as not yet covered and ends with exit status `3`. Run it again later. If `events FILE` reports the same event as not yet covered on two runs at least a day apart, treat it as a verification failure and escalate as for exit status `1`.
- **Not logged:** the event was served without an index. An event is served without `transparency_log_leaf_index` only while your organization is not enrolled in Access Transparency, or when it was recorded before your organization's log was created (see [The `transparency_log_leaf_index` field on Activity Feed events](#the-transparency-log-leaf-index-field-on-activity-feed-events)). `axt-verify` reports such events as not logged and does not fail the run on them. An index-less event is not expected when it is dated after your log was created and falls inside a period when you were enrolled. Review the not-logged list in the run's summary or `--json` output rather than relying on the exit status alone.
- **Failed:** see exit status `1`.
The exit status tells your scheduler what happened:
- **`0`:** Nothing failed. Not-logged events are reported, not failed, and so are pending events in `run`.
- **`1`:** A verification failure. This is a security finding, not a transient error. Keep the state file and the output, and report it to your Anthropic account representative or [Anthropic support](https://support.claude.com). The causes are:
- A checkpoint with the wrong origin, or a signature that does not verify under the key built into your `axt-verify` release. Compare the `key_hash` on the failing checkpoint's signature line with [Published key fingerprints](#published-key-fingerprints). A key listed there with a switch date you have not upgraded for means you need the matching release. A key not listed there is a security finding, whatever release you run. On this failure `axt-verify` prints the key hash of every signature on the served checkpoint and the key hash of the key it trusts, as eight hex digits each. That output is enough to make the comparison.
- A checkpoint that is not a well-formed signed note, for example one whose root hash is not 32 bytes.
- A log that shrank, or that cannot prove it extends the checkpoint you saved. The output then carries both checkpoints and the proof, so the evidence stands on its own.
- Two signed checkpoints for the same tree size with different root hashes. The output carries both checkpoints.
- A checkpoint file passed with `--from` whose signature does not verify under the key built into your `axt-verify` release, or a `--from` or `--from-trusted` file whose origin is not yours. For an archive signed before a key rotation, see [Keep your own checkpoint archive](#keep-your-own-checkpoint-archive).
- An inclusion proof that does not reproduce the signed root hash for the event you were served.
- An event inside the run's window served differently from how an earlier run recorded it: different leaf bytes, a different index, or no index where it had one.
- An event still pending 24 hours after the run first saw it.
- A refused inclusion proof (`400`, `401`, or `403`) for an event the feed served you.
- An inclusion proof returned for a different `leaf_index` from the one requested.
- In `events FILE`, an event at an index the latest checkpoint already covered when the check began, for which no inclusion proof is served before the wait runs out.
- An event whose `organization_uuid` is not the organization UUID you passed with `--org`. When a parent organization runs `events FILE` on an export that covers several child organizations, every other organization's event fails this way, so split the export by organization first and verify each part with its own `--org`.
- The same activity `id` at two different indices, or twice at one index with different content, within one run or one `events FILE` input.
- An event whose leaf cannot be rebuilt: for example, a documented field that is not a string, a field name that appears twice, or a `type` that is missing or is an unrecognized variant of an Access Transparency type. `events FILE` skips rows of other activity types and does not fail them.
- **`2`:** A usage or configuration error. The causes are:
- A missing or malformed flag.
- No `ANTHROPIC_COMPLIANCE_ACCESS_KEY`.
- A key the API rejects (`401` or `403`) before any checkpoint has verified.
- A state file that cannot be read or belongs to another origin.
- **`3`:** The run could not complete. In `run`, what had already been verified is saved to the state file. Run again. The causes are:
- An event in `events FILE` whose index no published checkpoint covered within the wait. For such an event the **Pending** outcome says when to stop re-running and escalate.
- Network errors, rate limiting, or server errors that outlasted the retries.
- An unexpected response.
- A state or `--save` file that could not be written.
An exit status `3` with `404` responses is expected until Anthropic has recorded a first Access Transparency event for your organization, because every transparency log endpoint returns `404` until that event creates the log. Escalate if the transparency log endpoints still return `404` more than a few days after your Activity Feed first shows an Access Transparency event, whether or not that event carries a `transparency_log_leaf_index`. That combination is not expected. Once a run has succeeded, escalate an exit status `3` that persists.
### Keep your own checkpoint archive
The strongest evidence you can hold is your own record of what the log said on a given day. `axt-verify --save FILE` writes the checkpoint a run verified, verbatim, and `--from FILE` on a later run makes the log prove it still extends that checkpoint. Archive a saved checkpoint in storage you control, for example daily. Months later, a [consistency proof](#fetch-a-consistency-proof) from that archived checkpoint's tree size must still lead to whatever checkpoint the log serves, or verification fails. Earlier keys stay listed in the [verifier key set](#read-the-verifier-keys) after a planned rotation, so an archived checkpoint keeps verifying. With `axt-verify`, if the key that signed an archived checkpoint has since rotated out, pass that archive with `--from-trusted` instead of `--from`. Keep the events too. The Access Transparency events you export from the feed are valid input to `axt-verify events FILE`, which proves at any later date that those copies are still committed in the log under its current checkpoint. Because `events FILE` does not re-read the feed, your kept export is also the reference you compare a later re-export of the same range against.
## Frequently asked questions
### Do I have to verify the transparency log?
No. Anthropic maintains the log for your organization whether anyone verifies it. Verification is how you check the log for yourself. An auditor can verify a sample of events you hand them with the same steps, given an API key with the Activity Feed scope.
### Why did an inclusion proof return 404 for an event I was just served?
The event was served moments before a checkpoint covering its position was published. A covering checkpoint follows shortly, so retry after a short delay. An event whose proof is still unavailable a day later is the anomaly to escalate.
### What happens when Anthropic rotates the signing key?
Planned rotations are announced in [Published key fingerprints](#published-key-fingerprints) at least 30 days ahead, with a switch date. On the switch date Anthropic starts signing with the new key and publishes the `axt-verify` release that carries it. On the same date Anthropic re-issues every organization's latest checkpoint under the new key, even for a log that has not grown. Each `axt-verify` release carries one key, so upgrade on the switch date. Running the old release after the switch fails with exit status `1`, and so does running the new release before it. Either failure clears once you run the matching release. Checkpoints you saved under the old key stay valid starting points, because the append-only proof from a saved checkpoint to a new one does not depend on which key signed the old one. The new key appears at the front of the [verifier key set](#read-the-verifier-keys) and earlier keys stay listed. Checkpoints you already verified or archived therefore keep verifying against that set. A verifier of your own that pins the published fingerprints needs the new fingerprint, with its switch date, added before that date. A verifier that holds the key set locally re-fetches it when it meets a signature whose key hash it does not hold. A checkpoint commits to the whole history. Once one checkpoint signed by the new key verifies, and a consistency proof from your saved checkpoint leads to it, every earlier entry is re-established as well.
### Can I compute proofs myself instead of calling the proof endpoints?
Yes. Hash tiles are the primary interface, and a tlog-tiles client that supports ECDSA note keys can compute inclusion and consistency proofs from them. The proof endpoints are a convenience. A generic client needs a thin wrapper to send the `x-api-key` header and, for a parent-organization key, the `organization_id` parameter.
### What if my organization stops using Access Transparency?
Nothing is deleted. Your log stays readable and verifiable through the same endpoints, so keep verifying it as before. If your organization enables Access Transparency again later, the same log continues, and consistency proofs span the gap.
### Can I verify all of my child organizations with one key?
Yes. A parent-organization key reads any enrolled child organization's log by passing `organization_id`. Each child organization has its own log, origin, and saved checkpoint. Run one verification per child organization, each with its own state.
## Related resources
- [Access Transparency](manage-claude-access-transparency.md)
- [Query the Activity Feed](manage-claude-compliance-activity-feed.md)
- [Compliance API overview](manage-claude-compliance-api.md)
- [Errors](manage-claude-compliance-errors.md)
- [axt-verify](https://github.com/anthropics/axt-verify), Anthropic's open-source verifier for the transparency log
- [C2SP tlog-tiles](https://c2sp.org/tlog-tiles) and [C2SP signed note](https://c2sp.org/signed-note), the wire formats for tiles, entry bundles, and checkpoints
- [RFC 9162](https://www.rfc-editor.org/rfc/rfc9162), the Merkle tree, inclusion proof, and consistency proof algorithms
- [RFC 8785](https://www.rfc-editor.org/rfc/rfc8785), the JSON Canonicalization Scheme used for leaves
---
## Access Transparency - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/access-transparency
Path: 04-API-Reference/Other/manage-claude-access-transparency.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Faccess-transparency)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Overview](manage-claude-access-transparency.md)[Transparency log (beta)](manage-claude-access-transparency-log.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Access Transparency
# Access Transparency
Copy page
Receive an audit record of human access to your organization's data by Anthropic personnel through the Compliance API.
Copy page
Learn how Access Transparency creates a record of human access to your organization's data by Anthropic personnel, what it covers, and how to receive events through the Compliance API.
When Access Transparency is enabled for your organization:
- Each human view of your retained data (see [covered content](#what-access-transparency-covers)) by an Anthropic employee writes an `anthropic_access` activity to your [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md).
- Access occurs only for safety review or incident response. See [Reason codes](#reason-codes).
Access Transparency is available to eligible customers on request and is not self-serve. For eligibility, refer to your contract terms or contact your Anthropic account representative.
## How Access Transparency works
Anthropic personnel access customer content only under defined conditions. Access Transparency is designed to make such access visible to you. The design rests on the following principles:
- **Human access happens only under a published reason code.**
- **Human views of your covered content are recorded.** Anthropic's internal tooling that can reach your covered content is instrumented to emit an event on each view.
- **Events represent human access, not automated processing.** Anthropic's automated safety systems process your content in a secured pipeline with no interactive human access; that processing does not generate `anthropic_access` events. The one event automated processing can initiate is a `cmek_preserve` preservation record (see [CMEK content preservation](#cmek-content-preservation)).
- **Events arrive on your existing feed.** Activities are accessible through your [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md). Existing credentials, audit, export, and SIEM integrations for the Compliance API still apply.
- **Events are tamper-evident.** Each event recorded after your organization's [transparency log](manage-claude-access-transparency-log.md) (beta) is created is also committed to that log. The log is an append-only, signed record that you can verify independently of Anthropic's serving systems.
## What Access Transparency covers
- **Covered content:** Access Transparency covers prompt and response content sent through the Claude Messages API or Claude Code sessions. Anthropic's [general ZDR documentation](../Guides/build-with-claude-api-and-data-retention.md) explains which APIs and features are covered by ZDR. The same APIs and features are covered by Access Transparency.
- **Manual views by Anthropic personnel:** Manual views of your covered content by Anthropic reviewers generate events.
## What Access Transparency does not cover
- **Automated processing:** Model serving, safety classifiers, and abuse-detection pipelines process your content as part of normal operation and do not generate `anthropic_access` events. Preservation initiated by automated processing does generate a `cmek_preserve` event (see [CMEK content preservation](#cmek-content-preservation)).
- **Your own organization's activity:** Your API calls, admin actions, and Compliance API reads are covered by standard [Activity Feed](manage-claude-compliance-activity-feed.md) event types.
- **Claude for Enterprise and Claude Apps:** claude.ai Enterprise seats, Claude for Work, Cowork, and Claude in Chrome are not covered.
- **Claude consumer products:** Claude Free, Pro, or Max plans.
- **Partner-operated platforms:** Amazon Bedrock and Google Cloud; refer to those platforms' transparency controls.
- **Anything ZDR does not cover:** Products that are not covered by ZDR (for example, the Files API, Anthropic-hosted stateful applications, and the Batch API) are not covered by Access Transparency. See [ZDR documentation](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) for additional details.
## Getting started
To enable Access Transparency:
1. 1
### Request Access Transparency
Contact your Anthropic account representative.
2. 2
### Anthropic reviews eligibility
Anthropic confirms your organization meets the eligibility criteria and enables the capability at the organization level.
3. 3
### Receive events through the Compliance API
`anthropic_access` activities appear in your existing Activity Feed under your existing Compliance Access Key; no new endpoint or credentials are required.
Access Transparency is enabled at the organization level and covers all workspaces. Per-workspace enrollment is not currently available.
## Receiving Access Transparency events
Access Transparency events are delivered as the `anthropic_access` activity type on the Compliance API Activity Feed. Filter with `activity_types[]`:
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--data-urlencode "activity_types[]=anthropic_access" \
--data-urlencode "limit=50" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Pagination, date-range filtering (`created_at.gte` / `.lt`), and the response envelope (`has_more`, `first_id`, `last_id`) are shared with the rest of the Activity Feed. See [Query the Activity Feed](manage-claude-compliance-activity-feed.md).
Each `anthropic_access` activity carries the standard Activity fields plus the following:
| Field | Type | Description |
|:------------------------------|:----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | string | Unique identifier for this activity |
| `accessed_at` | RFC 3339 string | When the access occurred. Might be earlier than when the activity becomes visible in your feed |
| `created_at` | RFC 3339 string | When Anthropic recorded the event. An event usually becomes visible in your feed shortly after; see [Timing](manage-claude-access-transparency-log.md#timing) for when it can lag |
| `actor` | object | Always `{ "type": "anthropic_actor", "email_address": null }`. Individual employee identity is not disclosed |
| `accessor_department` | string | The Anthropic team that performed the access (for example, `Safeguards`) |
| `reason_code` | enum | See [Reason codes](#reason-codes) |
| `resource_details.type` | enum | A resource type, currently only `message`. Extensible for future resource types |
| `resource_details.id` | string or null | Identifier of the content accessed |
| `resource_details.parent` | string or null | Identifier of the content's parent, for example the conversation ID containing a message. Currently `null` or omitted until resources with parents are supported |
| `organization_id` | string | The organization the content belongs to. Tagged ID format (`org_...`) |
| `organization_uuid` | string | The organization the content belongs to. UUID format |
| `workspace_id` | string or null | The workspace the content belongs to |
| `workspace_uuid` | string | The workspace the content belongs to. UUID format. Present when the access was scoped to a workspace, and absent otherwise. Not one of the transparency log's [leaf fields](manage-claude-access-transparency-log.md#how-an-event-becomes-a-leaf), so an inclusion proof does not cover it |
| `transparency_log_leaf_index` | integer | The event's zero-based position in your organization's transparency log (beta). Present whenever the event has a leaf, and absent otherwise. See [Verify Access Transparency events with the transparency log](manage-claude-access-transparency-log.md) |
Example JSON message:
```python
{
"id": "activity_013b013744txqZtFHLUaRqLr",
"type": "anthropic_access",
"created_at": "2026-06-08T17:12:09.812446Z",
"accessed_at": "2026-06-08T17:12:06.478035Z",
"organization_id": "org_0910d9133038914eta7i3vt",
"actor": { "type": "anthropic_actor", "email_address": null },
"resource_details": { "type": "message", "id": "msg_1234ABCD" },
"accessor_department": "Safeguards",
"reason_code": "safety_review",
"organization_uuid": "5b236db4-3fb4-4bf3-a560-b5e266038a15",
"transparency_log_leaf_index": 41
}
```
## CMEK content preservation
In rare cases, Anthropic preserves specific content beyond the standard retention window (for example, when a safety review confirms severely harmful content that must be retained for an ongoing investigation). Preservation is itself a logged, customer-visible action:
- **A preservation event is written to your feed.** When content is preserved, an event with type `cmek_preserve` is written to your Compliance API Activity Feed. Preservation events carry the same fields as an `anthropic_access` event; only the event type differs, so a parser that handles one handles both. See [Reason codes](#reason-codes).
- **A preservation event is written regardless of how the preservation was initiated.** Preservation ordinarily follows human review of the content, but the event is written whether the preservation was initiated by a human reviewer or by an automated safety pipeline: the record reflects that your content's retention state changed, independent of who changed it.
- **For CMEK organizations, preservation is a visible key movement.** Preserved content is re-encrypted outside your customer-managed key so that the investigation can continue independent of your key. The preservation event is your record that this occurred. All other retained content remains under your key.
Filter for preservation events the same way as access events:
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--data-urlencode "activity_types[]=cmek_preserve" \
--data-urlencode "limit=50" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Example JSON message:
```python
{
"id": "activity_01AbCdEfGhJkMnPqRsTuVwXy",
"type": "cmek_preserve",
"created_at": "2026-07-02T09:41:53.204118Z",
"accessed_at": "2026-07-02T09:41:50.118764Z",
"organization_id": "org_0123456789abcdefghijklmn",
"actor": { "type": "anthropic_actor", "email_address": null },
"resource_details": { "type": "message", "id": "msg_0ExampleExampleExample" },
"accessor_department": "Safeguards",
"reason_code": "policy_violation_investigation",
"organization_uuid": "00000000-1111-2222-3333-444444444444",
"transparency_log_leaf_index": 57
}
```
For preservation events, `accessed_at` records when the content was preserved.
## Reason codes
The set of reason codes is closed. Anthropic will update this page in the event it introduces a new code.
| Code | Meaning |
|:---------------------------------|:-------------------------------------------------------------------------------|
| `safety_review` | Content was viewed as part of a usage-policy or safety investigation |
| `incident_response` | Content was viewed while investigating an incident affecting your organization |
| `policy_violation_investigation` | Content was preserved during a Trust and Safety policy-violation investigation |
| `csae_report` | Content was preserved as evidence for a child safety (CSAE) report |
## Surface eligibility
The following table lists which surfaces are covered by Access Transparency. Coverage means human access to content from that surface generates `anthropic_access` events.
| Surface | Covered | Details |
|:------------------------------------------------|:--------|:-----------------------------------------------------------------------------------------------------------|
| Claude API (`api.anthropic.com`) | Yes | Prompts, completions, and data directly embedded in the API inputs |
| Claude Code (using an API key) | Yes | API traffic from Claude Code is covered as Claude API traffic |
| Claude Platform on AWS | Yes | Claude Platform on AWS generates Access Transparency events within the Compliance API (not AWS CloudTrail) |
| Claude API (`api.anthropic.com`) (Batch, Files) | No | The Claude API Batch and Files APIs are not covered, just like they are not covered by ZDR |
| Claude for Enterprise (claude.ai seats) | No | Not covered |
| Claude for Work | No | Not covered |
| Claude Free, Pro, Max | No | Consumer plans are not eligible |
| Playground (Claude Console) | No | Not covered |
| Microsoft Foundry | No | Not available |
| Amazon Bedrock, Google Cloud | No | Partner-operated platforms; refer to those platforms' transparency controls |
## Limitations and exclusions
### Coverage timing
Access Transparency applies from the time it is enabled for your organization. Content already in your retention window at enablement might also generate events when accessed, but Anthropic does not guarantee coverage for content written before enablement. Treat your enablement date as the start of reliable coverage. There might be a delay of up to two hours between enabling Access Transparency and your content being covered.
### Notification timing
`anthropic_access` and `cmek_preserve` events are delivered to your Compliance API feed within two business days of the access or preservation they record. This feed should not be treated as a real-time alerting channel, and the `accessed_at` timestamp reflects when the access occurred, which might be up to two business days before the activity becomes visible in your feed. The `created_at` field reflects the time Anthropic recorded the event, and the event usually becomes visible in your feed shortly after that time. These events do not follow the Activity Feed's usual 1-minute [indexing lag](manage-claude-compliance-integration-patterns.md#window-polling): an event can become visible up to two business days after its `created_at`. If you poll the feed by `created_at` window, overlap consecutive windows by at least two business days for `anthropic_access` and `cmek_preserve` events so that a late-indexed event is not dropped.
### Automated processing does not generate access events
`anthropic_access` events record human access only. Anthropic's automated safety systems and classifiers continue to process your content as part of normal operation, and that processing does not generate `anthropic_access` events. The one event automated processing can initiate is a `cmek_preserve` preservation record (see [CMEK content preservation](#cmek-content-preservation)). An empty feed means no human at Anthropic has viewed your content; it does not mean your content was not processed by automated systems.
### Access Transparency does not change what Anthropic can access
Access Transparency records access; it does not grant or restrict it. The purposes for which Anthropic personnel may access your content are governed by your agreement with Anthropic and the [Usage Policies](https://www.anthropic.com/legal/aup), and are the same regardless of whether Access Transparency is enabled.
### CMEK key-use logs are not a per-read record
For organizations that also enable CMEK, your cloud KMS audit log (CloudTrail, Cloud Audit Logs, or Azure Monitor) records Anthropic's use of your key. Because keys are cached for short periods during operation, an individual human read does not necessarily produce a distinct KMS decryption entry. Use the Access Transparency feed as the per-access record; your KMS log independently confirms key usage patterns.
## Frequently asked questions
### How do I know if my organization has Access Transparency enabled?
Contact your Anthropic account representative.
### Will I see an event each time a safety classifier runs on my traffic?
No. Automated processing does not generate `anthropic_access` events; you will see an `anthropic_access` event only if a human reviewer subsequently views the content. Separately, a `cmek_preserve` event is written when content is preserved, whether the preservation was initiated by a human reviewer or an automated safety pipeline.
### We are a platform that serves Claude to our own end users. Can we enable Access Transparency?
Access Transparency is not available for platform deployments. Contact your Anthropic account representative to discuss your use case.
### Will I see events for access that happened before we enrolled, or for our older data?
Access Transparency is not guaranteed to be retroactive. It covers human access to content written to the Claude API on or after your enrollment date. You might see events for access to content that was written before enrollment.
### How soon after an access will I see the event?
Within two business days of the access. Configure any SIEM alerting or scheduled exports with a matching lookback window rather than assuming real-time arrival.
### How do I know which request an anthropic_access event refers to?
Use the `resource_details.id` field. It contains the same message ID (`msg_...`) that the [Messages API](../Endpoints/messages-create.md) returns in the `id` field of every response body. To make this useful, log `id` in your own systems alongside your internal metadata, such as the application, end user, or conversation that produced the request. When an event arrives, join its `resource_details.id` against your logs to identify exactly which request was viewed.
### Can I enable Access Transparency for a single workspace?
Access Transparency is enabled at the organization level and covers all workspaces.
### How does Access Transparency relate to CMEK?
They are independent. With CMEK, safety preservation outside your key emits a separate `cmek_preserve` event on the same feed. See [CMEK content preservation](#cmek-content-preservation) and [CMEK](manage-claude-cmek.md).
### How can I tell that my Access Transparency record has not been altered?
Verify your organization's transparency log (beta): an append-only, signed record of your Access Transparency events, with inclusion and consistency proofs you check on your own infrastructure. See [Verify Access Transparency events with the transparency log](manage-claude-access-transparency-log.md).
### How do I request Access Transparency?
Contact your Anthropic account representative.
## Related resources
- [Verify Access Transparency events with the transparency log (beta)](manage-claude-access-transparency-log.md)
- [Compliance API overview](manage-claude-compliance-api.md)
- [Activity Feed](manage-claude-compliance-activity-feed.md)
- [API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
- [Customer-Managed Encryption Keys (CMEK)](manage-claude-cmek.md)
- [Claude Code data usage](../../13-Enterprise-Admin/data-usage.md)
- [Trust Center](https://trust.anthropic.com/resources)
---
## Create an Admin API key - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/admin-api-keys
Path: 04-API-Reference/Other/manage-claude-admin-api-keys.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fadmin-api-keys)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Authentication
# Create an Admin API key
Copy page
Create an Admin API key for your Claude Console or Claude Enterprise organization.
Copy page
An Admin API key authenticates every API in the **Admin** section of this guide: the [Admin API](manage-claude-admin-api.md), [Analytics APIs](manage-claude-analytics-api.md), [Compliance API](manage-claude-compliance-api.md), [Spend Limits API](manage-claude-spend-limits-api.md), [Usage and Cost API](manage-claude-usage-cost-api.md), and [Rate Limits API](manage-claude-rate-limits-api.md). You do not need a separate key for each API. The one exception is the Admin API's service-account, federation-issuer, and federation-rule endpoints, which accept only an OAuth bearer token with the `org:admin` scope. See [Obtain an OAuth bearer token](manage-claude-admin-api.md#oauth-bearer-token).
Where you create the key depends on which Claude product your organization uses.
## Which key do you need?
| Your organization | Create the key in | Key prefix | Who can create it | Works with |
|-------------------------------------------------------------|---------------------------------------------------------------------------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Claude Console** (Claude Platform, `platform.claude.com`) | [Claude Console \> Settings \> Admin keys](playground.md) | `sk-ant-admin01-...` | Organization members with the **admin** role | [Admin API](manage-claude-admin-api.md), [Usage and Cost API](manage-claude-usage-cost-api.md), [Rate Limits API](manage-claude-rate-limits-api.md), [Claude Code Analytics API](manage-claude-claude-code-analytics-api.md), and the Compliance API [Activity Feed](manage-claude-compliance-activity-feed.md) |
| **Claude Enterprise** (`claude.ai`) | [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) | `sk-ant-api01-...` | The parent organization's **primary owner** (all linked organizations). An **organization owner** can create one carrying Compliance API scopes only, restricted to their own organization | [User management](manage-claude-user-management.md) (the Admin API's member, invite, and group endpoints), [Compliance API](manage-claude-compliance-api.md), [Claude Enterprise Analytics API](manage-claude-analytics-api.md), and [Spend Limits API](manage-claude-spend-limits-api.md), according to the [scopes](#choose-scopes-for-a-claude-enterprise-key) you select |
A key created in one organization cannot be used to manage a different organization. If your company uses both Claude Console and Claude Enterprise, create one key in each.
## Create a key for a Claude Console organization
1. 1
### Sign in as an organization admin
Only organization members with the **admin** role can create Admin API keys. See [Organization roles and permissions](manage-claude-admin-api.md#organization-roles-and-permissions).
2. 2
### Open Admin keys settings
Go to [Claude Console \> Settings \> Admin keys](playground.md).
3. 3
### Create the key
Click **Create key**, give it a name, choose a [key expiration](manage-claude-authentication.md#key-expiration), and click **Create**. Claude Console keys do not have selectable scopes; every key carries full access to all endpoints that accept Admin API keys (the service-account and federation endpoints noted at the top of this page do not accept Admin API keys).
4. 4
### Copy and store the secret
Copy the displayed secret (starting with `sk-ant-admin01-`) and store it in your secrets manager. The full secret is shown only once.
## Create a key for a Claude Enterprise organization
1. 1
### Sign in as the primary owner or an organization owner
The **primary owner** of the Claude Enterprise parent organization can create a key that can access every linked organization, or one restricted to a single organization. An **organization owner** can create a key with Compliance API scopes only, restricted to their own organization.
2. 2
### Open API settings
Go to [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) and find the **Keys** section.
3. 3
### Click + Create key
Name the key and select the scopes you need from the [scopes table](#choose-scopes-for-a-claude-enterprise-key). The primary owner can combine scopes from different APIs (for example, `read:analytics` and `read:spend_limits`) on a single key.
4. 4
### Copy and store the secret
Copy the displayed secret (starting with `sk-ant-api01-`) and store it in your secrets manager. The full secret is shown only once.
## Choose scopes for a Claude Enterprise key
When you create a Claude Enterprise key, select every scope that the APIs you plan to call require. Scopes are fixed at creation; to add a scope later, create a new key.
| To call... | Select these scopes |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------|
| Admin API [user management](manage-claude-user-management.md): list and look up members and invites; read custom roles and their permissions | `read:members` |
| Admin API [user management](manage-claude-user-management.md): change member roles, remove members, create and withdraw invites | `write:members` |
| Admin API [user management](manage-claude-user-management.md): read groups and their members | `read:rbac_groups` |
| Admin API [user management](manage-claude-user-management.md): create, rename, and delete groups; add and remove group members; assign groups on invite creation | `write:rbac_groups` |
| [Spend Limits API](manage-claude-spend-limits-api.md): read members' effective spend limits and increase requests | `read:spend_limits` |
| [Spend Limits API](manage-claude-spend-limits-api.md): set or clear per-user spend limits; approve or deny increase requests | `write:spend_limits` |
| [Claude Enterprise Analytics API](manage-claude-analytics-api.md): engagement, adoption, cost, and usage reports | `read:analytics` |
| [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md): organization-wide activity events | `read:compliance_activities` |
| [Compliance API chat, file, and project endpoints](manage-claude-compliance-content-data.md) and [Compliance API session endpoints](manage-claude-compliance-sessions.md): read chats, files, projects, session transcripts, and [organization users](manage-claude-compliance-org-data.md#list-organization-users) | `read:compliance_user_data` |
| [Compliance API chat, file, and project endpoints](manage-claude-compliance-content-data.md): delete chats, files, and projects | `delete:compliance_user_data` |
| [Compliance API organization endpoints](manage-claude-compliance-org-data.md): read organization metadata and effective settings | `read:compliance_org_data` |
| Admin API [user management](manage-claude-user-management.md) read endpoints and every Compliance API read endpoint, with a single read-only scope (for security-audit integrations; does not include the Spend Limits or Analytics APIs) | `read:org_audit` |
The Compliance and Analytics APIs must be enabled for your organization before keys with those scopes can be used. See [Set up the Compliance API](manage-claude-compliance-api-access.md#set-up-the-compliance-api) and [Get access to the Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api).
## Use the key
Pass the key in the `x-api-key` header on every request. See each API's documentation for complete request examples.
A call that exceeds the key's scopes returns `403 Forbidden` with a message listing the scopes the key has and the scopes the endpoint needs.
## Next steps
[Admin API](manage-claude-admin-api.md)
Manage organization members, workspaces, and API keys.
[Spend Limits API](manage-claude-spend-limits-api.md)
Set per-member spend limits and review increase requests for your Claude Enterprise organization.
[Analytics APIs](manage-claude-analytics-api.md)
Report on Claude Code productivity or Claude Enterprise engagement and adoption.
[Compliance API](manage-claude-compliance-api.md)
Audit activity and retrieve or delete user content across your organization.
---
## Admin API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/admin-api
Path: 04-API-Reference/Other/manage-claude-admin-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fadmin-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
AdminOrganization
# Admin API
Copy page
Manage organization members, workspaces, invites, and API keys programmatically with the Admin API, using an Admin API key, an `org:admin` OAuth token, or a personal or service account key.
Copy page
**The Admin API is unavailable for individual accounts.** To collaborate with teammates and add members, set up your organization in **Console → Settings → Organization**.
The [Admin API](../Admin/http-beta-organization.md) lets you manage your organization's members, workspaces, invites, and API keys programmatically instead of by hand in the [Claude Console](usage-limits.md).
**The Admin API requires special access**
The Admin API accepts three credentials:
- An **Admin API key** (starting with `sk-ant-admin...`) sent in the `x-api-key` header. Only organization members with the admin role can provision one. See [Create an Admin API key](manage-claude-admin-api-keys.md).
- An **OAuth bearer token** with the `org:admin` scope sent in the `authorization: Bearer` header. Only members with the admin, owner, or primary owner role can obtain one. See [Obtain an OAuth bearer token](#oauth-bearer-token).
- A **personal key** or **service account key** that isn't scoped to a specific workspace, sent in the `x-api-key` header. The key has the same permissions as the linked account. See [Key types](manage-claude-authentication.md#key-types).
**Claude Enterprise:** Claude Enterprise (claude.ai) organizations call the Admin API with a scoped API key created in claude.ai. From this page, only the members and invites endpoints apply to them. They also get Enterprise-only endpoints: group and custom-role reads, and [spend limits](manage-claude-spend-limits-api.md). See [User management](manage-claude-user-management.md).
**Claude Platform on AWS:** Only the workspace endpoints (create, get, list, update, and archive on `/v1/organizations/workspaces`) and the external key endpoints (register, get, list, update, and delete on `/v1/organizations/external_keys`, for [CMEK](manage-claude-cmek-aws-kms.md#claude-platform-on-aws); there is no validate endpoint, because keys are validated when attached to a workspace) are available on Claude Platform on AWS. Organization members, workspace members, invites, API keys, and the usage, cost, and rate limit reports aren't. See [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md).
## Authentication
Authenticate with any of the three credentials. An Admin API key covers most endpoints. The service-account, federation-issuer, and federation-rule endpoints accept only an `org:admin` OAuth token. Send a personal key or service account key in the `x-api-key` header, as you would an Admin API key. The following examples call the [organization info endpoint](#accessing-organization-info) with an OAuth token and with an Admin API key.
The Python, TypeScript, C#, Go, Java, PHP, and Ruby SDKs expose the Admin API under `client.beta.organization`, and the `ant` CLI under `ant beta:organization`. The examples on this page use the default client, which reads an Admin API key from `ANTHROPIC_API_KEY` or an OAuth bearer token from `ANTHROPIC_AUTH_TOKEN`. SDK list methods in Python, TypeScript, C#, Go, and Java return an iterator that fetches more pages on demand, so `limit` sets the page size, not the total. The PHP, Ruby, and curl examples return one page. In the CLI, `--limit` caps the results on the member, invite, workspace, workspace-member, and API-key lists. For each endpoint's parameters and responses, see the [Admin API reference](../Admin/http-beta-organization.md).
### OAuth bearer token
Log in with the [`ant` CLI](cli-sdks-libraries-cli-quickstart.md) under a dedicated profile with the `org:admin` scope (see [Admin access](cli-sdks-libraries-cli-authentication.md#admin-access)), then export the bearer token. `--profile admin` stores the `org:admin` credential under its own profile and makes it the CLI's active profile. The exported variable applies to every SDK and CLI call in that shell. Use a shell you reserve for administration, unset the variable when you're done, and switch the CLI back with `ant profile activate default`:
CLI
```python
ant auth login --profile admin --scope "org:admin"
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 to refresh the token.
The SDKs and the `ant` CLI read `ANTHROPIC_AUTH_TOKEN` automatically. Leave `ANTHROPIC_API_KEY` unset in the same shell so they send the bearer token. Automated workloads skip the login: they authenticate through workload identity federation, and the SDKs and CLI perform the token exchange from the federation environment variables. See [Bootstrap a workload to manage WIF](manage-claude-wif-admin-api.md#bootstrap-a-workload-to-manage-wif).
Call the Admin API with the exported token:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
organization = client.beta.organization.retrieve()
print(f"id: {organization.id}")
print(f"name: {organization.name}")
```
An `org:admin` token grants access to the whole organization, regardless of the workspace the underlying profile or [federation rule](#federation-rules) is bound to.
For CI and other non-interactive workloads, mint the token with Workload Identity Federation instead of logging in interactively. See [Manage WIF with the Admin API](manage-claude-wif-admin-api.md#workload-ci-and-automation).
### Admin API key
To create an Admin API key for your organization type, see [Create an Admin API key](manage-claude-admin-api-keys.md).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
organization = client.beta.organization.retrieve()
print(f"id: {organization.id}")
print(f"name: {organization.name}")
```
## How the Admin API works
Authenticate with any credential from [Authentication](#authentication), then manage the following resources:
- Organization members and their roles
- Organization invites
- Workspaces and their members
- API keys
- Service accounts, federation issuers, and federation rules (`org:admin` OAuth token only)
Common uses include automating onboarding and offboarding, managing workspace access, and auditing API keys.
## Organization roles and permissions
There are five organization-level roles. For details, see [API Console roles and permissions](https://support.claude.com/en/articles/10186004-api-console-roles-and-permissions).
| Role | Permissions |
|------------------|--------------------------------------------------------------------------------|
| user | Can use playground |
| claude_code_user | Can use playground and [Claude Code](../../02-Claude-Code-CLI/code-home.md) |
| developer | Can use playground and manage API keys |
| billing | Can use playground and manage billing details |
| admin | Can do all of the preceding, plus manage users |
Organization owners and primary owners have all admin permissions and can also manage admins. All references to the admin role on this page also apply to owners and primary owners.
## Key concepts
### Organization members
List [organization members](../Admin/beta-organization-users-retrieve.md), update their roles, and remove them.
List the members of your organization:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
users = client.beta.organization.users.list(limit=10)
# Automatically fetches more pages as needed.
for user in users:
print(f"{user.id}: {user.email} ({user.role})")
```
Update a member's role:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
user = client.beta.organization.users.update(
"user_01XyDMpzjS89pFZXqSFUBDr6", role="developer"
)
print(f"id: {user.id}")
print(f"role: {user.role}")
```
Remove a member from the organization:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
removed_user = client.beta.organization.users.remove("user_01XyDMpzjS89pFZXqSFUBDr6")
print(f"id: {removed_user.id}")
```
### Organization invites
Invite users to your organization and manage pending [invites](../Admin/beta-organization-invites-retrieve.md).
Invite a user to your organization:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
invite = client.beta.organization.invites.create(
email="user@example.com", role="developer"
)
print(f"id: {invite.id}")
print(f"email: {invite.email}")
print(f"status: {invite.status}")
print(f"expires_at: {invite.expires_at}")
```
List pending invites:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
invites = client.beta.organization.invites.list(limit=10)
# Automatically fetches more pages as needed.
for invite in invites:
print(f"{invite.id}: {invite.email} ({invite.status})")
```
Delete an invite:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
deleted_invite = client.beta.organization.invites.delete(
"invite_015gWxHNr6h6TdRPZTmuCGnn"
)
print(f"id: {deleted_invite.id}")
```
### Workspaces
See [Workspaces](manage-claude-workspaces.md) for Console and API examples.
### Workspace members
Manage [user access to specific workspaces](../Admin/beta-organization-workspaces-members-retrieve.md):
Add a member to a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.add(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
user_id="user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_role="workspace_developer",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
```
List the members of a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
members = client.beta.organization.workspaces.members.list(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ", limit=10
)
# Automatically fetches more pages as needed.
for member in members:
print(f"{member.user_id}: {member.workspace_role}")
```
Update a workspace member's role:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.update(
"user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
workspace_role="workspace_admin",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
```
Remove a member from a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
removed_member = client.beta.organization.workspaces.members.remove(
"user_01XyDMpzjS89pFZXqSFUBDr6", workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
print(f"user_id: {removed_member.user_id}")
```
### API keys
Monitor and manage [API keys](../Admin/beta-organization-api-keys-list.md). Each key in the response includes its `expires_at` timestamp (`null` for keys without an [expiration](manage-claude-authentication.md#key-expiration)) and `principal`, the identity it acts as (see [Key types](manage-claude-authentication.md#key-types)). For a personal key, `principal` is `{"type": "user_actor", "user_id": "user_..."}`; for a service account key, `{"type": "service_account_actor", "service_account_id": "svac_..."}`; and for a workspace key, `null`. Each key also has a `scope` object: `{"type": "workspace", "workspace_id": "wrkspc_..."}` for a key bound to one workspace, or `{"type": "organization"}` for a key that can work across any workspace the account has access to. The top-level `workspace_id` field is deprecated and is `null` both for keys bound to the Default Workspace and for keys without a workspace scope; use `scope` to tell them apart. Filtering the list by `workspace_id` with the Default Workspace's ID returns only keys bound to the Default Workspace; keys without a workspace scope aren't returned under any `workspace_id` filter.
List the active API keys in a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
api_keys = client.beta.organization.api_keys.list(
limit=10, status="active", workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
# Automatically fetches more pages as needed.
for api_key in api_keys:
print(f"{api_key.id}: {api_key.name} ({api_key.status})")
```
Rename or deactivate an API key:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
api_key = client.beta.organization.api_keys.update(
"apikey_01Rj2N8SVvo6BePZj99NhmiT", status="inactive", name="New Key Name"
)
print(f"id: {api_key.id}")
print(f"name: {api_key.name}")
print(f"status: {api_key.status}")
```
### Service accounts
Create and manage service accounts (`svac_...`), the non-human identities that [service account keys](manage-claude-authentication.md#key-types) and [Workload Identity Federation](manage-claude-workload-identity-federation.md) tokens act as. These endpoints, like the federation-issuer and federation-rule endpoints, require an `org:admin` OAuth token. See [Manage WIF with the Admin API](manage-claude-wif-admin-api.md#service-accounts).
### Federation issuers
Register the OIDC identity providers (`fdis_...`) whose tokens may assert workload identity for your organization. See [Manage WIF with the Admin API](manage-claude-wif-admin-api.md#federation-issuers).
### Federation rules
Manage the rules (`fdrl_...`) that map issuer tokens to service accounts and scopes. See [Manage WIF with the Admin API](manage-claude-wif-admin-api.md#federation-rules).
## Accessing organization info
The `/v1/organizations/me` endpoint returns the organization that your credential belongs to:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
organization = client.beta.organization.retrieve()
print(f"id: {organization.id}")
print(f"name: {organization.name}")
```
```python
{
"id": "12345678-1234-5678-1234-567812345678",
"type": "organization",
"name": "Organization Name"
}
```
For parameter details and response schemas, see the [Organization Info API reference](../Admin/beta-organization-retrieve.md).
## Usage and cost reports
Track your organization's usage and costs with the [Usage and Cost API](manage-claude-usage-cost-api.md).
## Claude Code analytics
Monitor developer productivity and Claude Code adoption with the [Claude Code Analytics API](manage-claude-claude-code-analytics-api.md).
## Rate limits
Read the rate limits configured for your organization and its workspaces with the [Rate Limits API](manage-claude-rate-limits-api.md).
## Compliance API
Retrieve audit and activity data for your organization with the [Compliance API](manage-claude-compliance-api.md). Admin API keys can read only the Activity Feed. For full access, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
## Best practices
- Use meaningful names and descriptions for workspaces and API keys
- Handle errors from failed operations
- Regularly audit member roles and permissions
- Clean up unused workspaces and expired invites
- Monitor API key usage, audit each key's [`expires_at`](manage-claude-authentication.md#key-expiration), and rotate keys periodically
## FAQ
### What permissions are needed to use the Admin API?
The Admin API accepts an Admin API key (starting with `sk-ant-admin`), an OAuth bearer token with the `org:admin` scope, or a personal key or service account key that isn't scoped to a specific workspace. Only organization members with the admin role can provision Admin API keys, and only members with the admin, owner, or primary owner role can obtain `org:admin` tokens. A personal key or service account key has the same permissions as the linked account. See [Authentication](#authentication).
### Can I create new API keys through the Admin API?
No. You create API keys in the Claude Console. The Admin API can only read, rename, and change the status of existing keys.
### What happens to API keys when removing a user?
Behavior depends on the [key type](manage-claude-authentication.md#key-types).
Personal keys stop working when their user is removed from the organization. Service account keys stop working if their service account is archived, but continue to work even if the user that created them is removed. Workspace API keys continue to work. In the [Claude Code workspace](manage-claude-workspaces.md#claude-code-workspace), each key is bound to the member who created it and stops working when that member is removed.
### Can organization admins be removed through the API?
No. The API can't remove members with the admin role.
### How long do organization invites last?
Invites expire after 21 days. The expiration period isn't configurable.
For workspace-specific questions, see the [Workspaces FAQ](manage-claude-workspaces.md#faq).
---
## Analytics APIs - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/analytics-api
Path: 04-API-Reference/Other/manage-claude-analytics-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fanalytics-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Monitoring
# Analytics APIs
Copy page
Understand which analytics API and API key your organization needs, then provision access to Claude Code productivity metrics or Claude Enterprise engagement and adoption data.
Copy page
Anthropic provides two analytics APIs, and which one you use depends on which Claude product your organization manages:
- The **Claude Code Analytics API** reports daily Claude Code productivity metrics for organizations that use the Claude Platform. It is part of the [Admin API](manage-claude-admin-api.md) and uses an Admin API key.
- The **Claude Enterprise Analytics API** reports organization-wide engagement, adoption, and cost data across Claude products (chat, projects, Claude Code, and more) for Claude Enterprise organizations. It uses an Analytics API key created in claude.ai.
The two APIs use different key types, created in different places by different roles. This page describes which API fits your organization and how to create the right key.
## Which API do you need?
| API | Key type | Created in | Who can create it | What it covers |
|-------------------------------------|--------------------------------------|---------------------------------------------------------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
| **Claude Code Analytics API** | Admin API key (`sk-ant-admin01-...`) | [Claude Console \> Settings \> Admin keys](playground.md) | Organization admin | Daily Claude Code metrics per user: sessions, lines of code, commits, pull requests, tool acceptance, and estimated cost by model |
| **Claude Enterprise Analytics API** | Analytics API key | [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) | Primary owner | Organization-wide engagement and adoption (user activity, active-user summaries, project, skill, and connector usage), plus cost and usage reports |
The key types are not interchangeable: an Admin API key cannot call the Claude Enterprise Analytics API, and an Analytics API key cannot call the Admin API. Both APIs appear under the [Admin API reference](../Admin/http-beta-organization.md), but they are separate APIs with separate key types. If your organization uses both the Claude Platform and Claude Enterprise, you can provision both keys and use each API for its own data.
Looking for API usage and cost data rather than product analytics? See the [Usage and Cost API](manage-claude-usage-cost-api.md), which explains the right path for both Claude Console and Claude Enterprise organizations.
If you want to view engagement and adoption data in the product rather than programmatically, use the [Analytics dashboard](https://claude.ai/analytics/activity) in claude.ai. For governance and auditing use cases (individual user actions, raw activity events, conversation content), see the [Compliance API](manage-claude-compliance-api-access.md).
## Get access to the Claude Code Analytics API
The Claude Code Analytics API is available to every organization with access to the [Admin API](manage-claude-admin-api.md), and is free to use.
1. 1
### Create an Admin API key
Follow the steps in [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-console-organization).
2. 2
### Call the API
Pass the key in the `x-api-key` header:
``` shiki
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?starting_at=2025-09-08" \
--header "anthropic-version: 2023-06-01" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
For the available metrics, request parameters, and response schema, see the [Claude Code Analytics API guide](manage-claude-claude-code-analytics-api.md) and the [API reference](../Admin/beta-organization-usage-report-retrieve-claude-code.md).
## Get access to the Claude Enterprise Analytics API
The Claude Enterprise Analytics API is available to Claude Enterprise organizations. Engagement and adoption data is available on all Enterprise plans. The cost and usage endpoints apply to usage-based Enterprise plans; for seat-based Enterprise plans, they reflect usage credits only.
1. 1
### Sign in as the primary owner
Only the primary owner of the organization can enable API access and create Analytics API keys.
2. 2
### Enable API access and create a key
Go to [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) and enable public API access, then create an Analytics API key. Keys carry the `read:analytics` scope. Copy the displayed secret and store it in your secrets manager.
3. 3
### Call the API
Pass the key in the `x-api-key` header and include the [`anthropic-version`](../Endpoints/versioning.md) header on every request. Endpoints live under `https://api.anthropic.com/v1/organizations/analytics/`. For request examples, parameters, and response schemas, see the [Claude Enterprise Analytics API reference](../Admin/http-beta-organization-analytics.md).
The Claude Enterprise Analytics API provides:
- **User activity:** per-user daily metrics across chat (conversations, messages, projects, files, artifacts), Claude Code (sessions, commits, pull requests, lines of code, tool actions), and other Claude products
- **Activity summaries:** organization-level daily, weekly, and monthly active users, seat counts, and pending invites
- **Project, skill, and connector usage:** adoption breakdowns for chat projects, skills, and connectors
- **Cost and usage reports:** per-user and organization-level token usage and cost over time (usage-based Enterprise plans)
For endpoint details, parameters, and response schemas, see the [Claude Enterprise Analytics API reference](../Admin/http-beta-organization-analytics.md). The following sections cover data freshness, metric definitions, and operational guidance that apply across those endpoints.
## Data availability and freshness
Claude Enterprise Analytics API data is available for dates on or after January 1, 2026.
**Engagement and adoption endpoints** (user activity, summaries, projects, skills, connectors) return a per-day snapshot for the date you specify. Data for a given day is typically available by about 13:00–13:30 UTC the following day (a 1-day lag); until then, the most recent available day is usually two days before the current UTC date. Data arrives later on days when an upstream data pipeline runs late, and exact freshness varies by query, so rather than assuming a fixed time, check the error response: requesting a date that is not yet available returns a 400 error naming the most recent available day. If data is not available well past the typical lag, it usually indicates a data pipeline failure on Anthropic's side; contact support if the gap persists.
**Cost and usage endpoints** follow a different freshness model. Data is typically available within four hours of the underlying usage but may take up to 24 hours. Values for a given date can be revised for up to 30 days as late events arrive and reconciliation runs. For invoicing-grade totals, query dates at least 30 days in the past.
Cost and usage responses include a `data_refreshed_at` timestamp. When `ending_at` is omitted (the default is the current time), the response includes a tail of data after `data_refreshed_at` that is incomplete. For stable results across repeated calls, set `ending_at` to a value at or before a previously returned `data_refreshed_at`.
## How metrics are defined
**Active users.** A user counts as active for a day if any of the following is true: they sent at least one chat message in Claude, they had at least one Claude Code session (local or remote) associated with your Claude Enterprise organization that included tool use or git activity, or they had at least one Cowork session with tool use or message activity.
**Per-product metric blocks.** Per-product metric objects (for example, Office Agent or Cowork metrics on a user-activity record) are always present on every record. Organizations without usage of that product see all-zero values rather than `null`.
**Connector names.** Connector names are normalized across sources. For example, `Atlassian MCP server`, `mcp-atlassian`, and `atlassian_MCP` all appear as `atlassian` in the connector usage endpoint.
## Working with the API
**Pagination cursors are bound to the query that issued them.** On the cost and usage endpoints, do not change query parameters mid-sequence: if you change `products[]`, `group_by[]`, `order_by`, the date range, or any filter and pass an old cursor, the request returns a 400 error. To change parameters, restart from the first page without a cursor.
**List parameters use bracket notation.** Repeat the parameter for each value, for example `products[]=chat&products[]=claude_code`.
**Amount fields are decimal strings in cents.** Currency amounts are returned as decimal strings such as `"41280.000000"` (which represents \$412.80). To convert to dollars, parse as a decimal and divide by 100. Avoid binary floating-point parsing for values that may exceed several million dollars.
**Rate limits apply at the organization level**, not per key, with a default of 60 requests per minute across all endpoints in this API. If that is not sufficient for your use case, contact your Anthropic account team to discuss adjusting the limit.
## Versioning
Send the `anthropic-version` header on every request; see [API versions](../Endpoints/versioning.md) for the available versions.
## Known limitations
If your organization uses Claude Code through Amazon Bedrock, the Claude Enterprise Analytics API does not return Claude Code activity for that usage.
## Next steps
[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)
Track Claude Code sessions, code changes, and tool usage with an Admin API key.
[Usage and Cost API](manage-claude-usage-cost-api.md)
Track API token usage and costs for your organization.
[Claude Enterprise Analytics API reference](../Admin/http-beta-organization-analytics.md)
Endpoint reference for engagement, adoption, and cost data.
[Set up the Compliance API](manage-claude-compliance-api-access.md)
Audit and compliance data uses its own key types.
---
## App Attest for iOS and macOS apps - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/app-attest
Path: 04-API-Reference/Other/manage-claude-app-attest.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fapp-attest)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Authentication
# App Attest for iOS and macOS apps
Copy page
Let genuine installations of your iOS or macOS app call the Claude API without shipping an API key or running a proxy, using Apple's App Attest service.
Copy page
App Attest authenticates iOS and macOS apps that call the Claude API directly from the device, with usage billed to your workspace. This page explains how App Attest works, how to register your app in the Claude Console, and how to revoke an app integration.
Apps use App Attest through the [Claude for Foundation Models](https://github.com/anthropics/ClaudeForFoundationModels) Swift package, which is in beta: it requires the OS 27 betas, and APIs might change during the beta. For the Swift configuration, see [Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md#app-attest-production).
## How App Attest works
Each installation of your app uses Apple's [App Attest](https://developer.apple.com/documentation/devicecheck/establishing-your-app-s-integrity) service to prove that it is a genuine, unmodified build of the app you registered. Anthropic then issues the device a short-lived access token that bills usage to your workspace. The app ships no API key, and there is no proxy for you to operate.
App Attest authentication is available only when your app calls the Claude API directly. It is not available through Amazon Bedrock, Google Cloud, or Microsoft Foundry.
The first time your app uses Claude on a device, the app requests a challenge from Anthropic, attests the device with Apple's `DCAppAttestService`, and exchanges the verified attestation for an access token. The Claude for Foundation Models package runs this flow automatically and requests new tokens as they expire; there is no attestation code for you to write.
Tokens are scoped to your workspace, expire after one hour, and authorize only [Messages API](../Endpoints/messages-create.md) calls. They carry no end-user identity: App Attest identifies your app, not the person using it, so handle any per-user logic in your app.
## Set up App Attest
App Attest requires a physical device. The Simulator, and hardware without a Secure Enclave, cannot perform App Attest. While developing in the Simulator, authenticate with an [API key](manage-claude-authentication.md#api-keys) instead.
To set up App Attest, you need your Apple Developer Team ID and the admin, owner, or primary owner role in your organization. Configure your Xcode project and register your app in the [Claude Console](usage-limits.md):
1. In Xcode, add the **App Attest** capability to your app target under **Signing & Capabilities**.
2. In your workspace's settings in the Claude Console, open **App integrations**.
3. Click **Create app integration** and enter a name, your Apple Developer Team ID, and one or more bundle IDs (up to 32).
4. Copy the client ID (`clid_...`) from the integration's **Overview** tab and pass it to your app's Claude configuration.
## Revoke an app integration
To stop a compromised or retired app, revoke its integration: in your workspace's settings in the Claude Console, open **App integrations**, select the integration, and click **Revoke**, then confirm. Revoking an integration revokes its outstanding tokens, and its registered devices can no longer request new ones. Revocation is permanent, so create a new app integration to restore access.
## Next steps
[Apple Foundation Models](cli-sdks-libraries-libraries-apple-foundation-models.md#app-attest-production)
Configure App Attest in the Claude for Foundation Models Swift package
[Authentication](manage-claude-authentication.md)
Compare API keys, Workload Identity Federation, and App Attest
---
## Authentication - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/authentication
Path: 04-API-Reference/Other/manage-claude-authentication.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fauthentication)
SearchCtrlK
First steps
[Intro to Claude](../../01-Getting-Started/intro.md)[Get your API key](get-api-key.md)[Quickstart](../../01-Getting-Started/get-started.md)[Authentication](manage-claude-authentication.md)
Building with Claude
[Features overview](../Guides/build-with-claude-overview.md)[Using the Messages API](../Guides/build-with-claude-working-with-messages.md)[Stop reasons and fallback](../Guides/build-with-claude-handling-stop-reasons.md)[Refusals and fallback](../Guides/build-with-claude-refusals-and-fallback.md)[Fallback credit](../Guides/build-with-claude-fallback-credit.md)
Model capabilities
[Effort](../Guides/build-with-claude-effort.md)[Task budgets (beta)](../Guides/build-with-claude-task-budgets.md)[Fast mode (research preview)](../Guides/build-with-claude-fast-mode.md)[Structured outputs](../Guides/build-with-claude-structured-outputs.md)[Citations](../Guides/build-with-claude-citations.md)[Streaming Messages](../Guides/build-with-claude-streaming.md)[Batch processing](../Guides/build-with-claude-batch-processing.md)[Search results](../Guides/build-with-claude-search-results.md)[Streaming refusals](../Test-Evaluate/test-and-evaluate-strengthen-guardrails-handle-streaming-refusals.md)[Multilingual support](../Guides/build-with-claude-multilingual-support.md)[Embeddings](../Guides/build-with-claude-embeddings.md)
[Thinking](../Guides/build-with-claude-thinking.md)
Tools
[Overview](../Agents-Tools/agents-and-tools-tool-use-overview.md)[How tool use works](../Agents-Tools/agents-and-tools-tool-use-how-tool-use-works.md)[Tutorial: Build a tool-using agent](../Agents-Tools/agents-and-tools-tool-use-build-a-tool-using-agent.md)[Define tools](../Agents-Tools/agents-and-tools-tool-use-implement-tool-use.md)[Handle tool calls](../Agents-Tools/agents-and-tools-tool-use-handle-tool-calls.md)[Parallel tool use](../Agents-Tools/agents-and-tools-tool-use-parallel-tool-use.md)[Tool Runner (SDK)](../Agents-Tools/agents-and-tools-tool-use-tool-runner.md)[Strict tool use](../Agents-Tools/agents-and-tools-tool-use-strict-tool-use.md)[Server tools](../Agents-Tools/agents-and-tools-tool-use-server-tools.md)[Web search tool](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md)[Web fetch tool](../Agents-Tools/agents-and-tools-tool-use-web-fetch-tool.md)[Code execution tool](../Agents-Tools/agents-and-tools-tool-use-code-execution-tool.md)[Advisor tool](../Agents-Tools/agents-and-tools-tool-use-advisor-tool.md)[Tool search tool](../Agents-Tools/agents-and-tools-tool-use-tool-search-tool.md)[Memory tool](../Agents-Tools/agents-and-tools-tool-use-memory-tool.md)[Bash tool](../Agents-Tools/agents-and-tools-tool-use-bash-tool.md)[Text editor tool](../Agents-Tools/agents-and-tools-tool-use-text-editor-tool.md)[Computer use tool](../Agents-Tools/agents-and-tools-tool-use-computer-use-tool.md)[Browser use tool](../Agents-Tools/agents-and-tools-tool-use-browser-use-tool.md)[Troubleshooting](../Agents-Tools/agents-and-tools-tool-use-troubleshooting-tool-use.md)
Tool infrastructure
[Tool reference](../Agents-Tools/agents-and-tools-tool-use-tool-reference.md)[Manage tool context](../Agents-Tools/agents-and-tools-tool-use-manage-tool-context.md)[Tool combinations](../Agents-Tools/agents-and-tools-tool-use-tool-combinations.md)[Tool use with prompt caching](../Agents-Tools/agents-and-tools-tool-use-tool-use-with-prompt-caching.md)[Programmatic tool calling](../Agents-Tools/agents-and-tools-tool-use-programmatic-tool-calling.md)[Fine-grained tool streaming](../Agents-Tools/agents-and-tools-tool-use-fine-grained-tool-streaming.md)
Context management
[Context windows](../Guides/build-with-claude-context-windows.md)[Context editing](../Guides/build-with-claude-context-editing.md)[Prompt caching](../Guides/build-with-claude-prompt-caching.md)[Mid-conversation system messages and tool changes](../Guides/build-with-claude-mid-conversation-system-messages.md)[Build an orchestration mode](../Guides/build-with-claude-mid-conversation-effort-example.md)[Cache diagnostics](../Guides/build-with-claude-cache-diagnostics.md)[Token counting](../Guides/build-with-claude-token-counting.md)
[Compaction](../Guides/build-with-claude-compaction.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Quickstart](../Agents-Tools/agents-and-tools-agent-skills-quickstart.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)[Skills in the API](../Guides/build-with-claude-skills-guide.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)[MCP connector](../Agents-Tools/agents-and-tools-mcp-connector.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Amazon Bedrock (Opus 4.7 and later)](../Guides/build-with-claude-claude-in-amazon-bedrock.md)[Amazon Bedrock (Opus 4.6 and earlier)](../Guides/build-with-claude-claude-on-amazon-bedrock-legacy.md)[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)[Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md)[Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
[Console](usage-limits.md)
[Messages](../../01-Getting-Started/intro.md)First steps
# Authentication
Copy page
Authenticate to the Claude API with API keys, Workload Identity Federation, or App Attest.
Copy page
The Claude API supports three ways to authenticate requests:
| Method | Credential | Best for |
|---------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| [API key](#api-keys) | Static `sk-ant-api...` secret sent as a bearer token in the `Authorization` header | Local development, prototyping, scripts, and servers where you control secret storage |
| [Workload Identity Federation](#workload-identity-federation) | Short-lived bearer token exchanged from your identity provider's identity token | Production workloads on cloud platforms (AWS, Google Cloud, Azure), CI/CD pipelines, and Kubernetes, where you want to eliminate static secrets |
| [App Attest](#app-attest) | Short-lived access token issued to a genuine, attested installation of your registered iOS or macOS app | iOS and macOS apps distributed to end users, where the app calls the Claude API directly with no back end or proxy |
API keys and Workload Identity Federation grant the same access to Claude API endpoints. Choose API keys to get started quickly: a personal key for your own development, or a service account key for anything shared. Move to Workload Identity Federation when your workload already has a platform-issued identity you can federate. Use App Attest for iOS and macOS apps you distribute to end users.
## API keys
API keys are static secrets that you generate in the Claude Console and send on every request as a bearer token in the `Authorization` header.
### Key types
When you create a key, you choose its type, which determines what the key can do, where it works, and when it stops working:
| Key type | Acts as | Works in | Stops working when |
|----------------------------|-------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Personal key** | You, the user, with your roles and permissions | Either a single workspace or the workspaces where your role allows API use, chosen when the key is created | You lose access to the organization or, for a single-workspace key, to that workspace. Personal keys are archived when you are removed from the organization. If you are re-invited, create new keys; archived keys are not restored |
| **Service account key** | A [service account](manage-claude-workload-identity-federation.md#service-accounts) | Either a single workspace or anything the service account has access to, chosen when the key is created. A service account has access to the Default Workspace and to workspaces it has been added to | The service account is archived or, for a single-workspace key, is removed from that workspace |
| **Workspace key** (legacy) | No one: it belongs to the workspace it was created in | That workspace | It expires, is disabled or deleted, or its workspace is archived, regardless of whether its creator leaves the organization |
Personal keys and service account keys are identity-backed: each belongs to a user or service account your organization already manages, and every request acts as that identity. When that identity is removed from the organization, the key stops working. This means that keys won't accidentally outlive the people or workloads that own them. Prefer them over workspace keys for new integrations.
Use a personal key for your own development and scripts. A shared personal key acts as one person and breaks when they leave. For shared or automated workloads (CI, production services), have an organization admin create a service account so the workload has its own identity.
Workspace API keys still work but should be considered legacy; identity-backed keys or [Workload Identity Federation](manage-claude-workload-identity-federation.md) are preferred. To migrate, see [Replacing workspace API keys](#replacing-workspace-api-keys).
### Create and use a key
- **Create a key:** Go to [Settings → API keys](playground.md) in the Claude Console and click **Create key**. Name the key and choose an [expiration](#key-expiration). Set **Linked account** to yourself for a personal key, or to a service account for a key shared across multiple users. You can also scope the key to a specific workspace, which lets you skip setting a workspace ID manually in future requests.
- **Use the key:** Send it as `Authorization: Bearer <key>` on direct HTTP requests, or set the `ANTHROPIC_API_KEY` environment variable and the [client SDKs](cli-sdks-libraries-overview.md) pick it up automatically.
```python
POST /v1/messages
Authorization: Bearer YOUR_API_KEY
anthropic-version: 2023-06-01
content-type: application/json
```
The legacy `x-api-key: YOUR_API_KEY` header is still supported in place of `Authorization`.
Store API keys in a secrets manager, rotate them periodically, and disable or delete any key you suspect has leaked. On the [API keys page](playground.md), **Disable** is reversible (the Admin API reports the key's `status` as `"inactive"`, and **Re-enable** returns it to `"active"`), while **Delete** is permanent: the key is archived and still appears in [List API Keys](../Admin/beta-organization-api-keys-list.md) with `status: "archived"`. Expired keys can only be deleted. You can also set an [expiration](#key-expiration) when you create a key to limit how long a leaked credential stays usable.
cURL
Python
TypeScript
Go
Java
C#
PHP
Ruby
CLI
```python
client = Anthropic(api_key="my-anthropic-api-key")
# or, with ANTHROPIC_API_KEY set in the environment:
client = Anthropic()
```
### Select a workspace
API keys that are created for a specific workspace only work in that workspace, and API requests using these keys can omit the workspace ID.
If your API key isn't scoped to a workspace, you must specify the workspace ID in the `anthropic-workspace-id` header for each request. See the following example for how to set this header in a request or in SDKs.
The [Admin API](manage-claude-admin-api.md) accepts a personal key or service account key only if the key isn't scoped to a specific workspace.
You can find a workspace's ID in the **ID** column of [Settings → Workspaces](playground.md) in the Claude Console, or by calling the [List Workspaces](../Admin/beta-organization-workspaces-list.md) endpoint. List Workspaces includes the Default Workspace only when you pass `include_default=true`; its ID is also in the `anthropic-workspace-id` [response header](manage-claude-workspaces.md#identify-the-workspace-behind-an-api-response) of any request that runs there.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = Anthropic() # reads ANTHROPIC_API_KEY
# Required on every request for a multi-workspace key.
# Omit extra_headers for a single-workspace key.
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
extra_headers={"anthropic-workspace-id": "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"},
)
print(message.content)
# Or set it once for every request from this client:
workspace_client = Anthropic(
default_headers={"anthropic-workspace-id": "wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"},
)
```
If a request made with a key that isn't scoped to a workspace omits the header, the API returns a 400 `invalid_request_error`:
JSON
```python
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "anthropic-workspace-id is required when authenticating with an identity-linked API key; send the id of the workspace this request acts in."
},
"request_id": "req_011CSHoEeqs5C35K2UUqR7Fy"
}
```
A header value that isn't a valid workspace ID returns a 400 `invalid_request_error` with the message `anthropic-workspace-id header must be a valid workspace ID.` If the workspace doesn't exist, or the key's user or service account doesn't have access to it, the API returns a 404 `not_found_error` with the message `` Workspace `<id>` not found. ``, the same response as for any unknown workspace.
Workload Identity Federation selects a workspace at token exchange instead; see the [WIF reference](manage-claude-wif-reference.md) for details.
### Key expiration
When you create an API key from the [API keys page](playground.md) in the Claude Console, you choose an expiration: a preset (3 hours, 1 day, 7 days, or 30 days), a custom duration, or **Never** for keys you store in a secrets manager and rotate yourself. If your organization has a maximum expiration policy, the Console limits presets and custom durations to the policy maximum, and **Never** is unavailable. Existing keys keep their current behavior; expiration is set at creation time and cannot be changed afterward. The same expiration choice applies when you [create an Admin API key](manage-claude-admin-api-keys.md) in the Claude Console.
Anthropic emails the key's creator as the expiration approaches: 7 days before expiration for keys created with a lifetime of at least 14 days, and 1 day before for keys with a lifetime of at least 7 days. Keys with shorter lifetimes expire without a warning email.
After a key expires, requests made with it return a `401 authentication_error`. Create a new key to restore access; expired keys cannot be reactivated.
The Console API keys table shows each key's expiration, and the Admin API reports each key's `expires_at` timestamp on the [List API Keys](../Admin/beta-organization-api-keys-list.md) and [Retrieve API Key](../Admin/beta-organization-api-keys-retrieve.md) endpoints, so you can audit and rotate keys before they expire. The field is `null` for keys without an expiration.
Expiration limits the lifetime of a leaked credential, but it is not a substitute for secret hygiene. Regardless of expiration, store keys in a secrets manager and disable or delete any key you suspect has leaked.
### Replacing workspace API keys
If you have a workspace key, you may want to replace it with [Workload Identity Federation](manage-claude-wif-reference.md) or a personal or service account key. This provides better security and observability.
See [Workload Identity Federation](manage-claude-wif-reference.md) for details on configuring Workload Identity Federation, which is preferred over long-lived keys.
To replace a workspace key with a personal or service account key:
1. **Decide the key type.** Your own tooling should use a personal key. A shared or unattended workload should use a service account key.
2. **Create a service account** if necessary. You may have to ask an organization admin to create one in [Settings → Service accounts](https://platform.claude.com/settings/service-accounts) and add it to the relevant workspace.
3. **Create the new key.** Create it specifically for the integration's workspace unless multiple workspaces are needed.
4. **Deploy the new key.** Replace the old key wherever the integration reads it, typically the `ANTHROPIC_API_KEY` environment variable or a secrets manager entry. For a multi-workspace key, also send the `anthropic-workspace-id` header as shown in [Select a workspace](#select-a-workspace).
5. **Delete the old key.** Confirm that requests succeed, then delete the workspace key on the [API keys page](playground.md).
## Workload Identity Federation
Workload Identity Federation (WIF) lets a workload authenticate with a short-lived identity token issued by an identity provider (IdP) you already trust, such as AWS IAM, Google Cloud, or any standards-compliant OIDC issuer (such as GitHub Actions, Kubernetes service accounts, SPIFFE, Microsoft Entra ID, or Okta). The workload exchanges its IdP-issued JWT at `POST /v1/oauth/token` for a short-lived Claude API access token, and the SDK refreshes that token automatically before it expires. There is no `sk-ant-api...` string to mint, distribute, or rotate.
Federation removes long-lived Claude API keys from your environment, which shrinks the blast radius of a leaked credential and lets you manage access with the same IdP controls you already use for cloud resources. It does not, on its own, guarantee end-to-end security: the trust chain is only as strong as your identity provider's configuration, and a long-lived secret one hop upstream (for example, a static cloud credential that can mint IdP tokens) can still undermine it. Pair federation with your provider's controls, such as IP allowlists, MFA, and audit logging.
To configure federation, you create three resources in the Claude Console (a service account, a federation issuer, and a federation rule) and then point your SDK at the rule. See [Workload Identity Federation](manage-claude-workload-identity-federation.md) for the full setup walkthrough.
## App Attest
App Attest authenticates iOS and macOS apps that call the Claude API directly from the device. Each installation proves that it is a genuine, unmodified build of an app you registered in the Claude Console, using Apple's App Attest service. Anthropic then issues the device a short-lived access token that bills usage to your workspace. Tokens are scoped to your workspace, expire after one hour, and authorize only [Messages API](../Endpoints/messages-create.md) calls.
To register your app and get a client ID, see [App Attest for iOS and macOS apps](manage-claude-app-attest.md).
## Next steps
[Set up Workload Identity Federation](manage-claude-workload-identity-federation.md)
Configure issuers, rules, and service accounts, then exchange tokens
[Identity provider guides](manage-claude-workload-identity-federation.md#identity-providers)
Step-by-step guides for AWS, Google Cloud, Azure, GitHub Actions, Kubernetes, SPIFFE, and Okta
[WIF reference](manage-claude-wif-reference.md)
Environment variables, validation rules, profile configuration, and error reference
[App Attest for iOS and macOS apps](manage-claude-app-attest.md)
Let genuine installations of your app call the Claude API without shipping an API key
[Client SDKs](cli-sdks-libraries-overview.md)
Python, TypeScript, C#, Go, Java, PHP, Ruby, and the CLI
---
## Claude Code Analytics API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/claude-code-analytics-api
Path: 04-API-Reference/Other/manage-claude-claude-code-analytics-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fclaude-code-analytics-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Monitoring
# Claude Code Analytics API
Copy page
Programmatically access your organization's Claude Code usage analytics and productivity metrics with the Claude Code Analytics Admin API.
Copy page
**The Admin API is unavailable for individual accounts.** To collaborate with teammates and add members, set up your organization in **Console → Settings → Organization**.
The Claude Code Analytics Admin API provides programmatic access to daily aggregated usage metrics for Claude Code users, enabling organizations to analyze developer productivity and build custom dashboards. This API provides more detail than the basic [Analytics dashboard](playground.md) without the complexity of the OpenTelemetry integration.
This API enables you to better monitor, analyze, and optimize your Claude Code adoption:
- **Developer productivity analysis:** Track sessions, lines of code added/removed, commits, and pull requests created using Claude Code
- **Tool usage metrics:** Monitor acceptance and rejection rates for different Claude Code tools (Edit, MultiEdit, Write, NotebookEdit)
- **Cost analysis:** View estimated costs and token usage broken down by Claude model
- **Custom reporting:** Export data to build executive dashboards and reports for management teams
- **Usage justification:** Provide metrics to justify and expand Claude Code adoption internally
**Admin API credentials required.** These endpoints are part of the Admin API. You can access them using an [Admin API key](manage-claude-admin-api-keys.md), an OAuth token with the `org:admin` scope, or a personal or service account key that isn't scoped to a workspace; workspace API keys don't work. See [Authentication](manage-claude-admin-api.md#authentication) for details.
**Claude Platform on AWS:** The Claude Code Analytics API is not currently available. View Claude Code usage on the **Usage** page in the Claude Console instead.
**Claude Enterprise organizations:** Claude Code activity for claude.ai users is reported by the Claude Enterprise Analytics API, which uses an Analytics API key instead of an Admin API key. See [Analytics APIs](manage-claude-analytics-api.md) to find which API and key type your organization needs.
## Quick start
Get your organization's Claude Code analytics for a specific day:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?\
starting_at=2025-09-08&\
limit=20" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
**Set a User-Agent header for integrations**
If you're building an integration, set your User-Agent header to help Anthropic understand usage patterns:
``` block
User-Agent: YourApp/1.0.0 (https://yourapp.com)
```
## Claude Code Analytics API
Track Claude Code usage, productivity metrics, and developer activity across your organization with the `/v1/organizations/usage_report/claude_code` endpoint.
### Key concepts
- **Daily aggregation:** Returns metrics for a single day specified by the `starting_at` parameter
- **User-level data:** Each record represents one user's activity for the specified day
- **Productivity metrics:** Track sessions, lines of code, commits, pull requests, and tool usage
- **Token and cost data:** Monitor usage and estimated costs broken down by Claude model
- **Cursor-based pagination:** Handle large datasets with stable pagination using opaque cursors
- **Data freshness:** Metrics are available with up to 1-hour delay for consistency
For complete parameter details and response schemas, see the [Claude Code Analytics API reference](../Admin/beta-organization-usage-report-retrieve-claude-code.md).
### Basic examples
#### Get analytics for a specific day
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?\
starting_at=2025-09-08" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
#### Get analytics with pagination
cURL
```python
# First request
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?\
starting_at=2025-09-08&\
limit=20" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
# Subsequent request using cursor from response
curl "https://api.anthropic.com/v1/organizations/usage_report/claude_code?\
starting_at=2025-09-08&\
page=page_MjAyNS0wNS0xNFQwMDowMDowMFo=" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
### Request parameters
| Parameter | Type | Required | Description |
|---------------|---------|----------|-------------------------------------------------------------------------|
| `starting_at` | string | Yes | UTC date in YYYY-MM-DD format; returns metrics for this single day only |
| `limit` | integer | No | Number of records per page (default: 20, max: 1000) |
| `page` | string | No | Opaque cursor token from previous response's `next_page` field |
### Available metrics
Each response record contains the following metrics for a single user on a single day:
#### Dimensions
- **date:** Date in RFC 3339 format (UTC timestamp)
- **actor:** The user or API key that performed the Claude Code actions (either `user_actor` with `email_address` or `api_actor` with `api_key_name`)
- **organization_id:** Organization UUID
- **customer_type:** Type of customer account (`api` for API customers, `subscription` for Pro/Team customers)
- **terminal_type:** Type of terminal or environment where Claude Code was used (for example, `vscode`, `iTerm.app`, `tmux`)
#### Core metrics
- **num_sessions:** Number of distinct Claude Code sessions initiated by this actor
- **lines_of_code.added:** Total number of lines of code added across all files by Claude Code
- **lines_of_code.removed:** Total number of lines of code removed across all files by Claude Code
- **commits_by_claude_code:** Number of git commits created through Claude Code's commit functionality
- **pull_requests_by_claude_code:** Number of pull requests created through Claude Code's PR functionality
#### Tool action metrics
Breakdown of tool action acceptance and rejection rates by tool type:
- **edit_tool.accepted/rejected:** Number of Edit tool proposals that the user accepted/rejected
- **multi_edit_tool.accepted/rejected:** Number of MultiEdit tool proposals that the user accepted/rejected
- **write_tool.accepted/rejected:** Number of Write tool proposals that the user accepted/rejected
- **notebook_edit_tool.accepted/rejected:** Number of NotebookEdit tool proposals that the user accepted/rejected
#### Model breakdown
For each Claude model used:
- **model:** Claude model identifier (for example, `claude-opus-5`)
- **tokens.input/output:** Input and output token counts for this model
- **tokens.cache_read/cache_creation:** Cache-related token usage for this model
- **estimated_cost.amount:** Estimated cost in cents USD for this model
- **estimated_cost.currency:** Currency code for the cost amount (currently always `USD`)
### Response structure
The API returns data in the following format:
```python
{
"data": [
{
"date": "2025-09-08T00:00:00Z",
"actor": {
"type": "user_actor",
"email_address": "developer@company.com"
},
"organization_id": "dc9f6c26-b22c-4831-8d01-0446bada88f1",
"customer_type": "api",
"terminal_type": "vscode",
"core_metrics": {
"num_sessions": 5,
"lines_of_code": {
"added": 1543,
"removed": 892
},
"commits_by_claude_code": 12,
"pull_requests_by_claude_code": 2
},
"tool_actions": {
"edit_tool": {
"accepted": 45,
"rejected": 5
},
"multi_edit_tool": {
"accepted": 12,
"rejected": 2
},
"write_tool": {
"accepted": 8,
"rejected": 1
},
"notebook_edit_tool": {
"accepted": 3,
"rejected": 0
}
},
"model_breakdown": [
{
"model": "claude-opus-5-5",
"tokens": {
"input": 100000,
"output": 35000,
"cache_read": 10000,
"cache_creation": 5000
},
"estimated_cost": {
"currency": "USD",
"amount": 113
}
}
]
}
],
"has_more": false,
"next_page": null
}
```
## Pagination
The API supports cursor-based pagination for organizations with large numbers of users:
1. Make your initial request with optional `limit` parameter.
2. If `has_more` is `true` in the response, use the `next_page` value in your next request.
3. Continue until `has_more` is `false`.
The cursor encodes the position of the last record and ensures stable pagination even as new data arrives. Each pagination session maintains a consistent data boundary to ensure you don't miss or duplicate records.
## Common use cases
- **Executive dashboards:** Create high-level reports showing Claude Code impact on development velocity
- **AI tool comparison:** Export metrics to compare Claude Code with other AI coding tools such as Copilot and Cursor
- **Developer productivity analysis:** Track individual and team productivity metrics over time
- **Cost tracking and allocation:** Monitor spending patterns and allocate costs by team or project
- **Adoption monitoring:** Identify which teams and users are getting the most value from Claude Code
- **ROI justification:** Provide concrete metrics to justify and expand Claude Code adoption internally
## Frequently asked questions
### How fresh is the analytics data?
Claude Code analytics data typically appears within 1 hour of user activity completion. To ensure consistent pagination results, only data older than 1 hour is included in responses.
### Can I get real-time metrics?
No, this API provides daily aggregated metrics only. For real-time monitoring, consider using the [OpenTelemetry integration](../../13-Enterprise-Admin/monitoring-usage.md).
### How are users identified in the data?
Users are identified through the `actor` field in two ways:
- **`user_actor`:** Contains `email_address` for users who authenticate through OAuth (most common)
- **`api_actor`:** Contains `api_key_name` for users who authenticate with an API key
The `customer_type` field indicates whether the usage is from `api` customers (pay-as-you-go API) or `subscription` customers (Pro/Team plans).
### What's the data retention period?
Historical Claude Code analytics data is retained and accessible through the API. There is no specified deletion period for this data.
### Which Claude Code deployments are supported?
This API only tracks Claude Code usage on the Claude API. Usage through [Claude in Amazon Bedrock](../Guides/build-with-claude-claude-in-amazon-bedrock.md), [Claude in Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md), [Claude on Google Cloud](../Guides/build-with-claude-claude-on-vertex-ai.md), or [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md) is not included.
### What does it cost to use this API?
The Claude Code Analytics API is free to use for all organizations with access to the Admin API.
### How do I calculate tool acceptance rates?
Tool acceptance rate = `accepted / (accepted + rejected)` for each tool type. For example, if the edit tool shows 45 accepted and 5 rejected, the acceptance rate is 90%.
### What time zone is used for the date parameter?
All dates are in UTC. The `starting_at` parameter should be in YYYY-MM-DD format and represents UTC midnight for that day.
## See also
The Claude Code Analytics API helps you understand and optimize your team's development workflow. Learn more about related features:
- [Admin API](manage-claude-admin-api.md)
- [Admin API reference](../Admin/http-beta-organization.md)
- [Claude Code Analytics dashboard](playground.md)
- [Usage and Cost API](manage-claude-usage-cost-api.md) - Track API usage across all Anthropic services
- [Compliance API](manage-claude-compliance-api.md) - Retrieve audit and activity data
- [Identity and access management](../../13-Enterprise-Admin/iam.md)
- [Monitoring usage with OpenTelemetry](../../13-Enterprise-Admin/monitoring-usage.md) for custom metrics and alerting
---
## Configure AWS KMS for CMEK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/cmek-aws-kms
Path: 04-API-Reference/Other/manage-claude-cmek-aws-kms.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcmek-aws-kms)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Overview](manage-claude-cmek.md)[AWS KMS](manage-claude-cmek-aws-kms.md)[Google Cloud KMS](manage-claude-cmek-google-cloud-kms.md)[Azure Key Vault](manage-claude-cmek-azure-key-vault.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Encryption keys (CMEK)
# Configure AWS KMS for CMEK
Copy page
Use AWS KMS to provide an encryption key for your organization.
Copy page
Configure with the /claude-api skill in Claude Code
```python
claude "/claude-api help me configure a customer-managed encryption key with AWS KMS"
```
This guide walks through configuring an [AWS KMS](https://aws.amazon.com/kms/) key as a [customer-managed encryption key (CMEK)](manage-claude-cmek.md) for your Anthropic organization.
Enabling CMEK is permanent. If your KMS key is deleted or disabled, Anthropic cannot recover the data encrypted under it. Review the [warnings and limitations](manage-claude-cmek.md) before you begin.
**Claude Platform on AWS:** On [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md), your key policy grants access to an AWS service principal instead of Anthropic's IAM role, there is no separate validation step, and you register and attach the key in the Claude Console. Follow [Set up CMEK on Claude Platform on AWS](#claude-platform-on-aws) on this page instead of the steps in the next sections.
## Prerequisites
- An AWS account with permissions to create KMS keys and set key policies (`kms:CreateKey` and `kms:PutKeyPolicy`).
- An Anthropic Admin API key for your organization.
- The [AWS CLI](https://aws.amazon.com/cli/) installed and authenticated.
## Amazon Resource Name (ARN) for Anthropic
To have Anthropic use your encryption key, you must give Anthropic's IAM role a KMS key it can use for encrypting data. The ARN for Anthropic CMEK is:
``` block
arn:aws:iam::915198916910:role/anthropic-cmek-client-us
```
Use only this published ARN. Never trust an identifier provided over email, chat, or any onboarding channel.
## Encryption key setup
1. 1
### Create the KMS key with a cross-account key policy
**Claude Platform on AWS:** Skip this step. Your key policy grants access to an AWS service principal, and it has no organization condition. [Set up CMEK on Claude Platform on AWS](#claude-platform-on-aws) gives that policy.
The key policy grants Anthropic's IAM role cross-account access. Three statements are required:
1. **Account root admin:** the standard KMS pattern. Your account retains full admin control.
2. **Anthropic encrypt and decrypt:** the `kms:Encrypt` and `kms:Decrypt` actions, which Anthropic uses to encrypt and decrypt the data keys that protect your workspace data (envelope encryption).
3. **Anthropic describe:** the metadata read Anthropic performs at startup. It is granted separately because `DescribeKey` has no `EncryptionContext` parameter, so an `EncryptionContext` condition on this action would always deny.
To find [your AWS account ID](https://docs.aws.amazon.com/IAM/latest/UserGuide/console-account-id.html), run `aws sts get-caller-identity --query Account --output text`.
In the policy, replace `<AWS_ACCOUNT_ID>` with your AWS account ID and `<ORGANIZATION_UUID>` with your organization ID. The `StringEquals` condition on `kms:EncryptionContext:anthropic:org_uuid` binds the key to your Anthropic organization, and validation refuses a key without it. To share one key among several Anthropic organizations, list each organization ID in the condition value.
**Finding your organization ID:** Copy the **Organization ID** field under **Settings \> Organization** in the Claude Console, or under **Organization settings \> Organization** in claude.ai, or read the `id` field from the [Organization Info](../Admin/beta-organization-retrieve.md) endpoint. Use the bare UUID, not the `org_`-prefixed ID.
Save the policy as `key-policy.json`. To create the key in the AWS Console instead, paste the policy there, as described later in this step.
key-policy.json
``` shiki
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AccountRootAdmin",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<AWS_ACCOUNT_ID>:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "AllowAnthropicCMEKCrypto",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": ["kms:Encrypt", "kms:Decrypt"],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:anthropic:org_uuid": ["<ORGANIZATION_UUID>"]
}
}
},
{
"Sid": "AllowAnthropicCMEKDescribe",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": "kms:DescribeKey",
"Resource": "*"
}
]
}
```
**Optional:** To limit the key to some of your workspaces, use this `AllowAnthropicCMEKCrypto` statement instead of the one in the preceding policy JSON, with one compartment ID for each workspace. Add a workspace's compartment ID before you attach the key to it. For a new workspace, create it without the key, add its compartment ID, and then attach the key.
``` shiki
{
"Sid": "AllowAnthropicCMEKCrypto",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": ["kms:Encrypt", "kms:Decrypt"],
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:anthropic:org_uuid": ["<ORGANIZATION_UUID>"]
},
"StringEqualsIfExists": {
"kms:EncryptionContext:anthropic:compartment_uuid": ["<COMPARTMENT_UUID>"]
}
}
}
```
``` shiki
aws kms create-key \
--region <REGION> \
--description "Anthropic CMEK" \
--key-usage ENCRYPT_DECRYPT \
--policy file://key-policy.json
```
Capture `KeyMetadata.Arn` from the output. You need it when you register the key in the next step.
If the key is already configured for CMEK and protects existing data, you must add a statement that lets Anthropic decrypt that data, in addition to the three statements in the preceding policy. In its condition, list the compartment ID of every workspace the key is or was attached to.
``` shiki
{
"Sid": "AllowAnthropicCMEKDecryptExistingData",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915198916910:role/anthropic-cmek-client-us"
},
"Action": "kms:Decrypt",
"Resource": "*",
"Condition": {
"StringEquals": {
"kms:EncryptionContext:anthropic:compartment_uuid": ["<COMPARTMENT_UUID>"]
}
}
}
```
Anthropic validates the key when you verify it or attach it to a workspace. Each validation adds four access-denied errors to CloudTrail. These are expected. If you need to filter them out, filter on all three of the following values. The first one alone isn't enough, because any caller can set it:
- `requestParameters.encryptionContext.associatedData`: `Y21lay12YWxpZGF0aW9u`
- `userIdentity.accountId`: `915198916910`
- `resources.ARN`: `arn:aws:kms:<REGION>:<AWS_ACCOUNT_ID>:key/<KEY_ID>`
**Finding your compartment ID:** See the **Claude Platform** tab under **Register the key with Anthropic**.
You can also create the key from the AWS Console. Choose a symmetric key with the encrypt and decrypt key usage, a single-region key, and KMS key material origin. The Create-key wizard commits a key policy at its **Review** step: If you add Anthropic's account ID `915198916910` under key usage permissions there, the generated policy grants the whole Anthropic account broader actions (such as `kms:ReEncrypt*` and `kms:GenerateDataKey*`) with no `EncryptionContext` condition, and validation refuses it. To avoid leaving an over-permissive key, finish the wizard with administrative permissions only, then open the key's **Key policy** tab and replace the JSON with the `key-policy.json` policy shown earlier in this step.
## Register the key with Anthropic
How you register the key depends on which product you use.
Claude Platform
Claude Enterprise
**Claude Platform on AWS:** The principal, key policy, and registration flow differ, and there is no separate validation step. Follow [Set up CMEK on Claude Platform on AWS](#claude-platform-on-aws) instead of this tab.
**Finding your compartment ID:** Each workspace has a compartment ID that scopes its CMEK data. To find it in the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. The ID is under **Encryption key**, in the **Compartment ID** field. You can also read the `compartment_id` field returned by the [Get Workspace](../Admin/beta-organization-workspaces-retrieve.md) endpoint.
You can set up the key in the Claude Console or through the Admin API, with the same result.
Claude Console
API
1. 1
### Register the key with Anthropic
In the Claude Console, open **Settings \> Encryption keys** and click **Add key**. Enter a display name, choose **AWS KMS**, and click **Continue**. Paste the key ARN into **KMS key ARN**, and click **Add**.
The key details step shows your organization ID. Add it to the [key policy](#key-policy) before you click **Add**.
2. 2
### Validate the key
On the **Encryption keys** page, click **Verify** next to the key. **Connected** appears when the check passes. If it fails, a message gives the reason.
3. 3
### Attach the key to a workspace
In the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. Under **Encryption key**, select the key, click **Save**, and confirm. Attaching a key can't be undone. For a workspace that already receives requests, the key can take [up to a day to take effect](manage-claude-cmek.md#how-it-works).
## Set up CMEK on Claude Platform on AWS
On [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md), CMEK uses AWS KMS keys only, and setup differs from the preceding sections in these ways:
- **Principal:** Your key policy grants access to the AWS service principal `aws-external-anthropic.amazonaws.com`. Anthropic's IAM role and account ID are not used, so the [ARN for Anthropic](#amazon-resource-name-arn-for-anthropic) does not apply.
- **Key requirements:** The key must be a symmetric KMS key with encrypt and decrypt usage, single-region, and in the same AWS account and region as the workspace you attach it to. Cross-account keys are not supported: the key must be in the AWS account that hosts your organization. Multi-region keys (key IDs that begin with `mrk-`) and alias ARNs are rejected when you register the key; use the key ARN.
- **No separate validation step:** Apart from those checks on the key ARN at registration, the key is validated when you attach it to a workspace. The attach call performs an encrypt/decrypt round against the key with that workspace's compartment ID as the encryption context, so a key policy problem surfaces at attach time rather than at registration. An `EncryptionContext` condition therefore needs no all-zeros entry.
- **Where you manage keys:** Register and attach keys in the Claude Console, signed in through AWS with the Admin role. The external key endpoints are also available on Claude Platform on AWS, authorized through [IAM actions](../Endpoints/claude-platform-on-aws-iam-actions.md#encryption-keys); there, a key is identified by its KMS key ARN rather than an `ekey_` ID.
Use only this published service principal name. Never trust an identifier provided over email, chat, or any onboarding channel.
### Prerequisites
- The AWS account that hosts your Claude Platform on AWS organization, with permissions to create KMS keys and set key policies (`kms:CreateKey` and `kms:PutKeyPolicy`).
- The **Admin** role in the Claude Console for Claude Platform on AWS. See [Using the Claude Console](../Guides/build-with-claude-claude-platform-on-aws.md#using-the-claude-console).
- For the IAM principal you sign in to the Claude Console with: besides `aws-external-anthropic:AssumeConsole`, the [IAM actions](../Endpoints/claude-platform-on-aws-iam-actions.md#encryption-keys) for the operations you perform there, because the Encryption keys page and key attachment go through the AWS gateway. Registering a key is `RegisterKey` (with `ListKeys` and `GetKey` to view registrations), and attaching one is `UpdateWorkspace` or `CreateWorkspace`. The external key actions (and `CreateWorkspace`) are account-scoped, so grant them on `Resource: "*"`; a policy limited to workspace ARNs does not include them.
- For the IAM principal that attaches the key to a workspace (the identity you signed in to the Claude Console with): `kms:DescribeKey`, `kms:Encrypt`, and `kms:Decrypt` on the key. Your principal's access to the key is checked when you attach it, in addition to the service principal's.
- Optional, for the key picker in the Claude Console: `kms:ListKeys` and `kms:DescribeKey` for the principal you sign in with. Without them, paste the key ARN instead.
### Create the KMS key
The key policy has three statements: your account's root admin statement; a statement that lets the Claude Platform on AWS service principal encrypt, decrypt, and generate data keys; and a separate statement for `kms:DescribeKey`. The crypto statement carries an optional `EncryptionContext` condition that binds the key to the workspaces you list. `DescribeKey` is granted separately because it has no `EncryptionContext` parameter, so an `EncryptionContext` condition on that action would always deny.
If you plan to use the optional `EncryptionContext` condition shown here, create the workspace first (without a key), copy its compartment ID, and substitute it for `<compartment-uuid>`. To find the ID in the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. The ID is under **Encryption key**, in the **Compartment ID** field. You can also read it from the `compartment_id` field returned by the [Get Workspace](../Admin/beta-organization-workspaces-retrieve.md) endpoint. If you don't plan to use the condition, delete the `Condition` block from that statement.
```python
export YOUR_ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
aws kms create-key \
--region <workspace-region> \
--description "Anthropic CMEK (Claude Platform on AWS)" \
--key-usage ENCRYPT_DECRYPT \
--policy "{
\"Version\": \"2012-10-17\",
\"Statement\": [
{
\"Sid\": \"AccountRootAdmin\",
\"Effect\": \"Allow\",
\"Principal\": {\"AWS\": \"arn:aws:iam::${YOUR_ACCOUNT}:root\"},
\"Action\": \"kms:*\",
\"Resource\": \"*\"
},
{
\"Sid\": \"AllowClaudePlatformOnAWSCrypto\",
\"Effect\": \"Allow\",
\"Principal\": {\"Service\": \"aws-external-anthropic.amazonaws.com\"},
\"Action\": [\"kms:Encrypt\", \"kms:Decrypt\", \"kms:GenerateDataKey\"],
\"Resource\": \"*\",
\"Condition\": {
\"StringEquals\": {
\"kms:EncryptionContext:anthropic:compartment_uuid\": [
\"<compartment-uuid>\"
]
}
}
},
{
\"Sid\": \"AllowClaudePlatformOnAWSDescribe\",
\"Effect\": \"Allow\",
\"Principal\": {\"Service\": \"aws-external-anthropic.amazonaws.com\"},
\"Action\": \"kms:DescribeKey\",
\"Resource\": \"*\"
}
]
}"
```
Capture `KeyMetadata.Arn` from the output. You need it when you register the key.
The `EncryptionContext` condition is optional. Every encrypt, decrypt, and data-key call made for a workspace, including the attach-time check, carries that workspace's compartment ID as `anthropic:compartment_uuid`, so the condition lists the compartment ID of each workspace you attach the key to and needs no all-zeros entry. Adding it binds the key to the workspaces you list at the IAM layer as well. Because a compartment ID exists only once its workspace exists, the order is: create the workspace, put its compartment ID in the condition (at key creation, or later with `kms:PutKeyPolicy`), then attach the key. Before attaching the key to each additional workspace, add that workspace's compartment ID the same way. To start without it, delete the `Condition` block from the `AllowClaudePlatformOnAWSCrypto` statement; if you add it later, include the compartment ID of every workspace the key is already attached to.
You can further restrict both service-principal statements with an `aws:SourceArn` condition. The service passes the [workspace's ARN](../Endpoints/claude-platform-on-aws-iam-actions.md#service-details) (`arn:aws:aws-external-anthropic:<region>:<account-id>:workspace/<workspace-id>`) as the source ARN on every call it makes with your key, so `"ArnLike": {"aws:SourceArn": "arn:aws:aws-external-anthropic:*:<account-id>:workspace/*"}` limits the grant to workspaces in your own AWS account, and a list of full workspace ARNs limits it to those workspaces. This condition is not required; the `EncryptionContext` condition on its own binds the key to the workspaces you list.
You can also create the key from the AWS Console: choose a symmetric key with the encrypt and decrypt key usage, a single-region key, and KMS key material origin, in the workspace's region. Leave key usage permissions empty in the Create-key wizard, then open the key's **Key policy** tab and replace the JSON with the policy shown here.
### Register and attach the key
1. 1
### Register the key
In the Claude Console, open **Settings \> Encryption keys** and click **Add key**. Enter a display name, then choose the key from the key picker or choose **Enter ARN manually** and paste the key ARN, and click **Add**. The key must be in the AWS account that hosts your organization; cross-account keys are not supported. The picker lists the enabled, customer-managed, symmetric, single-region keys in your account in one of your organization's regions; for a key the picker doesn't list, enter the ARN. It lists keys only if the principal you signed in with can call `kms:ListKeys` and `kms:DescribeKey`.
2. 2
### Attach the key to a workspace
Attach the key to a new workspace before you send any requests to that workspace. For a workspace that already receives requests, the key can take [up to a day to take effect](manage-claude-cmek.md#how-it-works). In the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. Under **Encryption key**, select the key, click **Save**, and confirm. You can also select a key when you create a workspace in the Claude Console, but only if your key policy does not yet name specific workspaces (no `EncryptionContext` condition), because the workspace's compartment ID is assigned at creation. Once attached, a workspace's key can't be changed.
This is when the key is validated: the attach call checks your principal's access to the key and performs an encrypt/decrypt round against it with the workspace's compartment ID as the encryption context, so a problem with either the key policy or your principal's permissions surfaces as an error on that call. If the attach fails with a KMS access error, check the following:
- The key policy names the `aws-external-anthropic.amazonaws.com` service principal and grants `kms:Encrypt`, `kms:Decrypt`, and `kms:GenerateDataKey`, plus `kms:DescribeKey` in a separate statement that has no `EncryptionContext` condition.
- Any `EncryptionContext` condition includes this workspace's compartment ID, and any `aws:SourceArn` condition you added matches this workspace's ARN.
- The key is enabled, single-region, and in the same AWS account and region as the workspace.
- The principal you are signed in as has `kms:DescribeKey`, `kms:Encrypt`, and `kms:Decrypt` on the key.
- No service control policy or resource control policy in your AWS organization prevents the service principal or your principal from using the key.
- If the policy looks right and the attach still fails, find the denied `kms:` event in CloudTrail in the key's account (it shows the calling principal and, for cryptographic calls, the encryption context), then correct the condition with `kms:PutKeyPolicy` and retry.
## Terraform
For infrastructure-as-code deployments, the same steps map to the `aws` provider with the `aws_kms_key` and `aws_kms_alias` resources.
---
## Configure Azure Key Vault for CMEK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/cmek-azure-key-vault
Path: 04-API-Reference/Other/manage-claude-cmek-azure-key-vault.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcmek-azure-key-vault)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Overview](manage-claude-cmek.md)[AWS KMS](manage-claude-cmek-aws-kms.md)[Google Cloud KMS](manage-claude-cmek-google-cloud-kms.md)[Azure Key Vault](manage-claude-cmek-azure-key-vault.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Encryption keys (CMEK)
# Configure Azure Key Vault for CMEK
Copy page
Use Azure Key Vault to provide an encryption key for your organization.
Copy page
Configure with the /claude-api skill in Claude Code
```python
claude "/claude-api help me configure a customer-managed encryption key with Azure Key Vault"
```
This guide walks through configuring an Azure Key Vault key as a [customer-managed encryption key (CMEK)](manage-claude-cmek.md) for your Anthropic organization.
Enabling CMEK is permanent. If your Key Vault key is deleted or disabled, Anthropic cannot recover the data encrypted under it. Review the [warnings and limitations](manage-claude-cmek.md) before you start.
## Prerequisites
- An Azure Key Vault with **RBAC authorization enabled** (`enableRbacAuthorization: true`) and **public network access allowed**. Anthropic calls your vault over the public data-plane endpoint; private endpoints are not supported.
- **Purge protection enabled** (`enablePurgeProtection: true`) on the vault. Without it, a deleted key can be permanently purged during the soft-delete retention window, causing irreversible loss of your CMEK-protected data. Purge protection cannot be disabled once enabled.
- Permissions to create keys in the vault and to assign RBAC roles on it.
- Permissions to create service principals in your Entra tenant (`Application Administrator`, `Cloud Application Administrator`, or an equivalent custom role).
- An Anthropic Admin API key for your organization.
- The [`az` CLI](https://learn.microsoft.com/en-us/cli/azure/?view=azure-cli-latest) installed and authenticated.
- **Diagnostic Settings** configured on the vault to route the `AuditEvent` log category to Log Analytics, a storage account, or an event hub. Azure Key Vault does not emit data-plane audit logs (such as `KeyWrap`, `KeyUnwrap`, and `KeyGet`) by default, so without this you get no audit trail for Anthropic's key operations.
## Anthropic app information
To have Anthropic use your encryption key, you must configure an Anthropic multitenant application ID and display name. Those values are:
| Field | Value |
|:-------------------------------|:---------------------------------------|
| Multitenant app client ID (US) | `8635ae1a-3e5d-44e8-a4ed-e0f614466f87` |
| App display name | `anthropic-cmek-client-us` |
Use only this published client ID and display name. Never trust an identifier provided over email, chat, or any onboarding channel.
## Encryption key setup
1. 1
### Consent to the Anthropic multitenant application
This creates a service principal in your Entra tenant for Anthropic's CMEK client application. The application requests no Microsoft Graph permissions; it exists solely as a federation target for Key Vault data-plane access.
``` shiki
az ad sp create --id 8635ae1a-3e5d-44e8-a4ed-e0f614466f87
```
From the output, capture the `id` field. This is the service principal's object ID in your tenant, which you use when you assign the RBAC role.
``` shiki
{
"appId": "8635ae1a-3e5d-44e8-a4ed-e0f614466f87",
"displayName": "anthropic-cmek-client-us",
"id": "<sp-object-id>"
}
```
If the service principal already exists in your tenant (from a prior attempt or another integration), `az ad sp create` exits with an "already exists" error. Fetch its object ID instead:
``` shiki
az ad sp show --id 8635ae1a-3e5d-44e8-a4ed-e0f614466f87 --query id -o tsv
```
This step has no Portal equivalent. If you do not have the Azure CLI installed locally, open Cloud Shell from the Portal's top navigation bar. After the command succeeds, you can find the service principal's object ID in **Microsoft Entra ID \> Enterprise applications** by clearing the default application-type filter and searching for `anthropic-cmek-client-us`.
2. 2
### Create an RSA key in your vault
Azure Key Vault does not support symmetric key wrapping, so the key must be RSA (3072-bit or larger) with `wrapKey` and `unwrapKey` in its allowed operations.
The `--tags` option adds the organization tag, `anthropic-org-<ORGANIZATION_UUID>` with the value `true`, where `<ORGANIZATION_UUID>` is your Anthropic organization ID in lowercase. The tag is required for Anthropic to validate the key.
**Finding your organization ID:** Copy the **Organization ID** field under **Settings \> Organization** in the Claude Console, or under **Organization settings \> Organization** in claude.ai, or read the `id` field from the [Organization Info](../Admin/beta-organization-retrieve.md) endpoint. Use the bare UUID, not the `org_`-prefixed ID.
``` shiki
az keyvault key create \
--vault-name <VAULT_NAME> \
--name <KEY_NAME> \
--kty RSA --size 3072 \
--ops wrapKey unwrapKey \
--tags anthropic-org-<ORGANIZATION_UUID>=true
```
For HSM-backed keys, use `--kty RSA-HSM` (requires a Premium-SKU vault). Software-protected RSA keys are acceptable for this integration.
From the Portal, open your Key Vault, select **Keys**, then **Generate/Import**. Set the key type to RSA and the size to 3072 or larger. To restrict the key to wrap and unwrap only, open the key version, scroll to **Permitted operations**, and uncheck everything except **Wrap Key** and **Unwrap Key**.
On the **Create a key** page, also add the organization tag under **Tags**.
To share one key among several Anthropic organizations, add one such tag for each organization. A key version can carry at most 15 tags, including your own.
To add the tag to a key you already have, open the key's current version in the Portal, select the link next to **Tags**, add the tag, and click **Save**. With the Azure CLI, run `az keyvault key set-attributes --vault-name <VAULT_NAME> --name <KEY_NAME> --tags anthropic-org-<ORGANIZATION_UUID>=true`. Its `--tags` option replaces the version's tags, so also put each tag the version already has in `--tags`, as `name=value`. For a key in a Managed HSM, use `--hsm-name <HSM_NAME>` instead of `--vault-name`.
3. 3
### Grant the Anthropic service principal access to your key
Assign the `Key Vault Crypto User` role to the service principal from the first step, scoped to the **individual key** rather than the whole vault.
``` shiki
VAULT_ID=$(az keyvault show --name <your-vault-name> --query id -o tsv)
az role assignment create \
--role "Key Vault Crypto User" \
--assignee-object-id <sp-object-id> \
--assignee-principal-type ServicePrincipal \
--scope "${VAULT_ID}/keys/<your-key-name>"
```
The built-in `Key Vault Crypto User` role grants key cryptographic operations (encrypt, decrypt, wrap, unwrap, sign, verify) plus key read on its assigned scope. The `--ops wrapKey unwrapKey` restriction you set on the key in the previous step further narrows which of those operations can succeed against this key, so in practice Anthropic can only wrap and unwrap.
From the Portal, open the **key** (not the vault), select its **Access control (IAM)** tab, click **Add \> Add role assignment**, select **Key Vault Crypto User**, and assign it to the `anthropic-cmek-client-us` service principal.
**Dedicated vault alternative:** Microsoft recommends a dedicated vault per application with roles assigned at the vault scope. If you provision a vault that holds only this Anthropic CMEK key, you can assign the role at the vault scope instead and the effect is identical. Scope to the individual key when the key lives in a shared vault.
4. 4
### Verify your vault configuration
``` shiki
az keyvault show --name <your-vault-name> \
--query "{rbac:properties.enableRbacAuthorization, purge:properties.enablePurgeProtection, pub:properties.publicNetworkAccess, net:properties.networkAcls.defaultAction, ipRules:properties.networkAcls.ipRules, uri:properties.vaultUri, tenantId:properties.tenantId}"
```
Confirm that:
- `rbac` is `true`.
- `purge` is `true`. If it is `false` or `null`, enable purge protection on the vault before proceeding. Without it, a soft-deleted key can be permanently purged during the retention window, making your CMEK-protected data unrecoverable.
- `pub` is `"Enabled"`. If it is `"Disabled"`, Anthropic cannot reach the vault over its public data-plane endpoint and validation fails.
- `net` is `"Allow"`, or, if it is `"Deny"`, that `ipRules` include Anthropic's egress ranges (contact Anthropic for the current list).
- `uri` is the vault URI you use when you register the key.
- `tenantId` is the tenant that governs the vault. Use this value as `tenant_id` when you register the key, not the tenant of your currently-active subscription (the two can differ in cross-tenant setups).
## Register the key with Anthropic
How you register the key depends on which product you use.
Claude Platform
Claude Enterprise
You can set up the key in the Claude Console or through the Admin API, with the same result.
Claude Console
API
1. 1
### Register the key with Anthropic
In the Claude Console, open **Settings \> Encryption keys** and click **Add key**. Enter a display name, choose **Azure Key Vault**, and click **Continue**. Fill in **Vault URI**, **Key name**, and **Tenant ID**, and click **Add**.
The key details step shows the organization tag. Add it to the key, as [the create step](#organization-tag) describes, before you click **Add**.
2. 2
### Validate the key
On the **Encryption keys** page, click **Verify** next to the key. **Connected** appears when the check passes. If it fails, a message gives the reason.
3. 3
### Attach the key to a workspace
In the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. Under **Encryption key**, select the key, click **Save**, and confirm. Attaching a key can't be undone. For a workspace that already receives requests, the key can take [up to a day to take effect](manage-claude-cmek.md#how-it-works).
## Terraform
For infrastructure-as-code deployments, the same steps map to the `azurerm` and `azuread` providers.
---
## Configure Google Cloud KMS for CMEK - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/cmek-google-cloud-kms
Path: 04-API-Reference/Other/manage-claude-cmek-google-cloud-kms.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcmek-google-cloud-kms)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Overview](manage-claude-cmek.md)[AWS KMS](manage-claude-cmek-aws-kms.md)[Google Cloud KMS](manage-claude-cmek-google-cloud-kms.md)[Azure Key Vault](manage-claude-cmek-azure-key-vault.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Encryption keys (CMEK)
# Configure Google Cloud KMS for CMEK
Copy page
Use Google Cloud KMS to provide an encryption key for your organization.
Copy page
Configure with the /claude-api skill in Claude Code
```python
claude "/claude-api help me configure a customer-managed encryption key with Google Cloud KMS"
```
This guide walks through configuring a Google Cloud KMS key as a [customer-managed encryption key (CMEK)](manage-claude-cmek.md) for your Anthropic organization.
Enabling CMEK is permanent. If your KMS key is deleted or disabled, Anthropic cannot recover the data encrypted under it. Review the [warnings and limitations](manage-claude-cmek.md) before you begin.
## Prerequisites
- A Google Cloud project with billing enabled.
- The Cloud KMS API enabled (`cloudkms.googleapis.com`).
- Permissions to create KMS key rings and keys, and to set IAM policy on them (`roles/cloudkms.admin` or equivalent).
- An Anthropic Admin API key for your organization.
- The [`gcloud` CLI](https://cloud.google.com/cli) installed and authenticated.
- Cloud KMS **Data Access audit logs** enabled for the project (IAM & Admin \> Audit Logs \> Cloud Key Management Service, with `DATA_READ` and `DATA_WRITE`). These are off by default; without them, Anthropic's encrypt and decrypt operations produce no entries in Cloud Logging.
## Anthropic service account email
To have Anthropic use your encryption key, you must give Anthropic's service account a key it can use for encrypting data. The service account email for Anthropic CMEK is:
``` block
anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com
```
Use only this published service account email. Never trust an identifier provided over email, chat, or any onboarding channel.
**Domain restricted sharing:** If your project is under a Google Cloud organization that enforces `constraints/iam.allowedPolicyMemberDomains`, the following IAM bindings are rejected because the Anthropic service account is outside your organization. You need either a project-level carve-out on that constraint, or to add Anthropic's Cloud Identity customer ID (format `C0xxxxxxxx`) to the allowed list. Contact Anthropic for the customer ID if needed.
## Encryption key setup
1. 1
### Create or choose a key ring
Skip this step if you already have a key ring to reuse. Key rings are regional. Choose a single-region US location such as `us-east5` that matches the Anthropic geography you are configuring. Multi-region locations like `us` and `global` are not supported.
``` shiki
gcloud kms keyrings create <your-keyring-name> \
--project=<your-project-id> \
--location=<region>
```
2. 2
### Create the crypto key
Create a symmetric key with the `ENCRYPT_DECRYPT` purpose. Anthropic strongly recommends HSM protection: Cloud KMS HSM keys are FIPS 140-2 Level 3 validated, and the cost delta over software keys is small.
The `--labels` option adds the organization label, `anthropic-org-<ORGANIZATION_UUID>` with the value `true`, where `<ORGANIZATION_UUID>` is your Anthropic organization ID in lowercase. The label is required for Anthropic to validate the key.
**Finding your organization ID:** Copy the **Organization ID** field under **Settings \> Organization** in the Claude Console, or under **Organization settings \> Organization** in claude.ai, or read the `id` field from the [Organization Info](../Admin/beta-organization-retrieve.md) endpoint. Use the bare UUID, not the `org_`-prefixed ID.
``` shiki
gcloud kms keys create <KEY_NAME> \
--project=<PROJECT_ID> \
--location=<REGION> \
--keyring=<KEYRING_NAME> \
--purpose=encryption \
--protection-level=hsm \
--labels=anthropic-org-<ORGANIZATION_UUID>=true
```
For software protection instead, omit `--protection-level=hsm`. Nothing else in this guide changes.
You can also create the key from the Google Cloud Console. Open the key ring, click **Create key**, select **Generated key**, set the purpose and algorithm to symmetric encrypt and decrypt, and choose **HSM** under protection level.
To share one key among several Anthropic organizations, add one such label for each organization. A key can carry at most 64 labels, including your own.
To add the label to a key that doesn't have it, run `gcloud kms keys update <KEY_NAME> --project=<PROJECT_ID> --location=<REGION> --keyring=<KEYRING_NAME> --update-labels=anthropic-org-<ORGANIZATION_UUID>=true`. It merges the label with any labels the key already has.
3. 3
### Grant Anthropic's service account access to the key
Two key-level IAM bindings are required. Both are scoped to the single crypto key, not project-wide or keyring-wide.
Encrypt and decrypt, which Anthropic uses to encrypt and decrypt the data keys that protect your workspace data (envelope encryption):
``` shiki
gcloud kms keys add-iam-policy-binding <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--member="serviceAccount:anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com" \
--role=roles/cloudkms.cryptoKeyEncrypterDecrypter
```
Viewer, for the metadata read (`cryptoKeys.get`) Anthropic performs at startup to validate the key's purpose and algorithm:
``` shiki
gcloud kms keys add-iam-policy-binding <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--member="serviceAccount:anthropic-cmek-client-us@gcp-anthropic-cmek-clients.iam.gserviceaccount.com" \
--role=roles/cloudkms.viewer
```
From the Console, select the key, open the **Permissions** panel, click **Grant access**, and add the service account with both the Cloud KMS CryptoKey Encrypter/Decrypter and Cloud KMS Viewer roles. Make sure you are on the key's permissions page, not the key ring or project, so the grant is scoped to this key only.
4. 4
### Note the full key resource name
You pass this to Anthropic when you register the key. The format is:
``` block
projects/<your-project-id>/locations/<region>/keyRings/<your-keyring-name>/cryptoKeys/<your-key-name>
```
Retrieve it with:
``` shiki
gcloud kms keys describe <your-key-name> \
--project=<your-project-id> \
--location=<region> \
--keyring=<your-keyring-name> \
--format="value(name)"
```
From the Console, open the key's details page and click **Copy resource name**.
## Register the key with Anthropic
How you register the key depends on which product you use.
Claude Platform
Claude Enterprise
You can set up the key in the Claude Console or through the Admin API, with the same result.
Claude Console
API
1. 1
### Register the key with Anthropic
In the Claude Console, open **Settings \> Encryption keys** and click **Add key**. Enter a display name, choose **Google Cloud KMS**, and click **Continue**. Paste the full key resource name into **Key resource name**, and click **Add**.
The key details step shows the organization label. Add it to the key, as [the create step](#organization-label) describes, before you click **Add**.
2. 2
### Validate the key
On the **Encryption keys** page, click **Verify** next to the key. **Connected** appears when the check passes. If it fails, a message gives the reason.
3. 3
### Attach the key to a workspace
In the Claude Console, go to [Manage \> Security](https://platform.claude.com/settings/workspaces/default/security-compliance) and select the workspace in the workspace picker at the top of the sidebar. Under **Encryption key**, select the key, click **Save**, and confirm. Attaching a key can't be undone. For a workspace that already receives requests, the key can take [up to a day to take effect](manage-claude-cmek.md#how-it-works).
## Terraform
For infrastructure-as-code deployments, the same steps map to the `google` provider with the `google_kms_key_ring`, `google_kms_crypto_key`, and `google_kms_crypto_key_iam_member` resources.
---
## Customer-managed encryption keys - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/cmek
Path: 04-API-Reference/Other/manage-claude-cmek.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcmek)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Overview](manage-claude-cmek.md)[AWS KMS](manage-claude-cmek-aws-kms.md)[Google Cloud KMS](manage-claude-cmek-google-cloud-kms.md)[Azure Key Vault](manage-claude-cmek-azure-key-vault.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Encryption keys (CMEK)
# Customer-managed encryption keys
Copy page
Encrypt Claude workspace data at rest with a key you control.
Copy page
Learn more with the /claude-api skill in Claude Code
```python
claude "/claude-api tell me about customer-managed encryption keys"
```
A customer-managed encryption key (CMEK) lets you provision an encryption key in your own [AWS KMS](https://aws.amazon.com/kms/), [Google Cloud KMS](https://cloud.google.com/security/products/security-key-management), or [Azure Key Vault](https://azure.microsoft.com/en-us/products/key-vault) and have Anthropic use it to encrypt certain workspace data at rest. You retain full control of the key, including rotation, audit, and revocation, and the key operations Anthropic performs against your key are recorded in your cloud provider's audit logs.
The use of CMEK is optional. Eligible organizations can **opt in** to use customer-managed encryption keys instead of the default encryption that Anthropic provides. To activate CMEK, contact your Anthropic account team.
**Enabling CMEK is permanent and can cause irreversible data loss**
Enabling CMEK is permanent. Anthropic keeps no copy of your key, so misconfiguration or key loss can permanently destroy your CMEK-protected data. If you are uncertain about any step, contact your Anthropic representative before applying changes.
- **Permanent data loss:** If your encryption key is deleted, scheduled for deletion, or has its key material destroyed, Anthropic cannot recover your data.
- **Identifier verification is mandatory:** Granting key access to an incorrect or spoofed principal can expose your data to an unauthorized party. Always verify the Anthropic identifier against the published production identities in each configuration guide. On Claude Platform on AWS, that identity is the AWS service principal published in the [AWS KMS guide](manage-claude-cmek-aws-kms.md#claude-platform-on-aws). Never trust an identifier provided over email, chat, or any onboarding channel.
## How it works
Only Organization Admins (on Claude Platform; the Admin role on Claude Platform on AWS) or Owners and the Primary Owner (on Claude Enterprise) can configure CMEK. On Claude Platform, CMEK is scoped per workspace and configured in the Claude Console or with the Admin API (on Claude Platform on AWS, in the Claude Console or through the IAM-authorized external key and workspace endpoints). On Claude Enterprise, CMEK is scoped per organization and configured in [claude.ai \> Organization settings \> Data and privacy](https://claude.ai/admin-settings/data-privacy-controls). On either product, CMEK protects data written after your key takes effect. Existing data (prior chats, files, and sessions) remains encrypted with Anthropic-managed keys and is not re-encrypted under your key.
On Claude Platform, Anthropic recommends attaching your key to a new workspace before you send any requests to that workspace. If you attach a key to a workspace that already receives requests, your key can take up to a day to take effect. Data written before then, like existing data, is encrypted with Anthropic-managed keys and is not re-encrypted.
CMEK configuration events appear in the [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md). The key operations Anthropic performs against your key (such as wrapping and unwrapping data keys) do not appear in the Compliance API; they appear in your cloud provider's audit logs.
Anthropic calls your key management service from its standard public IP range. If you restrict access to your key management service by IP, allow the addresses listed in [IP addresses](../Endpoints/ip-addresses.md). On Claude Platform on AWS, don't rely on IP-based restrictions for your key; scope access with the key policy described in the [AWS KMS guide](manage-claude-cmek-aws-kms.md#claude-platform-on-aws) instead.
## Prerequisites
- Permissions to create encryption keys and manage key access in the account, project, or subscription that will host the encryption key.
- An Organization Admin role in the Claude Console on Claude Platform (the Admin role on Claude Platform on AWS), or an Owner or Primary Owner role on Claude Enterprise.
- Data retention configuration: CMEK is allowed with [Zero data retention (ZDR)](../Guides/build-with-claude-api-and-data-retention.md) for both Claude Platform and Claude Enterprise.
## Availability and regions
Except on Claude Platform on AWS (covered at the end of this section), CMEK is currently available in US regions only, and all encryption operations are processed in US regions. For minimal latency, choose a region close to Anthropic's US infrastructure:
| Provider | Recommended regions |
|:-------------|:----------------------------|
| AWS | `us-east-2` |
| Google Cloud | `us-central1`, `us-east5` |
| Azure | `northcentralus`, `eastus2` |
On [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md), CMEK is available with AWS KMS keys only; Google Cloud KMS and Azure Key Vault keys cannot be registered. These region recommendations do not apply there: the key must be a single-region KMS key in the same AWS account and region as the workspace it is attached to, and its key policy must grant access to an AWS service principal rather than Anthropic's IAM role; see [Set up CMEK on Claude Platform on AWS](manage-claude-cmek-aws-kms.md#claude-platform-on-aws). Register and attach keys in the Claude Console; the external key endpoints are also available on Claude Platform on AWS, authorized through [IAM actions](../Endpoints/claude-platform-on-aws-iam-actions.md#encryption-keys). There is no separate validation step: the key is implicitly validated when you attach it to a workspace (the attach call performs an encrypt/decrypt round), so a key policy problem surfaces at attach time rather than at registration.
## What CMEK protects
What CMEK covers depends on which product you use.
### Encrypted with CMEK key
**Claude Platform**
- Message content, files and attachments (both inline attachments sent with a request and Files API uploads), and MCP and tool configuration.
- [Claude Managed Agents](managed-agents-overview.md) (beta) data, including agent configurations, environments, webhooks, sessions and their events, [memory stores](managed-agents-memory.md) (beta) and their memories and memory versions, and [dreams](managed-agents-dreams.md) (research preview).
- Data stored at rest by the Batch and Skills APIs, and by these tools: web search, web fetch, code execution, bash, text editor, MCP connector (beta), structured outputs, advisor tool (beta), computer use, browser use, and context management.
**Claude Enterprise**
- Chat content, including skills and plugins.
- Chat attachments and project attachments.
- Claude Code on the CLI, including message content.
- Cowork in Claude Desktop.
- Compliance API [local session transcripts](manage-claude-compliance-sessions.md#retrieve-local-sessions) captured from sessions on users' machines. If your key cannot be used, the messages endpoint returns [503 Service Unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable) instead of transcript content. Session metadata is still listed.
- Office agents.
- Claude in Chrome.
- Claude Science, which is in beta.
On both products, backups and snapshots inherit the key.
### Disabled or modified
Some features are turned off or substantially modified when CMEK is enabled. This list is not exhaustive; review it with your team before enabling CMEK.
**Claude Platform**
- Playground in the Claude Console is disabled.
- Portions of the Compliance API that return raw content, such as prompts, responses, and files, are disabled.
- Structured outputs are not available for Claude Fable or Claude Mythos models in CMEK organizations.
- Other beta and research preview features might not be covered by CMEK.
**Claude Enterprise**
- Chat search is disabled because chat titles and content are encrypted under your key. Members cannot search past chats, and the **Search and reference chats** toggle stays off, so Claude cannot search them either.
- [Project knowledge search](../../15-Claude-AI-Features/retrieval-augmented-generation-rag-for-projects.md) (retrieval-augmented generation, or RAG) is disabled. Project knowledge loads directly into each conversation's context instead of being indexed and searched. As a result, a project can use substantially less knowledge than it could without CMEK. Knowledge beyond what can be loaded is left out of the conversation.
- Claude Code on the web and Claude in Slack are unavailable: new sessions cannot be started and Claude in Slack declines requests, even if an admin turns these products on. Claude Code Desktop remains available for local sessions but is off unless an admin turns it on under [claude.ai \> Organization settings \> Claude Code](https://claude.ai/admin-settings/claude-code).
- Artifacts cannot be shared publicly, only within your organization.
- Certain analytics are degraded: admin analytics for claude.ai skills and connectors (under claude.ai/analytics/usage and through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md)), Claude smart reports (under claude.ai/analytics/insights), and Claude Code contribution metrics (under claude.ai/analytics/claude-code).
- Organization data exports and audit log exports, both under [claude.ai \> Organization settings \> Data and privacy](https://claude.ai/admin-settings/data-privacy-controls), are disabled.
- Response ratings (thumbs up and thumbs down on Claude's responses) are disabled.
- The following beta and research preview features are unavailable in CMEK organizations, regardless of admin settings: Claude Design, Claude Slides, and Claude Docs in conversations and the **Artifacts** tab, and routines.
### Encrypted with Anthropic key
These features remain available, but their data is not encrypted under your key. You can disable any feature that is not appropriate for your use case in **Settings**.
**Claude Platform**
- Data that is not at rest (such as cache) and data with a TTL shorter than 24 hours.
- Activity Feed, audit logs, and telemetry network traffic such as OTEL, so customers can maintain compliance even if a key is revoked.
- Claude Managed Agents [vault credential](managed-agents-vaults.md) values, such as OAuth tokens and client secrets. These are stored under Anthropic-managed encryption, are write-only, and are never returned in API responses.
- [User profiles](../Endpoints/http-beta-user-profiles.md): the `name`, `external_id`, and `metadata` fields are stored under Anthropic-managed encryption, not your key. Do not store sensitive personal data in profile `metadata`.
**Claude Enterprise**
- Beta and research preview features might not be covered by CMEK and can break in CMEK organizations, for example, Claude Security and the Claude Design app at claude.ai/design.
- [Personal preferences - Instructions for Claude section](https://claude.ai/new#settings/account) and Cowork Global instructions. These are set at the account level and shared across all of a user's organizations.
On both products, account data for users in your organization (such as names, email addresses, and profile pictures) is not encrypted under your key.
## Limited preservation outside your key
In three narrow cases, Anthropic may preserve specific records under Anthropic-managed encryption:
- Where Anthropic is required by law to retain records (for example, material reported to NCMEC under 18 U.S.C. § 2258A).
- Exigent risk of serious harm (for example, CBRNE weapons development, offensive cyberattacks, or imminent threats of violence).
- Violations of Section D.4 of Anthropic's [Commercial Terms of Service](https://www.anthropic.com/legal/commercial-terms) or equivalent terms in a customer's other applicable agreement with Anthropic.
Outside of [CSAM screening](../../22-Safety-Policy/csam-detection-and-reporting.md), preservation requires a human reviewer's explicit decision and follows Anthropic's [retention policy for commercial data](https://privacy.claude.com/en/articles/10023548-how-long-do-you-store-my-data). For every instance of preservation, a corresponding [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md) event is generated with a reason code conveying the purpose of the preservation. See [CMEK content preservation](manage-claude-access-transparency.md#cmek-content-preservation) for details. Safety screening metadata (records derived from Anthropic's automated safety scans, such as pattern identifiers and match indicators, not conversation content) is retained under Anthropic-managed encryption and remains readable after key revocation.
## Limitations
- **Irreversible action:** Once a key is attached to a workspace, it cannot be detached or swapped. On Claude Platform, attaching a key also locks the workspace's data retention setting: you cannot turn off 30-day data retention for that workspace, and returning to zero data retention requires creating a new workspace and moving your traffic to it. Rotating the key material within the same key (for example, AWS KMS automatic rotation, a Cloud KMS rotation schedule, or an Azure Key Vault rotation policy) is supported transparently and requires no change in Anthropic. Switching to a *different* key requires creating a new workspace with the new key and migrating your data. Revoking or disabling the key makes all CMEK-protected data in that workspace permanently inaccessible, with no backout path.
- **No retroactive encryption:** CMEK only protects data written after your key takes effect (see [How it works](#how-it-works)).
- **Latency:** Operations that wrap or unwrap data keys make a round-trip to your key management service, which can add a small amount of latency to actions that read or write data at rest.
- **Revocation delay:** Key revocation can take up to 1 hour (the cache TTL). Requests already in flight during that window may continue to succeed.
- **KMS costs:** CMEK requires a key in a third-party key management service (AWS KMS, Google Cloud KMS, or Azure Key Vault), which might incur separate charges billed by your KMS provider.
- **Claude Code telemetry behind a gateway:** When Claude Code connects through an LLM gateway or proxy (a custom `ANTHROPIC_BASE_URL`), CMEK does not apply to Claude Code's operational telemetry. To turn this telemetry off, set the `DISABLE_TELEMETRY` environment variable to `1`, as described under [Telemetry services](../../13-Enterprise-Admin/data-usage.md#telemetry-services) in the Claude Code documentation.
## Configure your provider
Follow the guide for the key management service you use.
[AWS KMS](manage-claude-cmek-aws-kms.md)
Create an AWS KMS key with a key policy that grants Anthropic access, then register it.
[Google Cloud KMS](manage-claude-cmek-google-cloud-kms.md)
Create a Cloud KMS crypto key, grant Anthropic's service account access, then register it.
[Azure Key Vault](manage-claude-cmek-azure-key-vault.md)
Create an RSA key, grant the Anthropic service principal access, then register and validate it.
---
## Query the Activity Feed - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-activity-feed
Path: 04-API-Reference/Other/manage-claude-compliance-activity-feed.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-activity-feed)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Query the Activity Feed
Copy page
Retrieve, filter, and paginate your organization's Compliance API Activity Feed.
Copy page
To enable the Compliance API, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
**Required scope:** `read:compliance_activities` on the Compliance Access Key or Admin API key.
Both Compliance Access Keys (`sk-ant-api01-...`) carrying this scope and Admin API keys (`sk-ant-admin01-...`) can call the Activity Feed. See [Set up the Compliance API](manage-claude-compliance-api-access.md) for the conditions under which each key type carries the scope.
The Activity Feed records authentication, chat, file, project, administrative, and platform activity across your organization and returns it in reverse chronological order. Activities are queryable within 1 minute of occurring and are retained for 6 years. Recording is not retroactive: it begins when the Compliance API is first enabled for your organization, and activity from before enablement is not backfilled.
cURL
```python
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/activities?limit=1" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"id": "activity_01XyDMpzjS89pFZXqSFUBDr6",
"created_at": "2026-04-10T08:09:10Z",
"organization_id": "org_01Wv6QeBcDfGhJkLmNpQrSt8",
"organization_uuid": "abcdef01-2345-6789-abcd-ef0123456789",
"actor": {
"type": "user_actor",
"email_address": "user@example.com",
"user_id": "user_01TuVwXyZaBcDeFgH2JkLmN4",
"ip_address": "192.0.2.34",
"user_agent": "Mozilla/5.0..."
},
"type": "claude_chat_created",
"claude_chat_id": "claude_chat_01XyDMpzjS89pFZXqSFUBDr6",
"claude_project_id": "claude_proj_01KGp4eZNug9ri4kE35RSppq"
}
],
"has_more": true,
"first_id": "activity_01XyDMpzjS89pFZXqSFUBDr6",
"last_id": "activity_01XyDMpzjS89pFZXqSFUBDr6"
}
```
## Filter activities
Filter by organization, actor, activity type, or a `created_at` time window using the dotted sub-parameters `created_at.gte`, `.gt`, `.lte`, and `.lt`. See the [API reference](../Admin/compliance-activities-list.md) for each parameter's type and accepted values.
Repeatable parameters use array-bracket query syntax: pass `activity_types[]=...`, `actor_ids[]=...`, or `organization_ids[]=...` once for each value.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--data-urlencode "activity_types[]=claude_file_uploaded" \
--data-urlencode "activity_types[]=claude_chat_created" \
--data-urlencode "created_at.gte=2026-04-01T00:00:00Z" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
The Activity Feed produces hundreds of distinct activity types. See [Query compliance activities](../Admin/compliance-activities-list.md) in the API reference for the full list of values that `activity_types[]` accepts.
## Paginate results
Activities are returned newest first, with ties in `created_at` broken by activity ID, and capped at `limit` results in each response (default 100, max 5,000). See the [API reference](../Admin/compliance-activities-list.md) for the full response schema.
The Compliance API uses two pagination schemes depending on the endpoint family:
| Endpoint family | Sort order | Scheme | Parameters |
|:----------------------------------------------------------------------------------------------------|:--------------------------------------------------------|:-----------|:------------------------------------------------------------|
| Activities | Newest first | Cursor | `after_id`, `before_id` (returned as `first_id`, `last_id`) |
| Chats and chat messages | Oldest first | Cursor | `after_id`, `before_id` (returned as `first_id`, `last_id`) |
| Organizations, projects, project attachments, users, roles, role permissions, groups, group members | Endpoint-specific | Page token | `page` (returned as `next_page`) |
| Local and remote sessions and session messages | Sessions newest first; messages oldest first by default | Page token | `page` (returned as `next_page`) |
Files do not paginate: they are retrieved individually by ID.
Pagination cursors and page tokens are opaque strings: pass them back unchanged. Their internal format is not stable, and parsing them will break without notice. Only one of `after_id` or `before_id` may be set in each request, and both schemes return `has_more` so you know when to stop. The session endpoints (local and remote) are the exception: they return `next_page` without `has_more`, so stop when `next_page` is `null`.
To page through activities:
- Pass the response's `last_id` as `after_id` to advance to the next page in result order. With activities sorted newest first, the next page contains older entries.
- Pass `first_id` as `before_id` to return to the previous page.
- Stop when `has_more` is `false`.
The cursor parameter sets the page direction; the endpoint's sort order sets the time direction. The same `after_id` parameter reaches older activities here. Chats sort oldest first; see [Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md) for the cursor semantics there.
**Cursors are safe to reuse on retry.** A cursor or page token from a successfully returned page remains valid; a request that fails (5xx, timeout, network error) does not advance your position. Retry the same request with the same cursor. Only move to the next cursor after you have stored the page it points past.
Page tokens on the local session endpoints are the exception over longer pauses. On the [local session messages endpoint](manage-claude-compliance-sessions.md#retrieve-a-local-session-transcript), a walk's `page` tokens expire 24 hours after its first page (a walk is one pass through the pages), so finish or resume within that window, or restart without the `page` parameter. On the [local session list](manage-claude-compliance-sessions.md#retrieve-local-sessions), an older `page` token is still accepted but is re-evaluated against the current retention boundary and can skip sessions, so complete list walks within 24 hours as well.
cURL
```python
# Fetch the first page (newest activities first) and capture its trailing cursor.
last_id=$(curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/activities?limit=2" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" | jq -er '.last_id')
# Pass the cursor back unchanged to fetch the next (older) page.
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "limit=2" \
--data-urlencode "after_id=${last_id}"
```
A production **backfill** loop pages through older activities by driving iteration off `has_more` and `last_id`:
1. Start from your stored cursor (or omit `after_id` to start from the beginning).
2. Page through with `after_id=<last_id>` until `has_more` is `false`.
3. Persist the final `last_id` only after you've stored every page it covers.
``` inline-block
cursor = stored_cursor
loop:
if cursor is not null:
page = GET /v1/compliance/activities?after_id={cursor}&limit=100
else:
page = GET /v1/compliance/activities?limit=100
store(page.data)
if page.last_id is not null:
cursor = page.last_id
if not page.has_more: break
persist(cursor)
```
## Understand the Activity object
Every entry in `data` is an Activity with this top-level shape:
| Field | Type | Description |
|:--------------------|:----------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | string | Unique identifier for the activity. |
| `created_at` | RFC 3339 string | When the activity occurred. |
| `organization_id` | string or null | Organization where the activity occurred, or `null` for events not tied to an organization (sign-in, sign-out, Compliance API calls). |
| `organization_uuid` | string or null | Same scoping as `organization_id`, expressed as a UUID. |
| `actor` | Actor union | Who or what performed the activity. See the following actor table. |
| `type` | string | The activity type, for example `claude_chat_created`. |
| *additional fields* | varies | Type-specific fields, for example `claude_chat_id` on chat events or `claude_file_id` on file events. See [Query compliance activities](../Admin/compliance-activities-list.md) in the API reference for the per-type field list. |
The `actor` field is a discriminated union. The `type` discriminator tells you which other fields are present:
| `actor.type` | When it appears | Key fields |
|:-----------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------|
| `user_actor` | A signed-in claude.ai or Claude Console user took the action, or a process Anthropic runs on that user's behalf did (see the paragraph after this table). | `email_address`, `user_id`, `ip_address`, `user_agent` |
| `api_actor` | A request called the Claude API or the Compliance API with a customer-issued API key. Compliance API calls produce this actor type for both Compliance Access Keys and Admin API keys. | `api_key_id`, `ip_address`, `user_agent` |
| `admin_api_key_actor` | An organization admin used an Admin API key to manage users, invites, workspaces, or API keys. | `admin_api_key_id`, `ip_address`, `user_agent` |
| `unauthenticated_user_actor` | An action occurred before sign-in completed, for example `sso_login_initiated`. | `unauthenticated_email_address`, `ip_address`, `user_agent` |
| `anthropic_actor` | Anthropic acted on the organization, for example through internal tooling. | `email_address` (always `null`; present for shape consistency with `user_actor`, because Anthropic operators are not represented by individual email) |
| `system_actor` | Automated background processing performed by Anthropic systems, acting without a user or customer credential. | `service` (nullable; the name of the automated process that performed the action, when known) |
| `scim_directory_sync_actor` | An identity provider (such as Okta, Microsoft Entra ID, or JumpCloud) pushed a change through SCIM directory sync. | `workos_event_id`, `directory_id`, `idp_connection_type` (nullable; for example `OktaSCIMV2`, `AzureSCIMV2`) |
A `user_actor` activity does not always mean the user took the action. Processes that Anthropic runs on a user's behalf can currently appear as `user_actor` for the affected user rather than as `system_actor`, and this attribution may change. For example, memory activities from a migration, such as `platform_memory_store_created`, `platform_memory_created`, and `platform_memory_deleted`, are attributed this way. Memory activities can show an `ip_address` of `0.0.0.0` whoever performed them, so this value does not on its own identify activity from a platform process.
A `claude_*_viewed` activity means a Claude app loaded content, not that a person viewed it. Types such as `claude_chat_viewed`, `claude_file_viewed`, and `claude_project_viewed` are recorded each time a Claude app loads the chat, file, or project from Anthropic's servers. Repeated loads are not deduplicated. The web, desktop, and mobile apps load content at different moments, sometimes in the background, and can display a cached copy without loading it. Counts of these activities vary by platform as a result, and they do not correspond to messages sent or screens viewed.
Activities about a file, project document, or artifact do not include its name or title. As of September 24, 2026, the `filename` and `title` fields on these activities are always `null`, an empty string, or omitted, including on activities recorded before that date. To look up a name or title, pass the activity's `claude_file_*`, `claude_proj_doc_*`, or `claude_artifact_version_*` ID to the matching metadata endpoint in [Retrieve files and artifacts](manage-claude-compliance-content-data.md#retrieve-files-and-artifacts), using a Compliance Access Key with the `read:compliance_user_data` scope. You cannot look up a name or title after the file, document, or artifact is deleted, or when the activity has no such ID.
**Build forward-compatible handlers.** Pass through unrecognized `type` and `actor.type` values, and ignore fields your handler does not expect, so your integration keeps working when new activity types ship.
## Next steps
[API reference](../Admin/compliance-activities-list.md)
The full request and response schema for `GET /v1/compliance/activities`, including every supported `activity_types[]` value.
[Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md)
Query and delete the underlying content for activities you find in the feed (Compliance Access Key required).
[Design your compliance integration](manage-claude-compliance-integration-patterns.md)
Choose a polling or batch consumption pattern and plan SIEM correlation.
[Handle Compliance API errors](manage-claude-compliance-errors.md)
The full error catalog.
---
## Set up the Compliance API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-api-access
Path: 04-API-Reference/Other/manage-claude-compliance-api-access.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-api-access)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Set up the Compliance API
Copy page
Enable the Compliance API for your organization, then create a Compliance Access Key (with scoped permissions) or an Admin API key, and learn which to use.
Copy page
Claude Enterprise organizations and eligible standalone Claude Console organizations have self-service access to the Compliance API. This page describes how to enable the Compliance API for your organization and create API keys.
**Required role:** organization admin (Claude Console), or primary owner or organization owner (claude.ai).
The Compliance API uses two key types, and which one you create depends on which Claude product your organization uses. Primary owners and organization owners create Compliance Access Keys in claude.ai; these keys unlock the full Compliance API. A primary owner's key can cover every organization under the parent organization; an organization owner's key covers their own organization only. Organization admins create Admin API keys in Claude Console; these keys unlock the [Activity Feed](manage-claude-compliance-activity-feed.md) only.
## Which key do you need?
| Key type | Created in | Used for | Works with the Compliance API? |
|------------------------------------------------|---------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
| **Compliance Access Key** (`sk-ant-api01-...`) | [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) | Activity Feed, chats, files, projects, sessions (in apps such as Cowork and Claude Code), users, organization metadata, and organization settings | Yes (all endpoints) |
| **Admin API key** (`sk-ant-admin01-...`) | [Claude Console \> Settings \> Admin keys](playground.md) | The [Admin API](manage-claude-admin-api.md) and the Compliance API Activity Feed | Activity Feed only |
| **Analytics API key** | [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) | The Claude Enterprise Analytics API (see [Analytics APIs](manage-claude-analytics-api.md)) | No |
| **Claude API key** (`sk-ant-api03-...`) | [Claude Console \> Settings \> API keys](playground.md) | Calling Claude models through the [Claude API](../Endpoints/overview.md) | No |
A Claude Enterprise tenant has one **parent organization** that centralizes identity, SSO, and SCIM for every workload organization beneath it. These workload organizations are the parent's **linked organizations**.
**Claude Enterprise parent organizations do not appear in Claude Console (`platform.claude.com`).** The parent carries no workloads, no Claude API keys, and no Admin API keys. Create Compliance Access Keys in claude.ai **Organization settings**, not in Claude Console.
## Set up the Compliance API
Setup is one flow: enable the Compliance API for your organization, then create a Compliance Access Key in claude.ai. A Claude Console organization instead [creates an Admin API key](#create-an-admin-api-key) after enablement; Admin API keys reach the [Activity Feed](manage-claude-compliance-activity-feed.md) only.
A Compliance Access Key with `read:compliance_user_data` can read every chat, file, project, and session transcript in every linked organization, including content the primary owner has not seen. A key with `delete:compliance_user_data` can permanently delete chats, files, and projects. Treat Compliance Access Keys like production database credentials: store them in a secrets manager, never in source control or SIEM forwarder configuration.
1. 1
### Enable the Compliance API
Where you enable the Compliance API depends on how your organization is set up:
- **Claude Enterprise organizations:** The primary owner enables the Compliance API at [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access). Enablement happens at the parent organization level and cascades to every linked organization, both claude.ai and Claude Console.
- **Standalone Claude Console organizations:** An organization admin turns on the **Compliance API** toggle at [Claude Console \> Settings \> Security](playground.md). Enablement is self-service for eligible organizations, and the change takes effect immediately. If the **Compliance API** section is not visible, you do not have the admin role, your organization is linked to a parent organization (the Compliance API is enabled from the parent organization instead), or your organization is not eligible for self-service enablement; contact your account team or [Anthropic support](https://support.claude.com) if you are not sure which applies.
- **Claude Console organizations linked to a parent organization:** There is nothing to turn on in Claude Console. Ask the primary owner of your parent organization to enable the Compliance API in claude.ai, or contact your account team.
**Turning the Compliance API off stops activity recording.** An organization admin can turn the Compliance API off at any time with the same **Compliance API** toggle that turns it on. While the Compliance API is off, no activity events are recorded for your organization, so the [Activity Feed](manage-claude-compliance-activity-feed.md) receives no new events. If your organization is enrolled in [Access Transparency](manage-claude-access-transparency.md), turning the Compliance API off also stops Access Transparency event delivery. Activity that is not recorded while the Compliance API is off cannot be recovered later. Turning the Compliance API back on resumes recording from that point forward; activity that was already recorded is not deleted. For Claude Enterprise organizations, the Compliance API setting in claude.ai also governs transcript capture for [local sessions](manage-claude-compliance-sessions.md#retrieve-local-sessions) (sessions on users' machines): capture starts when the Compliance API is enabled and stops while it is off, and transcript content from sessions that run while it is off is not captured and cannot be recovered later.
A standalone Claude Console organization uses Admin API keys rather than Compliance Access Keys: after enablement, skip the remaining steps and [create a new Admin API key](#create-an-admin-api-key) instead. The remaining steps provision Compliance Access Keys, which are available only to organizations that are part of a Claude Enterprise tenant.
2. 2
### Decide the key's scope
A key's access is set when it is created. Decide which organizations the key covers:
- A key for the **parent organization** can access every organization under the parent organization.
- A key for a **single organization** can access that organization only.
3. 3
### Sign in with the matching role
Sign in to claude.ai. The primary owner of the parent organization can create a key with either scope. An organization owner can create a key restricted to their own organization only.
If the **API** page described in the next step is not visible, or compliance scopes are unavailable when creating a key, either your role cannot create Compliance Access Keys, or the Compliance API has not been enabled for your organization yet (return to the first step).
4. 4
### Open API settings
Go to [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) and find the **Keys** section.
5. 5
### Create the key
Click **Create key**, name the key, and select one or more scopes from the following table. Click **Create**.
| Scope | Grants |
|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `read:compliance_activities` | Read the Activity Feed. A key covering the parent organization reads events for the parent organization and all linked organizations. |
| `read:compliance_user_data` | Read user chats, messages, files, projects, session metadata and transcripts, organization users, and group members |
| `delete:compliance_user_data` | Delete user chats, files, and projects |
| `read:compliance_org_data` | Read organization metadata (names, types, roles, and groups) and the effective settings in force for organizations under the parent organization. User listings and group membership require `read:compliance_user_data`. |
With the Plugins API, `read:compliance_org_data` also reads your organization's plugin and plugin marketplace inventory through the Admin API, including members' personal plugins and their files. That inventory identifies members: it carries the user ID of each member who owns or created a plugin, or with whom a plugin has been shared individually, and the email address of a plugin's creator while they are still a member; see [Read plugins and plugin marketplaces](manage-claude-compliance-org-data.md#read-plugins-and-plugin-marketplaces).
Choose the smallest scope set that your integration needs:
- An audit pipeline that reads the Activity Feed only needs `read:compliance_activities`.
- An eDiscovery tool that reads chats and files but never deletes them does not need `delete:compliance_user_data`.
- If your workflow both reads and deletes, use **two keys** with separate scopes so a leaked read key cannot delete data.
Compliance Access Key scopes are immutable after creation. To change scopes, create a new key with the scopes you want, then delete the old one.
6. 6
### Copy and store the secret
Copy the displayed secret key (starting with `sk-ant-api01-`) and store it in your secrets manager. The full secret is displayed only once.
7. 7
### Export the key for the examples in this guide
Set the key as an environment variable so the shell samples in this guide can read it:
``` shiki
export ANTHROPIC_COMPLIANCE_ACCESS_KEY=sk-ant-api01-...
```
## Create an Admin API key
The Compliance API must already be [enabled for your Claude Console organization](#set-up-the-compliance-api) before an Admin API key can call the Activity Feed.
Follow the steps in [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-console-organization), then set the key as an environment variable:
```python
export ANTHROPIC_ADMIN_KEY=sk-ant-admin01-...
```
The distinct variable name keeps the Admin API key from overwriting a Compliance Access Key if you provision both. The cURL examples in this guide read the key from `$ANTHROPIC_COMPLIANCE_ACCESS_KEY`; substitute `$ANTHROPIC_ADMIN_KEY` when calling the [Activity Feed](manage-claude-compliance-activity-feed.md) with an Admin API key.
Admin API keys carry the `read:compliance_activities` scope only if the Compliance API was enabled for the organization at the time the key was created; see [Set up the Compliance API](#set-up-the-compliance-api). They cannot be granted any other Compliance API scope, so calls to any endpoint other than the Activity Feed return [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden).
For the same key's role in managing your Claude Console organization, see [Admin API](manage-claude-admin-api.md).
## Check your key's scopes
To inspect the scopes on a key you already have, use one of the following signals.
- **Key prefix.** `sk-ant-admin01-` is an Admin API key (carries `read:compliance_activities` only, subject to the enablement timing in the preceding section). `sk-ant-api01-` is a Compliance Access Key; its scopes are the subset you selected at creation.
- **Settings UI.** Open the **Keys** section in [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access), or the **Admin keys** section in [Claude Console \> Settings \> Admin keys](playground.md), and read the **Scopes** column for the key.
- **Error responses.** A call that exceeds the key's scopes returns a 403 with a message in the format `Missing required scopes. Got: [<scopes the key carries>] Needed one of: [<scopes the endpoint accepts>]` (`Needed: [...]` on the delete endpoints). See [Handle Compliance API errors](manage-claude-compliance-errors.md#403-forbidden) for the full error catalog.
```python
{
"error": {
"type": "permission_error",
"message": "Missing required scopes. Got: ['read:compliance_activities'] Needed one of: ['read:compliance_user_data', 'read:org_audit']"
}
}
```
## Manage and rotate keys
Delete a Compliance Access Key from the same **Keys** table where you created it: go to [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access). Delete an Admin API key from [Claude Console \> Settings \> Admin keys](playground.md).
Deleting a key takes effect on the next request: there is no grace period. Compliance Access Keys do not expire on their own.
To rotate a key without an outage:
1. Create a new key with the same scopes.
2. Update your integration to use the new key.
3. Verify the integration succeeds with the new key.
4. Delete the old key.
Pagination cursors stored before a rotation remain valid: cursors are scoped to the organization, not the key.
If a Compliance Access Key leaks, delete it immediately, audit the [Activity Feed](manage-claude-compliance-activity-feed.md) for `compliance_api_accessed` activities by the compromised key, and rotate any downstream credentials that the leaked key could reach. Pass `activity_types[]=compliance_api_accessed` to scope the query, then in your client, keep the activities whose `actor.type` is `api_actor` and whose `actor.api_key_id` matches the compromised key; see [Understand the Activity object](manage-claude-compliance-activity-feed.md#understand-the-activity-object) for the actor schema.
## Next steps
[Query the Activity Feed](manage-claude-compliance-activity-feed.md)
Read organization-wide activity events with any key that has `read:compliance_activities`.
[Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md)
Use a Compliance Access Key with `read:compliance_user_data` to retrieve claude.ai chats, files, and projects, and `delete:compliance_user_data` to delete them.
[Retrieve session transcripts](manage-claude-compliance-sessions.md)
Use a Compliance Access Key with `read:compliance_user_data` to list the sessions your users run in Claude apps and agents, such as Cowork and Claude Code, and retrieve their transcripts.
---
## Compliance API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-api
Path: 04-API-Reference/Other/manage-claude-compliance-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Compliance API
Copy page
Programmatic access to your organization's Claude activity, chats, files, projects, sessions in Claude apps, and users for compliance, audit, and governance.
Copy page
The Compliance API gives Claude Enterprise and Claude Console customers programmatic access to their organization's Activity Feed. For Claude Enterprise organizations, it also covers the directory of users, roles, and groups across every linked organization; the effective settings in force for each organization; the underlying chats, files, and projects in claude.ai organizations; and Cowork, Claude Code, Claude Science, Claude for Microsoft 365, and Claude in Chrome sessions. Security, legal, and compliance teams use it to audit activity, retrieve or delete content, and feed events into downstream tooling.
Two key types unlock the Compliance API. A **Compliance Access Key** (created in claude.ai) reaches every endpoint, and an **Admin API key** (created in Claude Console) reaches the Activity Feed only. See [Which key do you need?](manage-claude-compliance-api-access.md#which-key-do-you-need) for the full key-type comparison.
The following call returns the most recent activity event in your organization. Any key with the `read:compliance_activities` scope can make it. To create a key and grant it that scope, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
cURL
```python
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/activities?limit=1" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
A successful response returns a JSON object containing `data` (an array of `Activity` records), `has_more`, `first_id`, and `last_id`:
Response
```python
{
"data": [
{
"id": "activity_01XyDMpzjS89pFZXqSFUBDr6",
"created_at": "2026-04-10T08:09:10Z",
"organization_id": "org_01Wv6QeBcDfGhJkLmNpQrSt8",
"organization_uuid": "abcdef01-2345-6789-abcd-ef0123456789",
"actor": {
"type": "user_actor",
"email_address": "user@example.com",
"user_id": "user_01TuVwXyZaBcDeFgH2JkLmN4",
"ip_address": "192.0.2.34",
"user_agent": "Mozilla/5.0..."
},
"type": "claude_chat_created",
"claude_chat_id": "claude_chat_01XyDMpzjS89pFZXqSFUBDr6",
"claude_project_id": "claude_proj_01KGp4eZNug9ri4kE35RSppq"
}
],
"has_more": true,
"first_id": "activity_01XyDMpzjS89pFZXqSFUBDr6",
"last_id": "activity_01XyDMpzjS89pFZXqSFUBDr6"
}
```
------------------------------------------------------------------------
## How the Compliance API works
Every endpoint lives under `/v1/compliance/*` on `https://api.anthropic.com`, authenticates through the `x-api-key` header, and takes the [`anthropic-version`](../Endpoints/versioning.md) header on every request. To provision a key, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
The Activity Feed (`GET /v1/compliance/activities`) is available to any key that carries the `read:compliance_activities` scope; see [Query the Activity Feed](manage-claude-compliance-activity-feed.md) for filters, pagination, and the full `Activity` object. The remaining endpoints require a Compliance Access Key carrying the relevant scope.
A Claude Enterprise tenant has one parent organization (the top-level container that centralizes identity) with linked organizations of two kinds: claude.ai organizations, where users chat and store content, and Claude Console organizations, where users manage Claude API workloads. For a key that covers the parent organization, the directory endpoints (organizations, users, roles, and groups) return data from every linked organization of either kind. The content endpoints (chats, files, projects, project attachments, and sessions) serve Claude Enterprise data only. The chat, file, and project endpoints return claude.ai chats, files, and projects. The session endpoints return transcripts of Cowork, Claude Code, Claude Science, Claude for Microsoft 365, and Claude in Chrome sessions on users' machines (local sessions), captured while users are signed in with their Claude Enterprise account. They also return transcripts of Cowork sessions started on claude.ai web or mobile, which run in the cloud in Anthropic-managed environments (remote sessions). A standalone Claude Console organization (one with no parent organization) is not part of a Claude Enterprise tenant; it uses Admin API keys and can query the Activity Feed only.
All `/v1/compliance/*` endpoints share a rate limit of 600 requests per minute per parent organization (for a standalone Claude Console organization, per organization). The local session endpoints count only against that shared limit, and the remote session endpoints carry a second request budget on top. See [429 Too Many Requests](manage-claude-compliance-errors.md#429-too-many-requests) for the response headers and retry contract.
------------------------------------------------------------------------
## Versioning
Send the `anthropic-version` header on every request; see [API versions](../Endpoints/versioning.md) for the available versions.
------------------------------------------------------------------------
## Compliance API versus related features
A few adjacent features overlap with the Compliance API; here is how to choose.
### Export audit logs
The audit log export is a separate feature in [claude.ai \> Organization settings \> Data and privacy](https://claude.ai/admin-settings/data-privacy-controls) that lets owners and primary owners download a CSV of organization events. It's significantly narrower than the Compliance API: a capped lookback window, CSV download only, and no access to chat, file, or project content. Standardize on the Compliance API for ongoing programmatic use.
### Analytics API
Anthropic provides two analytics APIs: the Claude Enterprise Analytics API and the [Claude Code Analytics API](manage-claude-claude-code-analytics-api.md). Both return aggregated usage and cost figures for IT, FinOps, and platform teams, whereas the Compliance API returns per-event records for security, legal, and compliance teams. The two API families answer different questions, use different keys, and are provisioned separately.
### OpenTelemetry logging
[Cowork's OpenTelemetry logging](../../15-Claude-AI-Features/monitor-claude-cowork-activity-with-opentelemetry.md) and [Claude Code monitoring](../../13-Enterprise-Admin/monitoring-usage.md) stream per-event telemetry, including token, cost, and host metadata, to a collector you run as activity happens, whereas the Compliance API returns retained per-session transcripts from Anthropic on request and works with your existing Compliance Access Key. OpenTelemetry logging can also capture prompts and responses, but Anthropic recommends the Compliance API for retrieving the content of Cowork and Claude Code sessions. For a table comparing local sessions, remote sessions, and OpenTelemetry logging, see the introduction to [Retrieve session transcripts](manage-claude-compliance-sessions.md).
### Inference hooks
[Inference hooks](manage-claude-inference-hooks.md) (beta) act inline: your organization's AI security server receives each governed prompt before inference and can deny it in real time, whereas the Compliance API retrieves records after the fact and returns richer data, such as organization settings and full non-text files.
------------------------------------------------------------------------
## In this section
[Set up the Compliance API](manage-claude-compliance-api-access.md)
Enable the Compliance API for your organization, then create a Compliance Access Key (with scoped permissions) or an Admin API key, and learn which to use.
[Query the Activity Feed](manage-claude-compliance-activity-feed.md)
Retrieve, filter, and paginate the shared Activity Feed. Supported by both key types.
[Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md)
Read chat content, files, and project attachments; delete chats, files, and projects on demand. Compliance Access Key required.
[Retrieve session transcripts](manage-claude-compliance-sessions.md)
List the sessions your users run in Claude apps and agents, such as Cowork and Claude Code, and retrieve their transcripts. Compliance Access Key required.
[List organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)
Enumerate linked organizations, members, roles, and directory groups, and read each organization's effective settings.
[Design your compliance integration](manage-claude-compliance-integration-patterns.md)
Choose a feed-consumption pattern, plan SIEM correlation, and decide your retention approach.
[Handle Compliance API errors](manage-claude-compliance-errors.md)
Common 400, 401, 403, 404, 409, 429, and 5xx responses from the Compliance API, with the cause and fix for each.
[API reference](../Endpoints/http-compliance.md)
Endpoint paths, parameters, and response schemas for every Compliance API call.
[Compliance API FAQ](manage-claude-compliance-faq.md)
Answers to common key, scope, availability, and integration questions.
---
## Retrieve and delete chats, files, and projects - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-content-data
Path: 04-API-Reference/Other/manage-claude-compliance-content-data.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-content-data)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Retrieve and delete chats, files, and projects
Copy page
Access chat content, file attachments, and projects for claude.ai organizations through the Compliance API.
Copy page
The endpoints on this page are available only to Claude Enterprise organizations. They retrieve and delete claude.ai chats, files, and projects; transcripts of sessions in apps such as Cowork and Claude Code are covered on [Retrieve session transcripts](manage-claude-compliance-sessions.md). See [Set up the Compliance API](manage-claude-compliance-api-access.md).
**Required scope:** `read:compliance_user_data` on the Compliance Access Key. The delete endpoints also require `delete:compliance_user_data`.
**Prerequisite:** None for listing chats organization-wide. To filter the chat list to specific users, you need user IDs from [List organization users](manage-claude-compliance-org-data.md#list-organization-users). The other endpoints on this page take resource IDs directly.
The endpoints on this page expose Claude Enterprise chat content, file uploads, projects, and project attachments to compliance reviewers. They support eDiscovery (electronic discovery) exports, data loss prevention (DLP) enforcement, and account-deletion responses. Chat, file, and project content is retained for as long as your organization's retention policy allows. When a user deletes a chat in claude.ai, its message content, attached files, tool-generated files, and artifacts are deleted with it. The Compliance API still lists the chat, with `deleted_at` populated and an empty `name`, and returns its messages without their content. Chats that have been hard-deleted (through the Compliance API itself, or after the organization's retention window expires) are not retrievable.
Both scopes are granted only on Compliance Access Keys (`sk-ant-api01-...`) created in claude.ai; see [Set up the Compliance API](manage-claude-compliance-api-access.md) to provision one. The `read:compliance_user_data` scope covers retrieval; `delete:compliance_user_data` is required only for the delete endpoints. The chat, file, project, and attachment endpoints are not available to Admin API keys (`sk-ant-admin01-...`); calls authenticated with an Admin API key return [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden).
Endpoints on this page paginate two ways; see [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results) for the full reference. Each section notes which scheme applies.
## Retrieve chats and messages
Use [List chats](../Admin/compliance-apps-chats-list.md) to page through chat metadata, then [Get chat messages](../Admin/compliance-apps-chats-messages-list.md) to fetch the full message content of one chat.
The chat list endpoint defaults to organization-wide scope: leave off `user_ids[]` to include every chat under your parent organization. Add `order_by=updated_at` to sort by last update time. This combination is the recommended way to export chats and keep an export current, because one paginated loop picks up new chats, chats with new messages, and chats deleted in claude.ai for every user without enumerating users first. The following request lists chats updated since a given date.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/apps/chats" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "order_by=updated_at" \
--data-urlencode "updated_at.gte=2025-06-01T00:00:00Z" \
--data-urlencode "limit=100"
```
Response
```python
{
"data": [
{
"id": "claude_chat_01H5CWunD7RpVJ5bHa8RCkja",
"name": "Product Requirements Discussion",
"created_at": "2026-04-10T08:09:10Z",
"updated_at": "2026-04-10T09:10:11Z",
"deleted_at": null,
"href": "https://claude.ai/chat/abcdef01-2345-6789-abcd-ef0123456789",
"model": "claude-opus-5-5",
"organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"project_id": "claude_proj_01KGp4eZNug9ri4kE35RSppq",
"user": {
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email_address": "user@example.com"
}
}
],
"has_more": true,
"first_id": "eyJrIjogInVwZGF0ZWRfYXQiLCAidCI6ICIyMDI2LTA0LTEwVDA5OjEwOjExKzAwOjAwIiwgImlkIjogImFiY2RlZjAxLS4uLiJ9",
"last_id": "eyJrIjogInVwZGF0ZWRfYXQiLCAidCI6ICIyMDI2LTA0LTEwVDA5OjEwOjExKzAwOjAwIiwgImlkIjogImFiY2RlZjAxLS4uLiJ9"
}
```
Results sort ascending by the `order_by` field, oldest first, with ties broken by `id`. Pagination uses the standard `first_id`/`last_id`/`has_more` cursor fields described in [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results). To walk forward toward newer chats, pass the response's `last_id` back as `after_id` on the next request.
That forward walk is also how you keep an export current across runs: persist the final page's `last_id` and resume from it as `after_id` on the next run. Because the list is ordered by `updated_at`, a chat reappears ahead of your saved cursor when it receives a new message, is moved into or out of a project, or is deleted in claude.ai. Each incremental run therefore returns both brand-new chats and older chats that have since changed in one of those ways. Other edits, such as a rename, are not guaranteed to make a chat reappear. Process results idempotently, keyed by chat `id`, to handle those reappearances. A chat that comes back with `deleted_at` populated has no content left to fetch, so treat it as deleted rather than updated.
A few constraints apply to these organization-wide queries. Cursors are opaque and bound to the sort key, so an `after_id` issued under one `order_by` value is rejected with a 400 error under the other. Time-filter bounds must match the sort key too: pair `updated_at.*` bounds with `order_by=updated_at`, and `created_at.*` bounds with the default `order_by=created_at`. Backward pagination with `before_id` is not supported, and the `project_ids[]` filter is not available. See [List chats](../Admin/compliance-apps-chats-list.md) for the full filter reference.
To scope the list to specific users instead (for example, a legal hold on named custodians), pass 1–10 `user_ids[]` values. Obtain the IDs from [List organization users](manage-claude-compliance-org-data.md#list-organization-users). User-filtered queries always sort by `created_at` (passing `order_by=updated_at` returns a 400 error) and support both `after_id` and `before_id`. Filtering by `project_ids[]` is only available in this user-filtered form. Combining `user_ids[]` with any `updated_at.*` bound is deprecated and will be rejected with a 400 error after 2026-09-22; to keep a custodian set current by update time, run the org-wide `order_by=updated_at` walk without `user_ids[]` and select the custodians' chats from its results, and keep the user-filtered listing for `created_at`-ordered exports.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/apps/chats" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "user_ids[]=user_01XyDMpzjS89pFZXqSFUBDr6" \
--data-urlencode "created_at.gte=2025-06-01T00:00:00Z" \
--data-urlencode "limit=100"
```
The list response carries chat metadata only. To pull the actual chat content, attached files, and inline artifacts (structured documents Claude generates inside a chat), follow up with the messages endpoint for each chat ID:
cURL
```python
chat_id="claude_chat_01H5CWunD7RpVJ5bHa8RCkja"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/apps/chats/$chat_id/messages" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
The messages endpoint returns the chat's metadata plus a `chat_messages` array sorted by `created_at`. When `limit` is omitted, the full message set is returned in one response; pass `limit`, `after_id`, or `before_id` to page through very long chats. The endpoint also accepts `created_at.*` and `updated_at.*` range bounds (`gt`, `gte`, `lt`, `lte`) and an `order` parameter (`asc` or `desc`). See [Get chat messages](../Admin/compliance-apps-chats-messages-list.md) for the full parameter list. For user messages, `created_at` is when the message was sent; for assistant messages, it is when Claude finished generating the message. Each message carries its text content and, when present, any uploaded files (typically on user messages), any tool-generated files, and any artifacts the assistant produced or updated (typically on assistant messages):
Response
```python
{
"id": "claude_chat_01H5CWunD7RpVJ5bHa8RCkja",
"name": "Product Requirements Discussion",
"created_at": "2026-04-10T08:09:10Z",
"updated_at": "2026-04-10T09:10:11Z",
"deleted_at": null,
"href": "https://claude.ai/chat/abcdef01-2345-6789-abcd-ef0123456789",
"model": "claude-opus-5-5",
"organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"project_id": "claude_proj_01KGp4eZNug9ri4kE35RSppq",
"user": {
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email_address": "user@example.com"
},
"chat_messages": [
{
"id": "claude_chat_msg_01VnBPkLmtj7YdW5QrXKEA8c",
"role": "user",
"created_at": "2026-04-10T08:09:10Z",
"content": [
{
"type": "text",
"text": "Can you help me draft requirements for our new dashboard feature?"
}
],
"files": [
{
"id": "claude_file_01UaT9wBcDfGhJkLmNpQrSv7",
"filename": "dashboard_mockup_v1.pdf",
"mime_type": "application/pdf",
"size_bytes": 482133,
"md5": "56367e4d2705cc9c025ad07424e944f0",
"created_at": "2026-04-10T08:09:10Z"
}
]
},
{
"id": "claude_chat_msg_01M8tFcHwbQ2kY6NpEjRZv4D",
"role": "assistant",
"created_at": "2026-04-10T08:09:11Z",
"content": [
{
"type": "text",
"text": "I'd be happy to help you draft requirements for your dashboard feature..."
}
],
"generated_files": [
{
"id": "claude_gen_file_01TbR8wAcCeFhJkLnPqStUvX",
"filename": "requirements_summary.csv",
"mime_type": "text/csv",
"size_bytes": 2048,
"md5": "89968669461d95416549937168269d6b"
}
],
"artifacts": [
{
"id": "claude_artifact_01HqRsTuVwXyZa2BcDeFgH4J",
"version_id": "claude_artifact_version_01KmNpQrSt3UvWxYz5AbCdEfG",
"title": "Dashboard Requirements Draft",
"artifact_type": "text/markdown"
}
]
}
],
"has_more": false,
"first_id": "eyJtc2dfdXVpZCI6ICIwZjcwYjA2Ni0uLi4ifQ==",
"last_id": "eyJtc2dfdXVpZCI6ICJhNGUwYjE3Mi0uLi4ifQ=="
}
```
`files`, `generated_files`, and `artifacts` can each be `null` on a given message. `files` are the files and text attachments (for example, PDFs, images, spreadsheets, documents, and pasted text) the user attached to the message, as claude.ai stored them. `generated_files` are binary files the assistant created during the conversation through tool use (for example, PDFs, spreadsheets, or slide decks). `artifacts` are versioned documents (for example, code or markdown) the assistant generated or updated in its response; an artifact can be revised across multiple assistant turns in the same chat, and each revision appears as a new `version_id` under the same artifact `id`. Pass each entry's `id` (or `version_id` for artifacts) to the matching content endpoint in [Retrieve files and artifacts](#retrieve-files-and-artifacts) to download it.
## Retrieve files and artifacts
Files and artifacts are downloaded by ID, not listed independently. The IDs come from the chat messages endpoint in [Retrieve chats and messages](#retrieve-chats-and-messages) (the `files`, `generated_files`, and `artifacts` arrays on each message) or, for project-level uploads, from the [project attachments endpoint](#retrieve-projects-and-attachments).
Pick the endpoint that matches your ID type and the data you need. The same file content endpoint serves both chat files and project files.
| You have | You want | Use this endpoint |
|--------------------------------|-----------------------------------------|-------------------------------------------------------------------------------------------------|
| `claude_file_*` ID | The file's content | [Download file content](../Admin/compliance-apps-chats-files-download.md) |
| `claude_file_*` ID | The file's metadata only | [Get file metadata](../Admin/compliance-apps-chats-files-retrieve.md) |
| `claude_gen_file_*` ID | A tool-generated file's binary content | [Download a Claude-generated file](../Admin/compliance-apps-chats-generated-files-download.md) |
| `claude_gen_file_*` ID | A tool-generated file's metadata only | [Get generated-file metadata](../Admin/compliance-apps-chats-generated-files-retrieve.md) |
| `claude_artifact_version_*` ID | One artifact version's text | [Download artifact content](../Admin/compliance-apps-artifacts-download.md) |
| `claude_artifact_version_*` ID | The artifact version's metadata only | [Get artifact metadata](../Admin/compliance-apps-artifacts-retrieve.md) |
| `claude_proj_doc_*` ID | A project document's plain-text content | [Get project document content](../Admin/compliance-apps-projects-documents-retrieve.md) |
| `claude_proj_doc_*` ID | A project document's metadata only | [Get project document metadata](../Admin/compliance-apps-projects-documents-metadata.md) |
The file content endpoint streams the content that claude.ai stored for the file as a chunked binary response. That content is not always identical to the file the user uploaded. Images can be served as a processed copy rather than the uploaded bytes. Some documents attached to chats (for example, Word files, PowerPoint files, and some PDFs) are stored as the text claude.ai extracted from them. For these documents, the endpoint returns the extracted text under the original file name, and the original document is not available through the Compliance API. The `size_bytes` and `md5` fields describe the stored content rather than the uploaded file. The file name and `mime_type` can still name the uploaded document's format. Identify a file's format from the returned bytes, not from its name or declared type.
The response carries these headers:
- `Content-Disposition: attachment; filename*=utf-8''<percent-encoded filename>` carries the original upload file name in RFC 5987 extended form. The extended form is used for every file name, not only non-ASCII ones.
- `Content-Type` carries the MIME type recorded for the stored content, which for a document stored as extracted text can still name the original document format.
- `Content-MD5` carries the MD5 digest of the served bytes, base64-encoded as specified in RFC 1864.
- `Transfer-Encoding: chunked` is always set.
cURL
```python
file_id="claude_file_01UaT9wBcDfGhJkLmNpQrSv7"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/apps/chats/files/$file_id/content" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--output "dashboard_mockup_v1.pdf"
```
In curl, the `--remote-header-name` (`-J`) option, which normally saves a download under the `Content-Disposition` file name, does not read the `filename*` form, so name the saved file yourself with `--output`. In a script, take the name from the file's `filename` field in the chat messages or [Get file metadata](../Admin/compliance-apps-chats-files-retrieve.md) response, or decode `filename*`. Either way, it is the name the user gave the upload, so treat it as untrusted before using it as an output path: keep only the base name, allow only characters that are safe on your filesystem, and refuse names that begin with `-` or `.`.
Unlike the file content endpoint, the artifact content endpoint returns a JSON object. Pass the `version_id` from one of the entries in an assistant message's `artifacts` array, not the artifact's stable `id`; each new version of an artifact has its own `version_id`. The response's `content` field holds exactly that version's text, and its `title` and `artifact_type` fields describe the artifact. [Get artifact metadata](../Admin/compliance-apps-artifacts-retrieve.md) computes `size_bytes` and `md5` over the UTF-8 encoding of that text, so compare them with the `content` value rather than the whole response body.
## Retrieve projects and attachments
Projects bundle related chats together with custom instructions, knowledge base content, and attached files or text documents. The Compliance API exposes project metadata, project details, and the list of attachments belonging to a project.
- [List projects](../Admin/compliance-apps-projects-list.md)
- [Get project details](../Admin/compliance-apps-projects-retrieve.md)
- [List project attachments](../Admin/compliance-apps-projects-attachments-list.md)
- [Get project document content](../Admin/compliance-apps-projects-documents-retrieve.md)
Project results are sorted by creation date ascending. Attachment results are sorted by `created_at` ascending, with ties broken by `id`. Project list and attachment list responses paginate with an opaque `next_page` page token instead of the `first_id`/`last_id` cursors used by chats and the Activity Feed. Pass the token back as the `page` query parameter on the next request.
### Project files versus project documents
A project attachment is one of two distinct shapes, identified by the `type` discriminator on each entry:
Entries with `type` of `project_file` are file uploads (PDFs, images, spreadsheets) whose IDs start with `claude_file_`; download them with [Download file content](../Admin/compliance-apps-chats-files-download.md). Entries with `type` of `project_doc` are plain-text documents (always `text/plain`) whose IDs start with `claude_proj_doc_`, including documents such as Word files that claude.ai converts to text when they are added to a project; fetch them with [Get project document content](../Admin/compliance-apps-projects-documents-retrieve.md).
A consumer that walks the attachment list must branch on `type` and call the matching content endpoint for each entry. The following request lists one page of attachments; paginate by passing `next_page` back as the `page` parameter until `has_more` is `false`.
cURL
```python
project_id="claude_proj_01KGp4eZNug9ri4kE35RSppq"
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/apps/projects/$project_id/attachments" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"id": "claude_file_01UaT9wBcDfGhJkLmNpQrSv7",
"created_at": "2026-04-10T08:09:10Z",
"filename": "dashboard_mockup_v1.pdf",
"mime_type": "application/pdf",
"size_bytes": 482133,
"md5": "56367e4d2705cc9c025ad07424e944f0",
"type": "project_file"
},
{
"id": "claude_proj_doc_01YnT8sBcWvUtXzQpMkRfDgH",
"created_at": "2026-04-10T08:09:11Z",
"filename": "requirements.md",
"mime_type": "text/plain",
"type": "project_doc"
}
],
"has_more": false,
"next_page": null
}
```
## Delete content
Every successful delete is permanent and immediate. There is no recovery window.
The Compliance API exposes hard-delete endpoints for chats, files, project documents, and entire projects. A hard-deleted chat cannot be restored, and it stops appearing in list responses afterward.
- [Delete chat](../Admin/compliance-apps-chats-delete.md): also removes the chat's messages and any files attached to those messages.
- [Delete file](../Admin/compliance-apps-chats-files-delete.md): handles both chat files and project files.
- [Delete project document](../Admin/compliance-apps-projects-documents-delete.md): removes a single project document by ID.
- [Delete project](../Admin/compliance-apps-projects-delete.md): see [Detach chats before deleting a project](#detach-chats-before-deleting-a-project).
All four endpoints require the `delete:compliance_user_data` scope, which is granted separately from the read scope when the Compliance Access Key is created.
The following request deletes one chat. The same pattern applies to the other delete endpoints; only the URL changes.
cURL
```python
# WARNING: This operation PERMANENTLY deletes the chat, all of its messages,
# and any attached files. Deletion is immediate and cannot be undone. It
# requires the `delete:compliance_user_data` scope, which is granted separately
# from `read:compliance_user_data` when the Compliance Access Key is created.
# Ensure you have explicit authorization before running this.
chat_id="claude_chat_01H5CWunD7RpVJ5bHa8RCkja"
curl --fail-with-body -sS -X DELETE \
"https://api.anthropic.com/v1/compliance/apps/chats/$chat_id" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Response
```python
{
"id": "claude_chat_01H5CWunD7RpVJ5bHa8RCkja",
"type": "claude_chat_deleted"
}
```
Each successful delete returns a small confirmation envelope with an `id` and a `type` discriminator. The chat endpoint returns `claude_chat_deleted`; check the `type` field before treating the delete as confirmed. See the response schema on each delete endpoint's [API reference](../Admin/compliance-apps.md) page for the exact `type` value the other endpoints return.
### Detach chats before deleting a project
A project cannot be deleted while any chats remain attached to it. The API returns 409 with this body:
```python
{
"error": {
"type": "invalid_request_error",
"message": "The \"claude_proj_01KGp4eZNug9ri4kE35RSppq\" project cannot be deleted as it has chats attached to it. Delete or detach all chats, and try deleting the project again."
}
}
```
To resolve, list the project's chats with `GET /v1/compliance/apps/chats?user_ids[]={user_id}&project_ids[]={project_id}` (the `project_ids[]` filter requires at least one `user_ids[]` value; enumerate IDs through [List organization users](manage-claude-compliance-org-data.md#list-organization-users)), delete each one with `DELETE /v1/compliance/apps/chats/{claude_chat_id}` (or move it out of the project from claude.ai), and then retry the project delete.
## Next steps
[API reference](../Admin/compliance-apps.md)
The full request and response schema for every chat, file, project, and artifact endpoint.
[Retrieve session transcripts](manage-claude-compliance-sessions.md)
List the sessions your users run in Claude apps and agents, such as Cowork and Claude Code, and retrieve their transcripts.
[List organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)
Enumerate the people and teams associated with the chats and projects on this page.
---
## Handle Compliance API errors - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-errors
Path: 04-API-Reference/Other/manage-claude-compliance-errors.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-errors)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Handle Compliance API errors
Copy page
Compliance API error responses by HTTP status code, with the cause and fix for each.
Copy page
To enable the Compliance API, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
This page lists common Compliance API error responses by HTTP status code, with the cause and the fix for each.
The Compliance API returns errors in the standard [Anthropic error format](../Endpoints/errors.md): a non-2xx status code, a `request-id` response header, and a JSON body with an `error` object containing `type` and `message`. Include the `request-id` header value when you escalate to support.
```python
{
"error": {
"type": "permission_error",
"message": "Missing required scopes. Got: ['read:compliance_activities'] Needed one of: ['read:compliance_user_data', 'read:org_audit']"
}
}
```
On this page, local sessions run on users' machines and remote sessions run in the cloud; see [Retrieve session transcripts](manage-claude-compliance-sessions.md).
Match on the HTTP status code and `error.type`, not on the message string. Messages are stable enough to copy into runbooks but might be reworded over time; the status codes and type values are part of the API contract. A few responses that share a status code and type are told apart by their message; each is called out where it applies.
The following table tells you at a glance whether to retry. Each section that follows shows the verbatim error body and the fix.
| Status | Retry? | When |
|---------------------------------------------------------|-----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [400 Bad Request](#400-bad-request) | No | Fix the request, or enable the Compliance API if the message says it is not enabled, then resend. |
| [401 Unauthorized](#401-unauthorized) | No | The key is not recognized, has been deactivated, or has expired; re-enable or replace it, then resend. |
| [403 Forbidden](#403-forbidden) | No | Add the missing scope or use the right key type, then resend. |
| [404 Not Found](#404-not-found) | Usually no | A message that names a resource means it was deleted or never existed; remove it from your queue. The bare message `Not found` means the request did not authenticate (or the path does not exist), not that a resource is gone; see [Request not authenticated](#request-not-authenticated). The session endpoints add two more cases: on the local session endpoints, the message `Local sessions are not available.` (returned on every call, including the list) means the endpoints are currently unavailable to your parent organization, not that a session is gone; keep your queued IDs and see [Local session not found](#local-session-not-found). A remote session still in `pending` status 404s on its messages endpoint until it starts; see [Remote session not found](#remote-session-not-found). |
| [409 Conflict](#409-conflict) | No | The request conflicts with the resource's current state; resolve the conflict (such as detaching child resources), then retry. |
| [429 Too Many Requests](#429-too-many-requests) | Yes, after `retry-after` | Wait the seconds in `retry-after`, then retry; do not advance your cursor. |
| [500 Internal Server Error](#500-internal-server-error) | Depends on `x-should-retry` | Check the `x-should-retry` response header before retrying. |
| [502, 503, 504, 529](#500-internal-server-error) | Yes, with backoff | Transient; retry with exponential backoff. Exception: some local session 503s are not transient. See [Local sessions temporarily unavailable](#local-sessions-temporarily-unavailable). |
## 400 Bad Request
The request was syntactically valid, but the server rejected a parameter or the Compliance API is not enabled for the organization. Fix the cause named in the message and resend.
### Compliance API not enabled
**Type:** `invalid_request_error`
``` block
Compliance API is not enabled for this organization
```
**Cause:** The key is valid, but the Compliance API is not enabled for the organization or parent organization the key belongs to. Every endpoint returns this response until the API is enabled, and again if an administrator turns the API off.
**Fix:** Enable the Compliance API by following [Set up the Compliance API](manage-claude-compliance-api-access.md#set-up-the-compliance-api), then resend the request.
### Unknown query parameter
**Type:** `invalid_request_error`
``` block
Unknown query parameter: 'created_at[gte]'. Did you mean 'created_at.gte'?
```
**Cause:** The request included a query parameter that the endpoint does not define; the Compliance API rejects unrecognized parameters rather than ignoring them. The message names the parameter and, for near misses such as bracket notation in place of a dot or a missing `[]` suffix, suggests the defined name.
**Fix:** Use the parameter names shown on the endpoint's [Compliance API reference](../Endpoints/http-compliance.md) page. Range filters use dot notation (for example, `created_at.gte`), array filters take a `[]` suffix (for example, `activity_types[]`), and the pagination parameters are `after_id`, `before_id`, or `page`, depending on the endpoint.
### Invalid parameter value
**Type:** `invalid_request_error`
``` block
limit: Input should be less than or equal to 1000
```
``` block
created_at.gte: Input should be a valid datetime or date, invalid character in year
```
``` block
activity_types[].0: Input is not one of the permitted values.
```
**Cause:** A query parameter's value failed validation. The message starts with the parameter name (followed by the element's position for an array parameter), then states the constraint that failed. Three common cases are shown: a `limit` above the endpoint's maximum (the number in the message is that endpoint's maximum), a `created_at.*` or `updated_at.*` value that cannot be parsed as a date or timestamp, and an `activity_types[]` value that is not a supported activity type.
**Fix:** Correct the parameter named in the message. Each list endpoint has its own `limit` range; see the parameter constraints on the corresponding [Compliance API reference](../Endpoints/http-compliance.md) page. Send timestamps in RFC 3339 format with an explicit UTC offset, for example, `2024-03-01T00:00:00Z` or `2024-03-01T00:00:00+00:00`; the local session list rejects a timestamp without an offset (`created_at.gte: Input should have timezone info`). For the supported `activity_types[]` values, see [Query compliance activities](../Admin/compliance-activities-list.md).
The local session list (`GET /v1/compliance/apps/sessions/local`) also returns a 400 `invalid_request_error` when both time bounds are supplied and `created_at.lt` is not strictly after `created_at.gte`. The body reads:
``` block
created_at.lt must be strictly after created_at.gte.
```
Send a `created_at.lt` later than `created_at.gte`, or omit one of the bounds.
The session transcript endpoints (`GET /v1/compliance/apps/sessions/local/{session_id}/messages` and `GET /v1/compliance/apps/sessions/remote/{session_id}/messages`) validate their truncation parameters the same way: `tool_use_input_max_bytes` and `tool_result_max_bytes` each accept a positive byte count or `-1` (the server maximum), so a value such as `0` returns the same 400 `invalid_request_error`.
### Invalid pagination cursor
**Type:** `invalid_request_error`
``` block
Invalid activity_id format: 'activity_invalid123'
```
``` block
Invalid pagination cursor for 'after_id'
```
**Cause:** A pagination cursor could not be decoded. On the Activity Feed, an `after_id` or `before_id` value that is neither a cursor the API issued nor a well-formed activity ID returns the first body, which echoes the value sent. On the chat and chat message endpoints, an `after_id` or `before_id` value that cannot be decoded returns the second body, which names the parameter.
**Fix:** Treat pagination cursors as opaque strings. Always copy the `first_id` or `last_id` value returned by the previous page; stop when `has_more` is `false`. Do not construct cursors from object IDs.
The directory, project, and session endpoints (organizations, users, roles, role permissions, groups, group members, projects, project attachments, local and remote sessions, and session messages) paginate with an opaque `page` token rather than `after_id` and `before_id`. The same advice applies: pass the `next_page` value from the previous response unchanged, and stop when `has_more` is `false` (or, on the session endpoints, which return no `has_more`, when `next_page` is `null`). A malformed `page` token returns the same 400 `invalid_request_error` as a malformed `after_id` or `before_id`, with a message specific to the endpoint.
The two paginated local session endpoints (the list and the messages endpoint) return the following 400 `invalid_request_error` for any `page` value they cannot decode, for example, a token that was truncated or altered after you stored it, or one issued by a different endpoint or under a different parent organization. On the local session messages endpoint (`GET /v1/compliance/apps/sessions/local/{session_id}/messages`), each `page` cursor is also bound to the session and `order` it was issued for, so a cursor issued for a different session or sort order returns the same body:
``` block
The page parameter is not a valid cursor for this request.
```
Cursors on the messages endpoint also expire 24 hours after the walk (one pass through the pages) began. An expired cursor returns:
``` block
The page cursor has expired. Restart the walk without a page parameter; results will reflect the current retention boundary.
```
For the first body, resend the unmodified `next_page` value from the previous response to the endpoint and session that issued it. For an expired cursor, restart without a `page` parameter; the new walk reflects the retention boundary in effect when it starts, so messages that aged out of the retention period in the meantime are no longer returned (see [Retrieve a local session transcript](manage-claude-compliance-sessions.md#retrieve-a-local-session-transcript)).
## 401 Unauthorized
The request carried a Compliance Access Key (`sk-ant-api01-...`) or Admin API key (`sk-ant-admin01-...`) that does not authenticate. A request that carries no key, or a key of another type, returns [404 Not Found](#request-not-authenticated) instead on every endpoint except organization settings, and a valid key with the wrong scopes returns [403 Forbidden](#403-forbidden).
### Invalid, deactivated, or expired API key
**Type:** `authentication_error`
``` block
API key is invalid.
```
``` block
API key has been deactivated.
```
``` block
API key has expired.
```
**Cause:** `API key is invalid.` means the value sent does not match a usable key, for example because it was truncated or altered when it was stored. `API key has been deactivated.` means the key has been disabled or deleted. `API key has expired.` means an Admin API key's expiration date has passed; Compliance Access Keys are not created with one.
**Fix:** For `API key is invalid.`, compare the value your client sends against the secret you stored when the key was created; the full secret is displayed only once, so if your stored copy is wrong, create a new key. For `API key has been deactivated.`, re-enable the key if it was only disabled, in [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) for a Compliance Access Key or [Claude Console \> Settings \> Admin keys](playground.md) for an Admin API key; a deleted key cannot be restored. For a deleted or expired key, create a new key and update your integration to use it, as described in [Manage and rotate keys](manage-claude-compliance-api-access.md#manage-and-rotate-keys).
## 403 Forbidden
The key in `x-api-key` is valid but does not carry a scope the endpoint accepts. The verbatim message lists the scopes the key carries (`Got:`) and the scopes the endpoint accepts (`Needed one of:` on read endpoints, where any one listed scope is sufficient, or `Needed:` on delete endpoints), so you can confirm what the key carries without rechecking Claude Console or claude.ai. On read endpoints the accepted list also includes `read:org_audit`, a read-only audit scope that covers every Compliance API read endpoint; see [Choose scopes for a Claude Enterprise key](manage-claude-admin-api-keys.md#choose-scopes-for-a-claude-enterprise-key). Compliance Access Key scopes are immutable after creation, so each insufficient-scope fix directs you to create a new key rather than edit the existing one. A standalone Claude Console organization (one with no parent organization) cannot create a Compliance Access Key, so fixes that require one do not apply to it; it can query the Activity Feed only.
### Insufficient scope: Activity Feed
**Type:** `permission_error`
``` block
Missing required scopes. Got: ['read:compliance_user_data'] Needed one of: ['read:compliance_activities', 'read:org_audit']
```
**Cause:** A key without `read:compliance_activities` was used to call `GET /v1/compliance/activities`. There are two common paths to this error:
- A Compliance Access Key (`sk-ant-api01-...`) was created without the `read:compliance_activities` scope.
- A Claude Console Admin API key (`sk-ant-admin01-...`) was created while the Compliance API was not enabled for the organization. Keys created while the Compliance API was not enabled do not carry the scope; see [Set up the Compliance API](manage-claude-compliance-api-access.md#set-up-the-compliance-api).
**Fix:** Compliance Access Key scopes are immutable after creation. Create a new key that includes `read:compliance_activities`, or use a Claude Console Admin API key. See [Which key do you need?](manage-claude-compliance-api-access.md#which-key-do-you-need) for the conditions under which an Admin API key carries this scope.
### Insufficient scope: organization data
**Type:** `permission_error`
``` block
Missing required scopes. Got: ['read:compliance_user_data'] Needed one of: ['read:compliance_org_data', 'read:org_audit']
```
**Cause:** A key without `read:compliance_org_data` was used to call an organizations, roles, groups, or effective-settings endpoint. There are two common paths to this error:
- A Compliance Access Key (`sk-ant-api01-...`) was created without the `read:compliance_org_data` scope.
- A Claude Console Admin API key (`sk-ant-admin01-...`) was used. Admin API keys carry only `read:compliance_activities` and cannot read organization metadata.
**Fix:** [Create a new Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) with `read:compliance_org_data` selected. Admin API keys cannot read organization metadata; the Compliance Access Key is required.
### Retired scope: organization settings
**Type:** `permission_error`
``` block
Missing required scopes. Got: ['read:compliance_org_settings'] Needed one of: ['read:compliance_org_data', 'read:org_audit']
```
**Cause:** The `read:compliance_org_settings` scope was retired on June 30, 2026. `GET /v1/compliance/organizations/{organization_id}/settings` now requires `read:compliance_org_data`, the same scope as the other organization endpoints, and the retired scope no longer authorizes anything. A Compliance Access Key that carries only `read:compliance_org_settings` returns this error on every call to the settings endpoint, even though the key worked before the retirement. The retired scope can no longer be selected or granted when creating a key.
**Fix:** Compliance Access Key scopes are immutable after creation. [Create a new Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) with `read:compliance_org_data` selected, update your integration to use it, then delete the old key. A key that already carries `read:compliance_org_data` is unaffected by the retirement.
### Insufficient scope: user data
**Type:** `permission_error`
``` block
Missing required scopes. Got: ['read:compliance_activities'] Needed one of: ['read:compliance_user_data', 'read:org_audit']
```
**Cause:** A key without `read:compliance_user_data` was used to call a chats, messages, files, projects, sessions, organization users, or group-members endpoint. There are two common paths to this error:
- A Compliance Access Key (`sk-ant-api01-...`) was created without the `read:compliance_user_data` scope.
- A Claude Console Admin API key (`sk-ant-admin01-...`) was used. Admin API keys carry only `read:compliance_activities` and cannot be granted `read:compliance_user_data`, so they cannot call the chat, file, project, project attachment, session, user, or group-member endpoints.
**Fix:** Use a [Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) created in claude.ai with `read:compliance_user_data` selected. If the request really should be Activity Feed only, point the Admin API key at `GET /v1/compliance/activities` instead.
### Insufficient scope: delete
**Type:** `permission_error`
``` block
Missing required scopes. Got: ['read:compliance_user_data'] Needed: ['delete:compliance_user_data']
```
**Cause:** A Compliance Access Key without `delete:compliance_user_data` was used to call a `DELETE` endpoint on chats, files, or projects.
**Fix:** [Create a new Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) with `delete:compliance_user_data` selected. The delete scope is separate from `read:compliance_user_data` so that read-only audit keys cannot delete content.
## 404 Not Found
A 404 whose message names a resource or a resource type means the ID in the path does not exist or has already been deleted. Compliance API deletes are immediate and permanent, so a 404 on a previously known ID usually means the content is gone: hard-deleted through a Compliance API delete call, removed by a retention policy, or, for files and artifacts, deleted along with their chat by a user in claude.ai. A 404 with the bare message `Not found` is different: the request did not authenticate (or the path does not exist), and any endpoint can return it, list endpoints included; see [Request not authenticated](#request-not-authenticated). The session endpoints add two cases. On the local session endpoints, a separate 404 message, `Local sessions are not available.`, is returned on every call (including the list) while the endpoints are unavailable to your parent organization; it does not depend on the session ID and can be temporary. See [Local session not found](#local-session-not-found). On the remote session endpoints, a session that is still being provisioned (`status` of `pending`) has no transcript yet, so its messages endpoint 404s until the session starts. See [Remote session not found](#remote-session-not-found). The activity-type strings cited in each Fix (for example, `claude_chat_created`) are values you can pass to the Activity Feed `activity_types[]` filter; see [Query compliance activities](../Admin/compliance-activities-list.md) for every supported value.
### Request not authenticated
**Type:** `not_found_error`
``` block
Not found
```
**Cause:** The request did not carry a credential the Compliance API accepts: no API key was sent, or the key is not a Compliance Access Key (`sk-ant-api01-...`) or Admin API key (`sk-ant-admin01-...`), for example, a Claude API key (`sk-ant-api03-...`). The status, type, and message are the same as for a path that does not exist and do not depend on the endpoint or any resource ID, so list endpoints such as `GET /v1/compliance/activities` return this body too. The one exception is `GET /v1/compliance/organizations/{organization_id}/settings`, which answers these requests with 401 `authentication_error`. On every endpoint, a Compliance Access Key or Admin API key that does not authenticate returns [401 Unauthorized](#401-unauthorized) instead.
**Fix:** Send the key in the `x-api-key` header and check its prefix: the Compliance API accepts only `sk-ant-api01-...` and `sk-ant-admin01-...` keys; see [Which key do you need?](manage-claude-compliance-api-access.md#which-key-do-you-need). If the header and key are right and one path still returns `Not found` while others succeed, check that path against the [Compliance API reference](../Endpoints/http-compliance.md).
### Chat not found
**Type:** `not_found_error`
``` block
Chat conversation not found: 'claude_chat_01H5CWunD7RpVJ5bHa8RCkja'
```
**Cause:** The chat ID in the path does not match a chat readable through the Compliance API. The chat might have been hard-deleted through a previous Compliance API call or removed by your organization's retention policy, or it might belong to an organization the calling key cannot read. Chats that a user deleted in claude.ai do not return 404; they remain readable, with `deleted_at` populated, but without their message content.
**Fix:** Confirm the chat ID against a recent `claude_chat_created` or `claude_chat_viewed` activity. If the activity is recent and the read still fails, the chat has been hard-deleted (through this API or by retention-policy expiry) or belongs to an organization outside your key's scope.
### File not found
**Type:** `not_found_error`
``` block
File not found: 0d3b8f72-6c1e-4a59-b2de-7f4c9a1e5b60
```
**Cause:** The file ID does not exist in an organization your key can read, or the file has been deleted. Deleting a chat in claude.ai also deletes the files attached to it, although the chat itself remains listed. The message identifies the file by its underlying UUID rather than by the `claude_file_...` ID sent in the request. The metadata, content, and delete endpoints return this body; it applies to both chat-attached files (`claude_file_...`) and project files.
**Fix:** Reconcile against recent `claude_file_uploaded` or `claude_file_deleted` activities. Files deleted along with a chat have no `claude_file_deleted` activity, so check for the chat's `claude_chat_deleted` activity as well. If the file was deleted, the binary is gone; the activity records remain in the feed for the 6-year retention window.
### Generated file or artifact not found
**Type:** `not_found_error`
``` block
Generated file not found: 'claude_gen_file_01TbR8wAcCeFhJkLnPqStUvX'
```
``` block
Generated file content not found: 'claude_gen_file_01TbR8wAcCeFhJkLnPqStUvX'
```
``` block
Artifact version not found: 'claude_artifact_version_01KmNpQrSt3UvWxYz5AbCdEfG'
```
**Cause:** The ID in the path does not match a tool-generated file or artifact version readable through the Compliance API. The generated-file metadata endpoint returns the first body, the content endpoint returns the second, and both artifact endpoints return the third. Generated files and artifacts are deleted with the chat they were created in, including when a user deletes the chat in claude.ai.
**Fix:** Use [Get chat messages](../Admin/compliance-apps-chats-messages-list.md) to look up the chat the ID came from. If the chat's `deleted_at` is populated, or a `claude_chat_deleted` activity names the chat, the content is gone; remove the ID from your queue. Otherwise, confirm the ID against the `generated_files` and `artifacts` arrays on the chat's messages.
### Project not found
**Type:** `not_found_error`
``` block
No project is found with the provided id.
```
``` block
No project found with provided id, or it has already been deleted.
```
**Cause:** The project ID does not exist or has been deleted. The project detail, attachments, and collaborators endpoints return the first body; `DELETE /v1/compliance/apps/projects/{project_id}` returns the second.
**Fix:** Reconcile against recent `claude_project_created` or `claude_project_deleted` activities. The Activity Feed continues to expose the project's lifecycle events even after the project itself is gone.
### Project document not found
**Type:** `not_found_error`
``` block
No project document found with the provided id.
```
``` block
No project document found with the provided id, or it has already been deleted.
```
**Cause:** The project document ID does not exist or has been deleted. The document content and metadata endpoints return the first body; `DELETE /v1/compliance/apps/projects/documents/{document_id}` returns the second. This error applies to text project documents (`claude_proj_doc_...`), not to project files.
**Fix:** Use `GET /v1/compliance/apps/projects/{project_id}/attachments` to list current attachments. If the document is missing, it was deleted; retrieve it through a `claude_project_document_uploaded` activity record if you only need the metadata. The activity record shows who uploaded the document, when, and to which project, but not its name.
### Local session not found
**Type:** `not_found_error`
``` block
Local session not found.
```
**Cause:** The session ID passed to `GET /v1/compliance/apps/sessions/local/{session_id}` or `GET /v1/compliance/apps/sessions/local/{session_id}/messages` does not match a local session readable through the Compliance API. Both endpoints return this one message, without distinguishing the cause, when the ID is not a session in an organization your key can read (including IDs that belong to another parent organization), when the session never existed, when [zero data retention](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) is in effect for the session, or when all of the session's activity has aged past the retention period that applies to the organization that ran it. The `Local session not found.` response has no transient form, because local sessions have no provisioning (`pending`) state; compare [Remote session not found](#remote-session-not-found), where a `pending` session 404s until it starts. A session ID that is not a well-formed `clls_` identifier returns [400 Bad Request](#400-bad-request) instead.
The local session endpoints, including the list endpoint, return a different 404 message, `Local sessions are not available.`, while the endpoints themselves are unavailable to your parent organization. That response does not depend on the session ID; no customer-side key, scope, or setting changes it, and it can be temporary. Both responses carry the `not_found_error` type; the message text is what tells them apart.
**Fix:** Confirm the session ID against `GET /v1/compliance/apps/sessions/local`; see [Sessions on users' machines](manage-claude-compliance-sessions.md#retrieve-local-sessions). If the session no longer appears in the list, its content has aged past retention (or the session is otherwise no longer in an organization your key can read) and its transcript is not retrievable; remove the ID from your queue. If every call, including the list, returns `Local sessions are not available.`, keep your queued session IDs and retry on your next scheduled run; if the response persists, contact your Anthropic representative and include the `request-id` response header.
### Remote session not found
**Type:** `not_found_error`
``` block
Remote session not found.
```
**Cause:** The session ID passed to `GET /v1/compliance/apps/sessions/remote/{session_id}/messages` does not match a session transcript readable through the Compliance API. This occurs when the session ID (`cse_...`) does not exist or the session has been deleted, when the session belongs to an organization your key cannot read, or when the session's `status` is still `pending`: a pending session has no transcript yet, so the messages endpoint returns 404 until the session starts. A session ID that is not a well-formed `cse_` identifier returns [400 Bad Request](#400-bad-request) instead.
**Fix:** Confirm the session ID and its `status` against `GET /v1/compliance/apps/sessions/remote`; see [Sessions in the cloud](manage-claude-compliance-sessions.md#retrieve-remote-sessions). If the session is `pending`, retry after it leaves that status. If the session no longer appears in the list, it has been deleted and its transcript is not retrievable.
### Organization, role, or group not found
**Type:** `not_found_error`
``` block
The "ce86b5f3-7c16-48b3-a9f3-e1d2c4b8a0f1" organization does not exist or the requester is not authorized to access it.
```
The organization, role, and group endpoints return a 404 `not_found_error` in the standard error format. The organization message names the `org_uuid`; the role and group messages are generic (`Role not found.`, `Group not found.`). This occurs when a path ID (`org_uuid`, `role_id`, or `group_id`) does not exist or no longer belongs to a tree the calling key can read.
**Cause:** The ID in the path does not match a record readable through the Compliance API. Roles and groups can be deleted, and organizations can be unlinked from the parent tree.
**Fix:** Verify the ID against the corresponding list endpoint, and reconcile against recent organization, role, or group activities in the [Activity Feed](manage-claude-compliance-activity-feed.md).
### Organization settings not available
**Type:** `not_found_error`
``` block
organization `91012d09-e48b-438e-a489-1bebfd8fa6f9` not found in this organization's hierarchy
```
**Cause:** `GET /v1/compliance/organizations/{organization_id}/settings` returns this 404 in three cases that intentionally share the same body so the response does not reveal whether an organization exists: the `organization_id` is not one of your parent's linked organizations, the value is not a valid UUID, or the settings endpoint is not yet enabled for your parent organization.
**Fix:** Verify the ID against [List organizations](../Admin/compliance-organizations-list.md). If a known-good organization ID still returns 404, the settings endpoint is not yet enabled for your parent organization; contact your Anthropic representative.
## 409 Conflict
The request is well-formed and authorized but conflicts with the resource's current state. The body carries the `invalid_request_error` type, which 400 responses also use, so distinguish a conflict by the 409 status code rather than by `error.type`.
### Project has attached chats
**Type:** `invalid_request_error`
``` block
The "claude_proj_01KGp4eZNug9ri4kE35RSppq" project cannot be deleted as it has chats attached to it. Delete or detach all chats, and try deleting the project again.
```
**Cause:** `DELETE /v1/compliance/apps/projects/{project_id}` was called on a project that still has chats attached.
**Fix:** List the project's chats with `GET /v1/compliance/apps/chats?user_ids[]={user_id}&project_ids[]={project_id}` (the `project_ids[]` filter requires at least one `user_ids[]` value; enumerate IDs through [List organization users](manage-claude-compliance-org-data.md#list-organization-users)), delete each one with `DELETE /v1/compliance/apps/chats/{claude_chat_id}`, and then retry the project delete.
## 429 Too Many Requests
Requests to the Compliance API are limited to **600 requests per minute per [parent organization](manage-claude-compliance-api.md#how-the-compliance-api-works)**. The limit is one budget shared across every key under the parent (Compliance Access Keys and the Admin API keys of all linked organizations) and across every `/v1/compliance/*` endpoint; the remote session endpoints carry a second request budget on top. For a standalone Claude Console organization, which has no parent organization, the same budget applies to the organization itself and is shared across its Admin API keys. Contact your Anthropic representative if your integration needs a higher limit.
Once your API key authenticates, Compliance API responses report the shared budget through the standard [rate-limit response headers](../Endpoints/rate-limits.md#response-headers) so your client can throttle proactively instead of waiting for a 429:
- `anthropic-ratelimit-requests-limit` is the per-minute request budget.
- `anthropic-ratelimit-requests-remaining` is the budget left in the current window.
- `anthropic-ratelimit-requests-reset` is the RFC 3339 timestamp when the window resets and the full budget is restored.
A 429 response also carries a `retry-after` header with the number of seconds to wait before sending the next request. This value might include a small safety margin beyond `anthropic-ratelimit-requests-reset`; honor `retry-after`.
```python
HTTP/1.1 429 Too Many Requests
date: Tue, 21 Apr 2026 14:38:02 GMT
retry-after: 25
anthropic-ratelimit-requests-limit: 600
anthropic-ratelimit-requests-remaining: 0
anthropic-ratelimit-requests-reset: 2026-04-21T14:38:25Z
```
```python
{
"error": {
"type": "rate_limit_error",
"message": "Compliance API rate limit of 600 requests per minute per parent organization has been exceeded. Retry after the time indicated by the retry-after header. Quote the request-id response header when contacting Anthropic support."
}
}
```
**Cause:** Your parent organization (or standalone Claude Console organization) sent more than 600 requests to `/v1/compliance/*` in a 1-minute window, across all of the keys that share its budget, or it exhausted the remote session endpoints' second request budget (described later in this section).
**Fix:** Wait the number of seconds in the `retry-after` header, then retry. If the header is absent (for example, stripped by an intermediary), fall back to exponential backoff (start at 1 second, double up to 60 seconds). Do not advance your pagination cursor on a 429: the failed request returned no data, so the cursor from the last successful page is still correct.
Requests that fail authentication (a missing or unrecognized key, or a Claude API key rather than a Compliance Access Key or Admin API key) are rejected before the rate limiter and do not consume quota. A valid key that lacks the endpoint's required scope consumes one quota unit before the 403 is returned.
The [local session endpoints](manage-claude-compliance-sessions.md#retrieve-local-sessions) count only against the shared limit. The [remote session endpoints](manage-claude-compliance-sessions.md#retrieve-remote-sessions) also carry a second request budget, keyed to your parent organization like the shared limit, on top of it. A 429 from that budget carries a `retry-after` header that is always `1` (a minimum wait, not the actual reset time); any `anthropic-ratelimit-*` headers on that response describe the shared limit rather than this budget, so back off exponentially if the 429 repeats.
If you poll the [Activity Feed](manage-claude-compliance-activity-feed.md) on a schedule, budget your aggregate request rate (across all keys, linked organizations, and concurrent workers) below the shared limit. Watch `anthropic-ratelimit-requests-remaining` to slow down before you reach it. See [Design your compliance integration](manage-claude-compliance-integration-patterns.md#choose-a-feed-consumption-pattern) for choosing between window-polling and cursor-driven ingestion.
## 500 Internal Server Error
A 500 from the Compliance API carries an `x-should-retry: false` response header when the failure is deterministic. The Claude SDK honors this header automatically. If you use a generic HTTP retry library that retries on every 5xx, suppress retries when `x-should-retry` is `false`; retrying this error fails identically on every attempt.
A 500 without the `x-should-retry: false` header is transient: retry with exponential backoff (start at 1 second, double up to 60 seconds). The same applies to 502, 503, 504, and 529 responses. The exception is a small set of local session 503s, described next, that depend on an organization's settings or encryption key rather than on load. See [Errors](../Endpoints/errors.md) for the platform-wide retry semantics.
### Local sessions temporarily unavailable
**Type:** `overloaded_error`
```python
The local-sessions index is temporarily unavailable. Try again shortly.
```
``` block
Captured content is temporarily unavailable. Try again shortly.
```
```python
The local-sessions index cannot currently evaluate retention overrides for this page. Try again later.
```
**Cause:** The [local session endpoints](manage-claude-compliance-sessions.md#retrieve-local-sessions) return 503 with one of these bodies. All three share the `overloaded_error` type, so this is one of the few errors on this page where you need the message text, not `error.type`, to tell the conditions apart:
- The `index is temporarily unavailable` body means session listings are briefly unavailable because of load or a backend condition. This is transient.
- The `Captured content` body means a session's transcript content cannot be returned right now. This is usually transient too. In organizations that use [customer-managed encryption keys](manage-claude-cmek.md), the messages endpoint also returns this body for every page that contains content your customer-managed key cannot decrypt, for example because you disabled, revoked, or destroyed the key, or because the key cannot be reached. In that case the error persists for as long as the key cannot be used. The message text is the same either way, so the only signal that the key is the cause is that the error keeps recurring for that organization. An unusable key is never reported as `not_captured`.
- The `retention overrides` body means a retention or data-handling setting that applies to one or more sessions in the requested range could not be evaluated yet. On the retrieve and messages endpoints it reads `for this session` instead of `for this page`. It depends on the data and settings of the organization that ran the session rather than on load, and it can persist for an extended period.
**Fix:** Handle each body as follows:
- For the two `Try again shortly.` bodies, retry with exponential backoff and do not advance your `page` cursor, because the failed request returned no data.
- If the `Captured content` body keeps recurring on the messages endpoint for an organization that uses a customer-managed key, treat it as persistent: stop walking that organization's transcripts and check the key's status in your key management service. Transcripts in other linked organizations, and session metadata everywhere, are unaffected. If you retry on a later run, restart each session's walk without `page`, because messages page cursors expire 24 hours after the walk's first page.
- For the `Try again later.` body, do not hold a walk open waiting for it to clear. On the list endpoint, either retry later by restarting without the `page` parameter (a list page token older than 24 hours is still accepted but is re-evaluated against the current retention boundary, so a parked walk can skip sessions), or narrow the `created_at.gte` and `created_at.lt` window until the request succeeds and export the skipped range separately on a later run. On the retrieve and messages endpoints, skip that session ID, continue with the rest of your export, and retry the session on a later run. Messages page cursors expire 24 hours after the walk's first page, so restart that session's walk without `page` when you return to it.
If any of these conditions recurs across runs, contact your Anthropic representative and include the `request-id` response header. For the customer-managed key case, do this only if the error continues while that key is usable.
For service-wide incidents, check [status.anthropic.com](https://status.anthropic.com).
## Next steps
[Compliance API FAQ](manage-claude-compliance-faq.md)
Common questions about access, scopes, retention, and integration.
[Errors](../Endpoints/errors.md)
The platform-wide error catalog and retry semantics.
---
## Compliance API FAQ - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-faq
Path: 04-API-Reference/Other/manage-claude-compliance-faq.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-faq)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Compliance API FAQ
Copy page
Answers to common questions about Compliance API access, scopes, retention, and integration.
Copy page
To enable the Compliance API, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
## Access and scopes
### Who can enable the Compliance API?
For a Claude Enterprise organization, the primary owner enables the Compliance API at [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access), and enablement cascades from the parent organization to every linked organization. For an eligible standalone Claude Console organization (one with no parent organization), an organization admin enables it at [Claude Console \> Settings \> Security](playground.md). A Claude Console organization that is linked to a parent organization does not enable the Compliance API itself; it is enabled from the parent organization. See [Set up the Compliance API](manage-claude-compliance-api-access.md#set-up-the-compliance-api) for the steps.
### Can I turn the Compliance API off after enabling it in Claude Console?
Yes. For a standalone Claude Console organization, an organization admin can turn the **Compliance API** toggle off at [Claude Console \> Settings \> Security](playground.md), the same place it is turned on. While the Compliance API is off, no activity events are recorded for your organization, so the [Activity Feed](manage-claude-compliance-activity-feed.md) receives no new events. If your organization is enrolled in [Access Transparency](manage-claude-access-transparency.md), turning the Compliance API off also stops Access Transparency event delivery. Activity that is not recorded while the Compliance API is off cannot be recovered later. Turning the Compliance API back on resumes recording from that point forward; activity that was already recorded is not deleted.
### Does turning the Compliance API off delete events that were already captured?
No. Turning the Compliance API off stops new activity events from being recorded, but it does not delete events that were already captured while it was on. Recording resumes from the point the Compliance API is turned back on.
### Is turning the Compliance API off in Claude Console recorded anywhere?
Yes. When the Compliance API is turned off (or back on) in Claude Console, the change is recorded as an `org_compliance_api_settings_updated` activity in the [Activity Feed](manage-claude-compliance-activity-feed.md), so your audit trail shows who changed the setting and when. This activity is an exception to the recording stop: the disable is recorded even though no other activity is recorded while the Compliance API is off.
### Why doesn't my parent organization appear in Claude Console when creating an Admin API key?
This is expected. A Claude Enterprise parent organization centralizes identity across all linked organizations; it does not carry workloads, and it does not appear in Claude Console at all. Claude Console only ever shows the Claude Console organizations linked beneath the parent.
To call the Compliance API, you create one of two key types instead:
- **For full Compliance API access ([Activity Feed](manage-claude-compliance-activity-feed.md) plus chats, files, projects, sessions, users, organization metadata, and organization settings),** the primary owner of the parent organization (or an organization owner, for a key restricted to their own organization only) creates a [Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) in claude.ai.
- **For Activity Feed access only,** an organization admin in your Claude Console organization creates an [Admin API key](manage-claude-compliance-api-access.md#create-an-admin-api-key) in Claude Console. The Compliance API must already be enabled for the organization, and the admin must create the Admin API key while the Compliance API is enabled for it to carry the `read:compliance_activities` scope.
### Can I use my regular Claude API key with the Compliance API?
No. A Claude API key (`sk-ant-api03-...`) authenticates calls to Claude models on the Claude API; it does not authenticate calls to `/v1/compliance/*`. The Compliance API accepts only Compliance Access Keys (`sk-ant-api01-...`) and Admin API keys (`sk-ant-admin01-...`). See [Which key do you need?](manage-claude-compliance-api-access.md#which-key-do-you-need) for the full mapping.
### Why does my Admin API key return 403 on chat or file endpoints?
An Admin API key's only Compliance API scope is `read:compliance_activities`, which authorizes the Activity Feed only. Every other Compliance API endpoint requires a scope that only a Compliance Access Key created in claude.ai can carry. Calling a content or directory endpoint with an Admin API key returns a 403 naming the scope that endpoint family requires: `read:compliance_user_data` for chats, files, projects, project attachments, sessions, users, and group members, and `read:compliance_org_data` for organizations, roles, groups, and effective organization settings. For example, listing chats returns the following response.
Response
```python
{
"error": {
"type": "permission_error",
"message": "Missing required scopes. Got: ['api:admin', 'read:compliance_activities'] Needed one of: ['read:compliance_user_data', 'read:org_audit']"
}
}
```
To access content endpoints, the primary owner of your parent organization (or an organization owner, for their own organization only) must [create a Compliance Access Key](manage-claude-compliance-api-access.md#set-up-the-compliance-api) with `read:compliance_user_data` (and `delete:compliance_user_data` for deletes), or `read:compliance_org_data` for organization, role, group, and effective-settings endpoints. A standalone Claude Console organization (one with no parent organization) cannot create a Compliance Access Key, so the content endpoints are not available to it; it can query the Activity Feed only. See [Handle Compliance API errors](manage-claude-compliance-errors.md#403-forbidden) for the full per-endpoint catalog.
## Data coverage and retention
### How far back does the Activity Feed go?
The Activity Feed retains 6 years of organization activity, and new events are queryable within 1 minute of occurring. The feed reaches back at most to the point the Compliance API was first enabled for your organization: recording is not retroactive, and activity from before enablement is not backfilled. Activity Feed retention is independent of your organization's content retention policy: chat, file, and project content follows the retention rules configured for your organization (indefinite by default), unless a user deletes it sooner.
### Does the Activity Feed include prompt or message content?
No. The Activity Feed records who did what and when (authentication, chat creation, file uploads, project changes, administrative actions, and similar resource events), but it does not capture the prompt text or model responses inside chats or messages.
To retrieve message bodies and file contents, use the chat, message, and file endpoints with a Compliance Access Key carrying `read:compliance_user_data`. The same key and scope retrieve transcripts of sessions on users' machines (such as Cowork and Claude Code sessions) through the [local session endpoints](manage-claude-compliance-sessions.md#retrieve-local-sessions), and transcripts of Cowork sessions in the cloud through the [remote session endpoints](manage-claude-compliance-sessions.md#retrieve-remote-sessions). These endpoints serve Claude Enterprise content only; Claude Console workloads, and Claude API workloads authenticated with an API key, expose administrative and resource events through the Activity Feed but do not expose prompt text or model responses through the Compliance API.
### Do Cowork, Claude Code, Claude Science, Claude for Microsoft 365, and Claude in Chrome sessions appear in the Compliance API?
Yes. Cowork sessions in Claude Desktop that run on users' machines, Claude Code sessions (in the terminal, in Claude Desktop, or in an IDE extension), sessions in the Claude Science desktop app, Claude for Microsoft 365 sessions (in Excel, PowerPoint, Word, and Outlook), and chats in the Claude in Chrome browser extension are captured while users are signed in with their Claude Enterprise account and are available through the [local session endpoints](manage-claude-compliance-sessions.md#retrieve-local-sessions). Cowork sessions started on claude.ai web or mobile, which run in the cloud in Anthropic-managed environments, are available through the [remote session endpoints](manage-claude-compliance-sessions.md#retrieve-remote-sessions). Each family has a list endpoint that returns session metadata and a messages endpoint that returns the session transcript (user prompts, assistant responses, and tool calls and results). The local family adds a third endpoint that retrieves one session's metadata. All of these endpoints use your existing Compliance Access Key with `read:compliance_user_data`; no new key or scope is needed.
Local sessions are captured as their requests reach the Claude API, so nothing is installed on the device, and on-device activity that never reaches the API is not captured. Claude Code sessions authenticated with a Claude Console API key, Claude Code sessions run through a third-party cloud platform (Amazon Bedrock, Google Cloud, or Microsoft Foundry), and [Claude Code cloud sessions](../../02-Claude-Code-CLI/claude-code-on-the-web.md), which run on cloud infrastructure instead of the user's machine, are not captured. These cloud sessions are not remote sessions, even though both run in the cloud; the remote session endpoints return Cowork sessions only. Each run of a Cowork scheduled task in the cloud is a remote session: the remote session list endpoint returns it as an [agent-owned session](manage-claude-compliance-sessions.md#retrieve-remote-sessions) (`agent_id` is set, and `started_by_user` identifies the human who initiated the run), and the messages endpoint returns its transcript, but neither returns the task's name or schedule. Claude Code routines that run in the cloud are Claude Code cloud sessions, so the session endpoints do not return them. Organizations with [HIPAA readiness](../Guides/build-with-claude-api-and-data-retention.md#hipaa-readiness) enabled get local session data for Cowork and Claude Code only, stored for 30 days, and sessions for which [zero data retention (ZDR)](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) is in effect are excluded.
The local and remote session endpoints are stable for Cowork, Claude Code, and Claude for Microsoft 365 sessions; coverage of Claude Science and Claude in Chrome sessions is in beta.
### What do session transcripts include?
Local and remote session transcripts both carry user prompts, assistant responses, and tool calls and results. For local sessions (on users' machines), that is what Claude was asked to do and what it returned, not what happened on the device.
| Data | Local sessions (on users' machines) | Remote sessions (in the cloud) |
|:----------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| User prompts | Yes; returned as `text` blocks. | Yes; returned as `text` blocks. |
| Assistant responses | Yes; text output only. | Yes; text output only. |
| Tool calls and results | Yes; each `tool_use` input and each `text` entry in a `tool_result` is truncated to 10,000 bytes by default (up to about 1 MiB each on request). | Yes; each `tool_use` input and each `text` entry in a `tool_result` is truncated to 10,000 bytes by default (up to about 1 MiB each on request). |
| File contents and file names | Yes; text that Claude reads through tools appears in the transcript, subject to the same truncation. Images, PDFs, and other binary or structured content appear only as placeholder `text` blocks. File names appear in tool-call inputs and outputs. | Yes; file contents and file names appear in the transcript through tool-call inputs and outputs (text only; other content is omitted). |
| Artifacts | Yes; generated content appears inside tool-call inputs in the transcript. | Yes; generated content appears inside tool-call inputs in the transcript. |
| Skills | Yes; skill content appears when the client sends it as message content, and it is not distinguished from other user text. | Yes; skill content appears in the transcript. |
| Session metadata | Yes; owner (`user.id` and email address), organization, workspace, `product_surface`, `created_at`, and `updated_at`, from the list and retrieve endpoints. Local sessions carry no `status`. | Yes; owner, organization, status, timestamps, and `product_surface`, from the list endpoint. |
| Thinking blocks | No. | No. |
| Images and other non-text content | No; each image, PDF, or other binary or structured block appears as a placeholder `text` block (for example, `[image content not shown]`) with `truncated` set to `true`. Raw file bytes are never returned. | No; non-text blocks are omitted, and raw file bytes are never returned. |
| Token usage, cost, and latency | No; token usage and cost are available through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api). | No; token usage and cost are available through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api). |
See [Sessions on users' machines](manage-claude-compliance-sessions.md#retrieve-local-sessions) and [Sessions in the cloud](manage-claude-compliance-sessions.md#retrieve-remote-sessions) for the endpoints and parameters.
### How does session coverage compare with OpenTelemetry logging (OTEL) for Cowork and Claude Code?
[Cowork's OpenTelemetry logging](../../15-Claude-AI-Features/monitor-claude-cowork-activity-with-opentelemetry.md) and [Claude Code monitoring](../../13-Enterprise-Admin/monitoring-usage.md) overlap with the session endpoints but answer different needs: OTEL streams per-event telemetry to infrastructure you run as activity happens, whereas the Compliance API lets you retrieve retained per-session transcripts from Anthropic after the fact. OTEL can also capture prompts and responses, but Anthropic recommends the Compliance API for retrieving the content of Cowork and Claude Code sessions. For a table comparing local sessions, remote sessions, and OTEL, see the introduction to [Retrieve session transcripts](manage-claude-compliance-sessions.md).
OTEL events and Compliance API records share organization and user identifiers, so you can join them.
### Is deleted content recoverable through the Compliance API?
No. Deletes performed through the Compliance API are immediate, permanent, and not recoverable. The content of a chat that a user deletes in claude.ai is not recoverable either: the Compliance API still returns the chat and its messages, with `deleted_at` populated, but not their content. A remote session that a user deletes is likewise not recoverable, and the remote session endpoints no longer return it. Pull any content you need to retain (for legal hold or archival) while it is still available. See [Plan content retention](manage-claude-compliance-integration-patterns.md#plan-content-retention) for when to export content to your own archive.
### What does the Compliance API not capture?
The Compliance API has known coverage boundaries: the Activity Feed records resource events but not prompt or response text, Claude Console and Claude API workloads authenticated with an API key expose no message content at all, and content removed by your retention policy, deleted by a user in claude.ai, or hard-deleted through the Compliance API is not recoverable. For the full coverage boundaries and delivery contract, see [Delivery guarantees and completeness](manage-claude-compliance-integration-patterns.md#delivery-guarantees-and-completeness).
Session transcripts have boundaries of their own. Local sessions are captured only as their requests reach the Claude API, so on-device activity that never reaches the API is not captured. Claude Code sessions authenticated with a Claude Console API key, Claude Code sessions run through a third-party cloud platform (Amazon Bedrock, Google Cloud, or Microsoft Foundry), and [Claude Code cloud sessions](../../02-Claude-Code-CLI/claude-code-on-the-web.md) (including Claude Code routines that run in the cloud), which run on cloud infrastructure instead of the user's machine, are not captured either; organizations with HIPAA readiness enabled get local session data for Cowork and Claude Code only, stored for 30 days; and sessions for which zero data retention is in effect are excluded. No session transcript, local or remote, includes thinking blocks or tool definitions. Organizations that use [customer-managed encryption keys](manage-claude-cmek.md) receive local session transcripts as usual. While the key cannot be used, the messages endpoint returns [503 Service Unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable) instead of transcript content, and session metadata is still listed.
No Compliance API endpoint lists an organization's Cowork scheduled tasks or Claude Code routines.
## Integration and pagination
### How do I correlate Compliance API records with my SIEM?
Join `Activity` records to your SIEM on `actor.user_id`, `actor.email_address`, `actor.ip_address`, `actor.user_agent`, and `created_at`. See [Design your compliance integration](manage-claude-compliance-integration-patterns.md#correlate-with-your-siem) for the join-key table and consumption patterns.
### Can one customer have multiple organizations under one parent?
Yes. A Claude Enterprise parent organization can have many linked organizations, including a mix of claude.ai organizations and Claude Console organizations (for example, separate production and staging Claude Console organizations). Identity, SSO, and SCIM are shared across the parent; billing, members, projects, and API keys remain separate for each organization. Compliance API enablement happens at the parent organization level and cascades to all linked organizations, and a Compliance Access Key that covers the parent organization and carries `read:compliance_org_data` can enumerate every organization beneath the parent through `GET /v1/compliance/organizations`.
### Are activities returned in order, and how do I detect when I have caught up to real time?
Activities are returned newest first, with ties in `created_at` broken by activity ID. To catch up, walk pages forward by `before_id` until `has_more` is `false`; that final response's `first_id` is your new cursor and you have reached the present. The full loop, including initial backfill and the safety conditions on cursor persistence, is in [Cursor-driven incremental reads](manage-claude-compliance-integration-patterns.md#cursor-driven-incremental-reads).
### How do I get a sandbox to test the Compliance API?
To test only the [Activity Feed](manage-claude-compliance-activity-feed.md), you do not need a Claude Enterprise organization: an organization admin can [enable the Compliance API](manage-claude-compliance-api-access.md#set-up-the-compliance-api) on an eligible standalone Claude Console test organization and query the feed with a new Admin API key. If the **Compliance API** section is not visible in that organization's Security settings, the organization is not eligible for self-service enablement.
To test every endpoint, set up a Claude Enterprise sandbox organization linked to a Claude Console organization under the same parent. This lets the sandbox exercise both the Activity Feed (through an Admin API key) and the chat, file, project, and session endpoints (through a Compliance Access Key).
1. **Provision the Claude Enterprise organization.** Contact your Anthropic representative to set up a Claude Enterprise sandbox organization. On an existing Claude Enterprise organization, the primary owner can [enable the Compliance API directly in claude.ai](manage-claude-compliance-api-access.md#set-up-the-compliance-api).
2. **Create the Claude Console organization.** Create a Claude Console organization yourself at `platform.claude.com` using the same email address.
3. **Link the two organizations.** Sign in as the primary owner of the Claude Enterprise organization, go to [claude.ai \> Organization settings \> Identity and access](https://claude.ai/admin-settings/identity), and use **Merge Organizations** to link the two under a shared parent.
Once linked, follow [Set up the Compliance API](manage-claude-compliance-api-access.md) to create keys and start querying. Test organizations use the same enablement process as production organizations.
---
## Design your compliance integration - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-integration-patterns
Path: 04-API-Reference/Other/manage-claude-compliance-integration-patterns.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-integration-patterns)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Design your compliance integration
Copy page
Choose between polling and cursor-driven Activity Feed consumption, correlate Compliance API events with your SIEM, and plan retention.
Copy page
To enable the Compliance API, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
**Required scope:** `read:compliance_activities` on the Compliance Access Key or Admin API key.
A production Compliance API integration makes three design choices: how it consumes the Activity Feed, how its output correlates with your security information and event management (SIEM) system, and where long-term copies of activity and content live. These choices are independent of the endpoints themselves; this page helps you evaluate the tradeoffs.
This page assumes you have read the following pages:
- [Query the Activity Feed](manage-claude-compliance-activity-feed.md), which defines the parameters and pagination contract referenced throughout.
- [Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md), which defines the chat, file, and project endpoints and the `deleted_at` semantics referenced in [Plan content retention](#plan-content-retention).
- [Retrieve session transcripts](manage-claude-compliance-sessions.md), which defines the local and remote session endpoints.
## Choose a feed-consumption pattern
The Activity Feed supports two consumption patterns: periodic window polling bounded by `created_at.gte` and `created_at.lt`, and cursor-driven incremental reads that persist a cursor from one response and pass it on the next request. Both return identical `Activity` objects; the difference is the state your client persists between calls.
Both patterns share these constraints:
- Activities are queryable within 1 minute of occurring and retained for 6 years. Recording is not retroactive: it begins when the Compliance API is first enabled for your organization, and activity from before enablement is not backfilled.
- The maximum `limit` for each page is 5,000.
- Cursor values are opaque strings that you must not parse.
- Requests are limited to 600 per minute per [parent organization](manage-claude-compliance-api.md#how-the-compliance-api-works), shared across every key, every linked organization, and every `/v1/compliance/*` endpoint; unlike the local session endpoints, the remote session endpoints carry a second request budget on top. See [429 Too Many Requests](manage-claude-compliance-errors.md#429-too-many-requests) for the response headers and retry contract.
| Pattern | Choose when |
|:--------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Window polling | Your pipeline runs on a fixed schedule, you prefer stateless workers, and you can tolerate replaying or overlapping windows |
| Cursor-driven incremental reads | You want the lowest latency between an activity occurring and your pipeline ingesting it, you want to avoid re-reading pages you already drained, and you have a durable place to persist a cursor between runs |
### Window polling
Set `created_at.lt` at least 1 minute in the past so that every activity in the window is already queryable. Use `created_at.gte` for the lower bound and `created_at.lt` for the upper bound so that consecutive windows tile without gaps or overlap; reuse the previous window's `lt` value as the next window's `gte`.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "created_at.gte=2026-04-20T07:00:00Z" \
--data-urlencode "created_at.lt=2026-04-20T08:00:00Z" \
--data-urlencode "limit=5000"
```
When the response has `has_more: true`, the window contains more than one page of activities. Either page within the window by passing the response's `last_id` as `after_id` on the next request (stopping when `has_more` is `false`), or choose a smaller time window. See [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results) for the full contract.
Even with clean tiling, an activity that indexes after its window has closed never appears in a later window. Deduplicate on the activity `id` and either widen each new window so it overlaps the previous one by a few minutes or run a periodic reconciliation pass that re-queries an older window.
A `created_at.lt` bound too close to the present silently and permanently drops late-indexed activities: once `created_at.gte` advances past them, no later window can recover them. Treat the 1-minute queryability figure as the documented indexing lag, not a soft recommendation.
### Cursor-driven incremental reads
cURL
```python
first_id="activity_01XyDMpzjS89pFZXqSFUBDr6" # first_id from a previous response
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "limit=5000" \
--data-urlencode "before_id=$first_id"
```
Page through until `has_more` is `false`, then persist `first_id` from the final response and pass it unchanged as `before_id` on the next run to retrieve activities newer than the saved cursor. To walk in the opposite direction for a backfill, persist `last_id` and pass it as `after_id` instead. For the full cursor-vs-page-token reference and retry semantics, see [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results).
A production **catch-up** loop fetches activities recorded since your last poll by driving iteration off `has_more` and `first_id`:
``` inline-block
cursor = stored_cursor
loop:
page = GET /v1/compliance/activities?before_id={cursor}&limit=5000
store(page.data)
if page.first_id is not null:
cursor = page.first_id
if not page.has_more: break
persist(cursor)
```
Cursors survive key rotation; see [Manage and rotate keys](manage-claude-compliance-api-access.md#manage-and-rotate-keys).
Each page is adjacent to the cursor you pass: the loop walks forward toward the present, one page at a time. Do not treat a single response as caught up while `has_more` is `true`. Persist the cursor only after `has_more` is `false`; the unfetched pages are the newer ones between this response's `first_id` and the present, and they stay unread until you finish the loop or run again.
## Correlate with your SIEM
Each `Activity` carries fields you can join against events already in your SIEM (Splunk, Datadog, Microsoft Sentinel, Cribl, or similar):
| Compliance API field | Join target |
|:----------------------|:------------------------------------------------------------------------|
| `actor.user_id` | Your identity provider's stable user identifier |
| `actor.email_address` | Directory email when a stable ID is unavailable |
| `actor.ip_address` | Network, VPN, and endpoint logs |
| `actor.user_agent` | Endpoint and device inventory, and the client app that made the request |
| `created_at` | Time-window correlation across any source |
`actor.user_id` and `actor.email_address` are present when `actor.type` is `user_actor`. `actor.ip_address` and `actor.user_agent` are absent on some actor types, such as `anthropic_actor` and `scim_directory_sync_actor`. Check the discriminator before reading any of these fields. `user_id` is a stable, opaque identifier for the user account: it is consistent across every Compliance API endpoint and activity payload, and it does not change when the user's email or display name changes. Use `user_id`, not `email_address`, as the primary join key.
Calls to the Compliance API itself emit `compliance_api_accessed` activities. Ingest these alongside other activity types so your SIEM records who queried compliance data, and when. Pass `activity_types[]=compliance_api_accessed` to scope the query, then in your client, read `actor.api_key_id` from each activity whose `actor.type` is `api_actor` to attribute the access to a specific Compliance Access Key or Admin API key.
## Plan content retention
Five retention horizons govern what you can retrieve later:
| Data | Retained for | Controlled by |
|:--------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|
| Activity Feed records | 6 years | Anthropic |
| Chat, file, and project content | Your organization's claude.ai retention policy, unless a user deletes it sooner | Your organization |
| Local session transcripts (sessions on users' machines) | 6 years by default, or your organization's custom conversation retention period when a finite one is set; 30 days in organizations with HIPAA readiness enabled | Anthropic by default; your organization when it sets a custom period |
| Remote session transcripts (sessions in the cloud) | 6 years, unless a user deletes the session sooner | Anthropic |
| Content hard-deleted through the Compliance API | Not retained; deletion is immediate and permanent | The caller of the `DELETE` endpoint |
To learn how the rest of the Claude Platform handles retention, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md).
Decide between export-and-archive and on-demand API retrieval as follows:
- If your legal-hold or audit horizon exceeds 6 years for activity metadata or session transcripts, export Activity Feed pages and session transcripts to your own archive as you ingest them.
- If your content-retention policy is shorter than your eDiscovery horizon, export chat and file content before the retention window expires; the Compliance API cannot return content that retention has already removed. The same applies to local session transcripts, which follow your organization's custom conversation retention period when a finite one is set, even when that period is shorter than 6 years. The local session endpoints stop returning messages older than your organization's current period as soon as the setting changes, and lengthening the period later does not restore transcripts that have already expired, so export any transcript you must keep beyond it.
- If you must retain chat content or remote session transcripts after users delete them in claude.ai (for example, under a legal hold), export chat, file, artifact, and remote session content to your own archive as you ingest it; the Compliance API cannot return content that a user has already deleted.
- If a workflow might issue a Compliance API hard-delete (for example, DLP enforcement), retrieve and archive the target content first. There is no recovery window after a hard-delete.
In every other case, rely on direct API retrieval and avoid maintaining a parallel copy.
### Delivery guarantees and completeness
Treat the Activity Feed as **at-least-once**: a correctly paginated traversal returns every activity at least once, but a retry after a partial failure can re-deliver activities you already stored. Deduplicate on the activity `id` field.
The list endpoints do not return a `total_count` field or a checksum. To attest that an export run is complete, log:
- The starting cursor and the terminal `last_id`.
- The number of records exported.
- The run timestamp and the `request-id` of the final page.
Activity volume is not a completeness check. The `claude_*_viewed` activity types, such as `claude_chat_viewed`, follow each app's loading pattern (see [Understand the Activity object](manage-claude-compliance-activity-feed.md#understand-the-activity-object)). A period with chat messages but no `claude_chat_viewed` activities does not on its own indicate missing data. Rely on the traversal and the overlap or reconciliation pass described in [Window polling](#window-polling) instead.
The content endpoints (chats, files, projects, project attachments, and local and remote session transcripts) serve Claude Enterprise data only. The Activity Feed surfaces administrative and resource events organization-wide. The Compliance API does not include:
- Prompt text or model responses from Claude Console, or from Claude API workloads authenticated with an API key.
- On-device activity in local sessions that is never sent to Anthropic, such as local files that Claude did not read.
- Claude Code usage authenticated with a Claude Console API key, run through a third-party cloud platform (Amazon Bedrock, Google Cloud, or Microsoft Foundry), or run in a [Claude Code cloud session](../../02-Claude-Code-CLI/claude-code-on-the-web.md), which runs on cloud infrastructure instead of the user's machine.
- Local sessions from products other than Cowork and Claude Code in organizations with [HIPAA readiness](../Guides/build-with-claude-api-and-data-retention.md#hipaa-readiness) enabled, and local sessions for which [zero data retention](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) is in effect.
- Thinking blocks, and images or other binary content, inside session transcripts (transcripts carry user prompts, assistant responses, and tool activity only; local session transcripts show a placeholder `text` block where binary content was omitted).
- The original file for a chat attachment that claude.ai stored as extracted text, such as some Word, PowerPoint, and PDF uploads (the file content endpoint returns the extracted text; see [Retrieve files and artifacts](manage-claude-compliance-content-data.md#retrieve-files-and-artifacts)).
- The system prompt of local sessions (a marker message stands in for it).
- Tool definitions and MCP server configuration in session transcripts (local or remote), and citation metadata on `text` blocks in local session transcripts.
- Local session transcript content in an organization whose [customer-managed encryption key](manage-claude-cmek.md) cannot currently be used. Those requests return [503 Service Unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable), and session metadata is still listed.
- Content removed by your organization's retention policy.
- Content of chats that users delete in claude.ai (the chats are still listed, with `deleted_at` populated).
- Remote sessions that users delete (deleted sessions are no longer listed, and the messages endpoint returns 404 for them).
- Content hard-deleted through the Compliance API.
See the [Compliance API FAQ](manage-claude-compliance-faq.md#data-coverage-and-retention) for more on what the Compliance API does and does not capture.
For chain of custody, store the exported records with provenance metadata: source endpoint, query parameters, run timestamp, and a content hash of each record.
## Next steps
[Query the Activity Feed](manage-claude-compliance-activity-feed.md)
Filter parameters, pagination, and the `Activity` object schema.
[Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md)
The chat, file, and project endpoints, including hard delete.
[Retrieve session transcripts](manage-claude-compliance-sessions.md)
List the sessions your users run in Claude apps and agents, such as Cowork and Claude Code, and retrieve their transcripts.
---
## List organizations, users, roles, groups, and settings - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-org-data
Path: 04-API-Reference/Other/manage-claude-compliance-org-data.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-org-data)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# List organizations, users, roles, groups, and settings
Copy page
Enumerate organizations under your parent organization (their users, roles, and groups) and read each organization's effective settings through the Compliance API.
Copy page
To enable the Compliance API, see [Set up the Compliance API](manage-claude-compliance-api-access.md).
**Required scope:** `read:compliance_org_data` on the Compliance Access Key. The user and group-member endpoints require `read:compliance_user_data` instead.
Compliance Access Keys (`sk-ant-api01-...`) created in claude.ai are the only key type accepted; see [Set up the Compliance API](manage-claude-compliance-api-access.md) to provision one. Calls authenticated with an Admin API key (`sk-ant-admin01-...`) return [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden).
The endpoints on this page expose the directory side of a Claude Enterprise organization: its linked organizations, the users in each one, the roles defined on each, and its role-based access control (RBAC) or SCIM (System for Cross-domain Identity Management)-provisioned groups and their members. Use them to seed eDiscovery user lists, build reporting dashboards, and reconcile group membership against an external system of record. A Compliance Access Key that covers the parent organization returns data from every linked organization underneath, so a single key reaches the entire tree. The [effective-settings endpoint](#get-effective-organization-settings) complements the directory: it returns the data-privacy, security, and capability settings actually in force for one organization.
## List organizations
The [List organizations](../Admin/compliance-organizations-list.md) endpoint returns every organization under the parent the key is bound to.
The following call lists every organization under your parent. The response is a `data` array of organization records sorted by `created_at` ascending, plus `has_more` and `next_page` for pagination. When `has_more` is `true`, pass the returned `next_page` token back unchanged as the `page` query parameter on your next request. See [List organizations](../Admin/compliance-organizations-list.md) in the API reference for the `limit` and `page` parameter defaults and ranges.
cURL
```python
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/organizations" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"name": "Acme Engineering",
"created_at": "2025-06-01T10:00:00Z"
},
{
"uuid": "5a1b2c3d-4e5f-6789-abcd-ef0123456789",
"name": "Acme Legal",
"created_at": "2025-07-15T14:30:00Z"
}
],
"has_more": false,
"next_page": null
}
```
The `uuid` field is the canonical identifier for downstream lookups. The following table maps it to the other organization identifiers across the Compliance API:
| Field | Where | Relationship to `uuid` |
|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| `{org_uuid}` | Path parameter on per-organization endpoints on this page | Same value |
| `organization_uuid` | Activity Feed, chat, project, and session records | Same value; join on these two fields directly |
| `organization_id` | Activity Feed, chat, and project records | Same organization, `org_`-prefixed. Deprecated on chat and project records; use `organization_uuid` instead. |
| `organization_ids[]` | Filter on [Query the Activity Feed](manage-claude-compliance-activity-feed.md), [Retrieve chats and messages](manage-claude-compliance-content-data.md#retrieve-chats-and-messages), and the [remote session list](manage-claude-compliance-sessions.md#retrieve-remote-sessions) (the local session list has no organization filter) | Accepts `uuid` or the `org_`-prefixed form |
| `organization_id` | [Effective organization settings](#get-effective-organization-settings) response | Same value, bare UUID; this response does **not** use the `org_`-prefixed form that `organization_id` carries on Activity Feed, chat, and project records |
Most other Anthropic APIs use the `org_`-prefixed form.
To track organization-membership changes over time, relist this endpoint periodically, following the `next_page` token through every page on each pass. The Activity Feed also surfaces membership events through the `org_deletion_requested`, `org_deleted_via_bulk`, `org_parent_join_proposal_created`, and `org_join_proposal_decided` activity types; see [Query the Activity Feed](manage-claude-compliance-activity-feed.md).
## List organization users
The [List organization users](../Admin/compliance-organizations-users-list.md) endpoint returns a paginated list of user records for one organization.
This endpoint requires `read:compliance_user_data`, not `read:compliance_org_data`. Create the Compliance Access Key with both scopes when you intend to use it for directory enumeration; otherwise the call returns [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden).
See [List organization users](../Admin/compliance-organizations-users-list.md) in the API reference for the `limit` and `page` query parameter defaults and ranges.
Results are sorted by organization join date ascending. Unlike the Activity Feed's `before_id`/`after_id` cursors (see [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results)), the directory endpoints paginate with a `next_page` token: when `has_more` is `true`, pass `next_page` back unchanged as the `page` query parameter on the next request.
cURL
```python
org_uuid="91012d09-e48b-438e-a489-1bebfd8fa6f9"
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/organizations/$org_uuid/users" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01" \
--data-urlencode "limit=500"
```
Response
```python
{
"data": [
{
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"full_name": "Priya Sharma",
"email": "priya@example.com",
"organization_role": "admin",
"created_at": "2025-06-01T10:00:00Z"
}
],
"has_more": true,
"next_page": "page_8aW5kZXgicG9zaXRpb25fdG9rZW5fOTE0"
}
```
The user IDs returned here are the same `user_...` identifiers accepted by the [Query the Activity Feed](manage-claude-compliance-activity-feed.md) `actor_ids[]` filter and the `user_ids[]` filters on [Retrieve chats and messages](manage-claude-compliance-content-data.md#retrieve-chats-and-messages) and the [remote session list](manage-claude-compliance-sessions.md#retrieve-remote-sessions); the [local session list](manage-claude-compliance-sessions.md#retrieve-local-sessions) has no user filter, so attribute local sessions by the `user.id` on each session object. The `organization_role` field carries the user's built-in membership level within the listed organization (one of `admin`, `billing`, `claude_code_user`, `developer`, `managed`, `membership_admin`, `owner`, `primary_owner`, or `user`), an axis independent of any custom RBAC role assignments returned by [List roles](#list-roles). A typical eDiscovery flow lists users for one or more organizations, filters against your own external records, and feeds the resulting IDs into chat and project queries.
A user only appears here while they are an active member of the organization. Removed users are dropped from the list immediately. Their historical activity remains queryable through the Activity Feed for the full retention window, indexed by the same `user_...` ID.
## List roles
The [List Compliance Roles](../Admin/compliance-organizations-roles-list.md) endpoint returns a paginated list of role records defined on one organization, and [Get Compliance Role](../Admin/compliance-organizations-roles-retrieve.md) returns one role by ID.
Both role endpoints require `read:compliance_org_data`. The list endpoint accepts the same `limit` and `page` parameters as the [organization users endpoint](#list-organization-users).
cURL
```python
org_uuid="91012d09-e48b-438e-a489-1bebfd8fa6f9"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/organizations/${org_uuid}/roles" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"id": "rbac_role_01N2pQrS8tUvWxYz5AbCdEfGh",
"name": "Compliance Reviewer",
"description": "Read-only access to chat and project content for legal review.",
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-15T14:30:00Z"
}
],
"has_more": false,
"next_page": null
}
```
See the [List Compliance Roles](../Admin/compliance-organizations-roles-list.md) response schema for the full role record shape. To list the permissions currently granted to a role, use [List Compliance Role Permissions](../Admin/compliance-organizations-roles-permissions-list.md). To audit historical role assignments and permission changes, query the RBAC activity types (for example, `rbac_role_assigned` and `rbac_role_permission_added`) through the Activity Feed; see [Filter activities](manage-claude-compliance-activity-feed.md#filter-activities).
## List groups and members
The [List Compliance Groups](../Admin/compliance-groups-list.md) endpoint returns a paginated list of RBAC and SCIM-provisioned groups, and [Get Compliance Group](../Admin/compliance-groups-retrieve.md) returns one group by ID. The [List Compliance Group Members](../Admin/compliance-groups-members-list.md) endpoint returns the members of one group.
The group list and retrieval endpoints require `read:compliance_org_data`. The members endpoint requires `read:compliance_user_data`. Create the key with both scopes to walk groups end to end. Both list endpoints accept the same `limit` and `page` parameters as the [organization users endpoint](#list-organization-users).
See the [List Compliance Groups](../Admin/compliance-groups-list.md) response schema for the full group record shape. The `roles` array lists role IDs assigned to the group, matching IDs from [List roles](#list-roles). `source_type` is the discriminator between groups created manually through claude.ai (`direct`) and groups synced from an external identity provider through SCIM (`scim`).
List groups, then for each group list its members:
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/groups" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"id": "rbac_group_01P9qRsTuVwXyZa2BcDeFgHjK",
"name": "Engineering",
"description": "Engineering team members",
"source_type": "scim",
"roles": ["rbac_role_01N2pQrS8tUvWxYz5AbCdEfGh"],
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-15T14:30:00Z"
}
],
"has_more": false,
"next_page": null
}
```
For each group ID, list its members:
cURL
```python
group_id="rbac_group_01P9qRsTuVwXyZa2BcDeFgHjK"
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/groups/$group_id/members" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01"
```
Response
```python
{
"data": [
{
"user_id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email": "priya@example.com",
"created_at": "2025-06-01T10:00:00Z",
"updated_at": "2025-06-15T14:30:00Z"
}
],
"has_more": false,
"next_page": null
}
```
See the [List Compliance Group Members](../Admin/compliance-groups-members-list.md) response schema for the full member record shape. The `user_id` field is the same `user_...` identifier that the Activity Feed, chat list, and remote session list accept; it also matches `user.id` on local session objects and on user-owned remote session objects (agent-owned remote sessions carry the human's ID in `started_by_user.id` instead). To get a member's full name, look it up through the organization users list.
## Get effective organization settings
The [Get effective organization settings](../Admin/compliance-organizations-settings-retrieve.md) endpoint returns the settings in force for one organization under your parent: the enforced state after regulatory restrictions (such as HIPAA), feature-availability rules, organization-type defaults, and inter-feature dependencies are applied, which can differ from what an administrator configured. Use it to attest that retention windows, content redaction, single sign-on enforcement, the IP allowlist, and session-duration controls match your documented baseline, without administrator Console access.
This endpoint requires `read:compliance_org_data`; a key without that scope returns [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden). The target must be one of the parent's linked organizations: the parent organization itself is not a valid target. An unknown organization, an organization ID that is not a valid UUID, an organization outside your parent's tree, and a parent organization that does not yet have access to this endpoint all return the same [404 Not Found](manage-claude-compliance-errors.md#404-not-found), so a 404 does not reveal whether an organization exists. The settings endpoint is enabled per parent organization separately from the rest of the Compliance API; if every request returns 404, contact your Anthropic representative.
Before June 30, 2026, this endpoint required the separate `read:compliance_org_settings` scope. That scope has been retired: it can no longer be selected or granted when creating a key, and a key that carries only the retired scope returns [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden). Create a new Compliance Access Key with `read:compliance_org_data` instead.
cURL
```python
org_uuid="91012d09-e48b-438e-a489-1bebfd8fa6f9"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/organizations/$org_uuid/settings" \
-H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
-H "anthropic-version: 2023-06-01"
```
The response is a list of typed setting rows, and which rows appear varies by organization: a setting the organization's administrators cannot change, because it is controlled by Anthropic policy or not available to the organization, is omitted from the list. Treat a missing row as "not controllable by this organization's administrators", not as "off". The following abridged example shows three of the rows a response can contain:
Response
```python
{
"type": "effective_organization_settings",
"organization_id": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"settings": [
{
"name": "data_retention_periods",
"type": "data_retention",
"value": {
"chat": {
"type": "fixed",
"timescale": "day",
"duration": 90
}
}
},
{
"name": "content_redaction_enabled",
"type": "boolean",
"value": true
},
{
"name": "ip_allowlist_ip_ranges",
"type": "string_list",
"value": ["10.0.0.0/8", "203.0.113.0/24"]
}
],
"api_keys": [
{
"type": "compliance_api_key",
"id": "apikey_01Hx7k2mP9nQ4rS6tU8vW0xY",
"name": "Compliance Export Key",
"scopes": ["read:compliance_activities", "read:compliance_org_data"],
"is_active": true,
"created_at": "2026-03-14T09:30:00Z",
"created_by_id": "user_01Jz3a4bC5dE6fG7hI8jK9lM",
"expires_at": null
}
]
}
```
Each row carries `name`, `type`, and `value`; the `type` field (`boolean`, `integer`, `string_list`, `provisioning_mode`, or `data_retention`) tells you the shape of `value`. The full list of setting names, and the `value` schema for each type, is in [Get effective organization settings](../Admin/compliance-organizations-settings-retrieve.md) in the API reference.
The `api_keys` array lists every Compliance Access Key configured for your parent organization, so the same list is returned regardless of which linked organization you query. Each entry carries the key's `type` (`compliance_api_key`), `id`, `name`, `scopes`, `is_active` flag, `created_at` and `expires_at` timestamps, and `created_by_id` (the ID of the user who created the key; may be `null`). The key's secret value is never returned. Deactivated keys are included with `is_active: false` so you can review keys that previously had access, and keys that carry only the retired `read:compliance_org_settings` scope remain in the list for audit and cleanup visibility even though that scope no longer grants access.
The top-level `organization_id` is the organization's bare UUID: the same value as `uuid` in the organizations list, not the `org_`-prefixed form that `organization_id` carries on Activity Feed, chat, and project records (see the [organization identifier table](#list-organizations)).
Rows reflect the enforced state rather than the last-stored configuration: for example, `sso_provisioning_mode` reports a configured SCIM mode only while directory sync is enabled, `ip_allowlist_enabled` is `true` only while the allowlist is on and has at least one active range, and `code_execution_network_egress_enabled` is `false` whenever code execution is off.
The response reflects the state at read time; nothing is snapshotted. Changes to most of these settings surface as events in the [Activity Feed](manage-claude-compliance-activity-feed.md); use this endpoint for the current resolved state and the feed to audit who changed what, and when.
## Read plugins and plugin marketplaces
Your organization's plugins and plugin marketplaces are part of the [Admin API](manage-claude-plugins-api.md) rather than the Compliance API, but a Compliance Access Key with `read:compliance_org_data` can call the Plugins API's read endpoints directly:
- List plugins and retrieve a plugin
- List a plugin's versions, retrieve a version, and download a version's files
- List a plugin's installation settings and its shares
- List plugin marketplaces and retrieve a marketplace
Send the `anthropic-beta: ce-plugins-2026-09-01` header on every request; without it these endpoints return [404 Not Found](manage-claude-plugins-api.md#error-responses). Each call reads one organization: a key that covers the parent organization reads the linked organization it was created in unless you pass the `organization_id` query parameter (a linked organization's UUID or its `org_`-prefixed ID) to [read a different one](manage-claude-plugins-api.md#reading-another-organization-under-the-same-parent), and a key restricted to one organization reads only that organization. These calls count against the Admin API's [rate limits](manage-claude-plugins-api.md#rate-limiting), not the Compliance API's.
Creating or changing plugins, their installation settings, or a marketplace's settings requires an Admin API key with `write:plugins`. Of the read calls, only downloading the files of a plugin in a member's personal marketplace records an [Activity Feed](manage-claude-compliance-activity-feed.md) event (`claude_plugin_archive_accessed`). See the [Plugins API guide](manage-claude-plugins-api.md) for each endpoint, request examples, and the [events the API records](manage-claude-plugins-api.md#activity-feed-events).
## Next steps
[Compliance organizations API reference](../Admin/compliance-organizations.md)
The full request and response schema for every organization, user, role, group, and settings endpoint.
[Handle Compliance API errors](manage-claude-compliance-errors.md)
Verbatim error payloads and the fix for each.
---
## Retrieve session transcripts - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/compliance-sessions
Path: 04-API-Reference/Other/manage-claude-compliance-sessions.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fcompliance-sessions)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Compliance API
# Retrieve session transcripts
Copy page
List the sessions your users run in Claude apps and agents, such as Claude Cowork and Claude Code, and retrieve their transcripts through the Compliance API.
Copy page
The endpoints on this page are available only to Claude Enterprise organizations. The local and remote session endpoints are stable for Cowork, Claude Code, and Claude for Microsoft 365 sessions; coverage of Claude Science and Claude in Chrome sessions is in beta. The endpoints work with the same Compliance Access Key and `read:compliance_user_data` scope as the [chat, file, and project endpoints](manage-claude-compliance-content-data.md); no new key, scope, setting, or client update is required. See [Set up the Compliance API](manage-claude-compliance-api-access.md).
**Required scope:** `read:compliance_user_data` on the Compliance Access Key.
**Prerequisite:** None for listing sessions organization-wide. To filter the remote session list (sessions in the cloud) to specific users, you need user IDs from [List organization users](manage-claude-compliance-org-data.md#list-organization-users); the local session list has no user filter.
The endpoints on this page expose transcripts of the sessions your users run in Claude apps and agents (today: Cowork, Claude Code, Claude Science, Claude for Microsoft 365, and Claude in Chrome) from your Claude Enterprise organizations to compliance reviewers. Each session is a single conversation with Claude; its transcript is the sequence of user prompts, assistant responses, and tool calls and results in that conversation. The endpoints support eDiscovery (electronic discovery) exports and data loss prevention (DLP) enforcement.
The Compliance API groups sessions into two endpoint families according to where they run: local session endpoints for sessions on users' machines, and remote session endpoints for sessions that run in the cloud in Anthropic-managed environments. Both families are read-only, and neither is available to Admin API keys (`sk-ant-admin01-...`): calls authenticated with an Admin API key return [403 Forbidden](manage-claude-compliance-errors.md#403-forbidden).
The following table maps each product, and where it runs, to the endpoint family that returns its sessions and the `product_surface` value that identifies them in responses. Products are added to this table as coverage expands.
| Product and where it runs | Endpoint family | `product_surface` |
|------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| Cowork in Claude Desktop, running on the user's machine | Local session endpoints (`/v1/compliance/apps/sessions/local`) | `cowork` |
| Claude Code in the terminal, in Claude Desktop, or in an IDE extension, running on the user's machine | Local session endpoints | `claude_code` |
| Claude Science desktop app, running on the user's machine | Local session endpoints | `claude_science` |
| Claude for Microsoft 365 (the Claude add-ins for Excel, PowerPoint, Word, and Outlook), running in the Microsoft 365 desktop or web apps | Local session endpoints | `office_agents/excel`, `office_agents/powerpoint`, `office_agents/word`, or `office_agents/outlook` (`office_agents` when the app is not identified) |
| Claude in Chrome (the browser extension's built-in chat), running on the user's machine | Local session endpoints | `claude_in_chrome` |
| Cowork sessions started on claude.ai web or mobile, running in the cloud in Anthropic-managed environments | Remote session endpoints (`/v1/compliance/apps/sessions/remote`) | `cowork_remote` |
Capture of local sessions is tied to the Compliance API being enabled for your organization and applies while users are signed in with their Claude Enterprise account. The session endpoints do not return the following:
- Claude Code sessions authenticated with a Claude Console API key, or run through a third-party cloud platform such as Amazon Bedrock, Google Cloud, or Microsoft Foundry.
- [Claude Code cloud sessions](../../02-Claude-Code-CLI/claude-code-on-the-web.md) (including Claude Code routines that run in the cloud), which run on cloud infrastructure instead of the user's machine. These cloud sessions are not remote sessions, even though both run in the cloud; the remote session endpoints return Cowork sessions only.
- Local sessions from products other than Cowork and Claude Code in organizations with [HIPAA readiness](../Guides/build-with-claude-api-and-data-retention.md#hipaa-readiness) enabled. In those organizations, the local session endpoints return Cowork and Claude Code sessions only, and captured session content is stored for 30 days.
- Local sessions for which [zero data retention (ZDR)](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) is in effect. These sessions are excluded from list results, and the retrieve and messages endpoints return 404 for them.
Anthropic recommends the Compliance API for retrieving session content. The following table compares [local sessions](#retrieve-local-sessions) and [remote sessions](#retrieve-remote-sessions) with the OpenTelemetry-based alternatives available for Cowork and Claude Code, [Cowork's OpenTelemetry logging](../../15-Claude-AI-Features/monitor-claude-cowork-activity-with-opentelemetry.md) and [Claude Code monitoring](../../13-Enterprise-Admin/monitoring-usage.md).
| | Local sessions (on users' machines) | Remote sessions (in the cloud) | OpenTelemetry logging |
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| Delivery | Pull: query and export over HTTPS | Pull: query and export over HTTPS | Push: streamed to your OTLP collector |
| Setup | Works with your existing Compliance Access Key | Works with your existing Compliance Access Key | Admin configures an OTLP endpoint and content-capture settings |
| Infrastructure | Anthropic-hosted | Anthropic-hosted | You run the collector and storage |
| ID prefix | `clls_` | `cse_` | N/A |
| `product_surface` values | `cowork`, `claude_code`, `claude_science`, `claude_in_chrome`, and values beginning with `office_agents` | `cowork_remote` | N/A |
| Retention | 6 years by default, or your organization's custom conversation retention period when a finite one is set; 30 days in organizations with HIPAA readiness enabled; held by Anthropic | 6 years, unless a user deletes the session sooner; held by Anthropic | Your infrastructure, your policies |
| User prompts and assistant responses | Yes | Yes | Yes, subject to content-capture settings |
| Tool inputs | Truncated to 10,000 bytes per input by default; up to about 1 MiB on request | Truncated to 10,000 bytes per input by default; up to about 1 MiB on request | Truncated summaries |
| Tool result content | Each text entry truncated to 10,000 bytes by default; up to about 1 MiB on request | Each text entry truncated to 10,000 bytes by default; up to about 1 MiB on request | Metadata such as size and success; Claude Code can also capture content with an optional, size-capped setting |
| File contents | Yes, through transcript tool calls (text only; other content appears as a placeholder) | Yes, through transcript tool calls (text only; other content is omitted) | File paths; Claude Code can also capture contents with an optional, size-capped setting |
| Host and device metadata (terminal type, workspace paths) | No | No | Yes |
| Token usage and cost | No; available through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api) | No; available through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api) | Yes |
## Sessions on users' machines (local sessions)
Local sessions run on users' machines while they are signed in with their Claude Enterprise account: today, Cowork in Claude Desktop, Claude Code (in the terminal, in Claude Desktop, or in an IDE extension), the Claude Science desktop app, Claude for Microsoft 365 (in Excel, PowerPoint, Word, and Outlook), and the Claude in Chrome browser extension.
The Compliance API exposes local sessions through three endpoints: `GET /v1/compliance/apps/sessions/local` lists session metadata, `GET /v1/compliance/apps/sessions/local/{session_id}` retrieves one session's metadata, and `GET /v1/compliance/apps/sessions/local/{session_id}/messages` returns one session's transcript. All three require the `read:compliance_user_data` scope and count only against the shared Compliance API rate limit; they are not subject to the second request budget that applies to the remote session endpoints. See [429 Too Many Requests](manage-claude-compliance-errors.md#429-too-many-requests). If local sessions are not available to your parent organization, all three endpoints return 404 with the message `Local sessions are not available.` (see [Local session not found](manage-claude-compliance-errors.md#local-session-not-found)); while session listings or captured content are temporarily unavailable, they return 503 (see [Local sessions temporarily unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable)).
For local sessions, Anthropic records each conversation server-side as its requests reach the Claude API; nothing is installed on the device, and nothing is collected beyond the requests the client already sends to the Claude API. Local session transcripts show what Claude was asked to do and what it returned, not what happened on the device. File and network activity is visible only through the tool calls and tool results in the transcript, so activity that never reaches the API (for example, local files the session never sent) is not captured.
In organizations that use [customer-managed encryption keys](manage-claude-cmek.md), local session transcripts are encrypted under your customer-managed key and returned as usual. While that key cannot be used (for example, because you disabled or revoked it, or because it cannot be reached), the messages endpoint returns [503 Service Unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable) for the affected pages instead of transcript content. Those messages are never reported as `not_captured` (see [Retrieve a local session transcript](#retrieve-a-local-session-transcript)). Listing sessions and retrieving session metadata are not affected.
The list endpoint returns session metadata, with no transcript content, for every linked organization your key can read. Unlike the remote session list, it has no organization or user filters: bound the results in time with the `created_at.gte` and `created_at.lt` parameters. Both take RFC 3339 timestamps with a required UTC offset, and when both are supplied, `created_at.lt` must be strictly after `created_at.gte` or the request returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request). A third time filter, `updated_at.gte`, bounds by last activity instead of first: it returns sessions whose last inference call is at or after the given time and combines with the `created_at` filters without changing the ordering or pagination. Use it to poll for sessions active since a previous pass, as described later in this section. New sessions and messages appear in results after a short processing delay, typically within minutes; a session that is missing immediately after it starts is not necessarily uncaptured. The following request lists sessions created since a given date.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/apps/sessions/local" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "created_at.gte=2026-07-01T00:00:00Z" \
--data-urlencode "limit=100"
```
Response
```python
{
"data": [
{
"type": "compliance_local_session",
"id": "clls_01HxKpLmNoPqRsTuVwXyZaBc",
"organization_uuid": "9a1e0000-0000-0000-0000-000000000000",
"workspace_id": "wrkspc_01SvYKoWVRVHoEbwESNvzYdR",
"user": {
"id": "user_01GpKpLmNoPqRsTuVwXyZaBc",
"email_address": "engineer@example.com"
},
"product_surface": "cowork",
"created_at": "2026-07-09T14:02:11Z",
"updated_at": "2026-07-09T14:02:38Z"
},
{
"type": "compliance_local_session",
"id": "clls_01HyLqMnOpQrStUvWxYzAbCd",
"organization_uuid": "9a1e0000-0000-0000-0000-000000000000",
"workspace_id": null,
"user": {
"id": "user_01HqRsTuVwXyZaBcDeFgHiJk",
"email_address": null
},
"product_surface": "claude_code",
"created_at": "2026-07-08T09:15:43Z",
"updated_at": "2026-07-08T09:52:10Z"
}
],
"next_page": "page_AAEfQx7mPdLkq9Rt2VwHbZk"
}
```
Results are sorted in reverse chronological order (newest first) by `created_at`, with ties broken in a fixed server-side order, and capped at `limit` results per response (default 100, max 500). The endpoint paginates forward only with `page` and `next_page` tokens (see [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results)): pass the response's `next_page` value back as the `page` query parameter on the next request, and stop when `next_page` is `null`. The response has no `has_more` field. Complete a list walk within 24 hours of starting it; an older list cursor is still accepted but is re-evaluated against the current retention boundary, so sessions whose oldest retained activity is about to age out of the retention period can be skipped.
In each session object, `user.id` is always set and survives account deletion; `user.email_address` is `null` when the user's account has been deleted or the user is no longer a member of an organization your key can read. `workspace_id` is `null` when the session was not associated with a workspace. A local session corresponds to one client session ID: starting a new conversation in the client, or clearing its context, begins a new session record. For Claude Science, the list can also include separate sessions for the app's own background work (for example, naming the conversation; on newer app versions also its reviewer and delegation tracks), and on older app versions some of that background work appears as extra messages inside the conversation's own transcript. A Claude Science conversation that continues across some app updates appears as two sessions. These behaviors are expected. Treat `id` values as opaque strings; the format may change without notice.
For Claude for Microsoft 365, deleting a conversation in the add-in happens only on the client, so it is not reflected in the API: local sessions have no `deleted_at` field, and the session stays listed until retention removes it.
Local sessions carry an `updated_at` but no `status`: a local session has no server-side lifecycle status, and its visibility is governed by retention instead. A local session is captured as the series of Claude API calls (inference calls) that the client makes during the session, and retention applies to each captured call individually. `created_at` is the timestamp of the session's earliest retained call and `updated_at` the timestamp of its last, both UTC. As older calls age past the retention period, `created_at` advances accordingly, and once every call in a session has aged out, the session is no longer returned; `updated_at` tracks the most recent call and is unaffected until then. Because `created_at` can shift between runs, deduplicate on `id` when you re-walk the list over time. To keep transcripts current as sessions gain messages, poll with the `updated_at.gte` filter, overlapping consecutive windows. On the list endpoint `updated_at` is a lower bound: for a session still active at a page or `created_at.lt` window boundary it can momentarily lag the session's true last activity, and a new call only becomes queryable after the short processing delay noted earlier. Because of that lag, set each run's `updated_at.gte` a few minutes before your previous run's start time, not to the previous run's time exactly. A bound set to the exact previous time silently and permanently drops a session whose final call was still indexing at that moment, because once the bound advances past that call no later run returns it. Deduplicate the returned sessions on `id`, re-fetch their transcripts, and deduplicate messages on `id`. Retrieving a session, or its messages, always reflects the exact latest retained call, so a periodic reconciliation pass over an older window is a more thorough alternative to widening the overlap.
The list is built from session activity metadata, so it can include sessions whose transcript content was not captured, for example sessions that ran before capture began for your organization (as far back as your retention period allows); the transcript of such a session returns each message with its content marked unavailable (see [Retrieve a local session transcript](#retrieve-a-local-session-transcript)).
Captured local session content is stored for 6 years from capture by default. If the organization that ran the session has set a finite custom conversation retention period in [claude.ai \> Organization settings \> Data and privacy](https://claude.ai/admin-settings/data-privacy-controls), that period applies instead, whether it is shorter or longer than the default; when the organization has more than one custom retention period configured, the shortest applies. A change to that setting takes effect in two different ways: the endpoints stop returning activity older than the organization's current period as soon as the setting changes, whereas each captured message is stored for the period that was in effect when it was captured, so lengthening the period later does not restore content that has already expired. In organizations with [HIPAA readiness](../Guides/build-with-claude-api-and-data-retention.md#hipaa-readiness) enabled, captured local session content is stored for 30 days from capture, or for the organization's custom conversation retention period when that is shorter; the 6-year default does not apply.
To fetch one session's metadata directly, pass its ID to `GET /v1/compliance/apps/sessions/local/{session_id}`. The response is the same session object the list endpoint returns, with no envelope and no transcript content. A malformed session ID returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request). A single [404 Not Found](manage-claude-compliance-errors.md#404-not-found) covers four cases that the response does not distinguish: the session is not in an organization your key can read (including sessions under another parent organization), it does not exist, zero data retention is in effect for it, or every call in it has aged past retention.
`product_surface` (string or `null`) identifies the product that created the session: `cowork` (Cowork in Claude Desktop on the user's machine), `claude_code` (Claude Code), `claude_science` (Claude Science), `claude_in_chrome` (the Claude in Chrome browser extension's built-in chat), or one of `office_agents/excel`, `office_agents/powerpoint`, `office_agents/word`, and `office_agents/outlook` (Claude for Microsoft 365, by app; `office_agents` alone when the app is not identified). New values appear as coverage expands.
**Build forward-compatible handlers.** Pass through unrecognized `product_surface` values, and ignore fields your handler does not expect, so your integration keeps working as new product surfaces ship.
### Retrieve a local session transcript
The messages endpoint returns the session's transcript, reconstructed from the captured Claude API calls: user prompts, assistant text, tool calls, and the text portions of tool results, all returned as they were sent apart from size truncation. Nothing masks URLs, credentials, or personal data in that content, so treat transcripts as sensitive. The transcript omits or replaces the following:
- Thinking blocks are never included.
- The request's system prompt is never returned. A marker message reading `[system prompt content not shown]` stands in for it (normally once per session; a session with no captured content carries no marker).
- Tool definitions and MCP server configuration are not part of the transcript.
- Images, PDFs, and other binary or structured blocks are not returned. Each appears as a `text` block reading `[<block type> content not shown]` (for example, `[image content not shown]`) with `truncated` set to `true`. Non-text items inside a tool result, such as [web search](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md) results or the output of the [code execution tool](../Agents-Tools/agents-and-tools-tool-use-code-execution-tool.md), are replaced by one `[N non-text item(s) not shown]` entry, and the tool result block's `truncated` is `true`. The matching tool call, with the search query or the code in its `input`, is still returned.
- Citation metadata on `text` blocks, such as the source citations on an answer that draws on web search results, is omitted. The text itself is returned, and the block carries `truncated` set to `true`.
Project instruction files such as `CLAUDE.md` appear as ordinary user-role content. Skill content appears when the client sends it as message content and is not distinguished from other user text. For a coverage summary, see the [Compliance API FAQ](manage-claude-compliance-faq.md#data-coverage-and-retention); for a table comparing local sessions with remote sessions and OpenTelemetry logging, see this page's introduction.
cURL
```python
session_id="clls_01HxKpLmNoPqRsTuVwXyZaBc"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/apps/sessions/local/$session_id/messages" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Response
```python
{
"session": {
"type": "compliance_local_session",
"id": "clls_01HxKpLmNoPqRsTuVwXyZaBc",
"organization_uuid": "9a1e0000-0000-0000-0000-000000000000",
"workspace_id": "wrkspc_01SvYKoWVRVHoEbwESNvzYdR",
"user": {
"id": "user_01GpKpLmNoPqRsTuVwXyZaBc",
"email_address": null
},
"product_surface": "cowork",
"created_at": "2026-07-09T14:02:11Z",
"updated_at": "2026-07-09T14:02:38Z"
},
"data": [
{
"type": "compliance_local_session_message",
"id": "clsm_01J4KpLmNoPqRsTuVwXyZaBa",
"role": "user",
"model": null,
"created_at": "2026-07-09T14:02:11Z",
"provenance": {
"type": "synthetic_marker"
},
"content": [
{
"type": "text",
"text": "[system prompt content not shown]",
"truncated": true
}
]
},
{
"type": "compliance_local_session_message",
"id": "clsm_01J4KpLmNoPqRsTuVwXyZaBc",
"role": "user",
"model": null,
"created_at": "2026-07-09T14:02:11Z",
"provenance": null,
"content": [
{
"type": "text",
"text": "Fix the failing test in tests/auth_test.py",
"truncated": false
}
]
},
{
"type": "compliance_local_session_message",
"id": "clsm_01J4KpLmNoPqRsTuVwXyZaBd",
"role": "assistant",
"model": "claude-opus-5-5",
"created_at": "2026-07-09T14:02:11Z",
"provenance": null,
"content": [
{
"type": "text",
"text": "I'll read the test file first.",
"truncated": false
},
{
"type": "tool_use",
"id": "toolu_01AbCdEfGhIjKlMnOpQrSt",
"name": "Read",
"input": "{\"file_path\":\"tests/auth_test.py\"}",
"truncated": false
}
]
},
{
"type": "compliance_local_session_message",
"id": "clsm_01J4KpLmNoPqRsTuVwXyZaBe",
"role": "user",
"model": null,
"created_at": "2026-07-09T14:02:38Z",
"provenance": null,
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01AbCdEfGhIjKlMnOpQrSt",
"name": "Read",
"is_error": false,
"content": [
{
"type": "text",
"text": "def test_login_expiry():\n ..."
}
],
"truncated": false
}
]
},
{
"type": "compliance_local_session_message",
"id": "clsm_01J4KpLmNoPqRsTuVwXyZaBf",
"role": "assistant",
"model": "claude-opus-5-5",
"created_at": "2026-07-09T14:02:38Z",
"provenance": null,
"content": [
{
"type": "text",
"text": "The test was asserting on a stale expiry timestamp. I've updated it.",
"truncated": false
}
]
}
],
"next_page": null
}
```
The response embeds a `session` envelope alongside the paginated `data` array. The first record in this example is the marker that stands in for the request's system prompt; its `provenance` is described later in this section. On this endpoint `user.email_address` is always `null`: the messages endpoint does not resolve email addresses, so a `null` here does not mean the user's account was deleted. To attribute a session to an email address, join `user.id` against the [list endpoint](#retrieve-local-sessions) or the retrieve endpoint (`GET /v1/compliance/apps/sessions/local/{session_id}`).
Messages are returned oldest first by default; pass `order=desc` to reverse. Pagination uses the same `page`/`next_page` scheme as the list endpoint, with a `limit` default of 100 and a max of 1,000. A page can end early when the response reaches its size limit, so a page with fewer than `limit` messages does not mean you have reached the end; keep paginating until `next_page` is `null`. Page cursors are bound to the session and sort order they were issued under, and a walk's cursors expire 24 hours after its first page: an expired cursor returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request) telling you to restart without the `page` parameter, and the restarted walk reflects the current retention boundary. A cursor issued for a different session or `order` also returns 400, as an invalid cursor.
On a very large session, the messages endpoint can return a 400 for a page, with a message saying that the page is too large to read; see [Transcript page too large](manage-claude-compliance-errors.md#transcript-page-too-large). Do not retry that request. If it used `order=desc`, read that session oldest first from its first page instead, with no `order` and no `page`, and set your client's request timeout to at least 5 minutes. If an oldest-first page returns the same error, that errors guide entry says what to do.
On a very large session, the messages endpoint might also return a 429 whose `error.details.error_code` is `transcript_read_server_busy`. It does not mean that your organization exceeded a rate limit. Unlike the 400, retry it: wait the number of seconds in the `retry-after` header, then send the same request again, unchanged (with the same `page` value, if it had one). See [Server busy reading large transcripts](manage-claude-compliance-errors.md#server-busy-reading-large-transcripts).
Each message carries a `role` (`user` or `assistant`) and a `content` array of `text`, `tool_use`, and `tool_result` blocks. It also carries a `model`: on an assistant turn captured from the Claude API this is the model that served the turn, and it is `null` on user messages and on any assistant message whose `provenance` is set, because client-asserted history and synthetic markers were not produced by a model and the serving model is unknown for unavailable content. A `text` block carries `text` and `truncated`. A `tool_use` block carries `id`, `name`, `input`, and `truncated`, where `input` is a JSON-encoded string rather than an object. A `tool_result` block carries `tool_use_id`, `name`, `is_error`, a `content` array of `text` entries, and `truncated`. MCP tool calls and results, and most server tool calls and results, are normalized into these same `tool_use` and `tool_result` shapes; any other block type appears as a `[<block type> content not shown]` placeholder. A message `id` is stable while the turn is retained. Every message reconstructed from the same inference call carries that call's timestamp, so consecutive messages often share a `created_at` value; preserve the returned order rather than re-sorting by timestamp.
Each message also carries a `provenance` field describing how its content was captured. `provenance` is `null` for verified content captured by the Claude API, which is the common case. Otherwise it is an object whose `type` marks the exception:
- `content_unavailable` means the content cannot be returned. The `content` array is empty, and `provenance.reason` states why. `not_captured` means no content is available for the turn. It does not prove that no record was stored: content that Anthropic's data-handling policies withhold from the Compliance API is reported with the same reason, and so are individual turns within an otherwise captured session that are unavailable for such reasons. An unusable customer-managed key is the one exception and returns [503 Service Unavailable](manage-claude-compliance-errors.md#local-sessions-temporarily-unavailable) instead. `client_aborted` means the client closed the connection or cancelled the request before the response completed, so the turn's response was not captured; any partial output already streamed to the client is not included, and this reason applies to assistant-role turns only. `cmek_key_revoked` is reserved for content encrypted under your organization's customer-managed key when that key is unavailable (for example, revoked). It is not currently returned, because an unusable key produces a 503 instead, but handle it for forward compatibility. `retention_elapsed` means the content aged past retention. `oversize` means a single message exceeded the per-message size bound; the message is still returned, with an empty `content` array.
- `client_asserted` marks assistant messages that the client supplied as conversation history and that could not be matched to a captured response; their authorship is not verified.
- `synthetic_marker` marks records generated by the endpoint itself, such as the marker that stands in for the system prompt. When the client rewrites or compacts its conversation history mid-session (for example, after context compaction), the transcript inserts a marker message at that point and continues with the new content the client sent. When your organization has a finite retention period and that new content includes assistant messages, the transcript withholds the new content up to and including its last assistant message (a second marker notes this) and shows only the user messages after that point, followed by the rest of the session.
Marker and client-asserted messages begin with a bracketed explanatory `text` block flagged `truncated: true`, for example `[system prompt content not shown]`. Treat these records as present but unavailable or unverified rather than missing, and tolerate unrecognized `provenance` types and reasons.
Two parameters cap how many bytes of each tool block are returned: `tool_use_input_max_bytes` and `tool_result_max_bytes`, both defaulting to 10,000 bytes. Pass `-1` for the server maximum (about 1 MiB per string); `0` returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request), and values above the maximum are clamped to it. A string cut off by either cap is cut on a character boundary and has an in-band suffix appended (for example, `…[truncated; pass tool_result_max_bytes=-1 for the server max]`), and its block carries `"truncated": true`. A truncated `tool_use` `input` is therefore no longer valid JSON, so parse tool inputs only from untruncated blocks (or raise the cap and refetch). Blocks of type `text` are always capped at the same server maximum of about 1 MiB; no parameter raises it, and a `text` block at the bound also carries `"truncated": true`.
Claude Science calls connectors (MCP servers) from code that it runs through its `repl` tool, not as separately named tools, so no block in a Claude Science transcript is named after a connector. Each connector call appears in the code inside a `repl` `tool_use` block's `input` (for example, a `host.mcp("<server>", "<tool>", ...)` call), and connector output appears in the matching `tool_result` only where that code printed it. Cowork and Claude Code sessions differ: they call each connector tool under its own `mcp__<server>__<tool>` name, which is the `name` of the `tool_use` block. To monitor connector use in Claude Science sessions, parse the `input` string and match on the code it contains rather than on a tool name. Pass `tool_use_input_max_bytes=-1` for these sessions so that a long code input is returned up to the server maximum instead of being cut at the 10,000-byte default before the connector call appears.
Transcript content honors the retention period described under [Sessions on users' machines](#retrieve-local-sessions). When the start of a session has aged past it, the transcript begins with a single `content_unavailable` placeholder with `reason` of `retention_elapsed`, and the retained messages follow. When every call in a session has aged out, the messages endpoint returns [404 Not Found](manage-claude-compliance-errors.md#404-not-found), as it does for sessions in organizations your key cannot read, sessions that do not exist, and sessions for which zero data retention is in effect. A malformed session ID returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request).
## Sessions in the cloud (remote sessions)
Cowork sessions started on claude.ai web or mobile run in the cloud in Anthropic-managed environments. The Compliance API exposes these remote sessions through two endpoints: `GET /v1/compliance/apps/sessions/remote` lists session metadata, and `GET /v1/compliance/apps/sessions/remote/{session_id}/messages` returns one session's transcript. Both require the `read:compliance_user_data` scope, and both count against the shared Compliance API rate limit plus a second request budget specific to these endpoints; see [429 Too Many Requests](manage-claude-compliance-errors.md#429-too-many-requests).
The list endpoint defaults to organization-wide scope: leave off `organization_ids[]` to include every claude.ai organization your key can read, or pass up to 500 values to narrow the scope. To scope the list to specific users instead, pass 1–10 `user_ids[]` values (obtain the IDs from [List organization users](manage-claude-compliance-org-data.md#list-organization-users)); the filter matches the session's owning user, so agent-owned sessions are excluded whenever `user_ids[]` is set. Bound the results in time with `created_at` range parameters (`gte`, `gt`, `lt`, `lte`, in RFC 3339 format). There is no `updated_at` filter. The following request lists sessions created since a given date.
cURL
```python
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/apps/sessions/remote" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "created_at.gte=2026-06-01T00:00:00Z" \
--data-urlencode "limit=100"
```
Response
```python
{
"data": [
{
"id": "cse_01WpQrStUvXyZaBcDeFgHjK6",
"organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"user": {
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email_address": "user@example.com"
},
"agent_id": null,
"started_by_user": null,
"status": "active",
"created_at": "2026-07-01T17:04:05Z",
"updated_at": "2026-07-01T18:00:41Z",
"product_surface": "cowork_remote",
"claude_project_id": "claude_proj_01KGp4eZNug9ri4kE35RSppq"
},
{
"id": "cse_01TkNpRsUvWxYzAbCdEfGhJ4",
"organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"user": null,
"agent_id": "cagt_01MnPqRsTuVwXyZaBcDeFgH8",
"started_by_user": {
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email_address": "user@example.com"
},
"status": "archived",
"created_at": "2026-06-28T09:15:22Z",
"updated_at": "2026-06-28T09:47:10Z",
"product_surface": "cowork_remote",
"claude_project_id": null
}
],
"next_page": "page_AAEfMk93cXpYdGxrZXk"
}
```
Results are sorted in reverse chronological order (newest first) by `created_at` and capped at `limit` results per response (default 100, max 500). The endpoint paginates with `page` and `next_page` tokens (see [Paginate results](manage-claude-compliance-activity-feed.md#paginate-results)): pass the response's `next_page` value back as the `page` query parameter on the next request, and stop when `next_page` is `null`.
A session is owned by either a user or an agent, never both. For user-owned sessions, `user` carries the owner's ID and email address (`email_address` is `null` when the user is no longer a member of an organization your key can read) and `agent_id` is `null`. For agent-owned sessions (for example, runs of a Cowork scheduled task in the cloud), `user` is `null`, `agent_id` carries the agent's ID (prefix `cagt_`), and `started_by_user` identifies the human who initiated the run; on user-owned sessions, `started_by_user` is `null`. Each run of a scheduled task is a separate session, and no session field carries the task's name or schedule.
`claude_project_id` is the ID of the claude.ai [project](manage-claude-compliance-content-data.md#retrieve-projects-and-attachments) the session belongs to (prefix `claude_proj_`), or `null` when the session is not in a project.
`status` is one of `pending`, `active`, `paused`, `archived`, or `failed`. A session is `pending` while it is being provisioned; a `pending` session has no transcript yet, and the messages endpoint returns 404 for it until provisioning completes. Sessions that have been deleted are never returned.
`product_surface` (string or `null`) identifies the product that created the session. The endpoint currently returns only sessions with `product_surface` of `cowork_remote`: Cowork sessions started on claude.ai web or mobile.
**Build forward-compatible handlers.** Pass through unrecognized `status` and `product_surface` values, and ignore fields your handler does not expect, so your integration keeps working as new statuses and product surfaces ship.
### Retrieve a remote session transcript
The messages endpoint returns the session's transcript: user prompts, assistant responses, and tool calls and results. Thinking blocks and images are not included. For a coverage summary, see the [Compliance API FAQ](manage-claude-compliance-faq.md#data-coverage-and-retention); for a table comparing remote sessions with local sessions and Cowork's OpenTelemetry logging, see this page's introduction.
cURL
```python
session_id="cse_01WpQrStUvXyZaBcDeFgHjK6"
curl --fail-with-body -sS \
"https://api.anthropic.com/v1/compliance/apps/sessions/remote/$session_id/messages" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01"
```
Response
```python
{
"session": {
"id": "cse_01WpQrStUvXyZaBcDeFgHjK6",
"organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
"user": {
"id": "user_01XyDMpzjS89pFZXqSFUBDr6",
"email_address": null
},
"agent_id": null,
"started_by_user": null,
"status": "active",
"created_at": "2026-07-01T17:04:05Z",
"updated_at": "2026-07-01T18:00:41Z",
"product_surface": "cowork_remote",
"claude_project_id": null
},
"data": [
{
"id": "csev_01HjKmNpQrStUvWxYzAbCdE2",
"role": "user",
"created_at": "2026-07-01T17:04:05Z",
"content": [
{
"type": "text",
"text": "Summarize the customer feedback in the attached spreadsheet.",
"truncated": false
}
],
"sent_by_user_id": null,
"content_unavailable": false
},
{
"id": "csev_01BcDeFgHjKmNpQrStUvWxY4",
"role": "assistant",
"created_at": "2026-07-01T17:04:06Z",
"content": [
{
"type": "text",
"text": "I'll start by reading the spreadsheet...",
"truncated": false
}
],
"sent_by_user_id": null,
"content_unavailable": false
}
],
"next_page": null
}
```
The response embeds a `session` envelope alongside the paginated `data` array. On this endpoint the envelope always has `user.email_address`, `started_by_user`, and `claude_project_id` set to `null`; get those values from the list endpoint instead.
Messages are returned oldest first by default; pass `order=desc` to reverse. Pagination uses the same `page`/`next_page` scheme as the list endpoint, with a `limit` default of 100 and a max of 1,000. A page can end early when the response reaches its size limit, so a page with fewer than `limit` messages does not mean you have reached the end; keep paginating until `next_page` is `null`.
Each message carries a `role` (`user` or `assistant`) and a `content` array of `text`, `tool_use`, and `tool_result` blocks. Message `created_at` values are commit timestamps: consecutive messages can share a timestamp or slightly invert, so preserve the returned order rather than re-sorting by `created_at`. On agent-owned sessions, `sent_by_user_id` records the user who sent a given user message when one is attributable; it is `null` otherwise, including on all assistant messages. When a message's content cannot be returned at all (for example, it exceeds size bounds), the message carries `content_unavailable` set to `true`.
Two parameters cap how many bytes of each tool block are returned: `tool_use_input_max_bytes` and `tool_result_max_bytes`, both defaulting to 10,000 bytes. Pass `-1` for the server maximum (about 1 MiB per string); `0` returns [400 Bad Request](manage-claude-compliance-errors.md#400-bad-request). A block cut off by either cap carries `"truncated": true`, and a truncated `tool_use` input is no longer valid JSON, so parse tool inputs only from untruncated blocks (or raise the cap and refetch).
The messages endpoint returns [404 Not Found](manage-claude-compliance-errors.md#404-not-found) for `pending` sessions, sessions that do not exist or have been deleted, and sessions in organizations your key cannot read.
## Retention and deletion
The session endpoints are read-only; local and remote sessions cannot be deleted through the Compliance API. Local session transcripts are retained for 6 years by default, or your organization's custom conversation retention period when a finite one is set, or 30 days in organizations with HIPAA readiness enabled, as described under [Sessions on users' machines](#retrieve-local-sessions). Remote session transcripts are retained for 6 years, unless a user deletes the session sooner. The remote session endpoints no longer return a session once a user deletes it, and its transcript is not recoverable through the Compliance API. To learn how these periods sit alongside Anthropic's other retention arrangements, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md).
## Next steps
[Retrieve and delete chats, files, and projects](manage-claude-compliance-content-data.md)
Access claude.ai chat content, file attachments, and projects with the same Compliance Access Key.
[Compliance API FAQ](manage-claude-compliance-faq.md#data-coverage-and-retention)
A field-by-field summary of what session transcripts include, and other common questions.
[Handle Compliance API errors](manage-claude-compliance-errors.md)
Verbatim error payloads and the fix for each.
[API reference](../Admin/compliance-apps.md)
Endpoint paths, parameters, and response schemas for the Compliance API.
---
## Data residency - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/data-residency
Path: 04-API-Reference/Other/manage-claude-data-residency.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fdata-residency)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Data & compliance
# Data residency
Copy page
Manage where model inference runs and where data is stored with geographic controls.
Copy page
Data residency controls let you manage where your data is processed and stored. Two independent settings govern this:
- **Inference geo:** Controls where model inference runs, on a per-request basis. Set through the `inference_geo` API parameter or as a workspace default.
- **Workspace geo:** Controls where data is stored at rest and where endpoint processing (such as image transcoding and code execution) happens. Configured at the workspace level in the [Claude Console](usage-limits.md).
[Claude Managed Agents](managed-agents-overview.md) supports geographic pinning at the agent level: `inference_geo` on an [agent's model configuration](managed-agents-agent-setup.md#pin-the-inference-geo) pins the geography that serves model requests for sessions running that agent, with [per-session overrides](managed-agents-sessions.md#pin-the-inference-geo-for-a-session) at session create. Agents without a pin follow the workspace's default inference geo on each request. Managed Agents also respects the Workspace geo configured in Console, and with [self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md), tool execution and the sandbox filesystem stay on infrastructure you control; the contents of attached [memory stores](managed-agents-self-hosted-sandboxes-memory.md) remain stored by Anthropic and are copied to your sandbox for the session.
## Inference geo
To learn how zero data retention (ZDR) applies to this feature, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md).
The `inference_geo` parameter controls where model inference runs for a specific API request. Add it to any `POST /v1/messages` call.
| Value | Description |
|:-----------|:------------------------------------------------------------------------------------------------|
| `"global"` | Default. Inference may run in any available geography for optimal performance and availability. |
| `"us"` | Inference runs only in US-based infrastructure. |
### API usage
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
inference_geo="us",
messages=[
{"role": "user", "content": "Summarize the key points of this document."}
],
)
for block in response.content:
if block.type == "text":
print(block.text)
# Check where inference actually ran
print(f"Inference geo: {response.usage.inference_geo}")
```
### Response
The response `usage` object includes an `inference_geo` field indicating where inference ran:
Output
```python
{
"usage": {
"input_tokens": 25,
"output_tokens": 150,
"inference_geo": "us"
}
}
```
### Model availability
The `inference_geo` parameter is supported on Claude 4.6 and later models. Requests with `inference_geo` on Claude Opus 4.5, Claude Sonnet 4.5, Claude Haiku 4.5, or earlier models return a 400 error.
The `inference_geo` parameter is available on the Claude API (first-party) and [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md). On Amazon Bedrock and Google Cloud, the inference region is determined by the endpoint URL or inference profile, so `inference_geo` is not applicable. On [Claude in Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md), `inference_geo` is likewise not applicable: deployments hosted on Azure can instead use the US Data Zone Standard deployment type, which keeps inference within the United States. The `inference_geo` parameter is also not available through the [OpenAI SDK compatibility endpoint](cli-sdks-libraries-libraries-openai-sdk.md).
### Workspace-level restrictions
Workspace settings also support restricting which inference geos are available:
- **`allowed_inference_geos`:** Restricts which geos a workspace can use. If a request specifies an `inference_geo` not in this list, the API returns an error.
- **`default_inference_geo`:** Sets the fallback geo when `inference_geo` is omitted from a request. Individual requests can override this by setting `inference_geo` explicitly.
These settings can be configured through the Console or the [Admin API](manage-claude-admin-api.md) under the `data_residency` field.
## Workspace geo
Workspace geo is set when you create a workspace and can't be changed afterward. Currently, `"us"` is the only available workspace geo.
To set workspace geo, create a new workspace in the [Console](usage-limits.md):
1. Go to **Settings** \> **Workspaces**.
2. Create a new workspace.
3. Select the workspace geo.
**Claude Platform on AWS:** Workspace geo is not configurable. Claude Managed Agents sessions on this platform run with an effective Workspace geo of `"us"`, which is currently the only available workspace geo. See [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md) for data residency considerations specific to that platform.
## Pricing
Data residency pricing varies by model generation:
- **Claude 4.6 and later models:** US-only inference (`inference_geo: "us"`) is priced at 1.1x the standard rate across all token pricing categories (input tokens, output tokens, cache writes, and cache reads).
- **Global routing** (`inference_geo: "global"`): Standard pricing applies.
- **Older models:** Don't support `inference_geo` (see [Model availability](#model-availability)); standard pricing applies. Requests that include the parameter return a 400 error.
This pricing applies to the Claude API (first-party) and Claude Platform on AWS. On Claude in Microsoft Foundry, the same 1.1x multiplier applies to deployments hosted on Azure that use the US Data Zone Standard deployment type. Partner-operated platforms (Bedrock and Google Cloud) have their own regional pricing. See [Data residency pricing](../../17-Billing-Plans/about-claude-pricing.md#data-residency-pricing) for details.
The same multiplier applies to [Claude Managed Agents](managed-agents-overview.md): when an agent's [model configuration](managed-agents-agent-setup.md) pins `inference_geo` to `"us"`, model requests in sessions running that agent are priced at 1.1x the standard rate.
If you have a [Priority Tier](../Endpoints/service-tiers.md) commitment, the 1.1x multiplier for US-only inference also affects how tokens are counted against your Priority Tier capacity. Each token consumed with `inference_geo: "us"` draws down 1.1 tokens from your committed TPM, consistent with how other pricing multipliers (such as prompt caching) affect burndown rates.
## Batch API support
The `inference_geo` parameter is supported on the [Batch API](../Guides/build-with-claude-batch-processing.md). Each request in a batch can specify its own `inference_geo` value.
## Migration from legacy opt-outs
If your organization previously opted out of global routing to keep inference in the US, your workspace has been automatically configured with `allowed_inference_geos: ["us"]` and `default_inference_geo: "us"`. No code changes are required. Your existing data residency requirements continue to be enforced through the new geo controls.
### What changed
The legacy opt-out was an organization-level setting that restricted all requests to US-based infrastructure. The new data residency controls replace this with two mechanisms:
- **Per-request control:** The `inference_geo` parameter lets you specify `"us"` or `"global"` on each API call, giving you request-level flexibility.
- **Workspace controls:** The `default_inference_geo` and `allowed_inference_geos` settings in the Console let you enforce geo policies across all keys in a workspace.
### What happened to your workspace
Your workspace was migrated automatically:
| Legacy setting | New equivalent |
|:---------------------------------|:----------------------------------------------------------------|
| Global routing opt-out (US only) | `allowed_inference_geos: ["us"]`, `default_inference_geo: "us"` |
All API requests using keys from your workspace continue to run on US-based infrastructure. No action is needed to maintain your current behavior.
### If you want to use global routing
If your data residency requirements have changed and you want to take advantage of global routing for better performance and availability, update your workspace's inference geo settings to include `"global"` in the allowed geos and set `default_inference_geo` to `"global"`. See [Workspace-level restrictions](#workspace-level-restrictions) for details.
### Pricing impact
Legacy models are unaffected by this migration. For current pricing on newer models, see [Pricing](#pricing).
## Current limitations
- **Shared rate limits:** Rate limits are shared across all geos.
- **Inference geo:** Only `"us"` and `"global"` are available.
- **Workspace geo:** Only `"us"` is currently available. Workspace geo can't be changed after workspace creation.
## Next steps
[Pricing](../../17-Billing-Plans/about-claude-pricing.md#data-residency-pricing)
View data residency pricing details.
[Workspaces](manage-claude-workspaces.md)
Learn about workspace configuration.
[Usage and Cost API](manage-claude-usage-cost-api.md)
Track usage and costs by data residency.
---
## Configure Inference hooks - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/inference-hooks-configuration
Path: 04-API-Reference/Other/manage-claude-inference-hooks-configuration.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Finference-hooks-configuration)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
[Overview](manage-claude-inference-hooks.md)[Configure Inference hooks](manage-claude-inference-hooks-configuration.md)[Build an integration](manage-claude-inference-hooks-endpoint.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Inference hooks
# Configure Inference hooks
Copy page
Allow Inference hooks for your Claude Enterprise organization, connect your AI security server, and control enforcement, failure handling, and rollout.
Copy page
Inference hooks are in beta and available to Claude Enterprise organizations. Configuring them requires the `organization:manage` permission, which only the Owner and Primary owner roles hold.
Inference hooks send prompts from your organization to an AI security server you choose, and hold each request for an allow or deny verdict before Claude processes it. This page walks through turning the feature on, connecting your server, and controlling enforcement. To learn what Inference hooks are and when to use them, see the [Inference hooks overview](manage-claude-inference-hooks.md). To build the AI security server itself, see [Develop an Inference hooks integration](manage-claude-inference-hooks-endpoint.md).
## Before you begin
You need:
- The `organization:manage` permission in claude.ai, which only the **Owner** and **Primary owner** roles hold. The **Admin** role doesn't have it.
- An AI security server HTTPS endpoint that accepts verdict requests: an `https://` URL on port 443, on a publicly routable host, reachable without redirects. Reverse-tunnel hosts (ngrok and similar tunnel services) are not supported: Anthropic's network policy blocks them. Don't test through a tunnel; host your server on a domain you control. For the full [hosting requirements](manage-claude-inference-hooks-endpoint.md#receive-a-request), and to build the server and verify signed requests, see [Develop an Inference hooks integration](manage-claude-inference-hooks-endpoint.md).
## Set up Inference hooks
There are three enforcement states: **off** (**Enforce verdicts** is off: your AI security server is never contacted and prompts are not inspected), **shadow** (**Enforce verdicts** is on with **Mode** set to **Shadow mode**: your AI security server receives prompts and returns verdicts, and nothing is blocked), and **enforcing** (**Enforce verdicts** is on with **Mode** set to **Allow the request** or **Block the request**: a deny blocks the request). The following steps take a new configuration from off to enforcing.
1. 1
### Allow Inference hooks for your organization
Go to claude.ai \> **Organization settings** \> **Data and privacy** and find the **Inference hooks** section. Turn on **Allow for your organization**.
Turning this on unlocks the Inference hooks settings page and always forces **Enforce verdicts** off, so allowing the feature never starts inspection by itself: even a configuration that previously had enforcement on stays uninspected until you turn **Enforce verdicts** back on in the final step.
2. 2
### Open the Inference hooks settings page
Still in **Data and privacy**, open the **Inference hooks** section to reach the Inference hooks settings page. It lives under Data and privacy rather than as its own entry in the settings nav, so its breadcrumb reads **Data and privacy / Inference hooks**. Until you save an endpoint, the page warns that prompts aren't being inspected yet, and **Enforce verdicts** stays off with a **Requires endpoint** badge.
3. 3
### Configure your endpoint
Click **Configure** to open the **Set up endpoint** dialog and enter the **Endpoint URL**: the `https://` URL that receives verdict requests. Only `https://` URLs are accepted.
The dialog asks for nothing else at this point: custom request headers come in step 5, and failure handling in step 6. Click **Next** to save. Once an endpoint is saved, the button reads **Edit**.
4. 4
### Store your signing secret
The first save generates your webhook signing secret and reveals it once. Copy it and store it securely before clicking **Next**: the secret cannot be retrieved later, only [rotated](#rotate-your-signing-secret).
Your AI security server uses this secret to verify the signature on every request it receives, including the connection test in the next step. For the verification procedure, see [Verify the signature](manage-claude-inference-hooks-endpoint.md#verify-the-signature).
5. 5
### Add request headers and test the connection
Clicking **Next** on the signing secret dialog reopens the endpoint dialog, now with two more controls:
- **Custom request headers:** up to 16 static headers sent with every verdict request so your AI security server can authenticate the caller. Header values are stored encrypted and never shown again; after saving, only the header names are displayed. Because values are write-only, saving any change to the headers requires re-entering every value. Changing the endpoint URL clears all stored header values so your credentials are never sent to a new destination; re-enter them after a URL change. Header names must use standard HTTP token characters with `-` rather than `_`, and must not collide with reserved names (request-framing headers such as `Content-*` and `Host`, proxy and cookie headers, client-address headers such as `X-Forwarded-*`, the `webhook-*` signature headers, and the `X-Anthropic-*` prefix). Values must be printable ASCII.
- **Test connection:** Claude sends a synthetic test prompt to the URL and headers currently in the form, not the saved values, so re-enter any stored header values before testing. On success, the result reports whether your AI security server returned an allow or a deny verdict for the test prompt, which surfaces a deny-everything default before you start enforcing.
Click **Save** to store any headers you entered.
Common failure results:
| Result | What to check |
|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| URL rejected | The URL failed a structural check. Use an `https://` URL on port 443. |
| Private or internal IP | The URL's host is a private or internal IP address, an IPv6 address, or `localhost`. Use a publicly routable host. |
| Timeout | The AI security server did not return a verdict within the timeout. |
| Transport error | DNS resolution, the TLS handshake, or the connection failed, or the hostname resolves to a private address. |
| Non-200 status | The AI security server responded with a status other than 200. Verdicts must come back as HTTP 200; redirects are not followed and count as failures. |
| Unparseable response | The AI security server responded, but the body is not a valid verdict. |
| Signing secret required | Your organization has no signing secret, so the test would be sent unsigned. Click **Generate secret** under **Request signing**, then test again. |
6. 6
### Choose failure handling and timeout
Under **Failure handling**, set **Mode** to choose what happens while the AI security server is unreachable or verdicts time out:
- **Block the request:** stop inference when your AI security server can't deliver a verdict (fail closed).
- **Allow the request:** let the request proceed to the model without inspection (fail open).
The dropdown's third option, **Shadow mode**, is a rollout tool rather than a failure policy; see [Shadow mode](#shadow-mode).
Then set **Prompt verdict timeout (ms)**: 1 to 10,000ms, with a default of 5,000ms. The budget covers the entire exchange, and a slower verdict counts as an unreachable server, so set the lowest value your server can reliably meet.
Changes in this section save as you make them. On first save, the defaults are **Allow the request** and 5,000ms.
7. 7
### Choose a rollout percentage
Under **Rollout**, set **Requests inspected (%)** to run inspection on a percentage of requests while you bring your AI security server up. The value ranges from 0 to 100: 100 inspects everything, and 0 turns inspection off.
Each request rolls once for its whole conversation turn, so a single conversation can be partially inspected across turns. Requests outside the sampled percentage proceed without inspection, even when failure handling is set to **Block the request**.
8. 8
### Turn on Enforce verdicts
To evaluate verdicts against live traffic without blocking anyone at first, set **Mode** to **Shadow mode** (step 6) before turning on enforcement; see [Shadow mode](#shadow-mode).
Turn on **Enforce verdicts** to gate Claude on your AI security server's verdict for every governed prompt, then confirm in the dialog, which restates your failure handling choice. Allow about a minute for the change to reach every Anthropic server; requests already in flight finish under the old setting. Turning it off stops prompts from being sent to your AI security server, again within about a minute; your configuration is kept.
**Validate tool calls**, below **Enforce verdicts**, also sends the tool calls in each of Claude's responses to your AI security server and waits for its verdict before they run; see [The tool call frame](manage-claude-inference-hooks-endpoint.md#the-tool-call-frame). It is on by default in a new configuration. It has no effect while **Enforce verdicts** is off, and a change to it takes about a minute to reach every Anthropic server, as with **Enforce verdicts**.
## Shadow mode
Shadow mode runs your hook against live traffic without blocking anything. Your AI security server receives governed prompts and returns verdicts exactly as it would when enforcing, but nothing is blocked: every request proceeds to the model, even when your server denies it or can't be reached, and the end user sees nothing. Use it to tune your policy against your organization's real traffic before you start enforcing. With **Validate tool calls** on, your server also receives tool call frames in shadow mode, and no tool call is blocked.
To use shadow mode, set **Mode** to **Shadow mode** under **Failure handling**, then turn on **Enforce verdicts** so prompts flow to your AI security server. While it is active, the settings page shows a **Shadow mode — not blocking** badge. To leave shadow mode, set **Mode** back to **Allow the request** or **Block the request**; verdicts are enforced again once enforcement is on.
## Exclusions
Under **Exclusions**, select roles whose members are not covered by Inference hooks: their prompts are never sent to your AI security server. Only custom roles your organization created can be excluded; the built-in roles aren't offered. Pick them in the role selector, whose placeholder reads **Select roles to exclude**, and manage who holds each role from the roles admin page (**Manage roles**); changing exclusions requires identity management permission. The list is empty by default, and with no roles excluded, every governed request is inspected.
Exclusion applies to a user's interactive sessions. Changes to the exclusion list are recorded in your organization's [Activity Feed](manage-claude-compliance-activity-feed.md) as role permission changes (`rbac_role_permission_added` and `rbac_role_permission_removed`).
## Custom blocked prompt message
Under **Custom blocked prompt message**, set custom text of up to 500 characters that is appended to the error an end user sees when your AI security server denies a request (typically who to contact or where to request an exception). The final message is your AI security server's per-request `deny_reason` (when present), a blank line, then this text. With no custom text configured, a built-in default directs the user to contact their administrators; you can also switch the appended message off entirely so the user sees only the `deny_reason`.
## Monitor your AI security server
The endpoint health area of the Inference hooks settings page shows:
- **Endpoint status:** Healthy, Tripped, Not enforcing, or Not configured before an endpoint is saved.
- **Failures per minute:** webhook failures over the last two minutes, averaged.
- **Block rate:** denials as a share of your AI security server's verdicts, shown while the rollout percentage is below 100.
- **Circuit breaker tripped:** when the breaker last tripped, if it has.
- **Recent errors:** each entry shows when the failure happened, an error type, and a category. The category is `webhook_error` for a problem with your endpoint or the connection to it, or `relay_error` for a failure inside Anthropic's systems. Entries never include request content or your endpoint URL. The list keeps the 10 most recent failures and clears an hour after the last one.
The error types in **Recent errors** mean:
| Error shown | What it means | Counts toward the circuit breaker |
|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------|
| `DlpWebhookTimeoutError` · `webhook_error` | Your AI security server didn't return a verdict within the **Prompt verdict timeout (ms)** you set. | Yes |
| `DlpWebhookStatusError` · `webhook_error` | Your AI security server answered with an HTTP status other than 200, such as a redirect. | Yes |
| `DlpWebhookResponseError` · `webhook_error` | Your AI security server answered 200, but the body isn't a valid verdict or is larger than 64 KiB. | Yes |
| `DlpWebhookTransportError` · `webhook_error` | The connection failed or dropped before a complete answer arrived: for example, the hostname didn't resolve or resolved to a private address, the connection was refused or reset, or the TLS handshake failed. A network problem on Anthropic's side can also show up this way. | No |
| `DlpWebhookDisallowedAddressError` · `webhook_error` | The endpoint URL's host is a private or internal IP address, an IPv6 address, or `localhost`. A hostname that resolves to such an address shows as a transport error instead. | Yes |
| `DlpWebhookBlockedError` · `webhook_error` | The endpoint URL isn't an `https://` URL on port 443, or can't be parsed, so no request was sent. | No |
| `DlpWebhookRelayError` · `relay_error` | The failure was inside Anthropic's systems, and your AI security server usually wasn't contacted. | No |
In shadow mode, no error counts toward the circuit breaker.
The panel is best-effort: if Anthropic cannot read the counters it shows zero failures and no errors rather than an error of its own, so a healthy-looking panel is not by itself proof that your AI security server is healthy. **Failures per minute** counts every failure, including the network and DNS errors that never trip the circuit breaker, so it can be high while **Circuit breaker tripped** stays empty.
## Circuit breaker
Sustained webhook failures attributable to your AI security server trip the circuit breaker, which stops enforcement: your server is no longer contacted, and your **Failure handling** choice applies to every inspected request. With **Block the request** selected, users in your organization are blocked until the breaker resets. When the breaker trips, administrators are also notified in the claude.ai notification center.
Each trip is also recorded in your organization's [Activity Feed](manage-claude-compliance-activity-feed.md) as an `inference_hooks_circuit_breaker_tripped` activity, so your security team or vendor can alert on trips from monitoring they already run, such as a SIEM that ingests the feed. One activity is recorded per trip, not one per affected request. Recording requires the Compliance API to be enabled for your organization; see [Set up the Compliance API](manage-claude-compliance-api-access.md).
To recover, fix the server, then turn **Enforce verdicts** back on to reset the breaker.
The breaker can also reset on its own. Starting 10 minutes after the trip, Anthropic checks whether your server has recovered by sending it a test request in the background, at most about once per minute; no user request is involved. If your server responds with a valid verdict, allow or deny, the breaker resets and enforcement resumes. Otherwise the breaker stays tripped and the checks continue.
Automatic recovery runs only while your Inference hooks settings are unchanged since the trip. If you change any Inference hooks setting after a trip, including rotating the signing secret, the checks stop and the breaker no longer resets on its own; turn **Enforce verdicts** back on when your server is fixed. Automatic recovery applies only to trips: if you turn **Enforce verdicts** off yourself, enforcement stays off until you turn it back on.
## Rotate your signing secret
Click **Rotate secret** under **Request signing** to replace your signing secret. If your organization has no secret yet, the same button reads **Generate secret** and creates the first one. Rotation is an immediate cutover: the new secret is generated and revealed once, the old secret can no longer be retrieved, and no request is ever signed with both secrets, so there is no overlap period to rely on.
Requests signed with the previous secret can still arrive briefly after rotation; [Verify the signature](manage-claude-inference-hooks-endpoint.md#verify-the-signature) covers how your AI security server should handle the switchover.
## Audit trail
Inference hooks activity is recorded in your organization's [Activity Feed](manage-claude-compliance-activity-feed.md): configuration changes, denials, circuit breaker trips, and requests that proceeded without inspection because no verdict could be obtained. That last kind is recorded only while **Enforce verdicts** is on and **Mode** is **Allow the request**. Its reason is `endpoint_timeout`, `endpoint_error` for any other problem calling your AI security server, or `internal_error` for a failure on Anthropic's side. Under **Block the request** or **Shadow mode**, failed requests aren't recorded individually, and the circuit breaker's automatic recovery isn't recorded. While the circuit breaker is tripped, no per-request Inference hooks activities are recorded; the trip activity is the feed's record of that window. Denial records carry identifiers that let you join each denial to the matching record in your own system.
## Turn Inference hooks off
There are two levels of off:
- **Enforce verdicts** off, on the Inference hooks settings page: within about a minute, prompts from your organization stop being sent to your AI security server; requests already in flight finish under the old setting. The settings page stays available, so use this to pause enforcement while you work on your AI security server.
- **Allow for your organization** off, in **Data and privacy** settings: prompts are no longer inspected, and the Inference hooks settings become unavailable until you turn it back on. Your endpoint configuration, custom headers, and signing secret are kept either way; turning it back on forces **Enforce verdicts** off and clears a tripped circuit breaker, so turn enforcement on again when you are ready.
## Next steps
[Develop an Inference hooks integration](manage-claude-inference-hooks-endpoint.md)
Build the AI security server: the request and verdict schemas, signature verification, and operational semantics.
[Inference hooks overview](manage-claude-inference-hooks.md)
What Inference hooks are, how the verdict round trip works, and what gets sent to your AI security server.
---
## Develop an Inference hooks integration - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/inference-hooks-endpoint
Path: 04-API-Reference/Other/manage-claude-inference-hooks-endpoint.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Finference-hooks-endpoint)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
[Overview](manage-claude-inference-hooks.md)[Configure Inference hooks](manage-claude-inference-hooks-configuration.md)[Build an integration](manage-claude-inference-hooks-endpoint.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Inference hooks
# Develop an Inference hooks integration
Copy page
Build the AI security server that receives signed Inference hooks requests, verifies them, and returns allow or deny verdicts.
Copy page
Inference hooks are in beta and available to Claude Enterprise organizations. Field names, request shapes, and headers may change during the beta.
An Inference hooks integration is an AI security server: an HTTPS service that Anthropic calls. For each governed request, your server receives a signed `POST` carrying the conversation transcript and responds with an allow or deny verdict. This page documents the protocol for building that server: the request and verdict schemas, signature verification, and the operational contract.
To turn Inference hooks on and point them at your endpoint, see [Configure Inference hooks](manage-claude-inference-hooks-configuration.md). To learn what Inference hooks are and when to use them, see the [Inference hooks overview](manage-claude-inference-hooks.md).
## Get a first verdict round trip
The smallest working integration is a server that reads each request and allows it. Run one of the following servers, expose it at a public `https://` URL (for example, behind a TLS-terminating reverse proxy on a host you control, not a reverse-tunnel service; see [Receive a request](#receive-a-request)), then have your administrator [set it as the endpoint and test the connection](manage-claude-inference-hooks-configuration.md): the **Test connection** result reports the allow verdict your server returned.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Run with: python server.py
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class VerdictHandler(BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1" # keep the connection open between verdicts
def do_POST(self):
# Drain the body; transcripts can be megabytes.
self.rfile.read(int(self.headers.get("Content-Length", 0)))
verdict = b'{"action": "allow"}'
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(verdict)))
self.end_headers()
self.wfile.write(verdict)
ThreadingHTTPServer(("", 8000), VerdictHandler).serve_forever()
```
These servers accept every request, including unsigned ones. Add [signature verification](#verify-the-signature) before you enforce.
## Receive a request
Anthropic sends an HTTPS `POST` to the URL your administrator configures. The whole configured URL is the endpoint: there is no fixed path suffix, so choose any path that suits your server.
Host your AI security server where Anthropic can reach it: an `https://` URL on port 443, on a publicly routable host (private, loopback, and carrier-grade NAT ranges are refused at connect time), with a certificate that validates against the public CA trust store, responding without redirects. The host must have an IPv4 address, which Anthropic uses even when the host also has IPv6 addresses; a URL whose host is `localhost` or an IPv6 address is refused. The configured URL must be the final destination. Reverse-tunnel hosts (ngrok and similar tunnel services) are not supported: Anthropic's network policy blocks them. Host your server on a domain you control. [Configure Inference hooks](manage-claude-inference-hooks-configuration.md) covers how your administrator sets and tests the URL.
Every request carries these fixed headers, along with any [custom request headers](manage-claude-inference-hooks-configuration.md) your administrator configured and, once your organization has a signing secret, the `webhook-*` signature headers described in [Verify the signature](#verify-the-signature):
| Header | Value |
|-------------------|--------------------|
| `Content-Type` | `application/json` |
| `User-Agent` | `anthropic-dlp/1` |
| `Accept-Encoding` | `identity` |
There are two hook events, told apart by the top-level `type` field. The prompt frame is sent once per governed inference request, before inference begins. The tool call frame is sent when a model response contains tool calls, before any of them runs, in organizations that have **Validate tool calls** on. Either way, Anthropic waits until your AI security server responds or the verdict timeout elapses.
## The prompt frame
The request body is a JSON object with these fields:
| Field | Type | Description |
|--------------|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | string | The hook event: `"prompt"` or `"tool_call"` (see [The tool call frame](#the-tool-call-frame)). Other event types will be introduced in the future, so handle an unrecognized value gracefully (see [Forward compatibility](#forward-compatibility)). |
| `request_id` | string | Opaque per-frame identifier for correlation. Equals the `webhook-id` header. |
| `tenant_id` | string or null | Opaque identifier for the organization the request belongs to. |
| `actor` | object | The principal the request is attributed to, discriminated on `type` (`"user"` is the only value sent today): `id` (a tagged identifier, stable across requests for the same account) and `email_address` (when available). Both `id` and `email_address` can be null. |
| `source` | object | The originating application: `application` (see [Source values](#source-values)). |
| `messages` | array | The conversation transcript up to the point of inference. See [Content blocks](#content-blocks). |
| `session_id` | string or null | Opaque conversation identifier, when one exists. Don't parse it. For Claude Code it is a best-effort, client-asserted session identifier. |
| `model` | string or null | Public model identifier for this request, when available. |
| `metadata` | object | Reserved extension map of string keys to string values, sent empty today. Require nothing from it, and tolerate its absence, its presence, and any keys that appear. |
An example request body:
```python
{
"type": "prompt",
"request_id": "req_abc123",
"tenant_id": "11111111-1111-1111-1111-111111111111",
"actor": {
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrStUv",
"email_address": "alice@example.com"
},
"source": {
"application": "claude-ai"
},
"session_id": "22222222-2222-2222-2222-222222222222",
"model": "claude-sonnet-4-5",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "Summarize the attached report."
},
{
"type": "attachment",
"file_name": "q2-report.pdf",
"media_type": "application/pdf",
"size_bytes": 48213,
"text": "Q2 revenue grew 14% quarter over quarter..."
}
]
}
],
"metadata": {}
}
```
### Content blocks
Each entry in `messages` has a `role` of `user` or `assistant` (tool results appear under the `user` role, matching the public Messages API content model) and a `content` array of blocks discriminated by `type`:
| Block `type` | Fields |
|---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `text` | `text`: the text content. |
| `tool_use` | `id`: the identifier the matching tool result references. `tool_name`: the tool's name. `input`: the arguments the model passed to the tool. `tool_info`: on a tool call frame only (left out elsewhere, never `null`), an object that says who runs or provides the tool; see [The tool call frame](#the-tool-call-frame). |
| `tool_result` | `content`: the tool's output as text, with parts joined by newlines; binary parts such as images are replaced by placeholder markers, and raw bytes are never sent. `is_error`: whether the tool call failed. `tool_name`: the tool's name, so a policy can condition on tool identity without cross-referencing an earlier block. `tool_use_id`: the `id` of the matching `tool_use` block. |
| `attachment` | `file_name`: the original file name or path. `media_type`: the attachment's media type. `size_bytes`: the size of the original file. `text`: the text content of the attachment when available, such as extracted document text, an audio transcript, or link metadata. Raw attachment bytes are never sent. |
Apart from `type`, a `text` block's `text`, and a `tool_result` block's `content` and `is_error`, any of these fields can be `null` when the value isn't known; for example, an image arrives as an `attachment` block with `file_name` and `text` set to `null`.
A block whose `type` you don't recognize is a forward-compatible addition. The only field it guarantees is `type`; your policy may inspect whatever other fields are present, but must not reject the request because of an unrecognized type.
### What the transcript contains
The transcript is the conversation as the end user sees it, up to the point of inference: transcript text, tool calls and their results, extracted attachment text, and prior turns. It never includes system prompts, tool definitions, Anthropic-internal context, Claude's hidden reasoning, or raw file bytes.
A turn whose every block is excluded is omitted entirely, so don't assume strict user and assistant alternation.
Transcripts are sent untruncated, so a long conversation with large attachments produces a large request body. In practice the model's context window keeps bodies under about 10 MB, but the protocol allows up to 64 MiB. Several common defaults are much smaller, including nginx `client_max_body_size` at 1 MB and Express `express.json()` at 100 kB, and a rejected body counts as a webhook failure, so under **Allow the request** failure handling an oversized prompt would reach the model uninspected.
### Source values
`source.application` is an open string, not a closed enum. Common values are `claude-ai`, `claude-code`, and `cowork`; [connection tests](manage-claude-inference-hooks-configuration.md) and automatic circuit-breaker [recovery checks](#circuit-breaker) use `config-test`. New values may appear, and your server must not reject a request because of one it doesn't recognize.
Treat `source.application` as advisory routing metadata, not a trust boundary: don't rest a security-critical policy decision on it alone.
## The tool call frame
When Claude produces tool calls, Anthropic sends one tool call frame that lists them. Calls to some of claude.ai's own tools may be left out, and a response whose only tool calls are left out doesn't produce a tool call frame; see [Availability](manage-claude-inference-hooks.md#availability). One verdict covers the whole frame: you can't allow some tool calls and deny others. The frame goes to the same endpoint as the prompt frame, with the same headers, signature, and top-level fields. Tool calls made by code that Claude runs in the code execution tool are sent the same way, in separate tool call frames, before they run.
It differs from the prompt frame in three ways:
- `type` is `"tool_call"`.
- `messages` holds only the latest message, the `assistant` message Claude just produced: any `text` blocks and one `tool_use` block per tool call the frame lists, in the order the model produced them. Earlier conversation is left out, because the prompt frame sent before that model call carried it. Read the last entry of `messages`, because the protocol may later add earlier messages before it.
- Each `tool_use` block carries a `tool_info` object that says who runs or provides the tool.
Where `session_id` is set, it is the same on both frames. The tool call frame has its own `request_id`, which is opaque like the prompt frame's.
`tool_info` says who runs or provides the tool, not what the tool can reach. It is one of four kinds, told apart by its `type` field, and each kind carries its own fields. Anthropic sends the first of the following kinds that fits the tool. New kinds may appear: accept a `type` you don't recognize, and for such a kind rely only on `type`.
An optional field that doesn't apply is left out, never `null`, so a `tool_info` can be just `{"type": "client"}`. `tool_name` is chosen by whoever defined the tool, and a server's `toolset_name` by whoever wrote the request, so don't treat either as a trust boundary.
### Platform tools
A platform tool is one that the Claude API itself runs while it serves the request, such as web search or code execution.
| Field | Present | Description |
|----------------|----------|---------------------------------------------------------------------------------------------------------------------|
| `type` | Always | `"platform"` |
| `tool_type` | Always | The tool's versioned type, such as `web_search_20250305`. Match it exactly; don't parse a name or a date out of it. |
| `toolset_name` | Optional | The group of tools it belongs to. |
### Application tools
An application tool is one that the Anthropic application making the request provides itself, such as claude.ai's own tools.
| Field | Present | Description |
|----------------|----------|-----------------------------------|
| `type` | Always | `"application"` |
| `toolset_name` | Optional | The group of tools it belongs to. |
### Third-party tools
A third-party tool is one on a server that Anthropic knows of, such as a claude.ai connector or an MCP server that the request names in `mcp_servers`. This doesn't mean Anthropic has vetted the server.
| Field | Present | Description |
|-------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Always | `"third_party"` |
| `toolset_name` | Optional | The name the request gives the server. |
| `origin` | Optional | The scheme, host, and non-default port of the server's URL. It can be a private or local address, `localhost` included. An absent `origin` means unknown, not safe. |
| `verified_origin` | Whenever `origin` is | `true` only when Anthropic's servers connect to the server themselves. Treat `false` as an origin that Anthropic hasn't confirmed. |
A `tool_use` block for a tool on an MCP server that the request names `crm` in `mcp_servers`:
```python
{
"type": "tool_use",
"id": "toolu_01GhIjKlMnOpQrStUvWxYzAb",
"tool_name": "crm_search",
"input": {
"query": "accounts renewing in Q4"
},
"tool_info": {
"type": "third_party",
"toolset_name": "crm",
"origin": "https://mcp.crm.example.com",
"verified_origin": true
}
}
```
### Client tools
A client tool is any other tool, usually one that the application calling Claude runs. A tool that Anthropic defines but the calling application runs, such as bash or computer use, is a client tool too, and carries its versioned type in `tool_type`. A call to a tool the request doesn't declare is also `"client"`.
A tool on an MCP server that Claude Code connects to directly from the user's computer, such as a local MCP server, is a client tool. Its `tool_info` is `{"type": "client"}`, and its `tool_name` is the name Claude Code gives it, in the form `mcp__<server>__<tool>`.
A deny stops the call before Claude Code runs it. The exchange between Claude Code and a local server doesn't pass through Anthropic, so your AI security server sees the tool's result only when Claude Code sends it back. Its text is then in a `tool_result` block in the next prompt frame.
| Field | Present | Description |
|----------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Always | `"client"` |
| `tool_type` | Optional | The tool's versioned type, such as `bash_20250124`, when Anthropic defines the tool. Match it exactly; don't parse a name or a date out of it. |
| `toolset_name` | Optional | The group of tools it belongs to, such as `browser`. |
A `tool_use` block for a tool that the calling application declares:
```python
{
"type": "tool_use",
"id": "toolu_01AbCdEfGhIjKlMnOpQrStUv",
"tool_name": "read_file",
"input": {
"path": "reports/q3.txt"
},
"tool_info": {
"type": "client"
}
}
```
A `tool_use` block for bash, which Anthropic defines and the calling application runs:
```python
{
"type": "tool_use",
"id": "toolu_01HiJkLmNoPqRsTuVwXyZaBc",
"tool_name": "bash",
"input": {
"command": "ls -la reports/"
},
"tool_info": {
"type": "client",
"tool_type": "bash_20250124"
}
}
```
## Return a verdict
Respond with HTTP 200 and a JSON verdict body for both outcomes; the `action` field discriminates. To allow the request:
```python
{
"action": "allow"
}
```
To deny it:
```python
{
"action": "deny",
"deny_reason": "This prompt appears to contain customer payment card data, which your organization's policy does not allow.",
"reference_id": "scan_01HXPT4R9V"
}
```
| Field | Constraints | Semantics |
|----------------|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `action` | `"allow"` or `"deny"`; required | `allow` lets inference proceed; `deny` rejects it. |
| `deny_reason` | string or null; at most 500 characters, longer values truncated | Shown to the end user when `action` is `deny`; ignored on `allow`. |
| `reference_id` | string or null; at most 50 characters from `[A-Za-z0-9._:/-]` | Your own identifier for this evaluation. It's recorded on the denial's `inference_hooks_request_denied` [compliance activity](manage-claude-compliance-activity-feed.md) and never shown to the end user. Keep it opaque: no request content and no personal data. |
A deny is never discarded over a formatting problem: an oversize `deny_reason` is truncated, a malformed `reference_id` is silently dropped, and the `action` is still honored.
The reverse doesn't hold. Anything other than HTTP 200 with a parseable verdict is a webhook failure, and your organization's [failure handling](manage-claude-inference-hooks-configuration.md) applies instead of a verdict. In particular:
- Don't signal a deny with an error status. A non-200 response is a failure, not a deny.
- Any `action` value other than `allow` or `deny` is treated as a webhook failure.
Anthropic reads at most 64 KiB of the response body, and the body must be uncompressed. Redirects are not followed, and cookies are ignored. Unknown fields in the verdict body are ignored, so you can return a richer object alongside the fields documented here.
A tool call frame takes the same verdict body, and every rule in this section applies to it unchanged. `allow` lets the tool calls run and the response continues; `deny` stops all of them and ends the response with the same error as a denied prompt, including your `deny_reason`. Text delivered before the first tool call is not taken back.
## Verify the signature
Requests are signed per the [Standard Webhooks](https://www.standardwebhooks.com/) specification, using three headers. Anthropic sends the header names in lowercase, and proxies are free to re-case them, so look them up case-insensitively.
| Header | Contents |
|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `webhook-id` | Unique identifier for this delivery. Equals the body's `request_id`. Use it as an idempotency key and as the first component of the signed payload. |
| `webhook-timestamp` | Unix time in seconds, as a decimal string, when the request was signed. Reject a timestamp more than five minutes from your server's clock, in either direction. |
| `webhook-signature` | One or more space-separated `v1,<base64>` values, each an HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{raw body bytes}`. Accept the request if any value matches yours, using a constant-time comparison. |
Two details cause most verification bugs:
- **Verify raw bytes.** Compute the HMAC over the body exactly as received, before any JSON parsing or re-encoding.
- **Decode the secret with a standard base64 decoder.** The signing secret is the value after the `whsec_` prefix, encoded with the standard base64 alphabet (`+` and `/`), as is the signature in the header. A URL-safe decoder derives the wrong key bytes whenever the secret contains `+` or `/`, which is most of the time.
Once your organization has a signing secret, every request Anthropic sends is signed, including the connection test, because the setup flow generates the secret before the first test. [Enabling Inference hooks requires a secret](manage-claude-inference-hooks-configuration.md), so reject any request that arrives unsigned. One exception: an organization that enabled Inference hooks before the secret was required keeps sending unsigned requests until its administrator generates one. Accept unsigned requests only until your administrator confirms the secret exists, then reject them.
[Rotating the secret](manage-claude-inference-hooks-configuration.md#rotate-your-signing-secret) is an immediate cutover, but requests signed with the previous secret can still arrive for about a minute afterward, plus anything already in flight. Have your AI security server accept signatures from both secrets during the switchover so those stragglers aren't rejected.
The following samples are server implementations, so there is no shell tab: an AI security server is a long-running HTTPS service rather than a one-shot request. Each sample uses only the language's standard library; the [Standard Webhooks](https://www.standardwebhooks.com/) project also publishes verification libraries for most languages.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import base64
import hashlib
import hmac
import time
TOLERANCE_SECONDS = 300
def verify(secret: str, headers: dict[str, str], body: bytes) -> bool:
"""Return True if the body was signed by Anthropic for this organization.
Anthropic sends header names in lowercase, but proxies are free to
re-case them, so normalize the lookup to lowercase.
"""
lowercased = {name.lower(): value for name, value in headers.items()}
try:
message_id = lowercased["webhook-id"]
timestamp = lowercased["webhook-timestamp"]
signatures = lowercased["webhook-signature"]
except KeyError:
return False # unsigned request: not from Anthropic
try:
signed_at = int(timestamp)
except ValueError:
return False
if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
return False # replayed, or the clocks disagree
try:
key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
except ValueError:
return False # misconfigured secret: reject rather than crash
payload = f"{message_id}.{timestamp}.".encode() + body
expected = b"v1," + base64.b64encode(
hmac.new(key, payload, hashlib.sha256).digest()
)
# Compare bytes: compare_digest on str raises on non-ASCII input.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)
```
## Operational semantics
### Timeout and retry
Your administrator sets a verdict timeout between 1 and 10,000ms (5,000ms by default). The budget covers the entire exchange: connection, TLS handshake, request, and response. The same timeout applies to tool call frames.
Anthropic retries exactly once, after a 100ms delay, and only when the connection attempt fails. The retry shares the same timeout budget and carries the same `webhook-id` and the same signature. Once your AI security server has responded, the exchange is never retried.
### Webhook failures
Timeouts, non-200 statuses (redirects included), unparseable or oversized response bodies, and unreachable endpoints are all webhook failures. A webhook failure never becomes a deny; instead, your organization's [failure handling](manage-claude-inference-hooks-configuration.md) setting decides whether the affected request is blocked or proceeds without inspection.
### Circuit breaker
Sustained webhook failures attributable to your AI security server trip a circuit breaker that stops enforcement: Anthropic stops contacting your server, and failure handling applies to every request.
Starting 10 minutes after the trip, Anthropic checks whether your server has recovered: at most about once per minute it sends your server the same synthetic test request that **Test connection** sends (`source.application` is `config-test`), signed like any other request and carrying no user content. Respond to it normally. A valid verdict, allow or deny, resets the breaker and enforcement resumes; a webhook failure leaves the breaker tripped, and the checks continue. An administrator can also reset the breaker at any time, and administrator configuration changes stop the automatic checks; see [Circuit breaker](manage-claude-inference-hooks-configuration.md#circuit-breaker).
Prompt frames and tool call frames share one circuit breaker, and failures on either count toward it.
Each trip is recorded as an `inference_hooks_circuit_breaker_tripped` activity in the [Activity Feed](manage-claude-compliance-activity-feed.md), one activity per trip. While the breaker is tripped, no per-request Inference hooks activities are recorded, so the trip activity is the feed's only record of the tripped window.
### Latency
Enforcement adds your AI security server's round trip to the latency of every governed request in your organization. With **Validate tool calls** on, a response that produces a tool call frame also waits for its verdict, in shadow mode as well. Keep the verdict fast, and load-test your server before rolling it out to a large organization.
### Source IP addresses
Requests to your AI security server originate from `160.79.106.0/24`, part of Anthropic's published [outbound IP ranges](../Endpoints/ip-addresses.md). Allowlist that block, not the inbound ranges on the same page, which don't cover it. Allowlisting narrows your server's exposure, but it is not a substitute for signature verification: the block carries Anthropic egress traffic beyond Inference hooks.
## Forward compatibility
The protocol grows without breaking correctly written servers. Your server must ignore:
- Unknown top-level fields on either frame.
- Unknown keys in `metadata`.
- New `source.application` values.
- New `actor.type` values. `actor` is a union discriminated on `type`, and `"user"` is the only kind sent today; a future kind guarantees only that `type` is present.
- Content blocks with an unrecognized `type`.
- Messages with a `role` other than `user` or `assistant`, which a later revision may add.
Never reject a request because of an unrecognized block type or field; read the fields you know and skip the rest.
Other hook event types will be introduced in the future. A new event type is an addition your server can't handle by skipping a field: the request still needs a verdict. When the top-level `type` is a value you don't recognize, return an allow verdict rather than an error status; an error response is a [webhook failure](#webhook-failures), and sustained failures trip the [circuit breaker](#circuit-breaker).
## Design your integration
A production AI security server makes a few design choices beyond the wire protocol.
**Deduplicate on `webhook-id`.** The `webhook-id` header is unique per delivery and equals the body's `request_id`, and a connection-failure retry reuses it, so it works as an idempotency key. If you record verdicts, key the records on it.
**Record verdicts and join denials.** Store each verdict you return along with its `reference_id`. Every denial is recorded as an `inference_hooks_request_denied` compliance activity carrying the `reference_id` your server returned, so you can join denials in the [Activity Feed](manage-claude-compliance-activity-feed.md) to the matching records in your own system.
**Archive with an always-allow server.** To capture transcripts in real time without policing them, return `{"action": "allow"}` unconditionally and persist the frame after responding. This is a push-based alternative to polling the [Compliance API](manage-claude-compliance-api.md), and answering before you persist keeps your round trip out of the user's critical path.
**Write `deny_reason` for the end user.** The text you return is what the user sees when their request is blocked, truncated at 500 characters. Tell them what to change, such as which kind of content to remove, rather than emitting a scanner code that only your team can interpret.
## Next steps
[Configure Inference hooks](manage-claude-inference-hooks-configuration.md)
Enable Inference hooks, connect and test your endpoint, and control enforcement, failure handling, and rollout.
[Inference hooks overview](manage-claude-inference-hooks.md)
What Inference hooks are, how the verdict round trip works, and when to use them.
---
## Inference hooks - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/inference-hooks
Path: 04-API-Reference/Other/manage-claude-inference-hooks.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Finference-hooks)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
[Overview](manage-claude-inference-hooks.md)[Configure Inference hooks](manage-claude-inference-hooks-configuration.md)[Build an integration](manage-claude-inference-hooks-endpoint.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Inference hooks
# Inference hooks
Copy page
Send each governed prompt to your organization's AI security server for an allow or deny verdict before inference proceeds.
Copy page
Inference hooks are in beta and available to Claude Enterprise organizations. Configuring them requires the `organization:manage` permission in claude.ai, which only the Owner and Primary owner roles hold; see [Configure Inference hooks](manage-claude-inference-hooks-configuration.md).
Inference hooks let a Claude Enterprise organization route every governed prompt through an AI security server, an HTTPS service that the organization or its security vendor operates, before inference runs. When a user submits a prompt, Anthropic sends the conversation transcript to your AI security server and waits for an allow or deny verdict; a denied request never reaches the model. Security and compliance teams use Inference hooks to enforce data policies inline, and developers build the AI security server that evaluates each request.
Because the hook runs on Anthropic's servers, after the request leaves the client and before the model runs, it applies to every governed request uniformly, with nothing to install or deploy on user devices.
There are two hook events. `prompt` fires once per governed inference request, before inference begins. `tool_call` fires when Claude's response contains tool calls, before any of them runs, in organizations that have **Validate tool calls** on.
------------------------------------------------------------------------
## How Inference hooks work
1. A user submits a prompt on a governed surface.
2. Anthropic sends an HTTPS `POST` to your organization's configured AI security server endpoint. The request body carries the conversation transcript, and each request is signed according to the [Standard Webhooks](https://www.standardwebhooks.com/) specification once your organization generates its signing secret, so your server can verify it came from Anthropic.
3. Your AI security server evaluates the content and responds with a verdict within the verdict timeout your organization configures (5 seconds by default).
4. On `allow`, inference proceeds normally. On `deny`, the request is rejected and the user sees a blocked-by-policy message assembled from two parts: the per-request reason your AI security server supplied in the verdict's `deny_reason` field, followed by a standing message your administrators configure (for example, who to contact or where to request an exception). If your administrators haven't configured one, a built-in default directs the user to contact them. Each denial is also recorded in your organization's [Activity Feed](manage-claude-compliance-activity-feed.md).
The following diagram traces one example (a Cowork request where Claude also calls an O365 tool) to illustrate which parts of the flow are hooked. The hooked points are the diagram's steps 1, 2, and 3, where the prompt arrives, Claude calls the tool, and the tool result returns. At each one, your AI security server returns a verdict before the flow continues. Step 2 is hooked only with **Validate tool calls** on, and its one verdict covers all the tool calls in a response.
A verdict is a small JSON object: `{"action": "allow"}` lets the request proceed, and a deny carries the user-facing reason. For the full verdict schema, see [Return a verdict](manage-claude-inference-hooks-endpoint.md#return-a-verdict).
Your AI security server sees what the user sees: transcript text, tool calls and their results, and text extracted from attachments. It never receives raw file or image bytes, system prompts, or Anthropic-internal context.
The Inference hooks system doesn't keep its own copy of prompt or response content. It stores only your hook configuration and metadata about hook activity, such as verdicts, timestamps, and request identifiers. The Claude product you use stores prompts and responses under its own data retention rules, whether or not hooks are on. For example, a message that a hook blocks on claude.ai stays in the conversation.
If your AI security server is unreachable, returns an error, or doesn't respond within the timeout, your organization's failure handling setting decides the outcome: block the request, or allow it to proceed without inspection. Sustained failures attributable to your server trip a circuit breaker: Anthropic stops contacting it and applies your failure handling setting to every request, then resets the breaker automatically once it detects that your server is returning verdicts again; see [Circuit breaker](manage-claude-inference-hooks-configuration.md#circuit-breaker).
Enforcement can roll out at your pace, so nobody has to be blocked on day one: shadow mode observes verdicts on live traffic without blocking anything, a rollout percentage inspects a chosen fraction of requests, and exclusions exempt members of chosen roles entirely. See [Configure Inference hooks](manage-claude-inference-hooks-configuration.md).
For the full request and response schemas, signature verification, and operational details, see [Develop an integration](manage-claude-inference-hooks-endpoint.md).
------------------------------------------------------------------------
## Continue a conversation after a denied request
Each request includes the whole conversation, so a denied message is sent again with every later message. If your AI security server evaluates the whole transcript, it denies those requests too. To continue, the user removes the denied content from what the app sends next, including any file Claude would read again.
The steps depend on the app:
- **claude.ai, including Claude Desktop and the mobile apps.** The user edits the denied message, or an earlier one, rather than sending a corrected copy as a new message. On the web and in Claude Desktop, the edit resends the message's attachments unless the user removes them. A new chat also works.
- **Claude Code.** The user runs `/rewind` and selects the prompt that first brought the content in. If asked, they select **Restore conversation**, then edit or clear the prompt that returns to the input field. `/clear` starts over. See [Checkpointing](../../02-Claude-Code-CLI/checkpointing.md).
- **Cowork.** The user edits the denied message if it is their latest one. The edit resends attached files, and **Restart from here** resends the whole message unchanged. If the content is in a file or an earlier message, the user selects **New task**.
- **Claude Tag.** In Slack, the user first edits the denied message, or deletes it if it is a reply. They then send `@Claude !restart` on its own where Claude was answering: in that thread, or at the channel's top level. The new session rereads the messages still in Slack, so the edit or deletion comes first. See the [`!restart` command](https://claude.com/docs/claude-tag/users/commands#restart-a-stuck-or-wrong-context-session).
------------------------------------------------------------------------
## Use cases
- **Data loss prevention (DLP).** Forward the transcript to your DLP scanner and deny prompts that carry regulated or classified material. This is the most common deployment.
- **Real-time transcript archival.** Archive each transcript as it arrives and always return `allow`, as a push-based alternative to polling the [Compliance API](manage-claude-compliance-api.md).
- **Prompt telemetry.** Measure how your organization uses Claude, at the moment of use.
- **Policy engines.** Enforce your own rules before inference: model allowlists, project-scoped restrictions, or working-hours controls.
------------------------------------------------------------------------
## Current limitations
- Attachments are represented by metadata and extracted text. Raw file and image bytes are never sent, so image-only content (for example, a screenshot of a document) is not inspected.
- Verdicts are allow or deny. Rewriting or redacting a prompt is not supported.
- Platform organizations (API access through the Claude Platform) are out of scope.
------------------------------------------------------------------------
## Availability
Inference hooks are available to Claude Enterprise organizations. Configuring them requires the `organization:manage` permission, which only the Owner and Primary owner roles hold.
One hook governs conversations across claude.ai, Cowork, Claude Code, and Claude Tag sessions in your Claude Enterprise organization, whether they run on the web, in the desktop or mobile apps, in the CLI, or in Slack. Inference hooks are not available on Amazon Bedrock or Google Cloud.
Governed requests are the inference requests behind the user's conversation. Ancillary requests aren't sent to your endpoint. These include conversation title generation and a measurement that Anthropic runs on Claude's reply after Claude Tag has posted it in Slack. System prompts and tool definitions are never included in what is sent. In voice mode, prompts and tool calls are sent to your endpoint too, though a small share of tool calls isn't, so don't rely on inference hooks as your only control for voice. If your endpoint denies a request in voice mode, the voice call usually ends with a general error message, and your deny reason isn't shown.
With **Validate tool calls** on, `tool_call` events may leave out calls to some of claude.ai's own tools, such as tools that list, search for, or suggest connectors, plugins, or skills, suggest starting research or turning on web search, search for other tools, switch to a larger model, end the chat, give Claude the user's local time, or read the user's own past chats and saved memory. These calls and their results stay in the conversation, so the next `prompt` event from that conversation, if there is one, includes them.
Some features that Anthropic runs for your organization make model calls of their own. Your organization sees the results of those calls but not their transcripts. These calls aren't governed requests and aren't sent to your endpoint. They include the following:
- **Claude Security scans (beta).** [Claude Security](../../15-Claude-AI-Features/product-claude-security.md) runs hosted scans on your connected repositories. A session a user opens to fix a finding, and the Claude Security plugin in Claude Code, are governed.
- **Code Review (research preview).** [Code Review](../../02-Claude-Code-CLI/code-review.md) runs reviews on your GitHub pull requests. A review a user runs locally in Claude Code with `/code-review` is governed.
- **Smart reports (beta).** Anthropic makes model calls to build your organization's [smart reports](../../15-Claude-AI-Features/get-started-with-smart-reports.md).
------------------------------------------------------------------------
## Inference hooks versus the Compliance API
Both features serve security, legal, and compliance teams at Claude Enterprise organizations.
| | Inference hooks | Compliance API |
|--------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------|
| When it acts | Inline, before inference runs | After the fact |
| What it does | Allows or denies each governed request in real time | Retrieves activity, chats, files, projects, session transcripts, and users for audit and export |
| Direction | Anthropic calls your AI security server | You call Anthropic's API |
Use Inference hooks to stop a request before it reaches the model, and the [Compliance API](manage-claude-compliance-api.md) to audit what happened afterward.
------------------------------------------------------------------------
## In this section
[Configure Inference hooks](manage-claude-inference-hooks-configuration.md)
Allow Inference hooks for your organization, set up and test your AI security server, choose failure handling, and enforce verdicts.
[Develop an Inference hooks integration](manage-claude-inference-hooks-endpoint.md)
The request and verdict schemas, signature verification, operational semantics, and integration patterns for building the AI security server.
---
## Plugins API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/plugins-api
Path: 04-API-Reference/Other/manage-claude-plugins-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fplugins-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Organization
# Plugins API
Copy page
Inventory and manage the plugins in your Claude Enterprise organization: upload plugins and versions, choose the version members are served, control who can use each plugin, download plugin files for review, and validate a marketplace before you connect it.
Copy page
The Plugins API lets you inventory every plugin in your Claude Enterprise organization, publish plugins and new versions from your own pipelines, choose which version members are served, control who can use each plugin, download plugin files for review, and check a Git marketplace before you connect it.
For plugin *usage* reporting (which plugins and skills members use, and how often), see [Analytics APIs](manage-claude-analytics-api.md).
**Scoped Admin API key required**
These endpoints require an Admin API key with the `read:plugins` scope (for `GET` endpoints, including archive downloads) or the `write:plugins` scope (for `POST` and `DELETE` endpoints, except marketplace validation, which either scope allows); [Scopes](#scopes) has the details, including two other read scopes that also work. See [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-enterprise-organization) for where your primary owner creates one. Pass the key in the `x-api-key` header on every request, together with the [`anthropic-version`](../Endpoints/versioning.md) header and the beta header shown in the following note.
The Plugins API is in **beta** and is available to Claude Enterprise organizations only. It is not available to Claude Platform (Claude Console) organizations, or to organizations with HIPAA readiness enabled.
Every request must include the [beta header](../Endpoints/beta-headers.md) `anthropic-beta: ce-plugins-2026-09-01` (the SDKs and the `ant` CLI send it for you). A request without it returns `404`, exactly as if the endpoint did not exist.
## Endpoints
The API exposes 18 endpoints across five resources:
[TABLE]
This release does not include standalone skills (skills a member writes in the skills editor or uploads as a single skill in claude.ai). They do not appear in the inventory and cannot be created here. Plugins that Anthropic publishes are not inventoried either; their usage is reported by the [Analytics APIs](manage-claude-analytics-api.md). Marketplaces are created, connected to repositories, and deleted in claude.ai, not through this API.
## Prerequisites
- Your organization must be on a Claude Enterprise plan.
- Your primary owner creates an Admin API key with the `read:plugins` scope, the `write:plugins` scope, or both in [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access). See [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-enterprise-organization).
- Every request carries three headers: `x-api-key`, `anthropic-version: 2023-06-01`, and `anthropic-beta: ce-plugins-2026-09-01`.
The Python, TypeScript, C#, Go, Java, PHP, and Ruby SDKs expose these endpoints under `client.beta.organization`, and the [`ant` CLI](cli-sdks-libraries-cli-quickstart.md) under `ant beta:organization`; they send the `anthropic-version` and `anthropic-beta` headers for you. The examples on this page use each SDK's default client, which, like the CLI, reads the Admin API key from the `ANTHROPIC_API_KEY` environment variable; the curl examples read the key from the same variable and pass it in the `x-api-key` header. Where an SDK or the CLI fetches more pages for you, `limit` sets the page size, not the total (see [Pagination](#pagination)).
API keys belong to the organization and keep working after the person who created them leaves. Do not share them or check them into source control.
## Quick start
List the plugins in your organization's own marketplaces, newest first:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
plugins = client.beta.organization.plugins.list(owner_type="organization", limit=20)
# Automatically fetches more pages as needed.
for plugin in plugins:
print(f"{plugin.id}: {plugin.name}")
```
```python
{
"data": [
{
"type": "plugin",
"id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"name": "sales-toolkit",
"display_name": "Sales Toolkit",
"description": "Account research and call prep for the sales team.",
"served_version_id": "pluginver_01Km7tL4pR9xF5sU2zV3jP6q",
"served_version_pinned": true,
"latest_version_id": "pluginver_01Jd5sK2nQ8wE4rT6yU1iO3p",
"manifest_version": "1.4.0",
"owner": { "type": "organization" },
"marketplace_id": "marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r",
"created_by": { "type": "api_actor", "api_key_id": "apikey_01Nq9vN6rT2zH7uW4bX5mR8s" },
"organization_installation_preference": "available",
"organization_installation_preference_inherited": true,
"content_scan": { "status": "completed", "assessment": "pass", "reason": null },
"components": [
{
"type": "skill",
"name": "account-research",
"description": "Researches a customer account before a call."
},
{ "type": "mcp_server", "name": "crm", "description": null }
],
"reach": "remote",
"created_at": "2026-09-01T17:04:11Z",
"updated_at": "2026-09-15T14:12:30Z"
}
],
"next_page": "page_xK9f2LqT7vNw3pRzBd8sHy"
}
```
In this example the plugin is pinned to an earlier version: a newer version (`latest_version_id`) is stored but not yet served.
## Scopes
| Scope | Grants |
|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `read:plugins` | Every `GET` endpoint on this page, including archive downloads, plus marketplace validation. |
| `write:plugins` | Every `POST` and `DELETE` endpoint on this page: create a plugin, create a version, change the served version, delete a plugin, set and remove installation settings, and set a marketplace's default, plus marketplace validation. It does not grant reads. |
| `read:org_audit` | A read-only scope for security-audit integrations: every `GET` endpoint on this page, including archive downloads, plus the [user management](manage-claude-user-management.md) and [Compliance API](manage-claude-compliance-api.md) read endpoints. It does not grant marketplace validation or any write. |
| `read:compliance_org_data` | The Compliance API's scope for organization metadata (names, types, roles, and groups) and effective settings. Grants every `GET` endpoint on this page, exactly as `read:org_audit` does, so a Compliance Access Key can read plugins without a second key. It does not grant marketplace validation or any write. |
A key can carry several scopes. An integration that uploads a plugin and then reads it back needs both `read:plugins` and `write:plugins`. Wherever this page says an endpoint requires the `read:plugins` scope, a key with `read:org_audit` or `read:compliance_org_data` works too.
### Access to members' plugin files
Each of these read scopes (`read:plugins`, `read:org_audit`, and `read:compliance_org_data`) can download the files of plugins in members' personal marketplaces, including files that claude.ai's admin settings do not show, and a `read:org_audit` or `read:compliance_org_data` key bound to your parent organization can do this in any organization under it that has access to this API, by passing `organization_id` (see [Reading another organization under the same parent](#reading-another-organization-under-the-same-parent)). Each such download records a `claude_plugin_archive_accessed` event on the [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md), identifying the key, the plugin, the version, and the member (see [Activity Feed events](#activity-feed-events)). Downloads of organization-owned plugins are not recorded.
### Reading another organization under the same parent
`read:plugins` and `write:plugins` keys read and write only the organization they were created in. If your company has several Claude organizations linked under one parent organization, a `read:org_audit` or `read:compliance_org_data` key that the parent's primary owner created for all linked organizations (see [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-enterprise-organization)) can also read any of them that has access to this API: pass that organization's ID in the `organization_id` query parameter on any `GET` endpoint on this page. The ID is the organization UUID shown in claude.ai's settings (its `org_`-prefixed form is accepted too). Without the parameter, the key reads the organization it was created in. A `404` means the named organization is not under the key's parent or the API is not available to it; a value that is not a UUID or `org_` ID returns `400`. Any other key that names an organization other than its own gets `404`. Writes do not accept `organization_id`.
## Key concepts
### Plugins and components
A **plugin** is a package that extends Claude for your organization's members. It contains any combination of these components:
| Component | What it is |
|------------|----------------------------------------------------------------------------------------------------------|
| Skill | Instructions and files that Claude loads when a task calls for it. |
| Command | A saved prompt a member runs by typing `/` followed by the command's name. |
| Agent | A helper assistant with its own instructions, to which Claude can hand part of a task. |
| Hook | A command that runs automatically when an event happens in a session, such as before Claude uses a tool. |
| MCP server | A connection from Claude to tools and data in another system (Model Context Protocol). |
| CLI | A command-line program that the plugin lets Claude run. |
Every plugin has a manifest at `.claude-plugin/plugin.json`. The manifest's `name` becomes the plugin's `name`: a lowercase identifier that is unique within its marketplace.
### Marketplaces
A **marketplace** is a container of plugins. Each marketplace has an owner and a source.
- **Owner.** The organization owns its marketplaces. Each member can also have personal marketplaces.
- **Source.** `manual` means plugins are uploaded, in claude.ai or, for an organization marketplace, through this API. `github`, `gitlab`, and `public_git` mean plugins are synchronized from a Git repository the owner connected. Nothing can be uploaded to a synchronized marketplace, and this API cannot delete its plugins, because the next synchronization would undo either change. Change the repository instead.
Your organization's **library marketplace** is the organization-owned `manual` marketplace that uploads go to when you do not name a marketplace. It is created the first time something is uploaded to it.
### Organization-owned and member-owned plugins
A plugin's `owner.type` says whose marketplace it lives in:
- `organization`: you can manage it through this API, except that a plugin in a marketplace synchronized from Git cannot receive uploads or be deleted here.
- `user`: it lives in one member's personal marketplace. You can read its details and download its files, and delete it if its marketplace is `manual`. Uploading versions and choosing the served version return `403`. Sharing is managed only by the member, in claude.ai.
Removing a member from the organization does not remove their plugins. They stay in the inventory under the member's `user_id`, and the `owner_user_id` filter still finds them, so you can review and remove a departed member's content. They are removed from the inventory when the member's account is deleted.
### Versions and the served version
Every upload creates a new, immutable **version**, whether it comes from this API, from claude.ai, or from a Git synchronization. A plugin has two pointers to its versions:
- `latest_version_id`: the newest version.
- `served_version_id`: the version members are served.
By default `served_version_pinned` is `false`: the served version normally follows the newest one. When content scanning is on, a version saved in claude.ai might **wait** for its content scan: it is stored but not served at once. This page uses "wait" only for a version saved in claude.ai, not for one that a member or an administrator publishes into a plugin. While scanning is on, a waiting version is served only after its scan completes with `pass` or `warn`. While a version waits, `served_version_id` names an earlier version than `latest_version_id`. Do not poll until the two IDs match: they might stay different, for example when the scan fails.
Choosing a version with `POST /v1/organizations/plugins/{plugin_id}` **pins** the plugin (`served_version_pinned: true`). So does an administrator choosing a version in claude.ai, or accepting a member's request to publish into the plugin. From then on, new uploads are stored and advance `latest_version_id`, but members keep the pinned version until you point `served_version_id` at another one. A plugin whose two pointers differ has a stored version that is not being served.
This lets a release pipeline upload each build, test it, and then promote it. To have your pipeline decide when each build is served, pin the plugin once by setting `served_version_id` to its current version; from then on, promote each build you want served. With content scanning on, that first pin returns `409 scan_pending` until the current version's scan completes, and `400 scan_failed` if the scan completed with `fail` or `unknown`, or errored (`warn` is accepted). A pinned plugin cannot currently be unpinned, here or in claude.ai.
To roll back, set `served_version_id` to an earlier version. Roll forward the same way.
These rules describe organization-owned plugins. A member-owned plugin's served version is controlled by its owner in claude.ai.
### Installation settings
**Installation settings** decide who can use an organization-owned plugin. Each setting has one of four values, carried in the fields named `installation_preference` (and, on the plugin and marketplace objects, `organization_installation_preference` and `default_installation_preference`):
| Value | Members see |
|-----------------|------------------------------------------------|
| `required` | The plugin is installed and cannot be removed. |
| `auto_install` | The plugin is installed and can be removed. |
| `available` | The plugin can be installed on request. |
| `not_available` | The plugin is hidden. |
A plugin can hold an organization-wide setting and one setting per group (the role-based access control groups managed in [User management](manage-claude-user-management.md#groups)). A member gets a value by these rules:
1. The organization-wide value is the plugin's own organization-wide setting if it has one, otherwise its marketplace's default, otherwise `not_available`. The plugin reports this value in `organization_installation_preference`, with `organization_installation_preference_inherited: true` while it comes from the marketplace default.
2. A member who belongs to no group holding a setting for the plugin gets the organization-wide value.
3. A member who belongs to one or more groups holding a setting gets the most permissive of those groups' settings instead, ranked `required`, `auto_install`, `available`, `not_available`.
A group's setting replaces the organization-wide value for its members; it does not add to it. For example, if the organization-wide value is `required` and the Pilot group holds `available`, Pilot members get `available`. When you move a plugin from a pilot group to the whole organization, set the organization-wide value and then remove the group's setting. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](#remove-an-installation-setting) returns the plugin to that default.
A plugin created through this API starts with no settings of its own, so it inherits its marketplace's default: `not_available` unless someone has set a default. Deleting a group removes its settings from every plugin.
### Shares
**Shares** decide who can use a member-owned plugin. The owner shares it in claude.ai with every member, with a group, or with named members. This API lists shares but cannot change them.
If your organization has turned off a kind of sharing in its claude.ai settings, shares of that kind still appear in the list but give no one access while that setting is off; the list itself does not show whether it is.
### Content scanning
Content scanning is an organization setting in claude.ai. When it is on, newly stored versions are scanned (claude.ai exempts a few) and the result is reported in `content_scan`; a version that was not scanned, for example one stored before scanning was turned on, has `content_scan: null`. Scanning is not offered to organizations that use customer-managed encryption keys or zero data retention.
While scanning is on, members are served a plugin only when its served version's scan is `completed` with `pass` or `warn`. While the scan runs, or after it fails, errors, or reaches no verdict, the plugin is withheld from members, and an earlier version is not served in its place. A version that was never scanned (`content_scan: null`) is served normally.
A version you upload through this API never waits for its content scan. When it becomes the served version, members lose the plugin until its scan passes, and stay without it if the scan fails. If members should keep the current version while a new one is scanned, pin the plugin first (see [Versions and the served version](#versions-and-the-served-version)).
While a version saved in claude.ai waits for its scan, the served version does not change. When your API upload becomes the served version, any wait ends: the waiting version is served only if someone chooses it. To see a waiting version's scan, read the version (see [List a plugin's versions](#list-a-plugins-versions)).
After an upload, `content_scan.status` is `processing` and the verdict arrives asynchronously. Read the version to see it; the plugin object shows only its served version's scan. Changing the served version to a version whose scan is still running returns `409 scan_pending`; to one whose scan failed, `400 scan_failed`.
### Reach
`reach` summarizes, in one value, how far a version reaches on members' machines and beyond:
| Value | Meaning |
|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `remote` | Declares an MCP server or a CLI, whatever else it declares. |
| `privileged` | Declares no MCP server or CLI, but declares a hook, a monitor (a background command that keeps running during a session), an LSP (Language Server Protocol) server, or settings the plugin applies to the member's app, or contains a skill, command, or any other Markdown file whose frontmatter pre-approves tools (`allowed-tools`). These run, or take effect, on the member's own computer. |
| `contained` | Declares no MCP server, CLI, hook, monitor, LSP server, or app settings, and none of its Markdown files pre-approves tools (for example, a plugin that holds only skills, commands, and agents, none with `allowed-tools`). |
`reach` counts everything the version declares, including monitors, LSP servers, and app settings, which `components` does not list, so a version with an empty `components` list can still be `privileged`. It is `null` for a version stored before components were recorded, and for a version whose reach could not be determined because the `SKILL.md` of one of its skills, or one of its command files, could not be read; treat `null` as unclassified.
### Upload requirements
Uploads follow the same rules as plugin uploads in claude.ai, so the same archives are accepted in both places.
- The upload is either one `.zip` or `.plugin` archive, or a set of individual files. An archive may wrap everything in one top-level folder.
- It must contain exactly one manifest, at `.claude-plugin/plugin.json`, which must declare a `name`. A bare `SKILL.md` without a manifest is rejected.
- A top-level `SKILL.md` whose frontmatter declares plugin components is merged into the manifest; `plugin.json` wins wherever both set a value.
- `name` may contain lowercase letters (from any alphabet), digits, and hyphens, up to 64 characters. Uppercase letters, spaces, underscores, and other punctuation are rejected.
- `displayName` is at most 64 characters and `description` at most 500.
- Every `SKILL.md` needs valid YAML frontmatter with a `name` and `description`, neither containing XML tags such as `<example>`. Two skills, or two commands, cannot share a name.
- No file may be under a top-level `bin/` directory.
- No nested `.zip` files. Packaged MCP servers (`.mcpb`, `.dxt`) are allowed.
- File paths must be relative, contain no `..`, and use only letters, digits, spaces, and `_ . - / ( ) ,`.
- The request body and the uncompressed archive are each at most 200 MB; a request body over the limit returns `413` (`request_too_large`) rather than `400`. An upload has at most 5,000 files, a path depth of 12, paths of 472 characters, and file or folder names of 255 characters.
- ZIP archives must use DEFLATE or STORE compression, and cannot be encrypted or contain symbolic links.
- A marketplace holds at most 500 items, counting its plugins and any standalone skills members keep in it. This limit and the 5,000-file limit are current values that may be raised.
## Example workflows
### Publish each build from a release pipeline
Upload every tagged build from CI, and let the pipeline decide when a build is served.
1. Find the marketplace to upload into with `GET /v1/organizations/plugin_marketplaces?owner_type=organization`, or omit `marketplace_id` to use the library marketplace.
2. On the first release, [create the plugin](#create-a-plugin) with `POST /v1/organizations/plugins`. On every later release, record the plugin's `latest_version_id`, then [upload a version](#create-a-version) with `POST /v1/organizations/plugins/{plugin_id}/versions`. If the upload's response is lost, read the plugin and retry only if `latest_version_id` is unchanged (see [Retrying uploads](#retrying-uploads)).
3. To keep members on the current version while each new build is checked, pin the plugin once by setting `served_version_id` to its current version. From then on each upload is stored without being served, and the pin cannot be undone: every build you want served needs step 5.
4. When content scanning is on, poll `GET /v1/organizations/plugins/{plugin_id}/versions/{version}` until `content_scan.status` is no longer `processing`, and promote only when it is `completed` with `pass` or `warn`.
5. [Promote the build](#change-the-served-version) with `POST /v1/organizations/plugins/{plugin_id}` and `{"served_version_id": "<the new version's ID>"}`. To roll back, send the previous version's ID the same way.
### Roll a plugin out to a pilot group, then to everyone
1. Look up the pilot group's ID with `GET /v1/organizations/rbac_groups`. That call needs the `read:rbac_groups` scope, which requires a key created for all linked organizations (see [User management](manage-claude-user-management.md#groups)). The next steps need `write:plugins`, which acts only on the organization its key was created in, so in an enterprise with several linked organizations, create this key in the organization that holds the plugin and give it both scopes, or use a second key created there for those steps.
2. [Give the group its own setting](#set-an-installation-setting), for example `auto_install`, with `POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group's `rbac_group_` ID, while the organization-wide value stays `not_available`. Only the group's members get the plugin.
3. When the pilot ends, set the organization-wide value with the following call, then remove the group's setting so the group follows the organization again. Setting the organization-wide value stops the plugin from inheriting its marketplace default; [removing the organization-wide setting](#remove-an-installation-setting) later returns the plugin to that default.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
client = anthropic.Anthropic()
setting = client.beta.organization.plugins.installation_settings.set(
"organization",
plugin_id="plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
installation_preference="required",
)
print(f"plugin_id: {setting.plugin_id}")
print(f"installation_preference: {setting.installation_preference}")
```
Then remove the group's setting with `DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}`, where `{target}` is the group's ID. A group's setting replaces the organization-wide value for its members rather than adding to it, so a leftover group setting of `available` would keep those members on `available`.
### Keep a security inventory in sync
Run a nightly job that flags plugins reaching outside the member's session or failing their content scan.
1. Page through [`GET /v1/organizations/plugins?limit=100`](#list-plugins) until `next_page` is `null` (see [Pagination](#pagination)). Read each plugin's `reach` and `content_scan` from that list on every run: a scan verdict that arrives later does not move `updated_at`. `updated_at` normally tells you which plugins have new content or a new served version since the last run (worth a fresh archive download). A waiting version (see [Versions and the served version](#versions-and-the-served-version)) moves `updated_at` when it becomes the served version, not necessarily when it is stored. To notice one, compare `latest_version_id` with the value from your last run. The full re-list is also what catches removals, because a plugin removed by a Git synchronization or an account deletion disappears without an event.
2. Flag each plugin whose `reach` is `remote` (it declares an MCP server or a CLI), or whose `content_scan.assessment` is `fail` or `unknown`. These fields describe the served version. To also check a newer version that is not served, list the plugin's versions when `latest_version_id` differs from `served_version_id` (see [List a plugin's versions](#list-a-plugins-versions)).
3. For each flagged plugin, download the served version's archive for review with `GET /v1/organizations/plugins/{plugin_id}/versions/{served_version_id}/content` (see [Download a version's files](#download-a-versions-files)). Also download any unserved version that meets step 2's test, by its ID.
4. To take a plugin away from members while you review it, see [Delete a plugin](#delete-a-plugin) for the reversible (organization-owned) and permanent options.
## Plugins
The plugin object describes a plugin in one of your organization's marketplaces or in a member's personal marketplace (the [Quick start](#quick-start) response shows a complete one). Its `display_name`, `description`, `manifest_version`, `content_scan`, `components`, and `reach` describe its **served** version, so one list call shows what members are being served.
| Field | Description |
|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | Prefixed `plugin_`. |
| `name` | From the manifest. Unique within its marketplace, not across the organization. Fixed for an organization-owned plugin; changes if a member renames their own plugin in claude.ai. |
| `display_name`, `description`, `manifest_version` | The served version's manifest `displayName`, `description`, and `version`; each is `null` when the manifest declares none. `manifest_version` is normalized for display: one leading `v` or `V` is dropped, so a manifest `version` of `"v1.4.0"` is returned as `"1.4.0"`. It is also `null` for a value that does not look like a version number, such as `"latest"`, and for a plugin version created before claude.ai began recording this field in August 2026. An upload is never refused because of its `version`, and `manifest_version` is not unique. |
| `served_version_id`, `latest_version_id` | Prefixed `pluginver_`: the version members are served, and the newest version. See [Versions and the served version](#versions-and-the-served-version). |
| `served_version_pinned` | `false` until the plugin is pinned (see [Versions and the served version](#versions-and-the-served-version)); `true` from then on. |
| `owner` | `{"type": "organization"}`, or `{"type": "user", "user_id": "user_..."}` for a member's personal marketplace. |
| `marketplace_id` | Prefixed `marketplace_`. |
| `created_by` | Who created the plugin: `{"type": "user_actor", "user_id": "user_...", "email_address": "..."}` for a person in claude.ai (`email_address` may be `null`), or `{"type": "api_actor", "api_key_id": "apikey_..."}` for an API key. Other actor types may appear. `null` when no creator is recorded, such as for plugins synchronized from Git. |
| `organization_installation_preference`, `organization_installation_preference_inherited` | Organization-owned: the organization-wide value, and whether it comes from the marketplace's default (see [Installation settings](#installation-settings)). Member-owned: both `null`. |
| `content_scan` | The served version's scan result, an object with `status`, `assessment`, and `reason` (described after this table). `null` when it was never scanned. |
| `components` | The served version's components, each `{"type", "name", "description"}` with `type` one of `skill`, `mcp_server`, `command`, `agent`, `hook`, or `cli`, listed in that type order and then by name. For an MCP server, `name` is its key in the manifest; for a hook, the event it runs on; for a CLI, the executable's name. `description` is always `null` for MCP servers, hooks, and CLIs. `null` when not recorded. |
| `reach` | `contained`, `privileged`, or `remote`. See [Reach](#reach). |
| `updated_at` | Changes when the served version changes, when the plugin becomes pinned, and normally when a new version is stored. A waiting version (see [Versions and the served version](#versions-and-the-served-version)) changes it when it becomes the served version, not necessarily when it is stored. It does not change for installation settings, shares, or new scan results. |
The `content_scan` object:
| Field | Description |
|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `status` | `processing` while the scan runs, `completed` when it finished, or `errored` when it could not finish (or, occasionally, when its result could not be read for this response, in which case a later read may report it). Members are not served a version whose scan is `processing` or `errored`; uploading the content again as a new version gets a fresh scan. |
| `assessment` | Set when `status` is `completed`: `pass` (nothing found), `warn` (something found that does not block use), `fail` (something found that blocks use), or `unknown` (no verdict). Otherwise `null`. |
| `reason` | For `warn` and `fail`, the main concern, from the following list. Otherwise `null`, and also `null` on an older scan that predates reason recording. |
### Content scan reason values
| `reason` | Meaning |
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------|
| `covert-usage-telemetry` | Tells Claude to send information about the member or their usage to an outside address without telling them. |
| `undisclosed-data-destination` | Sends files, emails, documents, or other content to a fixed outside destination the member is not shown and does not control. |
| `remote-code-instruction-loader` | Tells Claude to download and run, or follow instructions from, outside content that can change after the plugin is installed. |
| `credential-exposure` | Contains live credentials, or collects credentials or tokens from the member's environment. |
| `guardrail-tampering` | Weakens the member's safeguards, for example by pre-approving every permission prompt. |
| `system-prompt-spoofing` | Imitates or tries to replace Claude's system instructions. |
| `covert-record-tampering` | Quietly changes, hides, or deletes information the member would otherwise see. |
| `covert-behavior-override` | Changes Claude's behavior beyond the plugin's purpose and hides the change from the member. |
| `hidden-code-execution` | Runs bundled code while telling Claude not to reveal what it does. |
| `undisclosed-promotion-injection` | Inserts undisclosed promotional content into Claude's output. |
| `hidden-identity-gate` | Changes or stops its behavior depending on which account runs it, without saying why. |
| `destructive-persistence` | Can delete or corrupt the member's files, or install programs that remain after the plugin. |
| `unanalyzable-binary` | Includes a compiled or unreadable program, so the scan could not verify what it does. |
| `other` | Any other concern, including one newer than this list. |
A `plugin_id` that lacks the `plugin_` prefix returns `400`. A `plugin_id` that carries the prefix but does not resolve, belongs to another organization, or refers to a standalone skill returns `404`.
### List plugins
`GET /v1/organizations/plugins` lists every plugin in your organization, in the organization's marketplaces and in members' personal marketplaces, ordered by `created_at` descending. Filter by `owner_type` (`organization` or `user`), `owner_user_id` (prefixed `user_`; a member's plugins, including after the member leaves the organization), `marketplace_id`, and `created_at[gte]`, `created_at[gt]`, `created_at[lte]`, `created_at[lt]` (RFC 3339 timestamps). Filters combine with AND. A `marketplace_id` or `owner_user_id` that matches nothing in your organization returns an empty page, not an error. The response has the shape shown in [Quick start](#quick-start). Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
plugins = client.beta.organization.plugins.list(owner_type="organization", limit=20)
# Automatically fetches more pages as needed.
for plugin in plugins:
print(f"{plugin.id}: {plugin.name}")
```
### Create a plugin
`POST /v1/organizations/plugins` creates an organization-owned plugin and its first version in one call; the version becomes the served version. The body is `multipart/form-data`: `files[]` is either one `.zip` or `.plugin` archive, or one part per file, where each part's filename is the file's path within the plugin (for example `.claude-plugin/plugin.json`). Optional fields are `marketplace_id` (an organization-owned `manual` marketplace; defaults to your library marketplace, which is created on first use) and `release_notes` (up to 5,000 characters, shown in claude.ai's version history and returned on the version). The plugin's `name`, `display_name`, `description`, and `manifest_version` come from the uploaded manifest, and the upload must meet the [upload requirements](#upload-requirements). When content scanning is on, the response's `content_scan.status` is `processing` and the verdict arrives asynchronously. Returns the plugin. Requires the `write:plugins` scope.
Upload an archive:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
with open("dist/sales-toolkit.zip", "rb") as archive:
plugin = client.beta.organization.plugins.create(
files=[archive],
release_notes="First release",
)
print(f"id: {plugin.id}")
print(f"served_version_id: {plugin.served_version_id}")
```
```python
{
"type": "plugin",
"id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"name": "sales-toolkit",
"display_name": "Sales Toolkit",
"description": "Account research and call prep for the sales team.",
"served_version_id": "pluginver_01Km7tL4pR9xF5sU2zV3jP6q",
"served_version_pinned": false,
"latest_version_id": "pluginver_01Km7tL4pR9xF5sU2zV3jP6q",
"manifest_version": "1.4.0",
"owner": { "type": "organization" },
"marketplace_id": "marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r",
"created_by": { "type": "api_actor", "api_key_id": "apikey_01Nq9vN6rT2zH7uW4bX5mR8s" },
"organization_installation_preference": "available",
"organization_installation_preference_inherited": true,
"content_scan": { "status": "processing", "assessment": null, "reason": null },
"components": [
{
"type": "skill",
"name": "account-research",
"description": "Researches a customer account before a call."
},
{ "type": "mcp_server", "name": "crm", "description": null }
],
"reach": "remote",
"created_at": "2026-09-01T17:04:11Z",
"updated_at": "2026-09-01T17:04:11Z"
}
```
Upload individual files into a named marketplace. Attach each file under its path within the plugin (the `;filename=` suffix in the cURL example, the filename arguments in the SDK examples); a file sent under its base name alone means the manifest is not found. The TypeScript and Java SDKs and the `ant` CLI cannot yet attach files under a path, so those examples upload the plugin as one archive into the marketplace instead:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
# A (filename, file) tuple keeps each file's path within the plugin;
# a bare file object would be sent under its base name only.
with (
open(".claude-plugin/plugin.json", "rb") as manifest,
open("skills/account-research/SKILL.md", "rb") as skill_md,
):
plugin = client.beta.organization.plugins.create(
files=[
(".claude-plugin/plugin.json", manifest),
("skills/account-research/SKILL.md", skill_md),
],
marketplace_id="marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r",
)
print(f"id: {plugin.id}")
print(f"served_version_id: {plugin.served_version_id}")
```
Besides a `400` for an upload that breaks the [upload requirements](#upload-requirements) (`413` for a request body over 200 MB) and the shared responses (a `403` when `marketplace_id` is a member's personal marketplace; see [Error responses](#error-responses)), a create can fail with:
| Status | Cause | What to do |
|----------------------------|----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| 404 | `marketplace_id` is not a marketplace of your organization. | Take the ID from [List marketplaces](#list-marketplaces). |
| 400 | The marketplace is synchronized from Git, or already holds 500 plugins and skills. | Upload to a `manual` marketplace, or change the repository instead. |
| 409 `plugin_name_taken` | The name is taken in that marketplace. | Continue with `details.plugin_id` (upload a version to it), or change the manifest `name`. |
| 409 `skill_name_taken` | The plugin is going into the library marketplace and one of its skills has an organization skill's name. | Rename the skill, or remove the organization skill in claude.ai. |
| 409 (no `error_code`) | Another upload with the same name to the same marketplace is still in progress. | Retry shortly. |
| 503 `registration_pending` | The plugin was created but its registration did not complete. | Do not resend; upload the same files as a version of `details.plugin_id` (see [Retrying uploads](#retrying-uploads)). |
### Get a plugin
`GET /v1/organizations/plugins/{plugin_id}` returns one plugin. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
plugin = client.beta.organization.plugins.retrieve("plugin_01Hq3vX8kZcN2mB7pR4tY9wL")
print(f"id: {plugin.id}")
print(f"name: {plugin.name}")
```
### Change the served version
`POST /v1/organizations/plugins/{plugin_id}` changes which version of an organization-owned plugin members are served. Pass an earlier version to roll back, or a newer one to promote a build that was stored without being served. This pins the plugin, and a pinned plugin cannot currently be unpinned, here or in claude.ai (see [Versions and the served version](#versions-and-the-served-version)). The only updatable field is `served_version_id`, and it is required. The change reaches members before the response returns and does not create a version. When content scanning is on, the version must be one members can be served (see [Content scanning](#content-scanning)). Passing the version already served on a pinned plugin changes nothing; passing it on an unpinned plugin pins it there, so later uploads stop being served automatically. Pinning also ends any wait: a version that was waiting for its content scan stays stored, and is served only if someone chooses it. Returns the plugin. Requires the `write:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
plugin = client.beta.organization.plugins.update(
"plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
served_version_id="pluginver_01Jd5sK2nQ8wE4rT6yU1iO3p",
)
print(f"id: {plugin.id}")
print(f"served_version_id: {plugin.served_version_id}")
```
```python
{
"type": "plugin",
"id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"name": "sales-toolkit",
"display_name": "Sales Toolkit",
"description": "Account research and call prep for the sales team.",
"served_version_id": "pluginver_01Jd5sK2nQ8wE4rT6yU1iO3p",
"served_version_pinned": true,
"latest_version_id": "pluginver_01Jd5sK2nQ8wE4rT6yU1iO3p",
"manifest_version": "1.5.0",
"owner": { "type": "organization" },
"marketplace_id": "marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r",
"created_by": { "type": "api_actor", "api_key_id": "apikey_01Nq9vN6rT2zH7uW4bX5mR8s" },
"organization_installation_preference": "available",
"organization_installation_preference_inherited": true,
"content_scan": { "status": "completed", "assessment": "pass", "reason": null },
"components": [
{
"type": "skill",
"name": "account-research",
"description": "Researches a customer account before a call."
},
{ "type": "mcp_server", "name": "crm", "description": null },
{
"type": "command",
"name": "call-prep",
"description": "Builds a one-page brief for an upcoming call."
}
],
"reach": "remote",
"created_at": "2026-09-01T17:04:11Z",
"updated_at": "2026-09-16T10:02:45Z"
}
```
Besides the shared responses (a `403` for a member-owned plugin, and `409 scan_pending` or `400 scan_failed` for a version members cannot be served; see [Error responses](#error-responses)), the request can fail with:
| Status | Cause | What to do |
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| 400 | The body omits `served_version_id`, sets it to `null`, or contains any other field; or the value lacks the `pluginver_` prefix or is `latest`. | Send exactly `{"served_version_id": "pluginver_…"}`. |
| 404 | `served_version_id` is not a version of this plugin. | Take the ID from [List a plugin's versions](#list-a-plugins-versions). |
| 409 (no `error_code`) | An upload to this plugin or another served-version change is still in progress. | Retry shortly. |
| 409 `skill_name_taken` | The plugin is in the library marketplace and the version has a skill whose name an organization skill now uses. | Choose another version, or rename one of the skills. |
### Delete a plugin
`DELETE /v1/organizations/plugins/{plugin_id}` permanently deletes a plugin and every version it holds, just as an administrator's delete in claude.ai does. It works on any plugin in a `manual` marketplace, including a member's plugin, even if that member has since left the organization. When the delete returns, the plugin, its versions, and their files are gone from every read, and members are no longer served it. An organization-owned plugin's installation settings are removed with it; a member-owned plugin's shares are withdrawn, and it disappears for its owner too. A plugin in a marketplace synchronized from Git returns `400`: remove it from the repository, or remove the marketplace in claude.ai. Requires the `write:plugins` scope.
Deletion cannot be undone, and there is no per-version delete. To withhold an organization-owned plugin reversibly instead, set its organization-wide installation setting to `not_available` (a plugin that was inheriting its marketplace's default gets a setting of its own, and [removing that organization-wide setting](#remove-an-installation-setting) later returns the plugin to the default), and remove (or set to `not_available`) every group setting that `GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists, because a group's setting overrides the organization-wide value for its members. Send these writes one after another, not in parallel (see [Set an installation setting](#set-an-installation-setting)). A member-owned plugin cannot be withheld through this API except by deleting it, and only if its marketplace is `manual`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
deleted_plugin = client.beta.organization.plugins.delete(
"plugin_01Hq3vX8kZcN2mB7pR4tY9wL"
)
print(f"id: {deleted_plugin.id}")
```
```python
{ "type": "plugin_deleted", "id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL" }
```
## Plugin versions
A plugin version is an immutable snapshot of a plugin's files from one upload (the [Create a version](#create-a-version) response shows a complete object). Its fields mirror the plugin's served-version fields (`display_name`, `description`, `manifest_version`, `content_scan`, `components`, `reach`) for this version, plus `release_notes` (as supplied with the upload; shown in claude.ai's version history) and `created_by` (who uploaded it).
A `{version}` that lacks the `pluginver_` prefix returns `400` (except the literal `latest` where noted). One that carries the prefix but does not identify a version of that plugin returns `404`.
### List a plugin's versions
`GET /v1/organizations/plugins/{plugin_id}/versions` lists a plugin's versions, ordered by `created_at` descending; the first item is the version `latest_version_id` identifies. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
versions = client.beta.organization.plugins.versions.list(
"plugin_01Hq3vX8kZcN2mB7pR4tY9wL", limit=50
)
# Automatically fetches more pages as needed.
for version in versions:
print(f"{version.id}: {version.manifest_version}")
```
### Create a version
`POST /v1/organizations/plugins/{plugin_id}/versions` adds a version to an organization-owned plugin in a `manual` marketplace. The body is `multipart/form-data`, with the same `files[]` and `release_notes` fields, [upload requirements](#upload-requirements), and file, manifest, archive, and size errors as [Create a plugin](#create-a-plugin). The uploaded name (the manifest's `name`) must equal the plugin's `name`. If the plugin is not pinned, the new version is normally served as soon as it is stored; if it is pinned, the version is stored but not served until you change the served version to it. To check, compare the response's `id` with the plugin's `served_version_id`. Returns the version. Requires the `write:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
with open("dist/sales-toolkit.zip", "rb") as archive:
version = client.beta.organization.plugins.versions.create(
"plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
files=[archive],
release_notes="Adds the call-prep command.",
)
print(f"id: {version.id}")
print(f"manifest_version: {version.manifest_version}")
```
```python
{
"type": "plugin_version",
"id": "pluginver_01Jd5sK2nQ8wE4rT6yU1iO3p",
"plugin_id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"display_name": "Sales Toolkit",
"description": "Account research and call prep for the sales team.",
"manifest_version": "1.5.0",
"release_notes": "Adds the call-prep command.",
"created_by": { "type": "api_actor", "api_key_id": "apikey_01Nq9vN6rT2zH7uW4bX5mR8s" },
"content_scan": { "status": "processing", "assessment": null, "reason": null },
"components": [
{
"type": "skill",
"name": "account-research",
"description": "Researches a customer account before a call."
},
{ "type": "mcp_server", "name": "crm", "description": null },
{
"type": "command",
"name": "call-prep",
"description": "Builds a one-page brief for an upcoming call."
}
],
"reach": "remote",
"created_at": "2026-09-15T14:12:30Z"
}
```
Besides a `400` for an upload that breaks the [upload requirements](#upload-requirements) (`413` for a request body over 200 MB) and the shared responses (a `403` for a member-owned plugin; see [Error responses](#error-responses)), the request can fail with:
| Status | Cause | What to do |
|----------------------------|----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
| 400 | The plugin is in a marketplace synchronized from Git, or the uploaded name differs from the plugin's. | Change the repository instead, or fix the manifest `name`. |
| 409 (no `error_code`) | Another upload to this plugin, or a served-version change, is still in progress. | Retry shortly. |
| 409 `skill_name_taken` | The plugin is in the library marketplace and the version adds a skill with an organization skill's name. | Rename the skill, or remove the organization skill in claude.ai. |
| 503 `registration_pending` | The version was stored but its registration did not complete. | Resend the same request when the response carries `x-should-retry: true` (see [Retrying uploads](#retrying-uploads)). |
### Get a version
`GET /v1/organizations/plugins/{plugin_id}/versions/{version}` returns one version. `{version}` is a version ID, or `latest` for the version `latest_version_id` identifies at the time of the request. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
version = client.beta.organization.plugins.versions.retrieve(
"latest",
plugin_id="plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
)
print(f"id: {version.id}")
print(f"manifest_version: {version.manifest_version}")
```
### Download a version's files
`GET /v1/organizations/plugins/{plugin_id}/versions/{version}/content` downloads a version's files as the stored `.zip` archive (`Content-Type: application/zip`). The archive is returned whatever its content-scan result, so you can inspect versions withheld from members. It is served exactly as stored, so for an organization-owned plugin in a `manual` marketplace you can re-upload it unchanged as a new version, provided it meets the current upload requirements. `{version}` must be a version ID, not `latest`: read the plugin's `served_version_id` or `latest_version_id` first, or resolve `latest` with `GET /v1/organizations/plugins/{plugin_id}/versions/latest`. The `Content-Disposition` filename is derived from the plugin's name and is not unique; name saved files by plugin and version ID. Requires the `read:plugins` scope.
Downloading a **member-owned** plugin's archive records a `claude_plugin_archive_accessed` event on the Compliance API Activity Feed, identifying the key (as `api_actor`), the plugin and its marketplace, the version, and the owning member by ID; it carries no names. Downloading an organization-owned plugin's archive records nothing.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
plugin_id = "plugin_01Hq3vX8kZcN2mB7pR4tY9wL"
version_id = "pluginver_01Km7tL4pR9xF5sU2zV3jP6q"
with client.beta.organization.plugins.versions.with_streaming_response.download(
version_id,
plugin_id=plugin_id,
) as response:
response.stream_to_file(f"{plugin_id}_{version_id}.zip")
```
## Plugin installation settings
These endpoints apply to organization-owned plugins. They return `404` for a member-owned plugin, which has [shares](#plugin-shares) instead. `{target}` is the literal `organization` for the plugin's organization-wide setting, or a group's `rbac_group_` ID for that group's setting; any other value returns `400`. Group IDs come from `GET /v1/organizations/rbac_groups` (scope `read:rbac_groups`; see [User management](manage-claude-user-management.md#groups)). A setting has no `id` of its own: it is addressed by `(plugin_id, target)`, and no actor is recorded on it (the actor is on its `plugin_installation_preference_updated` activity event).
### List a plugin's installation settings
`GET /v1/organizations/plugins/{plugin_id}/installation_settings` lists the settings an organization-owned plugin holds, ordered by `created_at` descending: its own organization-wide setting (absent while it inherits its marketplace's default) and each group's setting. Filter by `target_type` (`organization` or `rbac_group`). Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
settings = client.beta.organization.plugins.installation_settings.list(
"plugin_01Hq3vX8kZcN2mB7pR4tY9wL"
)
# Automatically fetches more pages as needed.
for setting in settings:
print(f"{setting.plugin_id}: {setting.installation_preference}")
```
### Set an installation setting
`POST /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` sets one target's installation setting for an organization-owned plugin, creating it or changing the value it already holds. The body's only field is `installation_preference` (`required`, `auto_install`, `available`, or `not_available`), and it is required. Setting the value the target already holds changes nothing. Setting the `organization` target stops the plugin inheriting its marketplace's default (`organization_installation_preference_inherited` becomes `false`), even when the value is the same as the default, so the plugin no longer follows later changes to the marketplace default. To return the plugin to its marketplace's default, [remove the organization-wide setting](#remove-an-installation-setting); group settings are unaffected. A group target must be a group your organization can see in `GET /v1/organizations/rbac_groups`, or the request returns `404`. The change does not alter the plugin's `updated_at`; it is recorded on the Activity Feed. Returns the setting. Requires the `write:plugins` scope.
Send a plugin's installation-setting writes one at a time. If several writes for the same plugin arrive at the same time, the server handles them one after another and can answer some of them with `503` instead of applying them. That `503` carries `x-should-retry: true`, and the write is safe to repeat: wait a second or two, then send it again.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
setting = client.beta.organization.plugins.installation_settings.set(
"rbac_group_01F3xQqQzXyWvUtSrQpOnMlK",
plugin_id="plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
installation_preference="available",
)
print(f"plugin_id: {setting.plugin_id}")
print(f"installation_preference: {setting.installation_preference}")
```
```python
{
"type": "plugin_installation_setting",
"plugin_id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"target": { "type": "rbac_group", "rbac_group_id": "rbac_group_01F3xQqQzXyWvUtSrQpOnMlK" },
"installation_preference": "available",
"created_at": "2026-09-02T10:00:00Z",
"updated_at": "2026-09-02T10:00:00Z"
}
```
### Remove an installation setting
`DELETE /v1/organizations/plugins/{plugin_id}/installation_settings/{target}` removes one target's installation setting for an organization-owned plugin. With `{target}` of `organization` it removes the plugin's own organization-wide setting. The plugin then inherits its marketplace's default again (`organization_installation_preference_inherited` becomes `true`) and follows later changes to that default; group settings are unaffected. With a group's `rbac_group_` ID it removes that group's setting: the group's members fall back to the organization-wide value, or to another of their groups' settings. A target that holds no setting of its own returns `404`; that includes the `organization` target of a plugin that already inherits its marketplace's default. The change does not alter the plugin's `updated_at`; it is recorded on the Activity Feed. The response carries the composite key in place of an `id`; for the `organization` target its `target` is `{"type": "organization"}`. Requires the `write:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
removed_setting = client.beta.organization.plugins.installation_settings.remove(
"rbac_group_01F3xQqQzXyWvUtSrQpOnMlK",
plugin_id="plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
)
print(f"plugin_id: {removed_setting.plugin_id}")
```
```python
{
"type": "plugin_installation_setting_deleted",
"plugin_id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL",
"target": { "type": "rbac_group", "rbac_group_id": "rbac_group_01F3xQqQzXyWvUtSrQpOnMlK" }
}
```
## Plugin shares
Shares exist only on member-owned plugins and are read-only in this API (see [Shares](#shares)).
### List a plugin's shares
`GET /v1/organizations/plugins/{plugin_id}/shares` lists who the owner of a member-owned plugin has shared it with, ordered by `granted_at` descending: every member (`organization`), a group (`rbac_group`), or a named member (`organization_member`). Filter by `target_type`. A plugin its owner has not shared returns an empty list; an organization-owned plugin returns `404`. Shares are read-only on this API, and a listed share gives access only while that kind of sharing is turned on for your organization in claude.ai (see [Shares](#shares)). `granted_at` is when the share was given; if the owner later changes the share in claude.ai, it is the time of that change. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
shares = client.beta.organization.plugins.shares.list("plugin_01Mr2wP7sU3aJ8vX5cY6nS9t")
# Automatically fetches more pages as needed.
for share in shares:
print(f"plugin_id: {share.plugin_id}")
```
```python
{
"data": [
{
"type": "plugin_share",
"plugin_id": "plugin_01Mr2wP7sU3aJ8vX5cY6nS9t",
"target": { "type": "organization_member", "user_id": "user_01WCz9BvGLdMYRUMmcAxMWvW" },
"granted_at": "2026-08-20T15:12:00Z"
}
],
"next_page": null
}
```
## Plugin marketplaces
This API reads [marketplaces](#marketplaces) and sets an organization marketplace's default installation setting; marketplaces themselves are created, connected to a repository, and deleted in claude.ai.
```python
{
"type": "plugin_marketplace",
"id": "marketplace_01VbNcMxZaSdFgHjKlQwErTy",
"name": "engineering-tools",
"owner": { "type": "organization" },
"source": "github",
"sync_status": "success",
"last_sync_ended_at": "2026-09-10T22:15:03Z",
"last_sync_read_sha": "9fceb02d0ae598e95dc970b74767f19372d61af8",
"default_installation_preference": "available",
"created_at": "2026-06-12T08:45:00Z"
}
```
| Field | Description |
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | The marketplace's name. Fixed for its lifetime. |
| `owner` | Same shape as on the plugin. |
| `source` | `manual`, `github`, `gitlab`, or `public_git`. See [Marketplaces](#marketplaces). |
| `sync_status` | Outcome of the most recent synchronization: `success`, `in_progress`, `failed_content`, `failed_transient`, `failed_auth`, or `failed_limits`. `null` until a synchronization is first attempted, which never happens for a marketplace whose source is `manual`. |
| `last_sync_ended_at` | When the most recent synchronization attempt finished, whatever its outcome; for a connected repository that has not synchronized yet, when the marketplace was created. `null` for a marketplace that is not synchronized. |
| `last_sync_read_sha` | The commit the last synchronization read from the repository. Not necessarily the commit the served versions came from. `null` for a marketplace that is not synchronized. |
| `default_installation_preference` | Organization marketplaces: the organization-wide value for every plugin in it without its own setting (`not_available` if never set). Personal marketplaces: `null`. |
A `marketplace_id` that lacks the `marketplace_` prefix returns `400`. One that carries the prefix but does not resolve, or belongs to another organization, returns `404`.
### List marketplaces
`GET /v1/organizations/plugin_marketplaces` lists your organization's marketplaces and members' personal marketplaces, ordered by `created_at` descending. Use it to find a marketplace's ID, to filter the plugin list by it or to upload into it, before it holds any plugin. The library marketplace appears once something has first been created in it, in claude.ai or through this API. Filter by `owner_type` (`organization` or `user`) and `source`. `limit` is 1 to 1,000. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
marketplaces = client.beta.organization.plugin_marketplaces.list(
owner_type="organization"
)
# Automatically fetches more pages as needed.
for marketplace in marketplaces:
print(f"{marketplace.id}: {marketplace.name}")
```
### Get a marketplace
`GET /v1/organizations/plugin_marketplaces/{marketplace_id}` returns one marketplace. Requires the `read:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
marketplace = client.beta.organization.plugin_marketplaces.retrieve(
"marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r"
)
print(f"id: {marketplace.id}")
print(f"name: {marketplace.name}")
```
### Set a marketplace's default installation setting
`POST /v1/organizations/plugin_marketplaces/{marketplace_id}` sets the default installation setting of an organization-owned marketplace. Every plugin in the marketplace without its own organization-wide setting reports this default as its `organization_installation_preference`, including plugins added later. It works for `manual` and synchronized marketplaces; a member's personal marketplace returns `403`. The only updatable field is `default_installation_preference`, and it is required. It cannot be set back to `null`: once a marketplace has a default, it keeps one, as in claude.ai. A change is recorded as one `marketplace_updated` event with no per-plugin events, and does not alter any plugin's `updated_at`. Setting the value already set changes nothing, with one exception: a marketplace whose default was never set reports `not_available` but holds no setting, so its first write (even `not_available`) counts as a change. Returns the marketplace. Requires the `write:plugins` scope.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
marketplace = client.beta.organization.plugin_marketplaces.update(
"marketplace_01Lp8uM5qS1yG6tV3aW4kQ7r",
default_installation_preference="available",
)
print(f"id: {marketplace.id}")
print(f"default_installation_preference: {marketplace.default_installation_preference}")
```
### Validate marketplace content
Two endpoints report what a synchronization of the given marketplace content would do, without connecting or storing anything: `POST /v1/organizations/plugin_marketplaces/validate_repository` reads a public GitHub repository, and `POST /v1/organizations/plugin_marketplaces/validate_archive` reads a `.zip` of the marketplace directory that you upload. Both return the same report: whether `marketplace.json` is well-formed, which plugins would be skipped and why, and which plugins would synchronize with some contents left out. The checks are the ones a real synchronization runs. Problems with the content come back in the report, not as HTTP errors: the request succeeds with `valid: false`, even when the repository or archive cannot be read at all. A validation counts as a read, and the two endpoints together are additionally limited to 10 validations a minute per organization (see [Rate limiting](#rate-limiting)); they record nothing on the Activity Feed. A validation can take up to 120 seconds before it returns, so set your client's timeout above that. Both endpoints require the `read:plugins` or `write:plugins` scope (`read:org_audit` and `read:compliance_org_data` do not grant them).
The repository, and any plugin source outside it on GitHub, are read anonymously, so a private repository or private plugin source is reported as not found. Plugin sources on hosts other than GitHub are not fetched; such a plugin normally gets a `marketplace_validate_source_not_checked` warning and is checked when the marketplace actually synchronizes. If the repository is, or the archive names, a marketplace that Anthropic synchronizes into every organization, stricter rules apply: every plugin source outside the marketplace must be pinned to a full commit SHA, unpinned or unsupported-host sources are reported as plugin errors, and the branch read defaults to the one that marketplace synchronizes from.
`validate_repository` takes a JSON body with two fields: `repository_url`, the `https://` URL of a public repository on github.com (required), and `ref`, a branch name or a full 40-character commit SHA (optional; when it is omitted or `null`, the branch a synchronization would read, usually the repository's default branch). `validate_archive` takes `multipart/form-data` with exactly one part, `archive`, sent as a file part with a filename: a `.zip` of the marketplace directory, at most 32 MB, with its contents at the root or wrapped in one folder (as a Git host's download produces), DEFLATE or STORE compression only. No other form field is accepted.
Validate a public repository at a branch:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
report = client.beta.organization.plugin_marketplaces.validate_repository(
repository_url="https://github.com/example-org/claude-plugins",
ref="release-candidate",
)
print(f"valid: {str(report.valid).lower()}")
print(f"total_plugin_count: {report.total_plugin_count}")
```
```python
{
"type": "plugin_marketplace_validation_report",
"valid": false,
"ref": "release-candidate",
"commit_sha": "9fceb02d0ae598e95dc970b74767f19372d61af8",
"total_plugin_count": 3,
"manifest_error": null,
"manifest_error_code": null,
"plugin_errors": [
{
"name": "deploy-helper",
"error": "The plugin has a top-level bin/ directory.",
"error_code": "marketplace_sync_bin_directory_not_allowed"
}
],
"plugin_warnings": [
{
"name": "release-notes",
"warnings": [
{
"message": "plugin.json has unrecognized top-level keys: owners",
"error_code": "marketplace_sync_plugin_unrecognized_keys"
}
]
}
]
}
```
| Field | Description |
|-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `valid` | `true` when `marketplace.json` is well-formed and no plugin would be skipped. Warnings do not make it `false`. |
| `ref` | The branch that was read, by name; `null` when no branch was named and the default branch was read, for a commit SHA, or for an archive. |
| `commit_sha` | The commit that was validated. For an archive downloaded from a Git host, the commit the host recorded in the ZIP file's comment field, if any (not verified). |
| `total_plugin_count` | How many plugins `marketplace.json` declares; `0` when it could not be read. |
| `manifest_error`, `manifest_error_code` | Set when nothing could be validated: the source could not be read, or `marketplace.json` is missing, malformed, or over a limit. Validation that did not finish within 120 seconds reports `manifest_error_code: "marketplace_validate_deadline_exceeded"`. |
| `plugin_errors` | One `{name, error, error_code}` per plugin a synchronization would skip. |
| `plugin_warnings` | One `{name, warnings: [{message, error_code}]}` per plugin that would synchronize with some contents left out. |
Validate a local copy of the marketplace directory instead, as a `.zip`; the response is the same report:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
with open("marketplace.zip", "rb") as archive:
report = client.beta.organization.plugin_marketplaces.validate_archive(
archive=archive
)
print(f"valid: {str(report.valid).lower()}")
print(f"total_plugin_count: {report.total_plugin_count}")
```
Problems with the content never fail the request. Apart from the responses every endpoint shares (a `403` for a key that has only `read:org_audit` or `read:compliance_org_data`; see [Error responses](#error-responses) and [Rate limiting](#rate-limiting)), the request itself can fail with:
| Status | Cause | What to do |
|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|
| 400 | On `validate_repository`: the body is not a JSON object; `repository_url` is missing, is longer than 2,048 characters, carries credentials, or is not of the form `https://github.com/{owner}/{repo}` (a `.git` suffix is accepted; another host, a longer path such as a branch page's `/tree/main`, or a port other than 443 or 80 is not); `ref` is empty, is longer than 255 characters, contains `..`, or contains a character other than ASCII letters, digits, `.`, `_`, `-`, `+`, and `/`; or another field is present. A `ref` that passes these checks but names a branch the repository does not have is not refused: the request succeeds with `valid: false` and `manifest_error` says the branch was not found. On `validate_archive`: the body is not `multipart/form-data`, the `archive` part is missing, repeated, or not sent as a file part with a filename, or another form field is present. | Fix the request and resend. |
| 413 | On `validate_archive`: the `archive` part, or the request's declared body length, exceeds 32 MB. | Validate the repository by URL instead, or trim the archive. |
#### Report codes
Each finding in a report has a stable code: `manifest_error_code` when nothing could be validated, `error_code` on each `plugin_errors` entry, and `error_code` on each warning. When a plugin has several problems, `error_code` is the first one's and `error` joins their messages. New codes may be added; an unrecognized `manifest_error_code` still means the content could not be validated, one on a `plugin_errors` entry still means the plugin would be skipped, and one on a warning still means the plugin would synchronize. These codes indicate transient conditions, so the same request may succeed later: `marketplace_host_rate_limited`, `marketplace_host_server_error`, `marketplace_host_timeout`, `marketplace_host_unreachable`, `marketplace_repo_access_denied`, `marketplace_sync_transient_fetch_budget_exhausted`, `marketplace_validate_network_error`, and usually `marketplace_validate_deadline_exceeded`.
## Unrecognized values
Every string value on this page (component types, `reach`, scan fields, marketplace `source`, error codes) may gain new values at any time. Treat a value you do not recognize as you would any unknown string rather than failing.
## Rate limiting
Read requests (every `GET` endpoint on this page) share a limit of **300 requests per minute** per organization, and write requests (creating a plugin or version, changing the served version, deleting, setting or removing an installation setting, and updating a marketplace) share a limit of **60 requests per minute** per organization. A marketplace validation (either endpoint) counts as a read, and validations are additionally limited to 10 a minute per organization across both endpoints; both limits are checked before the request body is read. These limits are counted across all of your organization's keys and are separate from your organization's other Admin API limits. Requests over a limit return **429 Too Many Requests** with a `retry-after` header. Responses include `anthropic-ratelimit-requests-*` headers for the limit that applies (on marketplace validation, its 10-per-minute limit; on a `429`, whichever limit refused the request).
An upload, a served-version change, or a validation can also return `429` with `retry-after` when the service briefly has no capacity for another one, and an upload returns `429` when your organization has exceeded its content-scan rate. Handle all of these the same way: wait for `retry-after`, then retry. Separately from these limits, send installation-setting writes for the same plugin one at a time: when several arrive at the same time, some can be answered with `503` and `x-should-retry: true`, and those are safe to send again after a second or two (see [Set an installation setting](#set-an-installation-setting)).
## Pagination
List endpoints use an **opaque cursor**. The first request returns up to `limit` rows plus a `next_page` cursor; pass the cursor unchanged as the `page` parameter on the next request, and repeat until `next_page` is `null`. Treat the cursor string as opaque: don't parse, modify, or construct it yourself. [List plugins](#list-plugins) can return a page with fewer than `limit` plugins, or none, while `next_page` is still set, so keep requesting pages until `next_page` is `null`. The Python, TypeScript, C#, Go, Java, and Ruby SDKs' list iterators and the CLI (except with `--format raw`) do that for you. They request the next page while `next_page` is set, so a short or empty page does not end them. In PHP and curl, request each page yourself and pass its `next_page` as `page`.
`limit` defaults to 20 and has a minimum of 1. The maximum is 100 for plugins, installation settings, and shares, and 1,000 for versions and marketplaces. Every list is ordered newest first.
## Error responses
Error responses follow the standard shape documented in [Errors](../Endpoints/errors.md). Quote the `request_id` from the response body when contacting support.
| Status | Meaning |
|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 400 | Invalid input, or the operation does not apply to this plugin or marketplace (see each endpoint's section). Also returned for a query parameter the endpoint does not recognize, and for an organization that is not a Claude Enterprise organization (`this endpoint is not supported for this organization type`). |
| 401 | Missing `x-api-key` header, or the key is not recognized. |
| 403 | The key is missing the required scope, or the request uploads to, changes the served version of, or sets the default for a member's plugin or personal marketplace. (Deleting a member's plugin is allowed.) |
| 404 | Resource not found. Also returned when the request omits the `anthropic-beta` value, or the API is not enabled for your organization, so that the endpoints read as nonexistent. |
| 409 | A name is taken, a content scan is still running, or a conflicting upload is in progress. |
| 413 | The request body is over the size limit: 200 MB for an upload, 32 MB for marketplace validation. |
| 429 | Rate limit exceeded. See [Rate limiting](#rate-limiting). |
| 500 | Internal error. |
| 503 | Temporary. Also returned when several installation-setting writes for one plugin arrive at the same time; send those one at a time. Retry with backoff, except `registration_pending` (see the following table). |
When one status has several causes that you would handle differently, the error also carries `error.details.error_code`, and `error.details.plugin_id` or `error.details.plugin_version_id` when the cause involves one:
```python
{
"type": "error",
"error": {
"type": "invalid_request_error",
"message": "...",
"details": {
"error_code": "plugin_name_taken",
"plugin_id": "plugin_01Hq3vX8kZcN2mB7pR4tY9wL"
}
},
"request_id": "req_018EeWyXxfu5pfWkrYcMdjWG"
}
```
| `error_code` | Status | Meaning and what to do |
|-------------------------------------------------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `plugin_name_taken` | 409 | A plugin with this name already exists in the marketplace. `details.plugin_id` is that plugin. If you are retrying a create whose response you lost, continue with that plugin. When `plugin_id` is absent, the name is held by a standalone skill: upload under another name, or delete the skill in claude.ai. |
| `skill_name_taken` | 409 | The plugin is in the library marketplace and one of its skills has the same name as an organization skill (a skill an administrator uploaded for the whole organization in claude.ai). `details.skill_name` names it. Rename or remove one of them. |
| `registration_pending` | 503 | The files were stored, but the plugin's skills could not yet be made available to members. See [Retrying uploads](#retrying-uploads). |
| `scan_pending` | 409 | The version's content scan is still running. Retry once it finishes. |
| `scan_failed` | 400 | The version's content scan failed, errored, or reached no verdict, so it cannot be served. Choose another version. |
| `cmek_key_disabled`, `cmek_key_network_blocked` | 400 | Your organization's customer-managed encryption key is unavailable. See [Customer-managed encryption keys](#customer-managed-encryption-keys). |
New codes may be added. Treat a code you do not recognize the way you treat its status.
### Retrying uploads
No endpoint accepts an `Idempotency-Key`. Changing the served version, setting an installation setting, and setting a marketplace default are safe to repeat. A repeated delete, or a repeated removal of an installation setting, returns `404`.
An upload that returns an error stored nothing, with one exception: `503` with `error_code: "registration_pending"`. After storing an upload's files, the server registers the new version's skills with claude.ai, which is what makes them usable by members; `registration_pending` means the files were stored but that last step did not complete. Uploading the same files once more completes it (and stores one more, identical version):
- On `POST /v1/organizations/plugins`, the plugin *was* created, and the response carries `x-should-retry: false`: do not resend the create (a resend returns `409 plugin_name_taken`); instead upload the same files as a version of `details.plugin_id`.
- On `POST /v1/organizations/plugins/{plugin_id}/versions`, the version *was* stored (`details.plugin_version_id`); resend the same request when the response carries `x-should-retry: true`, and do not when it carries `false`.
If a create's response is lost, retry it: the retry returns `409 plugin_name_taken` with the plugin's ID in `details.plugin_id`, and you continue with that plugin. Retrying a version create whose response was lost stores a second, identical version. To avoid that, record the plugin's `latest_version_id` before each upload; if a response is lost, read the plugin and retry only if `latest_version_id` is unchanged.
## Activity Feed events
Every write through this API is recorded on your organization's [Compliance API Activity Feed](manage-claude-compliance-activity-feed.md), attributed to the API key as an `api_actor` carrying its `apikey_` ID. The same actor appears in `created_by` on the plugins and versions the key creates.
| Event | Emitted when |
|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `claude_plugin_created` | A plugin is created by an upload (here or in claude.ai) or by an accepted request to publish. A plugin created by a Git synchronization emits only `claude_plugin_version_created`. |
| `claude_plugin_version_created` | A version is stored. Versions stored by a Git synchronization are attributed to a `system_actor`. |
| `claude_plugin_updated` | A new version is uploaded to an existing plugin. |
| `claude_plugin_served_version_updated` | The served version changes. |
| `claude_plugin_deleted` | A plugin is deleted on its own, here or in claude.ai. |
| `plugin_installation_preference_updated` | An installation setting is set or removed. |
| `marketplace_created` | The first upload creates the library marketplace. |
| `marketplace_updated` | A marketplace's default installation setting changes, or an administrator or owner starts a synchronization in claude.ai. |
| `marketplace_deleted` | A marketplace is deleted in claude.ai together with its plugins (no per-plugin events). |
| `claude_plugin_archive_accessed` | A member-owned plugin's archive is downloaded. |
| `claude_plugin_security_scan_completed` | A content scan finishes. |
The plugin, version, and marketplace IDs in these events are the same IDs this API returns. `plugin_installation_preference_updated` identifies the plugin by its `name` and `marketplace_id` rather than its `id`.
Changing a marketplace's default records one `marketplace_updated` event and no per-plugin events, even though it changes the value of every plugin that inherits the default. Reads are not recorded, except downloads of a member-owned plugin's archive. A write that changes nothing records nothing.
Shares given or withdrawn in claude.ai appear on the feed as `role_assignment_granted` and `role_assignment_revoked` events. This API does not report deletions: a deleted plugin is simply absent from the next list. A plugin removed by a Git synchronization, by deleting its marketplace (one `marketplace_deleted` event), or by deleting a member's account or the organization emits no per-plugin event, so re-list the full inventory periodically to catch removals.
## Customer-managed encryption keys
If your organization uses a [customer-managed encryption key](manage-claude-cmek.md), a version's `description`, `release_notes`, `components`, and files are encrypted with it. While the key is unavailable:
- Reads and lists still succeed, with `description`, `release_notes`, and `components` returned as `null`.
- Archive downloads, creates, version creates, and served-version changes return `400` with `cmek_key_disabled` or `cmek_key_network_blocked`.
- Deleting a plugin in the library marketplace returns `400 cmek_key_disabled` and deletes nothing, because its skills must first be withdrawn from claude.ai and that needs the key. Other deletes, installation settings, and marketplace defaults work normally.
Restoring the key clears all of these.
## See also
[Create an Admin API key](manage-claude-admin-api-keys.md)
Where your primary owner creates a scoped key.
[User management](manage-claude-user-management.md)
The group endpoints that supply the `rbac_group_` IDs used in installation settings.
[Compliance API Activity Feed](manage-claude-compliance-activity-feed.md)
Where plugin writes and member-archive downloads are recorded.
[Analytics APIs](manage-claude-analytics-api.md)
Plugin and skill usage reporting for Claude Enterprise.
---
## Rate Limits API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/rate-limits-api
Path: 04-API-Reference/Other/manage-claude-rate-limits-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Frate-limits-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Monitoring
# Rate Limits API
Copy page
Programmatically query your organization's API rate limits with the Rate Limits API.
Copy page
**The Admin API is unavailable for individual accounts.** To collaborate with teammates and add members, set up your organization in **Console → Settings → Organization**.
The Rate Limits API provides programmatic access to the rate limits configured for your organization and its workspaces. This is the same information shown on the [Rate limits](playground.md) page in the Claude Console.
Use this API to:
- **Keep gateways and proxies in sync:** Read your current limits at startup and on a schedule instead of hardcoding values that drift when Anthropic adjusts them.
- **Power internal alerting:** Compare usage data from the [Usage and Cost API](manage-claude-usage-cost-api.md) against your configured limits.
- **Audit workspace configuration:** Verify that workspace overrides match what your provisioning automation expects.
**Admin API credentials required.** These endpoints are part of the Admin API. You can access them using an [Admin API key](manage-claude-admin-api-keys.md), an OAuth token with the `org:admin` scope, or a personal or service account key that isn't scoped to a workspace; workspace API keys don't work. See [Authentication](manage-claude-admin-api.md#authentication) for details.
The SDK and CLI examples on this page construct the default client, which reads the Admin API key from the `ANTHROPIC_API_KEY` environment variable. The SDKs expose these endpoints as `client.beta.organization.rate_limits` and `client.beta.organization.workspaces.rate_limits`; the Python, TypeScript, C#, Go, and Java list methods return an iterator that follows `next_page` for you, while the PHP, Ruby, and curl examples read one page.
## Quick start
List the rate limits configured for your organization:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
rate_limits = client.beta.organization.rate_limits.list()
for entry in rate_limits:
models = f" ({', '.join(entry.models)})" if entry.models else ""
print(f"{entry.group.type}{models}")
for limit in entry.limits:
print(f" {limit.type}: {limit.value}")
```
## Organization rate limits
The `/v1/organizations/rate_limits` endpoint returns the rate limits applied at the organization level for the Messages API and its supporting resources. Limits for other products, such as [Claude Managed Agents](managed-agents-overview.md), are not included.
### Key concepts
- **Rate limit groups:** Each entry in the response represents one rate limit group. Model rate limits are grouped so that several model versions share a single set of limits, and other groups cover resources such as the Message Batches API, the Files API, the Token Counting API, agent skills, and the web search tool.
- **`group` object:** Present on every entry, it identifies the rate limit group the entry applies to. It always has `type`, which is one of the `group_type` values, and `id`, an opaque identifier with the `rlg_` prefix. On `model_group` entries it also has `display_name`, Anthropic's current label for the group, such as `Claude Sonnet 4.x`. The label is for display only and may change. Other group types have no `display_name`.
- **The `id` inside `group`:** A group has the same `id` in every organization and on every workspace override, and it never changes. Use it to match entries across organizations or against your own catalog. The entry's own `id` differs per organization, and `models` changes when Anthropic moves a model between groups. Neither is a stable key for the group.
- **`group_type`:** Deprecated in favor of the `type` inside `group`. It's still returned, always equals that value, and has no removal date. The `group_type` query parameter isn't deprecated. See [Filtering by group type](#filtering-by-group-type) for the list of values.
- **`models` list:** For `model_group` entries, the `models` field lists every model ID and alias that counts against that group's limits. Use this list to look up which group any model string falls under. For other group types, `models` is `null`.
- **`limits` list:** Each group carries a list of `{type, value}` pairs. The `type` field identifies the limiter (such as `requests_per_minute`, `input_tokens_per_minute`, or `output_tokens_per_minute`) and `value` is the configured limit. See [Rate limits](../Endpoints/rate-limits.md) for how each limiter is measured and enforced.
For complete parameter details and response schemas, see the [Organization Rate Limits API reference](../Admin/beta-organization-rate-limits-list.md).
### List all organization rate limits
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
rate_limits = client.beta.organization.rate_limits.list()
for entry in rate_limits:
models = f" ({', '.join(entry.models)})" if entry.models else ""
print(f"{entry.group.type}{models}")
for limit in entry.limits:
print(f" {limit.type}: {limit.value}")
```
```python
{
"data": [
{
"type": "rate_limit",
"group_type": "model_group",
"group": {
"type": "model_group",
"id": "rlg_01Hq7YkP3mZ9dTwRx4cVbN2s",
"display_name": "Claude Opus 5.5"
},
"models": ["claude-opus-5-5"],
"limits": [
{ "type": "requests_per_minute", "value": 4000 },
{ "type": "input_tokens_per_minute", "value": 10000000 },
{ "type": "output_tokens_per_minute", "value": 800000 }
]
},
{
"type": "rate_limit",
"group_type": "model_group",
"group": {
"type": "model_group",
"id": "rlg_01Kd5wMv8nSq2LcXy6tRfJ4b",
"display_name": "Claude Opus 4.x"
},
"models": [
"claude-opus-4-5",
"claude-opus-4-5-20251101",
"claude-opus-4-6",
"claude-opus-4-7",
"claude-opus-4-8"
],
"limits": [
{ "type": "requests_per_minute", "value": 4000 },
{ "type": "input_tokens_per_minute", "value": 10000000 },
{ "type": "output_tokens_per_minute", "value": 800000 }
]
},
{
"type": "rate_limit",
"group_type": "batch",
"group": { "type": "batch", "id": "rlg_01Wn3pBz6kCg9vHtQ7mLxD5a" },
"models": null,
"limits": [{ "type": "enqueued_batch_requests", "value": 500000 }]
}
],
"next_page": null
}
```
### Look up the limits for a specific model
Pass any model ID or alias as the `model` query parameter to return only the entry that contains it:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
rate_limits = client.beta.organization.rate_limits.list(model="claude-opus-5")
for entry in rate_limits:
models = f" ({', '.join(entry.models)})" if entry.models else ""
print(f"{entry.group.type}{models}")
for limit in entry.limits:
print(f" {limit.type}: {limit.value}")
```
If the model string doesn't match any group, the endpoint returns a 404 error. The `model` parameter is supported on the organization endpoint only; the workspace endpoint doesn't accept it.
## Workspace rate limits
The `/v1/organizations/workspaces/{workspace_id}/rate_limits` endpoint returns the rate limit overrides configured for a single workspace.
The response only includes overrides, so anything missing from it is inherited from the organization:
- A group that is absent from `data` has no workspace override at all. The workspace inherits the organization-level limits for that group (it is not unlimited).
- Within a group that is present, a limiter type that is absent from `limits[]` has no workspace override for that limiter. The workspace inherits the organization value for it.
- For each limiter that is present, `org_limit` is the organization-level value for the same limiter, or `null` if the organization has no configured limit for that limiter type.
For complete parameter details and response schemas, see the [Workspace Rate Limits API reference](../Admin/beta-organization-workspaces-rate-limits-list.md).
To retrieve your organization's workspace IDs, use the [List Workspaces](../Admin/beta-organization-workspaces-list.md) endpoint, or find them in the [Claude Console](playground.md). The default workspace cannot have rate limit overrides, so it has no entry on this endpoint; use the organization endpoint to read its limits.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
rate_limits = client.beta.organization.workspaces.rate_limits.list(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
for entry in rate_limits:
models = f" ({', '.join(entry.models)})" if entry.models else ""
print(f"{entry.group.type}{models}")
for limit in entry.limits:
print(f" {limit.type}: {limit.value}")
```
```python
{
"data": [
{
"type": "workspace_rate_limit",
"group_type": "model_group",
"group": {
"type": "model_group",
"id": "rlg_01Hq7YkP3mZ9dTwRx4cVbN2s",
"display_name": "Claude Opus 5.5"
},
"models": ["claude-opus-5-5"],
"limits": [
{ "type": "requests_per_minute", "value": 1000, "org_limit": 4000 },
{ "type": "input_tokens_per_minute", "value": 500000, "org_limit": 10000000 }
]
},
{
"type": "workspace_rate_limit",
"group_type": "model_group",
"group": {
"type": "model_group",
"id": "rlg_01Kd5wMv8nSq2LcXy6tRfJ4b",
"display_name": "Claude Opus 4.x"
},
"models": [
"claude-opus-4-5",
"claude-opus-4-5-20251101",
"claude-opus-4-6",
"claude-opus-4-7",
"claude-opus-4-8"
],
"limits": [
{ "type": "requests_per_minute", "value": 1000, "org_limit": 4000 },
{ "type": "input_tokens_per_minute", "value": 500000, "org_limit": 10000000 }
]
}
],
"next_page": null
}
```
## Filtering by group type
Both endpoints accept an optional `group_type` query parameter that restricts the response to a single category:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
rate_limits = client.beta.organization.rate_limits.list(group_type="batch")
for entry in rate_limits:
models = f" ({', '.join(entry.models)})" if entry.models else ""
print(f"{entry.group.type}{models}")
for limit in entry.limits:
print(f" {limit.type}: {limit.value}")
```
Valid values are `model_group`, `batch`, `token_count`, `files`, `skills`, and `web_search`.
## Pagination
Both endpoints accept a `page` query parameter and return a `next_page` field. Responses are currently always a single page, so `next_page` is `null`. Loop on `next_page` so your client paginates correctly without changes when the response grows.
## Frequently asked questions
### Which model strings appear in the `models` list?
Every model ID and alias that counts against the group, including dated IDs (such as `claude-sonnet-4-5-20250929`) and undated aliases (such as `claude-sonnet-4-5`). Look up any model string you pass to the Messages API and you'll find it in exactly one `model_group` entry.
### What does it mean if a group is missing from the workspace response?
The workspace has no override for that group and inherits the organization-level limit. Query the organization endpoint to see the inherited values.
### Can I update rate limits with this API?
No. To set workspace rate limits, open the workspace in the [Claude Console](playground.md) and use the **Rate limits** tab.
## See also
- [Rate limits](../Endpoints/rate-limits.md)
- [Admin API](manage-claude-admin-api.md)
- [Admin API reference](../Admin/http-beta-organization.md)
- [Workspaces](manage-claude-workspaces.md)
- [Usage and Cost API](manage-claude-usage-cost-api.md)
---
## Spend Limits API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/spend-limits-api
Path: 04-API-Reference/Other/manage-claude-spend-limits-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fspend-limits-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Monitoring
# Spend Limits API
Copy page
Set a spend limit on each Claude Enterprise member, see where each member's spend limit is inherited from, and review or act on members' requests for a higher limit.
Copy page
The Spend Limits API lets you set a spend limit on each Claude Enterprise member, see where each member's spend limit is inherited from, and review or act on members' requests for a higher limit.
For per-user and time-bucketed usage and cost *reporting*, see [Analytics APIs](manage-claude-analytics-api.md).
**Scoped Admin API key required**
These endpoints require an Admin API key with the `read:spend_limits` scope (for `GET` endpoints) or the `write:spend_limits` scope (for `POST` and `DELETE` endpoints). See [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-enterprise-organization) for where your primary owner creates one and which scopes to select. Pass the key in the `x-api-key` header on every request, together with the [`anthropic-version`](../Endpoints/versioning.md) header.
The Spend Limits API is available to Claude Enterprise organizations only. It is not available to Claude Platform (Claude Console) organizations.
## Overview
The API exposes eight endpoints across two resources:
[TABLE]
Use the **spend limits** endpoints to answer "what spend limit applies to each member, where does it come from, and how close are they to it?" and to set a per-user override. Use the **spend limit increase requests** endpoints to work the queue of member-submitted requests.
## Prerequisites
- Your organization must be on a Claude Enterprise plan.
- Usage credits must be turned on for your organization. Your primary owner can turn them on in claude.ai billing settings.
## Quick start
List every member's effective monthly spend limit and period-to-date spend:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/spend_limits/effective?limit=20" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
## Key concepts
### The spend limit hierarchy
An **effective spend limit** applies to each member's spend, resolved from a hierarchy of scope levels. When a member has no per-user override, they inherit the spend limit configured for their group (if your organization uses group-based limits), their seat tier, or the organization-wide default. A group spend limit is a per-member default: each member inheriting it is gated against their own spend, not a pooled group budget.
Reading `GET /v1/organizations/spend_limits/effective` returns every current member with their resolved effective spend limit, where that limit was resolved from (`source`), and their period-to-date spend. Setting a per-user override with `POST /v1/organizations/spend_limits` pins a member to a specific spend limit regardless of what they would otherwise inherit. Deleting the override returns them to the inherited spend limit (or leaves them unlimited if none exists).
The `source` field on each member's row tells you which level their spend limit resolved from: `user` (a per-user override), `seat_tier`, `rbac_group`, or `organization`. Treat scope types as an open set; fall through on unknown values rather than failing.
### Period
`period` is the recurring window over which the spend limit is enforced and spend resets. A spend limit is identified by its `(scope, period)` pair. Currently `monthly` is the only supported period; monthly spend resets at 00:00 UTC on the first of each calendar month. Treat `period` as an open set.
### Amounts and currency
All monetary values are strings in **minor units of the organization's billing currency** (cents, for USD). For example, `"50000"` represents 500.00 USD. Parse as a decimal and divide by 100 to display dollars; avoid binary floating-point for large values.
`amount` is **nullable**. In a member's effective row, `null` means **unlimited** (no spend limit) and `"0"` means the member cannot use Claude beyond their plan's included usage. On a configured spend-limit row (as returned by `GET /v1/organizations/spend_limits/{id}`), `null` only means no numeric spend limit is set; read the member's effective row to distinguish unlimited from included-usage-only.
`period_to_date_spend` is the member's spend accrued since the start of the current `period`, in the same minor-unit format; it may include a fractional part (for example, `"41280.125"`). It may read as `"0"` if the spend reading is temporarily unavailable; treat it as informational, not transactional.
### Increase request lifecycle
A **spend limit increase request** is created when a member clicks **Request more usage** in claude.ai. Requests are not created through this API. A request's `status` is one of:
| Status | Meaning |
|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `pending` | Awaiting admin action. The request normally carries a live `spend_summary` so you can see the member's current effective spend limit and period-to-date spend while deciding; `spend_summary` may be `null` if it could not be computed. |
| `approved` | The request was resolved with approval: either an admin approved it explicitly, another admin action raised the member's spend limit, or Anthropic support raised a spend limit on the organization's behalf. `spend_summary` is `null`. |
| `denied` | An admin declined. `spend_summary` is `null`. The member can send a new request right away; only a `pending` request blocks a new one. An admin can still raise the member's spend limit directly at any time. |
Both `approved` and `denied` are terminal. A member has at most one `pending` request at a time.
Approving with `POST /v1/organizations/spend_limit_increase_requests/{id}/approve` writes the same per-user spend limit row that `POST /v1/organizations/spend_limits` writes. Setting a spend limit directly does **not** transition a pending request; use the approve endpoint to resolve a request.
By default, Anthropic emails the member when their request is approved or denied. Pass `suppress_notification: true` on approve or deny to suppress that email (for example, when your own system notifies the member).
## Versioning
Send the `anthropic-version` header on every request; see [API versions](../Endpoints/versioning.md) for the available versions.
## Rate limiting
All eight endpoints share a single per-organization limit of **60 requests per minute**. Requests over the limit return **429 Too Many Requests**.
## Pagination
`GET /v1/organizations/spend_limits/effective` and `GET /v1/organizations/spend_limit_increase_requests` are paginated with an **opaque cursor**. The first request returns up to `limit` rows plus a `next_page` cursor; pass that cursor unchanged as the `page` parameter on the next request, and repeat until `next_page` is `null`.
**Do not change query parameters mid-sequence.** Cursors are bound to the filters that issued them. If you change `user_ids[]`, `period[]`, `status[]`, or `actor_ids[]` and pass an old cursor, you'll get a 400 with *"cursor does not match current query parameters"*. Start a new sequence from the first page instead.
## Serializing list parameters
List parameters use bracket notation: repeat the parameter name with `[]` for each value.
``` block
user_ids[]=user_01AbCdEfGh&user_ids[]=user_01JkLmNoPq
```
## Error responses
Error responses follow the standard shape documented in [Errors](../Endpoints/errors.md). Quote the `request_id` from the response body when contacting support.
## Spend limits
### List each member's effective spend limit
`GET /v1/organizations/spend_limits/effective` returns one row per current member, reflecting each member's effective spend limit, its `source` in the scope hierarchy, and their `period_to_date_spend`. Requires the `read:spend_limits` scope.
For complete parameter details and response schemas, see [List effective spend limits](../Admin/beta-organization-spend-limits-effective-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/spend_limits/effective?limit=20" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"scope": { "type": "user", "user_id": "user_01AbCdEfGh" },
"actor": {
"type": "user_actor",
"user_id": "user_01AbCdEfGh",
"name": "Jane Smith",
"email_address": "jane@example.com",
"deleted": false
},
"amount": "50000",
"currency": "USD",
"period": "monthly",
"source": { "type": "seat_tier", "seat_tier": "enterprise_standard" },
"spend_limit_id": "spl_01XyZaBcDeFgHiJkLmNoPq",
"period_to_date_spend": "31402.5"
}
],
"next_page": "page_..."
}
```
### Get a single spend limit
`GET /v1/organizations/spend_limits/{spend_limit_id}` returns one configured spend limit by ID. Use it to inspect the row that a `spend_limit_id` field referenced. Requires the `read:spend_limits` scope.
For complete parameter details and response schemas, see [Retrieve a spend limit](../Admin/beta-organization-spend-limits-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/spend_limits/spl_01AbCdEfGhIjKlMnOpQrSt" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
### Set a per-user override
`POST /v1/organizations/spend_limits` sets a per-user spend limit override. This is an upsert keyed on `(scope, period)`: setting a limit for a user and period that already has one overwrites it in place. This endpoint accepts only `scope.type: "user"`; seat-tier, group, and organization-level defaults are configured in claude.ai settings. Requires the `write:spend_limits` scope.
For complete parameter details and response schemas, see [Create a spend limit](../Admin/beta-organization-spend-limits-set.md) in the API reference.
cURL
```python
curl --request POST "https://api.anthropic.com/v1/organizations/spend_limits" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"scope": {"type": "user", "user_id": "user_01AbCdEfGh"}, "amount": "75000"}'
```
```python
{
"type": "spend_limit",
"id": "spl_01RsTuVwXyZaBcDeFgHiJk",
"created_at": "2026-05-11T10:02:44Z",
"updated_at": "2026-05-11T10:02:44Z",
"scope": { "type": "user", "user_id": "user_01AbCdEfGh" },
"amount": "75000",
"currency": "USD",
"period": "monthly"
}
```
### Remove a per-user override
`DELETE /v1/organizations/spend_limits/{spend_limit_id}` removes a per-user override, after which the member falls back to any inherited seat-tier, group, or organization default. Seat-tier, group, and organization-level rows cannot be deleted through this endpoint. Requires the `write:spend_limits` scope.
For complete parameter details and response schemas, see [Delete a spend limit](../Admin/beta-organization-spend-limits-delete.md) in the API reference.
cURL
```python
curl --request DELETE "https://api.anthropic.com/v1/organizations/spend_limits/spl_01RsTuVwXyZaBcDeFgHiJk" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
## Spend limit increase requests
### List increase requests
`GET /v1/organizations/spend_limit_increase_requests` lists requests, most recent first. Filter by `status[]` (`pending`, `approved`, `denied`) and `actor_ids[]`. The list excludes requests whose requester is no longer a member of the organization. Requires the `read:spend_limits` scope.
For complete parameter details and response schemas, see [List spend limit increase requests](../Admin/beta-organization-spend-limits-increase-requests-list.md) in the API reference.
cURL
```python
curl --globoff "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests?status[]=pending&limit=50" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
Each pending request carries a live `spend_summary` showing the requester's current effective spend limit and period-to-date spend, enough to decide without a separate lookup.
### Get a single increase request
`GET /v1/organizations/spend_limit_increase_requests/{id}` returns one request by ID. Requires the `read:spend_limits` scope.
For complete parameter details and response schemas, see [Retrieve a spend limit increase request](../Admin/beta-organization-spend-limits-increase-requests-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests/slir_01AbCdEfGhIjKlMnOpQrSt" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
### Approve an increase request
`POST /v1/organizations/spend_limit_increase_requests/{id}/approve` approves a pending request: it writes a per-user spend limit at the admin-supplied `amount` for the requester and transitions the request to `approved`. The request does not carry a requested amount; you supply the new spend limit on approval. Requires the `write:spend_limits` scope.
For complete parameter details and response schemas, see [Approve a spend limit increase request](../Admin/beta-organization-spend-limits-increase-requests-approve.md) in the API reference.
cURL
```python
curl --request POST "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests/slir_01AbCdEfGhIjKlMnOpQrSt/approve" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"amount": "75000", "suppress_notification": true}'
```
### Deny an increase request
`POST /v1/organizations/spend_limit_increase_requests/{id}/deny` denies a pending request. Idempotent on `denied`: denying an already-denied request returns 200 with the existing resource. The endpoint rejects an attempt to deny an already-approved request so automation can distinguish a retry from a conflicting decision. Requires the `write:spend_limits` scope.
For complete parameter details and response schemas, see [Deny a spend limit increase request](../Admin/beta-organization-spend-limits-increase-requests-deny.md) in the API reference.
cURL
```python
curl --request POST "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests/slir_01AbCdEfGhIjKlMnOpQrSt/deny" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"suppress_notification": true}'
```
## Example workflows
Some of these workflows combine the Spend Limits API with the [Analytics APIs](manage-claude-analytics-api.md) cost endpoints. The Analytics cost endpoints are designed for organization-wide spend reporting across a date range. `GET /spend_limits/effective` returns the cap that currently applies to each member. Start a sweep with Analytics to discover which members to look at, then read their current caps with `/effective`.
Spend Limits endpoints require the `spend_limits` scopes and Analytics cost endpoints require `read:analytics`; see [Analytics APIs](manage-claude-analytics-api.md) for how to provision access. All monetary values on both are decimal strings in minor units (cents). Both APIs paginate with an opaque cursor. Set an explicit `limit` and page through `next_page` until it's `null` to cover the whole organization.
### Automate the increase-request review flow
Run a scheduled job that fetches pending requests, applies your organization's approval policy, and resolves each one.
1. List pending requests:
cURL
``` shiki
curl --globoff "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests?status[]=pending&limit=100" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
Each request carries the requester's `actor.user_id` and a live `spend_summary` with their current effective `amount` and `period_to_date_spend`, enough to decide without a separate lookup.
2. Apply your policy. For example, auto-approve when the member's current `amount` is below a threshold, and route larger caps for manual review.
3. Resolve each request. To approve, supply the new cap:
cURL
``` shiki
curl --request POST "https://api.anthropic.com/v1/organizations/spend_limit_increase_requests/{id}/approve" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"amount": "75000", "suppress_notification": true}'
```
To deny, `POST` to `.../{id}/deny` instead. Pass `suppress_notification: true` when your own system notifies the requester.
### Identify members close to their spend limit
Find members approaching their cap so you can raise it before they're blocked.
1. Pull each member's month-to-date spend from the Analytics API (one row per member, highest spend first by default):
cURL
``` shiki
curl "https://api.anthropic.com/v1/organizations/analytics/user_cost_report?starting_at=2026-06-01T00:00:00Z&limit=1000" \
--header "x-api-key: $ANALYTICS_API_KEY" \
--header "anthropic-version: 2023-06-01"
```
Each row carries `actor.user_id`, `actor.email`, and `amount` (the member's spend in cents). Page through `next_page` to cover the whole organization.
2. For the top spenders (or everyone above a dollar threshold), fetch effective caps in batches:
cURL
``` shiki
curl --globoff "https://api.anthropic.com/v1/organizations/spend_limits/effective?user_ids[]=user_01Ab...&user_ids[]=user_01Cd...&limit=100" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
Each row returns the cap as `amount` (`null` = unlimited, `"0"` = included usage only) alongside `period_to_date_spend`.
3. For each member with a positive cap, compute `period_to_date_spend / amount` and flag those at or above your threshold (for example, 80 percent). Treat a `"0"` cap as already at-limit. There is no server-side filter for this ratio.
4. Act on flagged members: raise the cap with `POST /v1/organizations/spend_limits`, approve a pending increase request if one exists, or reach out to the member.
### Find members with rapidly changing usage
Surface members whose spend has jumped week over week.
1. Pull per-member daily cost for the trailing two weeks from the Analytics API:
cURL
``` shiki
curl "https://api.anthropic.com/v1/organizations/analytics/user_cost_report?starting_at=2026-06-09T00:00:00Z&ending_at=2026-06-23T00:00:00Z&bucket_width=1d&limit=1000" \
--header "x-api-key: $ANALYTICS_API_KEY" \
--header "anthropic-version: 2023-06-01"
```
With `bucket_width` set, each member spans one row per day with usage; page through `next_page` to collect every member's full series.
2. Group rows by `actor.user_id`. For each member, sum the most recent seven days and the prior seven days. Flag members whose recent week exceeds the prior week by your chosen multiple (for example, three). Recent-day cost is provisional and can be revised upward; for repeatable comparisons, set `ending_at` at or before a previously returned `data_refreshed_at` (see [Data availability and freshness](manage-claude-analytics-api.md#data-availability-and-freshness)).
3. Act on flagged members: adjust the cap with `POST /v1/organizations/spend_limits`, or reach out.
### Temporarily raise a member's spend limit during an incident
Give an incident responder room to work while an incident is open: raise their spend cap when the incident starts, and roll it back after the incident closes. Gate the raise on your incident management system, for example by requiring a live incident ID with the member assigned to it.
1. Read the member's current cap, and record it for the rollback:
cURL
``` shiki
curl --globoff "https://api.anthropic.com/v1/organizations/spend_limits/effective?user_ids[]=user_01AbCdEfGh&period[]=monthly" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01"
```
2. Raise the cap:
cURL
``` shiki
curl --request POST "https://api.anthropic.com/v1/organizations/spend_limits" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"scope": {"type": "user", "user_id": "user_01AbCdEfGh"}, "amount": "500000", "period": "monthly"}'
```
3. If responders need broader access during an incident, pre-provision an incident-responders group whose [custom role](manage-claude-user-management.md#custom-roles) grants it, and add the member for the duration:
cURL
``` shiki
curl --request POST "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn/members" \
--header "content-type: application/json" \
--header "x-api-key: $ANTHROPIC_ADMIN_KEY" \
--header "anthropic-version: 2023-06-01" \
--data '{"user_id": "user_01AbCdEfGh"}'
```
See [User management](manage-claude-user-management.md#groups) for the group endpoints.
4. When your incident system marks the incident closed, roll both changes back: restore the spend limit you recorded in step 1 (or delete the override with `DELETE /v1/organizations/spend_limits/{spend_limit_id}` if the member had none), and remove the member from the group with `DELETE /v1/organizations/rbac_groups/{rbac_group_id}/members/{user_id}`.
## Frequently asked questions
### Does setting a spend limit directly resolve a member's pending increase request?
No. `POST /v1/organizations/spend_limits` writes the override but leaves the pending request untouched. Use `POST /v1/organizations/spend_limit_increase_requests/{id}/approve` to resolve the request and write the override in one call.
### What happens when I delete a per-user override?
The member falls back to whatever they'd inherit from the hierarchy: their group, seat-tier, or organization default. If no default exists at any level, the member is unlimited.
### Can I set a seat-tier or organization-wide default through this API?
No. Only per-user overrides can be written through this API. Seat-tier, group, and organization-level defaults are configured in claude.ai Organization settings.
### Why does `period_to_date_spend` sometimes read as `"0"` for an active member?
The spend reading can be temporarily unavailable, in which case the field reads `"0"` rather than erroring. Treat it as informational.
## See also
[Spend Limits API reference](../Admin/beta-organization-spend-limits.md)
Generated request and response schemas for every Spend Limits API endpoint.
[Spend Limit Increase Requests API reference](../Admin/beta-organization-spend-limits-increase-requests.md)
Generated request and response schemas for the increase-request endpoints.
[Analytics APIs](manage-claude-analytics-api.md)
Per-user and time-bucketed usage and cost reporting for Claude Enterprise.
---
## Usage and Cost API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/usage-cost-api
Path: 04-API-Reference/Other/manage-claude-usage-cost-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fusage-cost-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Monitoring
# Usage and Cost API
Copy page
Programmatically access your organization's API usage and cost data with the Usage & Cost Admin API.
Copy page
**The Admin API is unavailable for individual accounts.** To collaborate with teammates and add members, set up your organization in **Console → Settings → Organization**.
The Usage & Cost Admin API provides programmatic and granular access to historical API usage and cost data for your organization. This data is similar to the information available in the [Usage](playground.md) and [Cost](playground.md) pages of the Claude Console.
This API enables you to better monitor, analyze, and optimize your Claude implementations:
- **Accurate usage tracking:** Get precise token counts and usage patterns instead of relying solely on response token counting
- **Cost reconciliation:** Match internal records with Anthropic billing for finance and accounting teams
- **Product performance and improvement:** Monitor product performance while measuring if changes to the system have improved it, or set up alerting
- **[Rate limit](../Endpoints/rate-limits.md) optimization:** Optimize features like [prompt caching](../Guides/build-with-claude-prompt-caching.md) or specific prompts to make the most of your allocated capacity.
- **Advanced analysis:** Perform deeper data analysis than what's available in Console
**Admin API credentials required.** These endpoints are part of the Admin API. You can access them using an [Admin API key](manage-claude-admin-api-keys.md), an OAuth token with the `org:admin` scope, or a personal or service account key that isn't scoped to a workspace; workspace API keys don't work. See [Authentication](manage-claude-admin-api.md#authentication) for details.
Claude Enterprise organizations use an Analytics API key with a different API instead; see [Which API do you need?](#which-api-do-you-need).
**Claude Platform on AWS:** The programmatic Usage and Cost API endpoints are not currently available. View usage and cost data on the **Usage** and **Cost** pages in the Claude Console instead.
## Which API do you need?
Anthropic provides cost and usage reporting through two APIs, depending on which Claude product your organization manages:
| Your organization | API | Key type |
|----------------------------------|----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| Claude Console (Claude Platform) | The Usage and Cost Admin API described on this page | Admin API key (`sk-ant-admin01-...`) or another [Admin API credential](manage-claude-admin-api.md#authentication) |
| Claude Enterprise (claude.ai) | The [Claude Enterprise Analytics API](../Admin/http-beta-organization-analytics.md) cost and usage endpoints | Analytics API key |
Claude Enterprise parent organizations do not appear in Claude Console and carry no Admin API keys, so for them the Analytics API key is the only path to this data. See [Analytics APIs](manage-claude-analytics-api.md) for how to create each key type and which plans the Claude Enterprise cost data applies to.
## Partner solutions
Leading observability platforms offer ready-to-use integrations for monitoring your Claude API usage and cost, without writing custom code. These integrations provide dashboards, alerting, and analytics to help you manage your API usage effectively.
[CloudZero](https://docs.cloudzero.com/docs/connections-anthropic)
Cloud intelligence platform for tracking and forecasting costs
[Datadog](https://docs.datadoghq.com/integrations/anthropic/)
LLM Observability with automatic tracing and monitoring
[Grafana Cloud](https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-anthropic/)
Agentless integration for easy LLM observability with out-of-the-box dashboards and alerts
[Harness](https://developer.harness.io/docs/cloud-cost-management/provider-integrations/ai-providers/anthropic/)
FinOps platform for cloud and AI cost management
[Honeycomb](https://docs.honeycomb.io/integrations/anthropic-usage-monitoring/)
Advanced querying and visualization through OpenTelemetry
[Vantage](https://docs.vantage.sh/connecting_anthropic)
FinOps platform for LLM cost & usage observability
## Quick start
Get your organization's daily usage for the last 7 days:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-08T00:00:00Z&\
ending_at=2025-01-15T00:00:00Z&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
**Set a User-Agent header for integrations**
If you're building an integration, set your User-Agent header to help Anthropic understand usage patterns:
``` block
User-Agent: YourApp/1.0.0 (https://yourapp.com)
```
## Usage API
Track token consumption across your organization with detailed breakdowns by model, workspace, and service tier with the `/v1/organizations/usage_report/messages` endpoint.
### Key concepts
- **Time buckets:** Aggregate usage data in fixed intervals (`1m`, `1h`, or `1d`)
- **Token tracking:** Measure uncached input, cached input, cache creation, and output tokens
- **Filtering & grouping:** Filter by API key, workspace, model, service tier, context window, [data residency](manage-claude-data-residency.md), or speed (beta), and group results by these dimensions
- **Server tool usage:** Track usage of server-side tools such as web search
For complete parameter details and response schemas, see the [Usage API reference](../Admin/beta-organization-usage-report-retrieve-messages.md).
### Basic examples
#### Daily usage by model
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-08T00:00:00Z&\
group_by[]=model&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
#### Hourly usage with filtering
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-15T00:00:00Z&\
ending_at=2025-01-15T23:59:59Z&\
models[]=claude-opus-5-5&\
service_tiers[]=batch&\
context_window[]=0-200k&\
bucket_width=1h" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
#### Filter usage by API keys and workspaces
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-08T00:00:00Z&\
api_key_ids[]=apikey_01Rj2N8SVvo6BePZj99NhmiT&\
api_key_ids[]=apikey_01ABC123DEF456GHI789JKL&\
workspace_ids[]=wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ&\
workspace_ids[]=wrkspc_01XYZ789ABC123DEF456MNO&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
To retrieve your organization's API key IDs, use the [List API Keys](../Admin/beta-organization-api-keys-list.md) endpoint.
To retrieve your organization's workspace IDs, use the [List Workspaces](../Admin/beta-organization-workspaces-list.md) endpoint, or find your organization's workspace IDs in the Claude Console.
#### Data residency
Track your [data residency controls](manage-claude-data-residency.md) by grouping and filtering usage with the `inference_geo` dimension. This is useful for verifying geographic routing across your organization.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2026-02-01T00:00:00Z&\
ending_at=2026-02-08T00:00:00Z&\
group_by[]=inference_geo&\
group_by[]=model&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
You can also filter to a specific geo. Valid values are `global`, `us`, and `not_available`:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2026-02-01T00:00:00Z&\
ending_at=2026-02-08T00:00:00Z&\
inference_geos[]=us&\
group_by[]=model&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
Models released before February 2026 (prior to Claude Opus 4.6 and Claude Sonnet 4.6) don't support the `inference_geo` request parameter, so their usage reports return `"not_available"` for this dimension. You can use `not_available` as a filter value in `inference_geos[]` to target those models.
#### Fast mode (research preview)
Track [fast mode](../Guides/build-with-claude-fast-mode.md) usage by grouping and filtering with the `speed` dimension. This is useful for monitoring standard versus fast mode usage.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2026-02-01T00:00:00Z&\
ending_at=2026-02-08T00:00:00Z&\
group_by[]=speed&\
group_by[]=model&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: fast-mode-2026-02-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
You can also filter to a specific speed. Valid values are `standard` and `fast`:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2026-02-01T00:00:00Z&\
ending_at=2026-02-08T00:00:00Z&\
speeds[]=fast&\
group_by[]=model&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: fast-mode-2026-02-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
Both the `speeds[]` filter and the `speed` group_by value require the `fast-mode-2026-02-01` beta header.
### Time granularity limits
| Granularity | Default limit | Maximum limit | Use case |
|-------------|---------------|---------------|------------------------|
| `1m` | 60 buckets | 1,440 buckets | Real-time monitoring |
| `1h` | 24 buckets | 168 buckets | Daily patterns |
| `1d` | 7 buckets | 31 buckets | Weekly/monthly reports |
## Cost API
Retrieve service-level cost breakdowns in USD with the `/v1/organizations/cost_report` endpoint.
### Key concepts
- **Currency:** All costs in USD, reported as decimal strings in lowest units (cents)
- **Cost types:** Track token usage, web search, and code execution costs
- **Grouping:** Group costs by workspace or description for detailed breakdowns. When grouping by `description`, responses include parsed fields such as `model` and `inference_geo`
- **Time buckets:** Daily granularity only (`1d`)
For complete parameter details and response schemas, see the [Cost API reference](../Admin/beta-organization-cost-report-retrieve.md).
Priority Tier costs use a different billing model and are not included in the cost endpoint. Track Priority Tier usage through the usage endpoint instead.
### Basic example
cURL
```python
curl "https://api.anthropic.com/v1/organizations/cost_report?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-31T00:00:00Z&\
group_by[]=workspace_id&\
group_by[]=description" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
## Pagination
Both endpoints support pagination for large datasets:
1. Make your initial request.
2. If `has_more` is `true`, use the `next_page` value in your next request.
3. Continue until `has_more` is `false`.
cURL
```python
# First request
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-31T00:00:00Z&\
limit=7" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
# Response includes: "has_more": true, "next_page": "page_xyz..."
# Next request with pagination
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-31T00:00:00Z&\
limit=7&\
page=page_xyz..." \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
## Common use cases
Explore detailed implementations in [Claude Cookbook](https://platform.claude.com/cookbook):
- **Daily usage reports:** Track token consumption trends
- **Cost attribution:** Allocate expenses by workspace for chargebacks
- **Cache efficiency:** Measure and optimize prompt caching
- **Budget monitoring:** Set up alerts for spending thresholds
- **CSV export:** Generate reports for finance teams
## Frequently asked questions
### How fresh is the data?
Usage and cost data typically appears within 5 minutes of API request completion, though delays may occasionally be longer.
### What's the recommended polling frequency?
The API supports polling once per minute for sustained use. For short bursts (for example, downloading paginated data), more frequent polling is acceptable. Cache results for dashboards that need frequent updates.
### How do I track code execution usage?
Code execution costs appear in the cost endpoint grouped under `Code Execution Usage` in the description field. Code execution is not included in the usage endpoint.
### How do I track Priority Tier usage?
Filter or group by `service_tier` in the usage endpoint and look for the `priority` value. Priority Tier costs are not available in the cost endpoint.
### What happens with playground usage?
API usage from playground in the Claude Console (and from the legacy Workbench before it) is not associated with an API key, so `api_key_id` will be `null` even when grouping by that dimension.
### How is the default workspace represented?
Usage and costs attributed to the default workspace have a `null` value for `workspace_id`.
### How do I get per-user cost breakdowns for Claude Code?
Use the [Claude Code Analytics API](manage-claude-claude-code-analytics-api.md), which provides per-user estimated costs and productivity metrics without the performance limitations of breaking down costs by many API keys. For general API usage with many keys, use the [Usage API](#usage-api) to track token consumption as a cost proxy.
## See also
Use the Usage and Cost APIs to deliver a better experience for your users, manage costs, and preserve your rate limit. Learn more about some of these other features:
- [Admin API](manage-claude-admin-api.md)
- [Admin API reference](../Admin/http-beta-organization.md)
- [Analytics APIs](manage-claude-analytics-api.md) - Which analytics API and key type your organization needs
- [Pricing](../../17-Billing-Plans/about-claude-pricing.md)
- [Prompt caching](../Guides/build-with-claude-prompt-caching.md) - Optimize costs with caching
- [Batch processing](../Guides/build-with-claude-batch-processing.md) - 50% discount on batch requests
- [Rate limits](../Endpoints/rate-limits.md) - Understand usage tiers
- [Rate Limits API](manage-claude-rate-limits-api.md) - Read your configured rate limits
- [Data residency](manage-claude-data-residency.md) - Control inference geography
---
## User management - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/user-management
Path: 04-API-Reference/Other/manage-claude-user-management.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fuser-management)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Organization
# User management
Copy page
Manage the people in your Claude Enterprise organization with the Admin API: list members and change roles, send and withdraw invites, manage groups, and read custom roles.
Copy page
This page covers managing the people in your **Claude Enterprise** (claude.ai) organization programmatically, using the [Admin API](../Admin/http-beta-organization.md): list members and look them up by email address, change a member's role, remove members, send and withdraw invites, manage your enterprise's groups and their membership, and read your organization's custom roles. For Claude Console (Claude Platform) organizations, see the [Admin API guide for Claude Console](manage-claude-admin-api.md).
Group and custom-role requests don't require the `anthropic-beta: ce-user-management-2026-07-13` [beta header](../Endpoints/beta-headers.md). Requests that still send it are accepted and behave identically.
## Which endpoints can your organization use?
The Admin API is a single set of endpoints under `https://api.anthropic.com/v1/organizations/`. Claude Console and Claude Enterprise organizations authenticate with [different keys](manage-claude-admin-api-keys.md) and each have access to a different subset of the endpoints:
| Endpoints | Claude Console (Claude Platform) | Claude Enterprise (claude.ai) |
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| [Members](#members) and [invites](#invites) | Available; see the [Admin API guide](manage-claude-admin-api.md) | Available (this page) |
| [Groups](#groups) | Not available | Available (this page) |
| [Custom roles](#custom-roles) | Not available | Available, read-only (this page) |
| [Spend limits](manage-claude-spend-limits-api.md) | Not available | Available |
| Usage and cost reports | Available; see the [Usage and Cost API](manage-claude-usage-cost-api.md) | Available through the [Claude Enterprise Analytics API](manage-claude-analytics-api.md#get-access-to-the-claude-enterprise-analytics-api) |
| [Workspaces](manage-claude-workspaces.md), [API keys](manage-claude-admin-api.md#api-keys), [rate limits](manage-claude-rate-limits-api.md), and the other endpoints in the [Admin API guide](manage-claude-admin-api.md) | Available | Not available |
Members and invites are the same endpoints for both organization types; this page documents their Claude Enterprise behavior, including the Claude Enterprise [organization roles](#organization-roles). The group and custom-role endpoints exist only for Claude Enterprise.
**Scoped Admin API key required**
These endpoints require an Admin API key with the `read:members` scope (member and invite `GET` endpoints, and all custom-role endpoints; there is no separate role scope), the `write:members` scope (member and invite `POST` and `DELETE` endpoints), the `read:rbac_groups` scope (group `GET` endpoints), or the `write:rbac_groups` scope (group `POST` and `DELETE` endpoints). A key carrying the `read:org_audit` scope (a read-only scope for security-audit integrations) can also call every `GET` endpoint on this page and the [Compliance API](manage-claude-compliance-api.md) read endpoints. See [Create an Admin API key](manage-claude-admin-api-keys.md#create-a-key-for-a-claude-enterprise-organization) for where your primary owner creates one and which scopes to select. Pass the key in the `x-api-key` header on every request, together with the [`anthropic-version`](../Endpoints/versioning.md) header.
## Overview
This page covers five resources:
[TABLE]
Custom roles and their group attachments are managed in [claude.ai organization settings](https://claude.ai/admin-settings); the API reads them but cannot change them.
## Quick start
List the organization's members, newest first:
cURL
```python
curl "https://api.anthropic.com/v1/organizations/users?limit=20" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrSt",
"email": "jane@example.com",
"name": "Jane Smith",
"role": "user",
"added_at": "2026-06-12T09:14:03Z"
}
],
"has_more": false,
"first_id": "user_01AbCdEfGhIjKlMnOpQrSt",
"last_id": "user_01AbCdEfGhIjKlMnOpQrSt"
}
```
## Key concepts
### Organization roles
Every member has exactly one organization role. Reads return the member's role as one of five values:
| Role | Meaning |
|--------------------|-------------------------------------------------------------------------------------------|
| `user` | A standard member. |
| `managed` | A member whose permissions are granted through the custom roles attached to their groups. |
| `owner` | An organization owner. |
| `membership_admin` | A member who can manage the organization's members. |
| `primary_owner` | The organization's primary owner. There is exactly one. |
The API can assign only the `user` and `managed` roles, on invite creation and on role updates. The administrative roles (`owner`, `membership_admin`, and `primary_owner`) are assigned in claude.ai organization settings, and members holding them cannot be modified or removed through this API.
### Members and invites
A person becomes a member by accepting an invite (or through your organization's single sign-on, where configured). Creating an invite sends an invitation email; the invite then reads as `pending` until the recipient accepts (`accepted`) or its server-assigned `expires_at` passes (`expired`). Only a `pending` invite can be withdrawn. To change a pending invitation's email address or role, withdraw it and create a new one.
If your organization's plan draws members from a finite pool of purchased seats, a pending invite consumes a seat. The create-invite endpoint does not take a seat or tier parameter: the seat is assigned automatically from the lowest tier that has availability. Creating an invite when no seat is free fails with a 400 error rather than purchasing a seat. Withdrawing the invite, letting it expire, or removing the member later returns the seat to the pool.
### Groups and roles
Groups connect members to custom roles (role-based access control, the `rbac` in the endpoint paths and scope names). Groups are owned by your enterprise as a whole (the parent organization together with every organization under it) rather than by a single organization, so the group scopes (`read:rbac_groups` and `write:rbac_groups`) require a key created for all linked organizations. Each group carries a `source_type`: `direct` for groups created in claude.ai, `scim` for groups provisioned by your identity provider. A group's `role_ids` field lists the IDs of the custom roles attached to it; resolve them to names and permissions with the [custom role endpoints](#custom-roles), noting that the role catalog is per-organization while groups are enterprise-wide, so fetching a role that belongs to a different organization of your enterprise returns 404 for your key. The field is `null` (rather than `[]`) when role data was temporarily unavailable, so retry to distinguish a degraded read from a group with no roles. Two deprecated fields are still returned: `roles` on groups, always equal to `role_ids`, and `group_id` on group members, always equal to `rbac_group_id`.
## Versioning
Send the `anthropic-version` header on every request; see [API versions](../Endpoints/versioning.md) for the available versions.
## Rate limits
Admin API endpoints share a per-organization limit of **100 requests per minute**; invite creation has its own limit of **1,200 requests per hour** instead. Requests over a limit return **429 Too Many Requests**.
## Pagination
Member and invite lists use ID-based pagination: pass `limit` (default 20, max 1000) plus at most one of `before_id` or `after_id`, and page using the `first_id` and `last_id` fields of each response until `has_more` is `false`. Group and custom-role lists use an **opaque cursor** instead: the response's `next_page` value is passed unchanged as the `page` parameter on the next request, until `next_page` is `null`.
## Error responses
Error responses follow the standard shape documented in [Errors](../Endpoints/errors.md).
## Members
### List members
`GET /v1/organizations/users` returns the organization's members, most recently added first. Filter by `email` to look up a specific member; the match is case-insensitive and tolerates common variants of the same address (for example, `jane+hiring@example.com` matches `jane@example.com`). Requires the `read:members` scope.
For complete parameter details and response schemas, see [List users](../Admin/beta-organization-users-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/users?email=jane@example.com" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### Get a member
`GET /v1/organizations/users/{user_id}` returns one member by ID. Requires the `read:members` scope.
For complete parameter details and response schemas, see [Get user](../Admin/beta-organization-users-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/users/user_01AbCdEfGhIjKlMnOpQrSt" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### Change a member's role
`POST /v1/organizations/users/{user_id}` sets the member's role to `user` or `managed`. Members holding an administrative role (`owner`, `membership_admin`, or `primary_owner`) cannot be changed through this endpoint, and administrative roles cannot be assigned; both return 400 and are managed in claude.ai organization settings. If your organization's identity provider manages roles (advanced SSO or advanced SCIM provisioning), role updates return 400. Requires the `write:members` scope.
For complete parameter details and response schemas, see [Update user](../Admin/platform-claude-com-beta-organization-users-update.md) in the API reference.
cURL
```python
curl -X POST "https://api.anthropic.com/v1/organizations/users/user_01AbCdEfGhIjKlMnOpQrSt" \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"role": "managed"}'
```
### Remove a member
`DELETE /v1/organizations/users/{user_id}` removes the member from the organization, returning any purchased seat they occupied to the organization's pool. Members holding an administrative role cannot be removed through this endpoint, and if your identity provider manages membership (SCIM), removals return 400. Requires the `write:members` scope.
For complete parameter details and response schemas, see [Remove user](../Admin/platform-claude-com-beta-organization-users-remove.md) in the API reference.
cURL
```python
curl -X DELETE "https://api.anthropic.com/v1/organizations/users/user_01AbCdEfGhIjKlMnOpQrSt" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"type": "user_deleted",
"id": "user_01AbCdEfGhIjKlMnOpQrSt"
}
```
## Invites
### Create an invite
`POST /v1/organizations/invites` sends an invitation email and returns the invite with a server-assigned `expires_at`. `role` must be `user` or `managed`. If a pending invite already exists for the email address, or the address already belongs to a member, the request returns 400 naming the existing resource. Organizations whose identity provider provisions users automatically (JIT or SCIM) cannot create invites through the API. Requires the `write:members` scope.
On plans that draw members from a finite seat pool, the invite automatically takes a seat from the lowest tier that has availability; the API does not take a tier parameter. If no seat is free, the request fails with a 400 error rather than purchasing a seat. Add seats through the organization's plan management and retry.
The optional `rbac_group_ids` field lists groups (by `rbac_group_`-prefixed ID) to assign to the member when they accept. Passing a non-empty `rbac_group_ids` additionally requires the key to carry the `write:rbac_groups` scope, because group assignment can grant the permissions attached to the group's roles.
For complete parameter details and response schemas, see [Create invite](../Admin/platform-claude-com-beta-organization-invites-create.md) in the API reference.
cURL
```python
curl -X POST "https://api.anthropic.com/v1/organizations/invites" \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"email": "newhire@example.com",
"role": "managed",
"rbac_group_ids": ["rbac_group_01UvWxYzAbCdEfGhIjKlMn"]
}'
```
```python
{
"type": "invite",
"id": "invite_01QrStUvWxYzAbCdEfGhIj",
"email": "newhire@example.com",
"role": "managed",
"invited_at": "2026-07-06T16:20:11Z",
"expires_at": "2026-07-27T16:20:11Z",
"accepted_at": null,
"status": "pending",
"rbac_group_ids": ["rbac_group_01UvWxYzAbCdEfGhIjKlMn"]
}
```
### List invites
`GET /v1/organizations/invites` returns the organization's invites, most recent first, across the `pending`, `accepted`, and `expired` states; there is no status filter. Requires the `read:members` scope.
For complete parameter details and response schemas, see [List invites](../Admin/beta-organization-invites-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/invites?limit=20" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### Get an invite
`GET /v1/organizations/invites/{invite_id}` returns one invite by ID. Requires the `read:members` scope.
For complete parameter details and response schemas, see [Get invite](../Admin/beta-organization-invites-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/invites/invite_01QrStUvWxYzAbCdEfGhIj" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### Withdraw an invite
`DELETE /v1/organizations/invites/{invite_id}` withdraws a `pending` invite, deactivating the link in the invitation email. Withdrawing an `accepted` invite returns 400 (remove the member instead); withdrawing an `expired` invite returns 400. Requires the `write:members` scope.
For complete parameter details and response schemas, see [Delete invite](../Admin/platform-claude-com-beta-organization-invites-delete.md) in the API reference.
cURL
```python
curl -X DELETE "https://api.anthropic.com/v1/organizations/invites/invite_01QrStUvWxYzAbCdEfGhIj" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
## Groups
Groups your enterprise creates directly, in [claude.ai organization settings](https://claude.ai/admin-settings) or through this API (`source_type: "direct"`), support every endpoint in this section. Groups provisioned by your identity provider (`source_type: "scim"`) can be read but not modified: renaming or deleting a SCIM group, or changing its membership, returns 400, because your identity provider owns it.
### List groups
`GET /v1/organizations/rbac_groups` returns your enterprise's groups, including identity-provider-managed (`scim`) groups. Requires the `read:rbac_groups` scope.
For complete parameter details and response schemas, see [List groups](../Admin/beta-organization-rbac-groups-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_groups?limit=20" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"type": "rbac_group",
"id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"name": "Engineering",
"source_type": "direct",
"role_ids": ["rbac_role_01CdEfGhIjKlMnOpQrStUv"],
"roles": ["rbac_role_01CdEfGhIjKlMnOpQrStUv"],
"created_at": "2026-03-18T10:01:42Z",
"updated_at": "2026-05-02T08:55:09Z"
}
],
"has_more": false,
"next_page": null
}
```
### Get a group
`GET /v1/organizations/rbac_groups/{rbac_group_id}` returns one group by ID. Requires the `read:rbac_groups` scope.
For complete parameter details and response schemas, see [Get group](../Admin/beta-organization-rbac-groups-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### Create a group
`POST /v1/organizations/rbac_groups` creates a group with the given `name` (1–255 characters) and no roles or members. Requires the `write:rbac_groups` scope.
For complete parameter details and response schemas, see [Create group](../Admin/beta-organization-rbac-groups-create.md) in the API reference.
cURL
```python
curl -X POST "https://api.anthropic.com/v1/organizations/rbac_groups" \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"name": "Engineering"}'
```
```python
{
"type": "rbac_group",
"id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"name": "Engineering",
"source_type": "direct",
"role_ids": [],
"roles": [],
"created_at": "2026-07-09T18:00:00Z",
"updated_at": "2026-07-09T18:00:00Z"
}
```
### Rename a group
`POST /v1/organizations/rbac_groups/{rbac_group_id}` updates the group. `name` is the only field this endpoint can change. Requires the `write:rbac_groups` scope.
For complete parameter details and response schemas, see [Update group](../Admin/beta-organization-rbac-groups-update.md) in the API reference.
cURL
```python
curl -X POST "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn" \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"name": "Platform Engineering"}'
```
### Delete a group
`DELETE /v1/organizations/rbac_groups/{rbac_group_id}` deletes the group. Its members remain members of their organizations, but they lose the permissions of its attached roles, and a group [spend limit](manage-claude-spend-limits-api.md), if one existed, stops applying to them. Requires the `write:rbac_groups` scope.
For complete parameter details and response schemas, see [Delete group](../Admin/beta-organization-rbac-groups-delete.md) in the API reference.
cURL
```python
curl -X DELETE "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"type": "rbac_group_deleted"
}
```
### List a group's members
`GET /v1/organizations/rbac_groups/{rbac_group_id}/members` returns the group's members (each with their `user_id` and email), oldest first. Only current members of your enterprise's organizations are returned, so a page might contain fewer than `limit` entries while `has_more` is `true`. Requires the `read:rbac_groups` scope.
For complete parameter details and response schemas, see [List group members](../Admin/beta-organization-rbac-groups-members-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn/members?limit=100" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"type": "rbac_group_member",
"rbac_group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"user_id": "user_01AbCdEfGhIjKlMnOpQrSt",
"email": "jane@example.com",
"created_at": "2026-04-07T12:30:00Z"
}
],
"has_more": false,
"next_page": null
}
```
### Add a member to a group
`POST /v1/organizations/rbac_groups/{rbac_group_id}/members` adds an organization member to the group by `user_id`. The user must already be a member of one of your enterprise's organizations (the request returns 404 otherwise), and adding someone who is already in the group returns 400. For `scim` groups, membership is managed in your identity provider and this request returns 400. To assign groups to a person who has not joined yet, use `rbac_group_ids` on [invite creation](#create-an-invite) instead. Requires the `write:rbac_groups` scope.
For complete parameter details and response schemas, see [Add group member](../Admin/beta-organization-rbac-groups-members-add.md) in the API reference.
cURL
```python
curl -X POST "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn/members" \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{"user_id": "user_01AbCdEfGhIjKlMnOpQrSt"}'
```
```python
{
"type": "rbac_group_member",
"rbac_group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"user_id": "user_01AbCdEfGhIjKlMnOpQrSt",
"email": "jane@example.com",
"created_at": "2026-07-09T18:00:00Z"
}
```
### Remove a member from a group
`DELETE /v1/organizations/rbac_groups/{rbac_group_id}/members/{user_id}` removes the member from the group; they remain a member of their organization. The request returns 404 if the user is not a member of the group, and 400 for `scim` groups, whose membership is managed in your identity provider. Requires the `write:rbac_groups` scope.
For complete parameter details and response schemas, see [Remove group member](../Admin/beta-organization-rbac-groups-members-remove.md) in the API reference.
cURL
```python
curl -X DELETE "https://api.anthropic.com/v1/organizations/rbac_groups/rbac_group_01UvWxYzAbCdEfGhIjKlMn/members/user_01AbCdEfGhIjKlMnOpQrSt" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"rbac_group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"group_id": "rbac_group_01UvWxYzAbCdEfGhIjKlMn",
"user_id": "user_01AbCdEfGhIjKlMnOpQrSt",
"type": "rbac_group_member_deleted"
}
```
## Custom roles
Custom roles are read-only through the API: these endpoints catalog your organization's custom roles (defined in [claude.ai organization settings](https://claude.ai/admin-settings) or provisioned by Anthropic) and the permissions each role grants. Custom-role reads use the `read:members` scope (there is no separate role scope) and work with an organization-level key: unlike the group endpoints, they do not require a key created for all linked organizations, and the catalog returned is your organization's own.
### List roles
`GET /v1/organizations/rbac_roles` returns your organization's custom roles. Requires the `read:members` scope.
For complete parameter details and response schemas, see [List roles](../Admin/beta-organization-rbac-roles-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_roles?limit=20" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"type": "rbac_role",
"id": "rbac_role_01CdEfGhIjKlMnOpQrStUv",
"name": "Engineering base",
"created_at": "2026-03-18T10:01:42Z",
"updated_at": "2026-05-02T08:55:09Z"
}
],
"has_more": false,
"next_page": null
}
```
### Get a role
`GET /v1/organizations/rbac_roles/{rbac_role_id}` returns one role by ID. Requires the `read:members` scope.
For complete parameter details and response schemas, see [Get role](../Admin/beta-organization-rbac-roles-retrieve.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_roles/rbac_role_01CdEfGhIjKlMnOpQrStUv" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
### List a role's permissions
`GET /v1/organizations/rbac_roles/{rbac_role_id}/permissions` returns the role's permissions. Each permission pairs a `resource` (what it applies to: the organization's product features, a connector tool, a connector OAuth scope, one connector, or every connector) with an `action` (what it grants on that resource). Rows for features not enabled for your organization are omitted, so a page might contain fewer than `limit` rows while `has_more` is `true`. Requires the `read:members` scope.
Two `action` values need special care: an `organization` permission whose action is `capability_access_all` (every product feature) or `capability_access_all_ga` (every stable product feature, that is, every feature not labeled beta or research preview) is a blanket grant (one that covers neither model access nor the `permission_`-prefixed admin-panel permissions) and is listed as that single row rather than expanded. When you tally what a role grants, treat a blanket row as covering everything its variant describes, not just the features named in other rows.
For complete parameter details and response schemas, see [List role permissions](../Admin/beta-organization-rbac-roles-permissions-list.md) in the API reference.
cURL
```python
curl "https://api.anthropic.com/v1/organizations/rbac_roles/rbac_role_01CdEfGhIjKlMnOpQrStUv/permissions?limit=20" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
```python
{
"data": [
{
"type": "rbac_role_permission",
"resource": {
"type": "organization",
"organization_id": "12345678-1234-5678-1234-567812345678"
},
"action": "capability_access_all_ga"
},
{
"type": "rbac_role_permission",
"resource": {
"type": "connector_tool",
"connector_id": "mcpsrv_01WxYzAbCdEfGhIjKlMnOp",
"tool_name": "search_tickets"
},
"action": "use"
}
],
"has_more": false,
"next_page": null
}
```
## Example workflows
### Offboard a departing employee
1. Look up the member by email:
cURL
``` shiki
curl "https://api.anthropic.com/v1/organizations/users?email=departing@example.com" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY" \
-H "anthropic-version: 2023-06-01"
```
2. Remove them with `DELETE /v1/organizations/users/{user_id}`, using the `id` from the response. Their seat, if any, returns to the pool.
3. If the person had not yet joined, the lookup returns no member; list invites and withdraw their `pending` invite instead.
### Audit group membership
1. List groups and record each group's `id`, `name`, and `role_ids`.
2. For each group that carries sensitive roles, page through `GET /v1/organizations/rbac_groups/{rbac_group_id}/members` and compare the member emails against your identity provider's roster.
3. Remove members who should no longer be in the group with `DELETE /v1/organizations/rbac_groups/{rbac_group_id}/members/{user_id}`. For `scim` groups, make the change in your identity provider instead.
For a workflow that combines group membership with a temporary spend limit raise, see [Temporarily raise a member's spend limit during an incident](manage-claude-spend-limits-api.md#temporarily-raise-a-members-spend-limit-during-an-incident) on the Spend Limits API page.
## Frequently asked questions
### Is this a different API from the Admin API?
No. The member and invite endpoints are the same `/v1/organizations/` endpoints that Claude Console organizations use; this page documents their Claude Enterprise behavior. The group and custom-role endpoints are part of the same API and exist only for Claude Enterprise organizations. The [availability table](#which-endpoints-can-your-organization-use) shows which endpoints each organization type can call.
### Can I assign the owner or membership admin role through the API?
No. The API assigns only `user` and `managed`, on invite creation and role updates. Administrative roles are assigned in claude.ai organization settings, and members holding them cannot be modified or removed through the API.
### Can I create or modify groups through the API?
Yes, with the `write:rbac_groups` scope: create, rename, and delete groups, and add or remove their members. Two things the API cannot change: groups provisioned by your identity provider (`source_type: "scim"`), whose name and membership are owned by the identity provider, and custom roles, which are managed in claude.ai organization settings (the API [reads them](#custom-roles)).
### Does an unaccepted invite consume a seat?
On plans with a finite seat pool, yes: a `pending` invite holds a seat. Withdrawing the invite or letting it expire frees the seat. On plans without a seat pool, invites consume nothing.
### My organization uses single sign-on. Which operations work?
If your identity provider provisions users automatically (JIT or SCIM), invite creation returns 400. If it manages roles (advanced SSO or advanced SCIM provisioning), role updates return 400. If it manages membership (SCIM provisioning), member removals return 400. Reads work regardless.
### What happens to an Admin API key when the person who created it leaves?
The key keeps working. Admin API keys are scoped to the organization, not to individual users, and a key created in claude.ai does not expire. Removing the creator from the organization or deprovisioning them through your identity provider ends their own access, but not the keys they created. Downgrading their role does not change the keys either: each key stays active with its original scopes. When you offboard someone who created Admin API keys, delete those keys in the **Keys** section of [claude.ai \> Organization settings \> API](https://claude.ai/admin-settings/api-access) and create replacements.
## See also
[Create an Admin API key](manage-claude-admin-api-keys.md)
Where your primary owner creates a scoped key and which scopes to select.
[Compliance API](manage-claude-compliance-api.md)
Audit activity and retrieve or delete user content across your organization.
[Analytics APIs](manage-claude-analytics-api.md)
Per-user and time-bucketed usage and cost reporting for Claude Enterprise.
[Spend Limits API](manage-claude-spend-limits-api.md)
Set per-member spend limits and review increase requests.
---
## Manage WIF with the Admin API - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-admin-api
Path: 04-API-Reference/Other/manage-claude-wif-admin-api.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-admin-api)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Authentication
# Manage WIF with the Admin API
Copy page
Create and manage Workload Identity Federation service accounts, issuers, and rules programmatically for infrastructure-as-code and CI workflows.
Copy page
The Admin API lets you create and manage [Workload Identity Federation](manage-claude-workload-identity-federation.md) resources programmatically: service accounts, federation issuers, and federation rules. Use it to keep your federation configuration in infrastructure as code, provision it from CI, and reproduce it across organizations instead of clicking through the Claude Console. These endpoints share the `/v1/organizations` path prefix with the rest of the [Admin API](manage-claude-admin-api.md).
## Prerequisites
Every request on this page authenticates with an OAuth bearer token that carries the `org:admin` scope. The scope is granted only to organization members with the admin, owner, or primary owner role, and it grants access to the whole organization: any workspace binding is ignored. There are two ways to obtain a token, and they carry different permissions: a token from your own login acts as a user, whereas a federated token acts as a service account and cannot perform every operation on this page.
### Interactive (your terminal)
Log in with the [`ant` CLI](cli-sdks-libraries-cli-quickstart.md) under a dedicated profile, requesting the `org:admin` scope (see [Admin access](cli-sdks-libraries-cli-authentication.md#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`:
CLI
```python
ant auth login --profile admin --scope "org:admin"
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)
Create a federation rule with `oauth_scope: org:admin` that targets a service account whose `organization_role` is `admin`. The rule itself must be created in the Claude Console: granting a workload organization-admin access is a deliberate human action, not something automation can bootstrap for itself. The next section walks through this once-per-organization setup.
## Bootstrap a workload to manage WIF
One Console-created rule is enough to put the rest of your federation configuration under infrastructure as code: grant a single trusted workload the `org:admin` scope, and let that workload manage federation issuers and every workspace-scoped federation rule through this API.
1. 1
### Create the org:admin rule in the Console
In the Claude Console, go to **Settings → Workload identity** and select **Connect workload** to create one federation rule for your automation workload, for example a GitHub Actions workflow in your infrastructure repository. Under **Advanced rule options**, set the rule's OAuth scope to `org:admin`: the wizard then creates the new service account with the Admin organization role (or asks you to pick an existing admin service account as the target).
Match the rule to one exact workload identity, not a broad pattern. `subject_prefix` is an exact match unless it ends in `*`. For GitHub Actions, pin the subject to a protected branch, such as `repo:my-org/my-repo:ref:refs/heads/main`. A trailing wildcard such as `repo:my-org/my-repo:*` also matches `pull_request` runs, including runs triggered from forks, so anyone who could open a pull request against the repository could mint an `org:admin` token. See [Restrict which workflows can authenticate](manage-claude-wif-providers-github-actions.md#restrict-which-workflows-can-authenticate).
2. 2
### Exchange the workload's identity token
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](manage-claude-workload-identity-federation.md#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:
``` shiki
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](manage-claude-wif-reference.md#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](manage-claude-workload-identity-federation.md#construct-the-sdk-client). See [Environment variables](manage-claude-wif-reference.md#environment-variables) and [Credential precedence](manage-claude-wif-reference.md#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](manage-claude-workload-identity-federation.md#authenticate-from-your-workload) as any other federated workload, and sends it in the `authorization: Bearer` header.
3. 3
### Manage issuers and workspace-scoped rules through the API
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.
For the operations a workload-minted token can and cannot perform, see [Permissions and constraints](#permissions-and-constraints). If you already created issuers, service accounts, or rules with the **Connect workload** wizard, list them with the following endpoints and import them into your infrastructure-as-code state instead of recreating them.
## Authentication
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:
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](#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.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
Admin API keys are not accepted on these endpoints; the Admin API page's `x-api-key` examples do not apply here.
## Service accounts
A [service account](manage-claude-workload-identity-federation.md#service-accounts) (`svac_...`) is the non-human identity that a federated token acts as. Set `organization_role` to `developer`.
Create a service account:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
List service accounts:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
Archive a service account:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
The create endpoint returns the new service account:
```python
{
"id": "svac_...",
"name": "inference-worker",
"organization_role": "developer",
"created_at": "...",
"type": "service_account",
"...": "..."
}
```
To read or update a single service account, use `GET` and `POST` on `/v1/organizations/service_accounts/{service_account_id}`. A service account must be a member of a workspace before federated tokens can act in it. Every service account has an implicit membership in your organization's default workspace; add explicit memberships for other workspaces with `GET`, `POST`, and `DELETE` on `/v1/organizations/service_accounts/{service_account_id}/workspaces`, where `DELETE` targets `.../workspaces/{workspace_id}`.
For complete parameter details and response schemas, see the [Service accounts API reference](../Admin/beta-organization-service-accounts.md).
## Federation issuers
A [federation issuer](manage-claude-workload-identity-federation.md#federation-issuers) (`fdis_...`) registers an OIDC identity provider with your organization. The `jwks` field is a discriminated union that controls how Anthropic fetches the provider's signing keys:
| `jwks` value | When to use |
|------------------------------------------|-----------------------------------------------------------------------------------|
| `{"type": "discovery"}` | The provider serves `/.well-known/openid-configuration` at the issuer URL. |
| `{"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. |
Register an issuer. This example registers GitHub Actions with JWKS discovery:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
List issuers:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
issuers = client.beta.organization.federation.issuers.list(limit=20)
for issuer in issuers:
print(f"{issuer.id}: {issuer.name}")
```
Archive an issuer:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
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](#permissions-and-constraints).
For complete parameter details and response schemas, see the [Federation issuers API reference](../Admin/beta-organization-federation-issuers.md).
## Federation rules
A [federation rule](manage-claude-workload-identity-federation.md#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.
Create a rule. This example lets GitHub Actions deploys from the main branch act as the service account:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
List rules, optionally filtered by issuer:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
Archive a rule:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```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}")
```
The list endpoint returns a page of rules and the cursor for the next page:
```python
{
"data": [{ "id": "fdrl_...", "name": "gha-deploy", "...": "..." }],
"next_page": "..."
}
```
To read or update a single rule, use `GET` and `POST` on `/v1/organizations/federation_rules/{rule_id}`. To manage the workspaces a rule can mint tokens in, use `GET` and `POST` on `/v1/organizations/federation_rules/{rule_id}/workspaces`, and `DELETE` on `/v1/organizations/federation_rules/{rule_id}/workspaces/{workspace_id}`.
For complete parameter details and response schemas, see the [Federation rules API reference](../Admin/beta-organization-federation-rules.md).
## Permissions and constraints
- OAuth-authenticated callers can only create or modify rules whose `oauth_scope` is `workspace:developer` or `workspace:inference`. To create or modify a rule with any other scope (such as `org:admin` or `workspace:manage_tunnels`), use the Console.
- An OAuth caller cannot update a federation issuer that backs a rule whose `oauth_scope` is anything other than `workspace:developer` or `workspace:inference` (such as `org:admin` or `workspace:manage_tunnels`). Consider registering a dedicated issuer for the bootstrap rule so the issuers behind workspace-scoped rules stay updatable through the API.
- Admin API keys are not accepted on these endpoints, for reads or writes; use an `org:admin` OAuth token.
A rule with `oauth_scope: org:admin` must target a service account whose `organization_role` is `admin`. Resource names must match `^[a-z0-9-]+$`, be 1 to 255 characters, and be unique within an organization for each resource type; for the full field-level constraints, see [Validation rules](manage-claude-wif-reference.md#validation-rules).
## Pagination and archiving
The service-account, federation-issuer, and federation-rule list endpoints accept `limit` (1 to 100, default 20) and a `page` cursor taken from the previous response. Pass the response's `next_page` value as the `page` query parameter on the next request. The rule-workspaces sub-resource list returns the full set without pagination. Archived resources are hidden from lists by default; pass `include_archived=true` to include them.
Archiving is a soft delete and is idempotent: archiving an already-archived resource succeeds. Archiving an issuer or a service account returns `400` while a live federation rule still references it; archive the rule first.
## See also
- [Workload Identity Federation](manage-claude-workload-identity-federation.md): concepts and the Console setup walkthrough
- [WIF reference](manage-claude-wif-reference.md): environment variables, validation rules, OAuth scopes, and error codes
- [Admin API](manage-claude-admin-api.md): the rest of the organization management surface
- [Admin API reference](../Admin/http-beta-organization.md): generated request and response schemas for every Admin API endpoint
---
## Use WIF with AWS - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/aws
Path: 04-API-Reference/Other/manage-claude-wif-providers-aws.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Faws)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with AWS
Copy page
Authenticate AWS workloads on Lambda, EC2, ECS, or EKS to the Claude API with Workload Identity Federation and STS-issued identity tokens.
Copy page
AWS workloads can authenticate to the Claude API without static API keys by exchanging an AWS-signed OIDC identity token. The recommended path calls the AWS STS [`GetWebIdentityToken`](https://docs.aws.amazon.com/STS/latest/APIReference/API_GetWebIdentityToken.html) API, which works anywhere the workload has AWS credentials: Lambda, EC2, ECS, and EKS. EKS workloads can alternatively use the [Kubernetes projected-token path](#use-eks-projected-service-account-tokens), which has fewer configuration steps but only works inside a pod.
This guide shows both paths. For the underlying concepts (service accounts, federation issuers, and federation rules), see [Workload Identity Federation](manage-claude-workload-identity-federation.md).
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- An AWS workload (EKS pod, ECS task, Lambda function, or EC2 instance) with an attached IAM role.
- The `aws` CLI or an AWS SDK available in the workload.
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
## Use STS web identity tokens (recommended)
The AWS STS `GetWebIdentityToken` API returns an OIDC token signed by AWS that asserts the caller's IAM identity. Because it uses the workload's ambient AWS credentials, the same integration covers Lambda, EC2, ECS, and EKS.
### Configure AWS
1. 1
### Enable outbound web identity federation for the account
This is an account-level flag, off by default. In the AWS console, open **IAM**, choose **Account settings**, and enable **Outbound web identity federation**. To enable it programmatically:
``` shiki
python3 -c "import boto3; boto3.client('iam').enable_outbound_web_identity_federation()"
```
If this is not enabled, calls to `GetWebIdentityToken` fail with `OutboundWebIdentityFederationDisabledException`.
2. 2
### Grant the workload's IAM role permission to call the API
Attach this policy to the IAM role that your Lambda function, EC2 instance, or ECS task runs as:
``` shiki
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["sts:GetWebIdentityToken"],
"Resource": "*"
}
]
}
```
3. 3
### Find your account's STS issuer URL
After enabling outbound federation, the **IAM \> Account settings** page shows a **Get Token Issuer URL** field with a value of the form `https://<uuid>.tokens.sts.global.api.aws`. This URL is unique to your AWS account; copy it for the next step. To retrieve it programmatically:
``` shiki
python3 -c "import boto3; print(boto3.client('iam').get_outbound_web_identity_federation_info())"
```
### Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **AWS** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Register the per-account STS issuer URL you copied in the prior step. It exposes a public JWKS endpoint, so use discovery mode.
```python
{
"name": "aws-sts",
"issuer_url": "https://<uuid>.tokens.sts.global.api.aws",
"jwks": { "type": "discovery" }
}
```
**Federation rule:** Match the audience you pass to `GetWebIdentityToken` and the calling role's IAM role ARN in the `sub` claim. The `sub` value is the IAM role ARN of the workload that called the API, in the form `arn:aws:iam::<account>:role/<role-name>`. The token also carries an `https://sts.amazonaws.com/` claim with `aws_account`, `org_id`, `principal_id`, and any `request_tags` you passed; you can match on those with the rule's `claims` map or a CEL `condition` for finer control.
```python
{
"name": "prod-inference",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "arn:aws:iam::123456789012:role/inference-worker",
"audience": "https://api.anthropic.com"
},
"target": { "type": "service_account", "service_account_id": "svac_..." },
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
Be as specific as the workload allows. Match the exact role ARN, and only broaden `subject_prefix` (for example, to `arn:aws:iam::123456789012:role/*`) if multiple IAM roles should map to the same Anthropic service account.
### Acquire and use the token
Call `GetWebIdentityToken` with `https://api.anthropic.com` as the audience, then pass the result to the SDK's federation credentials. Because `identity_token_provider` takes a callable, the SDK re-invokes STS on each refresh.
`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.
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import os
import anthropic
import boto3
from anthropic import WorkloadIdentityCredentials
def get_sts_web_identity_token() -> str:
sts = boto3.client("sts", region_name="us-east-1")
resp = sts.get_web_identity_token(
Audience=["https://api.anthropic.com"],
SigningAlgorithm="RS256",
DurationSeconds=900,
)
return resp["WebIdentityToken"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=get_sts_web_identity_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from AWS"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
### Verify the setup
From inside the workload, exchange an STS-issued token directly and inspect the response:
cURL
```python
JWT=$(aws sts get-web-identity-token \
--region us-east-1 \
--audience "https://api.anthropic.com" \
--signing-algorithm RS256 \
--duration-seconds 900 \
--query WebIdentityToken --output text)
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\": \"$JWT\",
\"federation_rule_id\": \"fdrl_...\",
\"organization_id\": \"00000000-0000-0000-0000-000000000000\",
\"service_account_id\": \"svac_...\",
\"workspace_id\": \"wrkspc_...\"
}" | jq
```
A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange); the most common AWS-side cause is an `iss` mismatch (the per-account STS issuer URL must match the registered `issuer_url` exactly).
## Use EKS projected service-account tokens
If your workload runs in an EKS pod, you can skip the STS call and read a Kubernetes-projected service-account token directly from disk. Kubernetes natively projects an OIDC-compatible token into the pod, and the SDK can read it from a file path, so no token-provider callable is required. This path has two fewer AWS configuration steps than the STS path but only works inside a pod; the underlying mechanism is the same as the [generic Kubernetes integration](manage-claude-wif-providers-kubernetes.md).
This path additionally requires an EKS cluster with an [IAM OIDC provider enabled](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) and `kubectl` access to the cluster.
### Configure your EKS cluster
1. 1
### Find your cluster's OIDC issuer URL
Each EKS cluster has a unique OIDC issuer. Retrieve it with the AWS CLI:
CLI
``` shiki
aws eks describe-cluster \
--name <cluster-name> \
--query "cluster.identity.oidc.issuer" \
--output text
```
The output looks like `https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...`. You'll register this URL as a federation issuer in the next section.
2. 2
### Create the service account and project an Anthropic-audience token
The EKS pod identity webhook detects the `eks.amazonaws.com/role-arn` annotation and automatically projects a token with `aud: sts.amazonaws.com`, exposing its path as `AWS_WEB_IDENTITY_TOKEN_FILE`. That token is for AWS role assumption. For the Anthropic exchange, project a second token with `audience: https://api.anthropic.com` and mount it at a dedicated path.
``` shiki
apiVersion: v1
kind: ServiceAccount
metadata:
name: inference-worker
namespace: inference
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/inference-worker
```
``` shiki
apiVersion: v1
kind: Pod
metadata:
name: inference-worker
namespace: inference
spec:
serviceAccountName: inference-worker
volumes:
- name: anthropic-token
projected:
sources:
- serviceAccountToken:
audience: https://api.anthropic.com
expirationSeconds: 3600
path: token
containers:
- name: app
image: your-registry/inference-worker:latest
env:
- name: ANTHROPIC_IDENTITY_TOKEN_FILE
value: /var/run/secrets/anthropic.com/token
- name: ANTHROPIC_FEDERATION_RULE_ID
value: fdrl_...
- name: ANTHROPIC_ORGANIZATION_ID
value: 00000000-0000-0000-0000-000000000000
- name: ANTHROPIC_SERVICE_ACCOUNT_ID
value: svac_...
- name: ANTHROPIC_WORKSPACE_ID # required when the rule covers multiple workspaces
value: wrkspc_...
volumeMounts:
- name: anthropic-token
mountPath: /var/run/secrets/anthropic.com
readOnly: true
```
3. 3
### Note the token's claim shape
The projected token is a JSON Web Token (JWT) signed by your cluster's OIDC issuer. Its `sub` claim follows the Kubernetes convention `system:serviceaccount:<namespace>:<service-account-name>`:
``` shiki
{
"iss": "https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...",
"sub": "system:serviceaccount:inference:inference-worker",
"aud": ["https://api.anthropic.com"],
"kubernetes.io": {
"namespace": "inference",
"serviceaccount": { "name": "inference-worker", "uid": "..." }
},
"exp": 1775527120,
"iat": 1775523520
}
```
The `serviceAccountToken` projection sets `aud` to `https://api.anthropic.com`. The separate IRSA-injected token at `AWS_WEB_IDENTITY_TOKEN_FILE` carries `aud: sts.amazonaws.com` and is for AWS API calls, not this exchange.
### Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **AWS** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** EKS issuers expose a public JWKS endpoint, so use discovery mode. The issuer URL must exactly match the token's `iss` claim. Register one issuer per cluster.
```python
{
"name": "prod-eks-uswest2",
"issuer_url": "https://oidc.eks.us-west-2.amazonaws.com/id/6FA42E7BFDE8549CB...",
"jwks": { "type": "discovery" }
}
```
**Federation rule:** Match the Kubernetes `sub` claim and the Anthropic audience `https://api.anthropic.com`. (Project a dedicated service-account token with that audience; don't reuse the IRSA default `sts.amazonaws.com` token.)
```python
{
"name": "prod-inference",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "system:serviceaccount:inference:inference-worker",
"audience": "https://api.anthropic.com"
},
"target": { "type": "service_account", "service_account_id": "svac_..." },
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
Be as specific as the workload allows. Loosen `subject_prefix` to `system:serviceaccount:inference:*` (the trailing `*` makes it a prefix match) only if every service account in the namespace should map to the same Anthropic service account.
### Acquire and use the token
Inside the pod, the projected token is at `/var/run/secrets/anthropic.com/token` (exposed as `ANTHROPIC_IDENTITY_TOKEN_FILE` in the Pod spec). Pass that file to the SDK's federation credentials and the SDK handles the exchange and refresh.
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import os
import anthropic
from anthropic import IdentityTokenFile, WorkloadIdentityCredentials
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=IdentityTokenFile(
os.environ["ANTHROPIC_IDENTITY_TOKEN_FILE"]
),
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from EKS"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
The Pod spec already sets `ANTHROPIC_IDENTITY_TOKEN_FILE`, `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, and `ANTHROPIC_WORKSPACE_ID`, so you can construct the client with no arguments and the SDK reads the federation environment variables automatically.
### Verify the setup
From inside the pod, exchange the projected token directly and inspect the response:
cURL
```python
JWT=$(cat "$ANTHROPIC_IDENTITY_TOKEN_FILE")
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\": \"$JWT\",
\"federation_rule_id\": \"$ANTHROPIC_FEDERATION_RULE_ID\",
\"organization_id\": \"$ANTHROPIC_ORGANIZATION_ID\",
\"service_account_id\": \"$ANTHROPIC_SERVICE_ACCOUNT_ID\",
\"workspace_id\": \"$ANTHROPIC_WORKSPACE_ID\"
}" | jq
```
A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange); the most common EKS-side cause is the projected token's `aud` not matching the rule (project a token with `audience: https://api.anthropic.com`, not the IRSA default `sts.amazonaws.com`).
## Scope your rule
A `subject_prefix` of `arn:aws:iam::123456789012:role/*` matches every IAM role in the account. Any principal that can assume any matching role can obtain a federated Anthropic token.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Pin the full role ARN:** Use `subject_prefix: "arn:aws:iam::<account>:role/<role-name>"` with no trailing `*` so other roles in the account do not match.
- **Pin the account ID:** Match the `aws_account` field of the token's `https://sts.amazonaws.com/` claim with the `claims` map or a CEL `condition` as a defense-in-depth check against a misconfigured prefix.
- **Pin namespace and service account on EKS:** Use the exact `system:serviceaccount:<namespace>:<name>` value with no `*` after the `system:serviceaccount:` prefix.
- **Use a separate rule per environment:** Create distinct rules for production, staging, and development workloads rather than widening one prefix to cover them all.
## Next steps
- Review the [WIF reference](manage-claude-wif-reference.md) for the full credential precedence, profile configuration, and rule matching reference.
- For self-managed Kubernetes clusters that aren't on EKS, see [Use WIF with Kubernetes](manage-claude-wif-providers-kubernetes.md).
---
## Use WIF with Microsoft Entra ID - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/azure
Path: 04-API-Reference/Other/manage-claude-wif-providers-azure.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fazure)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with Microsoft Entra ID
Copy page
Federate Azure managed identities and Entra Workload Identity with the Claude API so your Azure workloads can call Claude without static API keys.
Copy page
Azure workloads authenticate to the Claude API by presenting a JSON Web Token (JWT) issued by Microsoft Entra ID, then exchanging it for a short-lived Anthropic access token. The setup follows the same shape on every Azure platform:
1. **Register the token audience:** Create one app registration in your Microsoft Entra tenant to represent the Claude API audience. Every workload in the tenant requests Entra tokens for it.
2. **Set up the identity for your platform:** A managed identity on VMs, VM Scale Sets, App Service, Functions, and Container Apps, or Entra Workload Identity on AKS.
3. **Configure Anthropic:** Register your tenant's Entra issuer, create a service account, and write a federation rule that matches the token's claims.
4. **Exchange at runtime:** Your workload exchanges its Entra-issued token at `POST /v1/oauth/token` for an `sk-ant-oat01-...` Anthropic access token and calls Claude with it.
On both paths the token you present to Anthropic carries your tenant-specific Entra issuer and the managed identity's object ID in the `sub` and `oid` claims; only how the workload obtains that token differs. Pick the section for where your workload runs: [Use a managed identity](#use-a-managed-identity) for VMs, VM Scale Sets, App Service, Functions, or Container Apps; [Use Entra Workload Identity on AKS](#use-entra-workload-identity-on-aks) for AKS.
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- An Azure subscription with permission to assign managed identities (or configure Entra Workload Identity on AKS).
- Permission to create one app registration and service principal in your Microsoft Entra tenant (the shared Claude API audience). Entra only issues tokens for an audience that exists in the tenant, so the [Register the token audience](#register-the-token-audience) step is required before any token request succeeds.
- Your Microsoft Entra tenant ID. Find it in the Azure portal under **Microsoft Entra ID → Overview → Tenant ID**.
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
## Register the token audience
Microsoft Entra ID only issues a token when the requested audience exists in your tenant as an app registration with a service principal. Create one app registration to represent the Claude API audience; every workload in the tenant can request tokens for it. Without this registration, token requests fail with a "resource not found in tenant" error (`AADSTS50001` from the managed identity endpoints, `AADSTS500011` from the Entra token endpoint).
```python
# Create the app registration that represents the Claude API audience.
APP_ID=$(az ad app create --display-name claude-api-federation --query appId -o tsv)
# Request v2.0 access tokens and set the api://<APP_ID> identifier URI.
az ad app update --id "$APP_ID" \
--identifier-uris "api://$APP_ID" \
--set api.requestedAccessTokenVersion=2
# Create the service principal so the audience resolves in your tenant.
az ad sp create --id "$APP_ID"
```
Use the `api://<APP_ID>` identifier URI format. Entra restricts `https://` identifier URIs to verified domains of your own tenant, so a URI such as `https://api.anthropic.com` cannot be registered in most tenants; `api://<APP_ID>` is accepted everywhere. With `requestedAccessTokenVersion: 2`, tokens for this audience are v2.0, which is what this guide assumes. If you reuse an existing registration that emits v1.0 tokens, see [If your tokens are v1.0](#if-your-tokens-are-v1-0).
## Use a managed identity
Use this path when your workload runs on a VM, a VM Scale Set, App Service, Functions, or Container Apps. The workload requests an Entra-issued JWT for its assigned managed identity from the platform's local token endpoint, then exchanges that JWT with Anthropic.
### Configure the managed identity
1. 1
### Attach a managed identity
Enable a system-assigned or user-assigned managed identity on your Azure resource. In the Azure portal, open the resource, go to **Identity**, and turn on **System assigned** (or attach a user-assigned identity).
After the identity is created, note its **Object (principal) ID**. This GUID appears as both the `sub` and `oid` claims in the issued token, and your Anthropic federation rule will match on it. You can find it on the resource's **Identity** page; for a user-assigned identity, it is the **Object (principal) ID** on the managed identity resource's **Overview** page. (A managed identity has only a service principal in Microsoft Entra ID, not an app registration.)
2. 2
### Find the platform's token endpoint
The platform exposes a local token endpoint once the identity is attached:
- **VMs and VM Scale Sets:** IMDS at `http://169.254.169.254/metadata/identity/oauth2/token` with the header `Metadata: true` and `api-version=2018-02-01`.
- **App Service, Functions, and Container Apps:** The URL in the `IDENTITY_ENDPOINT` environment variable with the header `X-IDENTITY-HEADER` set to the value of `IDENTITY_HEADER`, and `api-version=2019-08-01`. IMDS is not reachable on these platforms.
If the resource has more than one user-assigned managed identity, add `client_id=<IDENTITY_CLIENT_ID>` to the token request to select one. Azure recommends always specifying it. Without it, the outcome depends on whether the resource also has a system-assigned identity enabled: if it does, the request silently falls back to that identity and then fails your federation rule's `oid` match; if it does not, the request fails outright as soon as a second user-assigned identity is attached.
3. 3
### Decode a sample token
Request a token from the endpoint and decode its payload to confirm the claims your federation rule needs to match. (For the decode command, see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange).) A v2.0 token for a managed identity carries these claims:
``` shiki
{
"iss": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"sub": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"aud": "<APP_ID>",
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>",
"azp": "<IDENTITY_CLIENT_ID>",
"ver": "2.0",
"exp": 1775527120
}
```
| Claim | Value | Match this when |
|:------|:---------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `oid` | The managed identity's object ID, identical to `sub` | You want to authorize one specific managed identity. This is the default; the rule in [Configure Anthropic](#configure-anthropic) matches it. |
| `azp` | The calling identity's client ID | You want to authorize every workload that shares one app registration. For a managed identity, `azp` is unique to that identity, so it is equivalent to `oid`. |
| `aud` | The audience app registration's client ID (the `<APP_ID>` GUID from [Register the token audience](#register-the-token-audience)) | Always. The rule's `audience` field must equal the token's `aud` value exactly. |
| `tid` | Your tenant ID | You want defense in depth. The issuer URL already pins the tenant. |
If the decoded token's `ver` claim is `1.0`, the claim names and values differ. See [If your tokens are v1.0](#if-your-tokens-are-v1-0) before continuing.
### Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **Microsoft Entra** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Choose **v2.0 (login.microsoftonline.com)** in the wizard's **Token issuer** selector. (The selector defaults to v1; that default exists for tenants reusing older registrations that still emit v1.0 tokens.) Entra publishes an OIDC discovery document at the per-tenant issuer URL, so use discovery mode. Each Microsoft Entra tenant you federate needs its own issuer record.
```python
{
"name": "azure-prod-tenant",
"issuer_url": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"jwks": { "type": "discovery" },
"max_jwt_lifetime_seconds": 86400
}
```
Managed identity workloads need `max_jwt_lifetime_seconds: 86400`. Azure issues managed identity tokens with up to 24 hours between `iat` and `exp` because it caches each resource's token for that window and offers no way to force an early refresh, and the issuer's 1-hour default rejects those tokens, so the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`). The Connect workload wizard's Microsoft Entra tile creates the issuer with `max_jwt_lifetime_seconds` set to `7500` and provides no field to change it during creation, so finish the wizard, then open **Settings → Workload identity → Issuers**, edit the issuer, and raise the value to `86400`. You can also update the issuer through the Admin API.
A longer accepted lifetime means a leaked Entra token stays exchangeable for longer. If a token leaks, the lever is disabling the federation rule; a tight `oid` match limits which identities can exchange a token in the first place, as described in [Scope your rule](#scope-your-rule).
**Federation rule:** Match on the managed identity's object ID and your tenant ID. For the v2.0 tokens this guide configures, the `audience` value is the audience app registration's client ID (the `<APP_ID>` GUID from [Register the token audience](#register-the-token-audience)). Use the exact `aud` value from your decoded token.
```python
{
"name": "azure-inference-worker",
"issuer_id": "fdis_...",
"match": {
"audience": "<APP_ID>",
"claims": {
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
`token_lifetime_seconds` is the lifetime of the Anthropic access token the exchange returns, not of the Entra token; the SDK refreshes it for you.
### Acquire and use the token
At 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`, as shown in the following examples. The cURL tab shows the raw flow.
The 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](#register-the-token-audience).
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` instead of calling the token endpoints directly. The library selects the correct endpoint on every Azure platform, including AKS with Entra Workload Identity.
cURL
Python
TypeScript
Go
Java
C#
PHP
Ruby
CLI
```python
import os
import anthropic
import requests
from anthropic import WorkloadIdentityCredentials
# The audience app registration's identifier URI (see Register the token audience).
AUDIENCE = "api://<APP_ID>"
def fetch_entra_token() -> str:
"""Fetch a managed identity token from the platform's token endpoint."""
# With multiple user-assigned identities, add client_id=<IDENTITY_CLIENT_ID>
# to the request params to select one.
if endpoint := os.environ.get("IDENTITY_ENDPOINT"):
# App Service, Functions, Container Apps
response = requests.get(
endpoint,
headers={"X-IDENTITY-HEADER": os.environ["IDENTITY_HEADER"]},
params={"api-version": "2019-08-01", "resource": AUDIENCE},
timeout=5,
)
else:
# VM or VM Scale Set: Azure Instance Metadata Service (IMDS)
response = requests.get(
"http://169.254.169.254/metadata/identity/oauth2/token",
headers={"Metadata": "true"},
params={"api-version": "2018-02-01", "resource": AUDIENCE},
timeout=5,
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_entra_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from Azure"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
### Verify the setup
From your Azure resource, run the cURL exchange shown in [Acquire and use the token](#acquire-and-use-the-token) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason, then decode the Entra token (see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
- **Issuer mismatch:** The registered `issuer_url` must match the token's `iss` claim exactly. A v2.0 token carries `https://login.microsoftonline.com/<TENANT_ID>/v2.0`; if the decoded `ver` claim is `1.0`, see [If your tokens are v1.0](#if-your-tokens-are-v1-0).
- **Token lifetime:** Managed identity tokens carry up to 24 hours between `iat` and `exp`. If the issuer still has the wizard's `7500` (or the 1-hour default), raise `max_jwt_lifetime_seconds` to `86400` as described in [Configure Anthropic](#configure-anthropic).
- **Audience mismatch:** The rule's `audience` must equal the token's `aud` exactly: the audience app registration's client ID for the v2.0 tokens this guide configures.
- **Claim name mismatch:** A rule that matches on a claim the token does not carry never passes. v1.0 tokens carry the client ID in `appid`, not `azp`; see [If your tokens are v1.0](#if-your-tokens-are-v1-0).
## Use Entra Workload Identity on AKS
Use this path when your workload runs in an AKS pod. Entra Workload Identity federates a Kubernetes service account with a user-assigned managed identity: Kubernetes projects a service account token (signed by the AKS cluster's OIDC issuer) into the pod at the path in `AZURE_FEDERATED_TOKEN_FILE`. That projected token is not an Entra-issued token, so to stay on the Entra-mediated path described on this page, the workload performs a two-hop exchange: it first redeems the projected token at `https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token` (federated `client_credentials` grant) for an Entra-issued access token, then passes that Entra token to the Anthropic SDK as the identity token.
AKS pods can alternatively skip the Entra exchange and present the Kubernetes-projected service account token to Anthropic directly. That path registers your AKS cluster's OIDC issuer with Anthropic instead of your Entra tenant. See [Use WIF with Kubernetes](manage-claude-wif-providers-kubernetes.md) for that flow.
### Configure Entra Workload Identity
1. 1
### Enable the OIDC issuer and workload identity on your cluster
Enabling workload identity installs the `azure-workload-identity` mutating webhook for you; deploy it manually only on non-AKS clusters. Capture the cluster's OIDC issuer URL for the federated credential you create in a later step.
``` shiki
az aks update \
--resource-group <RESOURCE_GROUP> \
--name <CLUSTER_NAME> \
--enable-oidc-issuer \
--enable-workload-identity
AKS_OIDC_ISSUER=$(az aks show \
--resource-group <RESOURCE_GROUP> \
--name <CLUSTER_NAME> \
--query oidcIssuerProfile.issuerUrl -o tsv)
```
2. 2
### Create a user-assigned managed identity
Capture two values from the identity: the **Client ID** goes into the service account annotation (and is injected into the pod as `AZURE_CLIENT_ID`), and the **Object (principal) ID** appears as the `oid` claim that your Anthropic federation rule matches.
``` shiki
az identity create \
--resource-group <RESOURCE_GROUP> \
--name claude-inference-identity \
--location <LOCATION>
# Goes in the service account annotation; injected into the pod as AZURE_CLIENT_ID.
IDENTITY_CLIENT_ID=$(az identity show \
--resource-group <RESOURCE_GROUP> \
--name claude-inference-identity \
--query clientId -o tsv)
# Appears as the oid claim that your federation rule matches.
IDENTITY_OBJECT_ID=$(az identity show \
--resource-group <RESOURCE_GROUP> \
--name claude-inference-identity \
--query principalId -o tsv)
```
3. 3
### Create the annotated Kubernetes service account
The `azure-workload-identity` webhook reads the `azure.workload.identity/client-id` annotation to inject `AZURE_CLIENT_ID` into the pod, which the samples in [Acquire and use the token](#acquire-and-use-the-token-2) read from the environment.
``` shiki
apiVersion: v1
kind: ServiceAccount
metadata:
name: claude-inference
namespace: inference
annotations:
azure.workload.identity/client-id: <IDENTITY_CLIENT_ID>
```
4. 4
### Create the federated credential on the managed identity
The federated credential trusts your cluster's OIDC issuer for that specific service account. The `--audience api://AzureADTokenExchange` value is Entra's fixed audience for incoming Kubernetes service account tokens; it is unrelated to the Claude API audience you registered earlier.
``` shiki
az identity federated-credential create \
--resource-group <RESOURCE_GROUP> \
--identity-name claude-inference-identity \
--name claude-inference-aks \
--issuer "$AKS_OIDC_ISSUER" \
--subject system:serviceaccount:inference:claude-inference \
--audience api://AzureADTokenExchange
```
5. 5
### Label the pod and set its service account
The pod must carry the `azure.workload.identity/use: "true"` label and run as the annotated service account. The webhook then injects `AZURE_FEDERATED_TOKEN_FILE`, `AZURE_CLIENT_ID`, and `AZURE_TENANT_ID` into the pod. The file at `AZURE_FEDERATED_TOKEN_FILE` contains the Kubernetes-projected service account token, signed by the AKS cluster's OIDC issuer.
``` shiki
apiVersion: v1
kind: Pod
metadata:
name: inference-worker
namespace: inference
labels:
azure.workload.identity/use: "true"
spec:
serviceAccountName: claude-inference
containers:
- name: app
image: your-registry/inference-worker:latest
```
6. 6
### Decode a sample token
The token your Anthropic federation rule sees is not the projected file; it is the Entra-issued token returned by the `client_credentials` exchange. From inside a labeled pod, run step 1 of the cURL sample in [Acquire and use the token](#acquire-and-use-the-token-2) and decode the result. It carries the same claim shape as the managed identity path:
``` shiki
{
"iss": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"sub": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"aud": "<APP_ID>",
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>",
"azp": "<IDENTITY_CLIENT_ID>",
"ver": "2.0",
"exp": 1775527120
}
```
`sub` and `oid` are the managed identity's object ID, `aud` is the audience app registration's client ID, and `azp` is the managed identity's client ID (the value of `AZURE_CLIENT_ID`). The lifetime differs from the managed identity path: `client_credentials` tokens default to a random 60 to 90 minute window between `iat` and `exp`, not 24 hours.
### Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **Microsoft Entra** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Choose **v2.0 (login.microsoftonline.com)** in the wizard's **Token issuer** selector. (The selector defaults to v1; that default exists for tenants reusing older registrations that still emit v1.0 tokens.) Entra publishes an OIDC discovery document at the per-tenant issuer URL, so use discovery mode. Each Microsoft Entra tenant you federate needs its own issuer record.
```python
{
"name": "azure-prod-tenant",
"issuer_url": "https://login.microsoftonline.com/<TENANT_ID>/v2.0",
"jwks": { "type": "discovery" },
"max_jwt_lifetime_seconds": 7500
}
```
The Connect workload wizard's Microsoft Entra tile creates the issuer with `max_jwt_lifetime_seconds` set to `7500` (just over 2 hours), which covers the default 60 to 90 minute lifetime of `client_credentials` tokens. A tenant token-lifetime policy or Continuous Access Evaluation (CAE) can extend that lifetime. If your decoded token's `exp` minus `iat` exceeds 7500 seconds, edit the issuer in **Settings → Workload identity → Issuers** and raise `max_jwt_lifetime_seconds` to match, or exchanges fail with the opaque `401` `authentication_error` response (message `Authentication failed`). If your tenant also runs managed-identity workloads from [Use a managed identity](#use-a-managed-identity), use that section's `86400` value, which covers both paths.
A longer accepted lifetime means a leaked Entra token stays exchangeable for longer. If a token leaks, the lever is disabling the federation rule; a tight `oid` match limits which identities can exchange a token in the first place, as described in [Scope your rule](#scope-your-rule).
**Federation rule:** Match on the managed identity's object ID and your tenant ID. For the v2.0 tokens this guide configures, the `audience` value is the audience app registration's client ID (the `<APP_ID>` GUID from [Register the token audience](#register-the-token-audience)). Use the exact `aud` value from your decoded token.
```python
{
"name": "azure-inference-worker",
"issuer_id": "fdis_...",
"match": {
"audience": "<APP_ID>",
"claims": {
"oid": "9f8e7d6c-1a2b-3c4d-5e6f-...",
"tid": "<TENANT_ID>"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
`token_lifetime_seconds` is the lifetime of the Anthropic access token the exchange returns, not of the Entra token; the SDK refreshes it for you.
### Acquire and use the token
At 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`, as shown in the following examples. The cURL tab shows the raw flow.
Two different client IDs appear in the samples. `<APP_ID>` is the audience app registration's client ID from [Register the token audience](#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.
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` 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.
cURL
Python
TypeScript
Go
Java
C#
PHP
Ruby
CLI
```python
import os
from pathlib import Path
import anthropic
import requests
from anthropic import WorkloadIdentityCredentials
def fetch_entra_token_via_federation() -> str:
federated_token = Path(os.environ["AZURE_FEDERATED_TOKEN_FILE"]).read_text()
response = requests.post(
f"https://login.microsoftonline.com/{os.environ['AZURE_TENANT_ID']}/oauth2/v2.0/token",
data={
"client_id": os.environ["AZURE_CLIENT_ID"],
"grant_type": "client_credentials",
"scope": "api://<APP_ID>/.default",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": federated_token,
},
timeout=5,
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_entra_token_via_federation,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from Azure"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
### Verify the setup
From inside a labeled pod, run the cURL exchange shown in [Acquire and use the token](#acquire-and-use-the-token-2) and confirm that `POST /v1/oauth/token` returns a `200` with an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason, then decode the Entra-issued token from step 1 (see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange) for the command) and check the most common Azure-side causes:
- **Issuer mismatch:** The registered `issuer_url` must match the token's `iss` claim exactly. A v2.0 token carries `https://login.microsoftonline.com/<TENANT_ID>/v2.0`; if the decoded `ver` claim is `1.0`, see [If your tokens are v1.0](#if-your-tokens-are-v1-0).
- **Token lifetime:** If a tenant token-lifetime policy or CAE extends the `client_credentials` token past 7500 seconds, raise the issuer's `max_jwt_lifetime_seconds` as described in [Configure Anthropic](#configure-anthropic-2).
- **Audience mismatch:** The rule's `audience` must equal the token's `aud` exactly: the audience app registration's client ID for the v2.0 tokens this guide configures.
- **Claim name mismatch:** A rule that matches on a claim the token does not carry never passes. v1.0 tokens carry the client ID in `appid`, not `azp`; see [If your tokens are v1.0](#if-your-tokens-are-v1-0).
## If your tokens are v1.0
This guide configures the audience app registration with `api.requestedAccessTokenVersion: 2`, so every token it shows is v2.0. If you reuse an existing registration that leaves `requestedAccessTokenVersion` unset, Entra issues v1.0 tokens instead. Decode a sample token and check its `ver` claim; if it is `1.0`, four things change:
- **Issuer:** The `iss` claim is `https://sts.windows.net/<TENANT_ID>/` instead of `https://login.microsoftonline.com/<TENANT_ID>/v2.0`. Register the issuer URL exactly as your token's `iss` claim carries it. The two URLs share the same JWKS, so discovery mode works for either.
- **Wizard selector:** Pick **v1 (sts.windows.net)** in the Connect workload wizard's **Token issuer** selector instead of **v2.0 (login.microsoftonline.com)**.
- **Audience:** The `aud` claim is the identifier URI you passed as `resource` (for example, `api://<APP_ID>`), not the registration's client ID. Set the federation rule's `audience` to the exact `aud` value from your decoded token.
- **Client ID claim:** The calling identity's client ID appears in `appid`, not `azp`. The two claims never appear in the same token, so a rule that matches on `azp` never passes against a v1.0 token.
The `oid`, `sub`, and `tid` claims carry the same values in both versions, so the rest of this guide applies unchanged.
## Scope your rule
A federation rule can match the token's subject with `subject_prefix` in addition to (or instead of) the `claims` map; see [Rule matching semantics](manage-claude-wif-reference.md#rule-matching-semantics) for how the fields combine. Entra `sub` values for these identities are fixed-length canonical GUIDs, so a `subject_prefix` containing the full 36-character object ID matches only that subject; this is a property of Entra's subject format, not of `subject_prefix` in general.
Every identity in your tenant can request a token for the registered audience, so `audience` and `tid` alone do not identify a specific workload. A rule that omits an `oid` (or `azp`/`appid`) match, or that uses a wildcard or partial-GUID `subject_prefix`, authorizes every managed identity and service principal in the tenant.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Match `oid` as an exact value:** Set `claims.oid` to the managed identity's full object ID. A `subject_prefix` set to that full object ID is equivalent (the Console wizard sets both); never use a wildcard or partial-GUID `subject_prefix`, which matches more identities than you intend.
- **Pin `tid` as defense in depth:** The issuer URL already pins your tenant, but adding `claims.tid` guards against configuration drift if the issuer record is later edited.
- **Pin the audience:** Set `audience` to the exact `aud` value from your decoded token so tokens minted for other applications are rejected.
- **Use a separate rule for each managed identity:** Create one rule for each identity rather than one rule that authorizes several, so you can revoke a single workload's access without affecting others.
## Next steps
- Review the full configuration model in [Workload Identity Federation](manage-claude-workload-identity-federation.md).
- See the [provider guides](manage-claude-workload-identity-federation.md#identity-providers) for AWS, Google Cloud, GitHub Actions, and Kubernetes.
- For environment variables, profile files, and credential precedence, see the [WIF reference](manage-claude-wif-reference.md).
---
## Use WIF with Google Cloud - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/gcp
Path: 04-API-Reference/Other/manage-claude-wif-providers-gcp.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fgcp)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with Google Cloud
Copy page
Federate Google Cloud workloads (Cloud Run, Cloud Functions, App Engine, GCE, GKE) to the Claude API using Google-signed identity tokens instead of static API keys.
Copy page
Any Google Cloud compute environment with access to the instance metadata server (Cloud Run, Cloud Functions, App Engine, Compute Engine (GCE), and GKE with Workload Identity) can request a Google-signed identity token for its attached service account. The token's issuer is `https://accounts.google.com`, and Anthropic can validate it directly through standard OIDC discovery, with no extra Google Cloud configuration required.
This guide shows how to register the Google issuer with Anthropic, bind a Google service account to an Anthropic service account, and have your workload exchange its identity token for a short-lived Claude API access token.
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- A Google Cloud project with a workload running on Cloud Run, Cloud Functions, App Engine, Compute Engine, or GKE.
- A user-managed Google service account attached to that workload (not the Compute Engine default service account).
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
## Configure Google Cloud
Google issues identity tokens automatically to any workload with an attached service account. There is nothing to enable on the Google side beyond attaching the right service account, but the steps differ slightly between standard compute and GKE.
Cloud Run, Cloud Functions, App Engine, GCE
GKE with Workload Identity
Attach a dedicated service account to your service or instance:
CLI
```python
gcloud run deploy my-service \
--service-account inference-worker@my-project.iam.gserviceaccount.com
```
Inside the workload, the metadata server returns a signed identity token on demand. Request it with the `audience` you intend to register on the Anthropic side, and include `format=full` so the response carries the `email` claim:
``` block
GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full
Metadata-Flavor: Google
```
Or, with the gcloud CLI:
CLI
```python
gcloud auth print-identity-token \
--audiences="https://api.anthropic.com" \
--include-email
```
The SDK equivalents are shown in [Acquire and use the token](#acquire-and-use-the-token).
The decoded token payload looks like this:
```python
{
"iss": "https://accounts.google.com",
"aud": "https://api.anthropic.com",
"sub": "104892...",
"azp": "104892...",
"email": "inference-worker@my-project.iam.gserviceaccount.com",
"email_verified": true,
"exp": 1775527120
}
```
The `sub` claim is the Google service account's opaque numeric unique ID. The `email` claim is the human-readable service account address. Match on both `sub` and `email` in your federation rule.
## Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **Google Cloud** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Google publishes its OIDC discovery document publicly, so use discovery mode. This single issuer covers every Google Cloud surface (Cloud Run, GCE, Cloud Functions, App Engine, and GKE with Workload Identity). Differentiate workloads with rules, not issuers.
```python
{
"name": "gcp",
"issuer_url": "https://accounts.google.com",
"jwks": { "type": "discovery" }
}
```
**Federation rule:** Match on both the `sub` and `email` claims. `email` is the readable service-account address; `sub` is the service account's numeric unique ID, which Google never reuses, so pinning it protects the rule if the service account is deleted and a new one is later created with the same email. Find the unique ID with `gcloud iam service-accounts describe SA_EMAIL --format='value(uniqueId)'`.
```python
{
"name": "gcp-inference-worker",
"issuer_id": "fdis_...",
"match": {
"audience": "https://api.anthropic.com",
"claims": {
"sub": "104892101234567890123",
"email": "inference-worker@my-project.iam.gserviceaccount.com"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
## Acquire and use the token
Inside 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`, as shown in the following examples.
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import os
import anthropic
import google.auth.transport.requests
import google.oauth2.id_token
from anthropic import WorkloadIdentityCredentials
AUDIENCE = "https://api.anthropic.com"
def fetch_google_identity_token() -> str:
request = google.auth.transport.requests.Request()
return google.oauth2.id_token.fetch_id_token(request, AUDIENCE)
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_google_identity_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello from Cloud Run"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
Google 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.
## Verify the setup
From inside your workload, decode the identity token and confirm the claims match your rule:
cURL
```python
curl -sS -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://api.anthropic.com&format=full" \
| jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
```
Check that `iss` is `https://accounts.google.com`, `aud` is `https://api.anthropic.com`, and `email` matches the value in your federation rule. Then run the exchange from the previous section. A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange); the most common Google Cloud-side cause is the `email` claim missing (request the token with `format=full` so it is included).
## Scope your rule
The Google `sub` claim is the service account's opaque numeric unique ID and has no stable prefix. A `subject_prefix` with a trailing `*` matches arbitrary service accounts across every Google Cloud project, and any of them could obtain a federated Anthropic token.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Match `sub` exactly:** Set the full numeric unique ID in `claims.sub` and never use `subject_prefix` for Google tokens.
- **Pin the `email` claim:** Add `claims.email` alongside `sub` so both the stable ID and the readable address must match.
- **Pin the audience:** Set `audience` to the exact value you request from the metadata server so tokens minted for other consumers are rejected.
- **Pin the project on GKE:** For `format=full` tokens, add a `condition` such as `claims.google.compute_engine.project_id == "my-project"` to restrict the rule to one project's nodes.
## Next steps
- Read the [Workload Identity Federation](manage-claude-workload-identity-federation.md) page for the full resource model and SDK credential precedence.
- Add a separate federation rule per environment (production, staging) so you can revoke one without affecting the others.
---
## Use WIF with GitHub Actions - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/github-actions
Path: 04-API-Reference/Other/manage-claude-wif-providers-github-actions.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fgithub-actions)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with GitHub Actions
Copy page
Authenticate GitHub Actions workflows to the Claude API with short-lived identity tokens instead of long-lived API keys.
Copy page
Every GitHub Actions workflow run can request a signed identity token from GitHub's hosted issuer at `https://token.actions.githubusercontent.com`. With Workload Identity Federation, your workflow exchanges that token for a short-lived Anthropic access token, so your CI jobs can call the Claude API without an `ANTHROPIC_API_KEY` secret stored in your repository.
The token's `sub` claim encodes the repository and trigger context. For a push to a branch it has the form `repo:<owner>/<repo>:ref:refs/heads/<branch>`. Pull-request runs use `repo:<owner>/<repo>:pull_request`, and environment-gated deployments use `repo:<owner>/<repo>:environment:<name>`. Your federation rule matches against this claim (and others, such as `repository_owner` and `ref`) to decide which workflow runs are allowed to authenticate.
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- A GitHub repository where you can edit workflow files and grant the `id-token: write` permission.
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
- Your Anthropic organization ID. You can find it in the Claude Console under **Settings → Organization**.
## Configure your workflow
```python
permissions:
id-token: write
contents: read
```
Inside the job, the runner exposes two environment variables: `ACTIONS_ID_TOKEN_REQUEST_URL` and `ACTIONS_ID_TOKEN_REQUEST_TOKEN`. Call the request URL with the request token as a bearer credential and your chosen audience as a query parameter, then write the returned JSON Web Token (JWT) to a file:
```python
- name: Fetch GitHub OIDC token
run: |
curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://api.anthropic.com" \
| jq -r .value > /tmp/gha-jwt
```
If you prefer JavaScript, `actions/github-script` exposes the same capability through `core.getIDToken(audience)`:
```python
- name: Fetch GitHub OIDC token
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const token = await core.getIDToken('https://api.anthropic.com');
fs.writeFileSync('/tmp/gha-jwt', token);
```
The decoded token carries claims that describe the workflow run. Your federation rule matches against these:
```python
{
"iss": "https://token.actions.githubusercontent.com",
"sub": "repo:your-org/your-repo:ref:refs/heads/main",
"aud": "https://api.anthropic.com",
"repository": "your-org/your-repo",
"repository_owner": "your-org",
"ref": "refs/heads/main",
"sha": "abc123...",
"workflow": "CI",
"actor": "octocat",
"event_name": "push"
}
```
See [GitHub's OIDC subject claim reference](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims) for the full list of `sub` formats.
## Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **GitHub Actions** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** GitHub publishes its OIDC discovery document and JWKS publicly, so use discovery mode. Anthropic refreshes the keys automatically when GitHub rotates them.
```python
{
"name": "github-actions",
"issuer_url": "https://token.actions.githubusercontent.com",
"jwks": { "type": "discovery" }
}
```
**Federation rule:** Match only the workflow runs you intend to trust. See [Restrict which workflows can authenticate](#restrict-which-workflows-can-authenticate) for how to scope these claims safely.
```python
{
"name": "gha-main",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "repo:your-org/your-repo:ref:refs/heads/main",
"audience": "https://api.anthropic.com",
"claims": {
"repository_owner": "your-org"
}
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
Be as specific as the workload allows. Loosen `subject_prefix` to `repo:your-org/your-repo:*` (paired with a `claims.ref` constraint) only if the rule must match multiple event types from the same repository, because the trailing segment of `sub` varies between `ref:...`, `environment:...`, and `pull_request` events.
## Acquire and use a token
Set the federation environment variables on the job and call the SDK normally. `Anthropic()` reads `ANTHROPIC_IDENTITY_TOKEN_FILE`, exchanges the JWT on the first request, and refreshes the access token automatically before it expires.
Workflow
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import anthropic
# Reads ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID,
# ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID, and ANTHROPIC_IDENTITY_TOKEN_FILE
# from the job environment.
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
Each 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.
## Verify the setup
A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. A denied exchange returns an opaque `401` `authentication_error` with the fixed message `Authentication failed`, whichever check failed; in most cases the deny reason is recorded on the attempt's entry in the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history), and [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange) walks the checks in order. The most common GitHub Actions-side cause is the `sub` claim format not matching (its trailing segment varies between `ref:...`, `environment:...`, and `pull_request` events); the history entry shows reason `match_subject_prefix`.
## Restrict which workflows can authenticate
A `subject_prefix` of `repo:your-org/*` alone matches every repository in your organization, and without a `ref` constraint it also matches `pull_request` runs triggered from forks. Anyone who can open a pull request against a matching repository could obtain a federated Anthropic token.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Pin to a single repository:** Use `subject_prefix: "repo:your-org/your-repo:*"` so other repositories in the organization do not match.
- **Pin to a protected branch:** Add `"ref": "refs/heads/main"` (or your release branch) under `claims` so pull-request runs and feature branches do not match.
- **Pin the owner explicitly:** Add `"repository_owner": "your-org"` under `claims` as a defense-in-depth check against `sub` parsing edge cases.
- **Pin to a deployment environment:** For deploy jobs, match `subject_prefix: "repo:your-org/your-repo:environment:production"` and gate that environment with required reviewers in GitHub.
## Next steps
- [Workload Identity Federation](manage-claude-workload-identity-federation.md): full setup walkthrough, environment variables, and credential precedence.
- [Authentication](manage-claude-authentication.md): how federation compares to API keys.
---
## Use WIF with Kubernetes - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/kubernetes
Path: 04-API-Reference/Other/manage-claude-wif-providers-kubernetes.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fkubernetes)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with Kubernetes
Copy page
Authenticate to the Claude API from self-managed Kubernetes clusters using projected service account tokens.
Copy page
Self-managed Kubernetes clusters (kubeadm, k3s, OpenShift, and on-premises distributions) sign OIDC JSON Web Tokens (JWTs) for every pod through [projected service account tokens](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#serviceaccount-token-volume-projection). The cluster's API server acts as the OIDC issuer, and each token's `sub` claim follows the form `system:serviceaccount:<namespace>:<service-account>`. You can find your cluster's issuer URL by reading its discovery document:
cURL
```python
kubectl get --raw /.well-known/openid-configuration | jq -r .issuer
```
The mechanism on this page (projected service-account token, cluster API server as the OIDC issuer) is native to Kubernetes itself, so it underlies every Kubernetes distribution. If you run on a managed Kubernetes service, the cloud provider guides walk through where to find the provider-managed issuer URL: [AWS (EKS)](manage-claude-wif-providers-aws.md#use-eks-projected-service-account-tokens), [Google Cloud (GKE)](manage-claude-wif-providers-gcp.md), or [Azure (AKS)](manage-claude-wif-providers-azure.md). If your cluster runs SPIRE, the SPIRE OIDC Discovery Provider is the issuer rather than the cluster API server; see [SPIFFE](manage-claude-wif-providers-spiffe.md). For any other distribution or a managed provider not listed there, follow this guide and use the issuer URL your cluster reports.
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- A Kubernetes cluster with the [`--service-account-issuer`](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) flag configured on the API server. Most distributions set this by default; kubeadm clusters typically use `https://kubernetes.default.svc.cluster.local`. Your platform team can confirm the value if you don't have direct access to the API server configuration.
- One of the following so Anthropic can validate token signatures:
- The issuer's JWKS endpoint is reachable from the public internet over HTTPS on port 443, or
- You can fetch the JWKS from inside the cluster and register it in `inline` mode (covered in [Configure Anthropic](#configure-anthropic)).
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
## Configure Kubernetes
Project a service account token into your pod with the audience and lifetime that your federation rule expects. The `serviceAccountToken` projection writes a fresh JWT to the mount path and rotates it before `expirationSeconds` elapses.
Pod
```python
apiVersion: v1
kind: Pod
metadata:
name: inference-worker
namespace: inference
spec:
serviceAccountName: inference-worker
volumes:
- name: anthropic-token
projected:
sources:
- serviceAccountToken:
audience: https://api.anthropic.com
expirationSeconds: 3600
path: token
containers:
- name: app
image: your-registry/inference-worker:latest
env:
- name: ANTHROPIC_IDENTITY_TOKEN_FILE
value: /var/run/secrets/anthropic.com/token
- name: ANTHROPIC_FEDERATION_RULE_ID
value: fdrl_...
- name: ANTHROPIC_ORGANIZATION_ID
value: 00000000-0000-0000-0000-000000000000
- name: ANTHROPIC_SERVICE_ACCOUNT_ID
value: svac_...
- name: ANTHROPIC_WORKSPACE_ID # required when the rule covers multiple workspaces
value: wrkspc_...
volumeMounts:
- name: anthropic-token
mountPath: /var/run/secrets/anthropic.com
readOnly: true
```
The token issued for this pod carries `sub: "system:serviceaccount:inference:inference-worker"` and `aud: ["https://api.anthropic.com"]`.
## Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select the **Kubernetes** tile. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Many self-managed clusters use an issuer URL such as `https://kubernetes.default.svc.cluster.local` that is not reachable from the public internet. If that applies to your cluster, choose the **inline** JWKS source and paste the cluster's keys. Fetch them from inside the cluster:
cURL
```python
kubectl get --raw /openid/v1/jwks
```
Then configure the issuer with the contents of the returned `keys` array (not the surrounding `{"keys": [...]}` wrapper):
```python
{
"name": "onprem-k8s",
"issuer_url": "https://kubernetes.default.svc.cluster.local",
"jwks": {
"type": "inline",
"keys": [{ "kty": "RSA", "kid": "...", "n": "...", "e": "AQAB" }]
}
}
```
In `inline` mode the `issuer_url` is only compared against the JWT's `iss` claim; Anthropic never attempts to reach it. If your issuer is publicly reachable, use `"jwks": {"type": "discovery"}` instead.
With `inline` keys you are responsible for updating the issuer when the cluster rotates its service account signing key. Rotation is rare (typically only during cluster upgrades), but token exchanges fail with a signature error until you push the new JWKS.
**Federation rule:** Match the service account's `sub` claim and the audience you set on the projected token.
```python
{
"name": "onprem-inference",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "system:serviceaccount:inference:inference-worker",
"audience": "https://api.anthropic.com"
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
Be as specific as the workload allows. Loosen `subject_prefix` to `system:serviceaccount:inference:*` (the trailing `*` makes it a prefix match) only if every service account in the namespace should map to the same Anthropic service account. Add the rule's `fdrl_...` ID to your pod's `ANTHROPIC_FEDERATION_RULE_ID` environment variable.
## Acquire and use the token
The pod spec in [Configure Kubernetes](#configure-kubernetes) sets `ANTHROPIC_IDENTITY_TOKEN_FILE` to the projected mount path, along with `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, and `ANTHROPIC_WORKSPACE_ID`. With those in place, the SDK reads the token from disk on every exchange and refreshes the Anthropic access token automatically.
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import anthropic
# Reads ANTHROPIC_IDENTITY_TOKEN_FILE, ANTHROPIC_FEDERATION_RULE_ID,
# ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID
# from the pod's environment.
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
## Verify the setup
A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange); the most common Kubernetes-side cause is a JWKS key mismatch (for `inline` mode, re-fetch with `kubectl get --raw /openid/v1/jwks` and update the issuer).
## Scope your rule
A `subject_prefix` of `system:serviceaccount:*` matches every service account in the cluster, so any pod can obtain a federated Anthropic token. Without an `audience` matcher, the rule also matches the cluster's default-audience tokens, which every pod already has projected.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Pin namespace and service-account name:** Use the full `system:serviceaccount:<namespace>:<name>` value with no trailing `*`.
- **Always set an audience:** Require `audience` on the rule and set the same value on the pod's `serviceAccountToken` projection so default-audience tokens are rejected.
- **Use a separate rule per namespace:** Create a distinct rule and Anthropic service account for each namespace rather than widening one rule.
- **Scope inline-JWKS issuers to one cluster:** When several clusters share an issuer URL, register each cluster's JWKS as its own federation issuer and bind rules to that issuer only.
## Next steps
- [Workload Identity Federation](manage-claude-workload-identity-federation.md): concepts, the token-exchange flow, and SDK configuration options.
- [WIF reference](manage-claude-wif-reference.md): environment variables, JWKS source modes, and rule match modes.
---
## Use WIF with Okta - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/okta
Path: 04-API-Reference/Other/manage-claude-wif-providers-okta.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fokta)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with Okta
Copy page
Federate Okta service application identities to the Claude API with Workload Identity Federation.
Copy page
Okta can act as a workload identity provider by issuing OIDC access tokens to a **service application** through the OAuth 2.0 `client_credentials` grant. Your workload authenticates to Okta (typically with `private_key_jwt`, so no shared secret is stored), receives a signed JSON Web Token (JWT), and exchanges that JWT with Anthropic for a short-lived access token.
The Okta authorization server's issuer URL takes the form `https://<your-domain>.okta.com/oauth2/<auth-server-id>`. If you use the built-in default server, the path is `/oauth2/default`.
You must use an Okta **custom authorization server** (including the `default` one). Tokens issued directly by the Okta org authorization server (the `/oauth2/v1/token` endpoint with no authorization server ID in the path) cannot be validated by external parties because Okta does not publish signing keys for them.
There are many ways to configure and authenticate to Okta that are outside the scope of this documentation. Ensure that your configuration and authentication mechanisms follow your company's guidance and security practices.
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- An Okta organization with API Access Management enabled (required for custom authorization servers).
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
- A workload that can request a token from Okta's `/v1/token` endpoint and reach `api.anthropic.com`.
## Configure Okta
At a high level you need to:
1. Create an Okta service application.
2. Configure your default authorization server (or create a new custom authorization server) with an audience, a scope, an access policy, and any custom claims you want to match on.
The exact navigation depends on your Okta org configuration and admin console version. The following numbered steps walk through one common path:
1. **Create a service app integration.** In the Okta Admin Console, create a new app integration of type **API Services** (OIDC, machine-to-machine). Note the generated **Client ID**.
2. **Configure client authentication.** For a keyless setup, choose **Public key / Private key** (`private_key_jwt`) and register your workload's public JWK. Alternatively, use a client secret if your environment can store one securely. For the following example you may need to disable the DPoP requirement on the application; ensure that your production setup adheres to your organization's security requirements.
3. **Set the audience.** On your custom authorization server, set the audience to `https://api.anthropic.com` so issued access tokens carry that `aud` claim. Anthropic validates `aud` against this fixed value.
4. **Grant a scope.** On your custom authorization server, ensure at least one scope exists that the service app is allowed to request (for example, `anthropic.access`). Okta rejects `client_credentials` requests that do not include a granted scope.
5. **Create an access policy.** On your custom authorization server, create an access policy with at least one rule that allows your service app to request the scope you granted in step 4.
6. **(Optional) Add custom claims.** If you want to match on something other than the client ID, add a claim to the access token in your authorization server's **Claims** tab.
For a service app using `client_credentials`, Okta sets the `sub` claim of the issued access token to the application's **Client ID**, and `iss` to the authorization server's issuer URL.
## Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select **Custom OIDC**. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Use your Okta custom authorization server URL and discovery mode. Anthropic reads Okta's `.well-known/openid-configuration` discovery document and fetches the JWKS from the `jwks_uri` it advertises.
```python
{
"name": "okta-prod",
"issuer_url": "https://acme.okta.com/oauth2/aus1a2b3c4d5e6f7g8h9",
"jwks": { "type": "discovery" }
}
```
**Federation rule:** Match on the Okta `sub` claim, which is the service app's Client ID. If you defined custom claims in Okta, you can match on those instead with the `claims` map or a CEL `condition`.
```python
{
"name": "okta-pipeline",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "0oa1b2c3d4e5f6g7h8i9",
"audience": "https://api.anthropic.com"
},
"target": { "type": "service_account", "service_account_id": "svac_..." },
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
## Acquire a token and call the Claude API
Unlike platform-native providers (AWS, Google Cloud, Kubernetes), which make a token available inside the workload's runtime (through a projected file or local metadata endpoint), Okta does not. Your workload must call Okta's token endpoint to obtain a JWT, then pass that JWT to the Anthropic SDK as the identity token.
cURL
Python
TypeScript
Go
Java
C#
CLI
PHP
Ruby
```python
import os
import httpx2
import anthropic
from anthropic import WorkloadIdentityCredentials
def fetch_okta_token() -> str:
response = httpx2.post(
f"{os.environ['OKTA_ISSUER']}/v1/token",
data={
"grant_type": "client_credentials",
"scope": "anthropic.access",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
# Build the RFC 7523 client_assertion JWT signed with your Okta app's private key
"client_assertion": build_signed_client_assertion(),
},
)
response.raise_for_status()
return response.json()["access_token"]
client = anthropic.Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=fetch_okta_token,
federation_rule_id=os.environ["ANTHROPIC_FEDERATION_RULE_ID"],
organization_id=os.environ["ANTHROPIC_ORGANIZATION_ID"],
service_account_id=os.environ["ANTHROPIC_SERVICE_ACCOUNT_ID"],
workspace_id=os.environ.get("ANTHROPIC_WORKSPACE_ID"),
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
Each SDK tab shows the callable pattern: the Anthropic SDK calls the function you passed to `identity_token_provider` 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.
## Verify the setup
A successful exchange returns an `access_token` beginning with `sk-ant-oat01-` and an `expires_in` value in seconds. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange); the most common Okta-side cause is an `issuer_url` mismatch (it must include the `/oauth2/<auth-server-id>` path; the Okta org authorization server is not usable).
## Scope your rule
Multiple service apps under the same Okta authorization server share the same issuer. A rule that omits `subject_prefix` matches every service app on that server, so any team that can register one could obtain a federated Anthropic token.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Pin the exact Client ID:** Set `subject_prefix` to the service app's full Client ID with no trailing `*`.
- **Pin the audience:** Match the `audience` value you configured on the authorization server so tokens minted for a different audience are rejected.
- **Match on custom claims:** For finer-grained scoping, add claims in the authorization server's **Claims** tab and match them with the rule's `claims` map or a CEL `condition`.
- **Use one rule per service app:** Create a separate federation rule for each service app rather than sharing one rule across apps.
## Next steps
- Review the [WIF reference](manage-claude-wif-reference.md) for the full credential resolution order and profile configuration.
- See the [WIF reference](manage-claude-wif-reference.md#rule-matching-semantics) to match on custom Okta claims with CEL expressions.
---
## Use WIF with SPIFFE - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-providers/spiffe
Path: 04-API-Reference/Other/manage-claude-wif-providers-spiffe.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-providers%2Fspiffe)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
[AWS](manage-claude-wif-providers-aws.md)[Google Cloud](manage-claude-wif-providers-gcp.md)[Microsoft Entra ID](manage-claude-wif-providers-azure.md)[GitHub Actions](manage-claude-wif-providers-github-actions.md)[Kubernetes](manage-claude-wif-providers-kubernetes.md)[SPIFFE](manage-claude-wif-providers-spiffe.md)[Okta](manage-claude-wif-providers-okta.md)
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Identity providers
# Use WIF with SPIFFE
Copy page
Authenticate SPIFFE workloads to the Claude API using JWT-SVIDs from SPIRE or any other SPIFFE-conformant issuer.
Copy page
[SPIFFE](https://spiffe.io/) is the CNCF standard for issuing identity to workloads. [SPIRE](https://spiffe.io/docs/latest/spire-about/) is its open-source reference implementation, and several commercial products also issue SPIFFE-conformant identities. Anthropic federates with any SPIFFE implementation that emits OIDC-compatible JWT-SVIDs. For a current list of implementations, see [Commercial software that implements SPIFFE](https://spiffe.io/docs/latest/spiffe-about/overview/#commercial-software-that-implements-spiffe) on the SPIFFE project site.
Federation works either through an OIDC discovery document at a public HTTPS URL (`discovery` mode, subject to the [URL constraints](manage-claude-wif-reference.md#url-fields)) or by registering the JWKS directly (`inline` mode).
The JWT-SVID spec defines `sub` as the workload's SPIFFE ID, and the SPIFFE Workload API requires the caller to supply `aud` at fetch time, so those claims are the same across implementations. Anthropic additionally requires `iss` and `iat`, neither of which the JWT-SVID spec mandates, so configure your implementation to populate both (in SPIRE, `iss` is the `jwt_issuer` server setting and `iat` is set automatically). With those in place, the [Configure Anthropic](#configure-anthropic), [Acquire and use the token](#acquire-and-use-the-token), and [Scope your rule](#scope-your-rule) sections of this guide apply to any SPIFFE implementation.
SPIFFE assigns every workload a stable identity URI of the form `spiffe://<trust-domain>/<path>`, and SPIRE issues that identity as a JWT-SVID on demand through the Workload API. A JWT-SVID is an ordinary signed JWT whose `sub` claim is the workload's SPIFFE ID and whose `aud` claim is supplied by the workload at fetch time.
The bridge from a SPIRE trust domain to standard OIDC is the [SPIRE OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md), a standalone helper that publishes `/.well-known/openid-configuration` and a JWKS endpoint for the trust domain's JWT signing keys. With the discovery provider running, a JWT-SVID validates like any other OIDC token: register the discovery URL as a federation issuer, write a federation rule that matches the workload's SPIFFE ID, and have the workload present its JWT-SVID to Anthropic's token-exchange endpoint.
This page's examples use SPIRE and apply anywhere SPIRE Agent runs: Kubernetes pods, virtual machines, and bare-metal hosts.
If your Kubernetes cluster does not run SPIRE and you want to authenticate with the cluster's native projected service-account tokens instead, see [Use WIF with Kubernetes](manage-claude-wif-providers-kubernetes.md).
## Prerequisites
- Familiarity with [WIF concepts](manage-claude-workload-identity-federation.md#concepts): service accounts, federation issuers, and federation rules.
- A SPIFFE deployment with workload identities issued (the examples on this page use SPIRE Server and Agent), and registration entries for the workloads that need to call the Claude API.
- An OIDC discovery endpoint for the trust domain (in SPIRE, the [OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md)) running with a publicly reachable HTTPS endpoint, or the JWKS exported for `inline` registration.
- Your SPIFFE issuer configured to set the `iss` claim on JWT-SVIDs to the value you will register as the federation issuer's `issuer_url`. For `discovery` mode, this is the discovery endpoint's public URL (in SPIRE, the `jwt_issuer` server setting).
- JWT-SVIDs available to your workloads. WIF accepts JWT-SVIDs only, not X.509-SVIDs.
- Permission to create service accounts, federation issuers, and federation rules in the Claude Console for your Anthropic organization.
The audience value to request when fetching a JWT-SVID is always `https://api.anthropic.com`. Use this value in spiffe-helper's `jwt_audience`, the Workload API `FetchJWTSVID` call, and the federation rule's `audience` matcher.
## Configure SPIRE
The instructions in this section are SPIRE-specific. If you use a different SPIFFE issuer, configure its OIDC discovery endpoint and JWT-SVID retrieval according to its own documentation, then continue at [Configure Anthropic](#configure-anthropic).
If you already run SPIRE with the OIDC Discovery Provider, federating with Anthropic requires three things on the SPIRE side: a `jwt_issuer` that matches the discovery URL, a registration entry for the workload that will call the Claude API, and a way for that workload to fetch a JWT-SVID with the Anthropic audience. The following subsections walk through each. The configuration snippets show only the settings relevant to Anthropic federation, not complete SPIRE deployment configs.
Setting up SPIRE for the first time? Deploy SPIRE Server and Agent following the [SPIRE quickstart](https://spiffe.io/docs/latest/try/), then add the [OIDC Discovery Provider](https://github.com/spiffe/spire/blob/main/support/oidc-discovery-provider/README.md) as a separate service alongside SPIRE Server. Discovery-mode federation depends on the provider being deployed and publicly reachable. The provider is not part of a default SPIRE install.
### Verify the JWT issuer
Anthropic validates a JWT-SVID by matching its `iss` claim against a registered federation issuer and fetching the JWKS from that issuer's discovery document. Two SPIRE settings must agree on the same URL: SPIRE Server's `jwt_issuer` (which becomes the `iss` claim in every minted JWT-SVID) and the OIDC Discovery Provider's `domains` list (which determines the host the discovery document and JWKS are served from). That shared URL is what you register with Anthropic.
The trust domain and the issuer URL are independent. The trust domain (`spiffe://prod.example.com`) scopes the `sub` claim. The issuer URL (`https://oidc-discovery.prod.example.com`) is where Anthropic fetches signing keys. They do not need to share a hostname.
Confirm `jwt_issuer` is set in SPIRE Server's configuration and points at the discovery provider's public URL. The following example also shows a default JWT-SVID lifetime. SPIRE's built-in default is 5 minutes, which is short enough that continuous rotation is required (see [Run spiffe-helper](#run-spiffe-helper)). Anthropic's token-exchange endpoint rejects any identity token whose lifetime exceeds the federation issuer's configured maximum, which is 1 hour by default (see [Validation rules](manage-claude-wif-reference.md#validation-rules)). This check applies to every SPIFFE implementation, not only SPIRE, so keep `default_jwt_svid_ttl` (or any per-entry override) at or below that maximum.
server.conf
``` inline-block
server {
trust_domain = "prod.example.com"
jwt_issuer = "https://oidc-discovery.prod.example.com"
default_jwt_svid_ttl = "5m"
# ...
}
```
In the OIDC Discovery Provider's configuration, the same hostname must appear under `domains`, and the provider must be able to reach SPIRE Server's API socket. The provider serves the discovery document and JWKS over HTTPS. Terminate TLS with its built-in ACME support, or front it with a load balancer that does.
oidc-discovery-provider.conf
``` inline-block
domains = ["oidc-discovery.prod.example.com"]
server_api {
address = "unix:///run/spire/sockets/private/api.sock"
}
acme {
email = "platform@example.com"
tos_accepted = true
}
```
The example uses `server_api`, which connects the discovery provider to SPIRE Server's privileged API socket. The provider also accepts a `workload_api` block (with `socket_path` and `trust_domain`) that obtains the bundle through a SPIRE Agent's Workload API instead. Use it when the discovery provider should not have access to the Server API or runs on a node that cannot reach the Server.
### Register the workload
Each workload that calls the Claude API needs a SPIRE registration entry that maps its runtime selectors to a SPIFFE ID. If the workload is already registered, note its SPIFFE ID, which you use in the federation rule's `subject_prefix`. If not, register it. For a Kubernetes pod, the selectors are typically the namespace and Kubernetes service account:
CLI
```python
# Replace NODE_UID with the node's UID:
# kubectl get node <node-name> -o jsonpath='{.metadata.uid}'
spire-server entry create \
-spiffeID spiffe://prod.example.com/ns/inference/sa/worker \
-parentID spiffe://prod.example.com/spire/agent/k8s_psat/prod-cluster/NODE_UID \
-selector k8s:ns:inference \
-selector k8s:sa:worker
```
The `parentID` shown is a single node's auto-generated agent ID. For cluster-wide registration, parent the entry to a [node alias](https://spiffe.io/docs/latest/deploying/registering/#mapping-workloads-to-multiple-nodes) so it matches workloads on every node, as the [SPIRE Kubernetes quickstart](https://spiffe.io/docs/latest/try/getting-started-k8s/) does.
Workloads outside Kubernetes use host-level selectors such as `unix:uid:1000` (`unix:path` is also available but requires `discover_workload_path = true` in the agent's unix workload attestor configuration). Clusters running [spire-controller-manager](https://github.com/spiffe/spire-controller-manager) can declare entries with the `ClusterSPIFFEID` custom resource instead of calling `spire-server entry create` directly.
### Run spiffe-helper
[spiffe-helper](https://github.com/spiffe/spiffe-helper) is a sidecar utility that connects to the SPIRE Agent socket, fetches a JWT-SVID for a given audience, writes it to a file, and re-fetches it before expiry. The helper runs in daemon mode by default. The following example sets `daemon_mode = true` explicitly.
helper.conf
``` inline-block
agent_address = "/run/spire/sockets/agent.sock"
# The JWT-SVID file is written under cert_dir
cert_dir = "/var/run/secrets/anthropic.com"
daemon_mode = true
jwt_svids = [{
jwt_audience = "https://api.anthropic.com"
jwt_svid_file_name = "token"
}]
```
In Kubernetes, run spiffe-helper as a sidecar container that shares a memory-backed `emptyDir` volume (`medium: Memory`) with your application container so the bearer SVID never lands on the node's disk. Mount the SPIRE Agent socket from the host into the sidecar, mount the shared volume at `/var/run/secrets/anthropic.com` in both containers, and set `ANTHROPIC_IDENTITY_TOKEN_FILE=/var/run/secrets/anthropic.com/token` on the application container. On VMs and bare metal, run spiffe-helper as a system service alongside the workload and point both at a shared directory.
## Configure Anthropic
In the Claude Console, open **Settings → Workload identity**, click **Connect workload**, and select **Custom OIDC**. The wizard walks you through registering the issuer, creating a service account, and creating a federation rule.
The wizard creates these resources for you. Use the following values whether you enter them in the wizard or send them to the [Admin API](manage-claude-wif-admin-api.md):
**Federation issuer:** Register the OIDC Discovery Provider's public URL in `discovery` mode. Anthropic fetches `/.well-known/openid-configuration` from this URL and follows the returned `jwks_uri` to retrieve the trust domain's signing keys.
```python
{
"name": "spire-prod",
"issuer_url": "https://oidc-discovery.prod.example.com",
"jwks": { "type": "discovery" }
}
```
If the discovery provider is not reachable from the public internet, fetch the JWKS yourself (`curl https://oidc-discovery.prod.example.com/keys`) and register the issuer with `"jwks": {"type": "inline", "keys": [...]}` using the contents of the returned `keys` array. In `inline` mode the `issuer_url` is only compared against the JWT-SVID's `iss` claim. Anthropic never attempts to reach it.
SPIRE rotates JWT signing keys frequently, by default on the same cadence as the CA (`ca_ttl`, 24 hours). If you register the issuer with an inline JWKS instead of a discovery URL, you must update the JWKS every time SPIRE rotates: add the new key before workloads start presenting it, and **remove superseded keys** once tokens signed with them have expired. Stale keys left in an inline JWKS remain trusted indefinitely.
To automate JWKS updates without exposing a public discovery endpoint, configure a SPIRE Server [BundlePublisher](https://spiffe.io/docs/latest/deploying/spire_server/#built-in-plugins) plugin (`aws_s3`, `gcp_cloudstorage`, or `k8s_configmap`) with `format = "jwks"` to push the JWT signing keys to external storage on every rotation, then update the issuer's inline keys through the [Admin API](manage-claude-wif-admin-api.md#federation-issuers).
**Federation rule:** Match the JWT-SVID's `sub` (the SPIFFE ID) and the `aud` you configured spiffe-helper to request. SPIFFE IDs are URI strings and `subject_prefix` matches them as opaque text, so an exact value or a trailing-`*` prefix match both work against them. For more complex patterns, use a CEL `condition`.
```python
{
"name": "spire-inference-worker",
"issuer_id": "fdis_...",
"match": {
"subject_prefix": "spiffe://prod.example.com/ns/inference/sa/worker",
"audience": "https://api.anthropic.com"
},
"target": {
"type": "service_account",
"service_account_id": "svac_..."
},
"workspace_id": "wrkspc_...",
"oauth_scope": "workspace:developer",
"token_lifetime_seconds": 600
}
```
`token_lifetime_seconds` is the lifetime of the Anthropic access token the exchange returns, not of the JWT-SVID. The SDK refreshes the access token automatically.
Be as specific as the workload allows. Loosen `subject_prefix` to `spiffe://prod.example.com/ns/inference/*` only if every workload registered under that path should map to the same Anthropic service account. Add the rule's `fdrl_...` ID to the workload's `ANTHROPIC_FEDERATION_RULE_ID` environment variable.
## Acquire and use the token
The 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.
File-based with spiffe-helper
Callable through the SPIFFE Workload API
With spiffe-helper writing a fresh JWT-SVID to `/var/run/secrets/anthropic.com/token`, set `ANTHROPIC_IDENTITY_TOKEN_FILE` to that path along with `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, and `ANTHROPIC_WORKSPACE_ID`. The SDK reads the file on every token exchange, so it always picks up the most recently rotated SVID, and refreshes the Anthropic access token automatically before it expires. See [Environment variables](manage-claude-wif-reference.md#environment-variables) for where each value comes from.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import anthropic
# Reads the JWT-SVID that spiffe-helper writes to
# ANTHROPIC_IDENTITY_TOKEN_FILE, plus ANTHROPIC_FEDERATION_RULE_ID,
# ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, and ANTHROPIC_WORKSPACE_ID.
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
## Verify the setup
Before wiring the SDK in, fetch a JWT-SVID directly from SPIRE Agent and confirm the claims match what your federation rule expects. If you use a different SPIFFE implementation, fetch a JWT-SVID with its CLI or Workload API client and decode the payload the same way.
The Workload API attests the calling process. For a Kubernetes registration entry, run this command inside a pod that satisfies the entry's selectors and has the agent socket mounted (for example, by using `kubectl exec`). On VMs and bare metal, run it as the user or process that matches the entry's `unix:` selectors. Running from an unattested host shell returns `no identity issued`, which is the most common verify-step failure.
CLI
```python
spire-agent api fetch jwt \
-audience https://api.anthropic.com \
-socketPath /run/spire/sockets/agent.sock \
-output json \
| jq -r '.[0].svids[0].svid' \
| jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson'
```
The `-output json` flag returns the SVID response and bundle response as a two-element JSON array, so `jq -r '.[0].svids[0].svid'` extracts the bare token. On older SPIRE versions without `-output`, the command prints a labeled block instead. In that case, pipe the default output through `awk '/^[[:space:]]*eyJ/{print $1; exit}'` to extract the token line. Check that `iss` is the OIDC Discovery Provider URL you registered, `sub` is the workload's SPIFFE ID, and `aud` contains `https://api.anthropic.com`. Then run the cURL example from [Acquire and use the token](#acquire-and-use-the-token). A successful exchange returns an `access_token` beginning with `sk-ant-oat01-`. If the exchange fails with the opaque `401` `authentication_error` response (message `Authentication failed`), check the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) for the deny reason and see [Troubleshoot a failed exchange](manage-claude-wif-reference.md#troubleshoot-a-failed-exchange). The most common SPIRE-side cause is a mismatch between SPIRE Server's `jwt_issuer` and the URL registered as the federation issuer.
## Scope your rule
SPIFFE ID path conventions are operator-defined, so the federation rule's `subject_prefix` matcher should reflect the path scheme your registration entries use. Common schemes include `spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>` (the default emitted by the `ClusterSPIFFEID` resource in spire-controller-manager) and `spiffe://<trust-domain>/host/<hostname>/<service>` for VM and bare-metal workloads.
A `subject_prefix` of `spiffe://prod.example.com/*` matches every workload in the trust domain. Without an `audience` matcher, the rule also accepts JWT-SVIDs minted for any audience, including ones the workload requested for unrelated relying parties.
Lock the rule's `match` block to the narrowest scope that fits your use case:
- **Pin to one workload:** Set `subject_prefix` to the full SPIFFE ID with no trailing `*`.
- **Always set an audience:** Require `audience` on the rule and configure spiffe-helper (or the Workload API call) with the same value so SVIDs minted for other relying parties are rejected.
- **Scope by path segment:** Use `spiffe://prod.example.com/ns/inference/*` to grant every workload registered under a namespace, and create a separate rule and Anthropic service account per namespace rather than widening one rule.
- **One issuer per trust domain:** Each SPIRE trust domain has its own signing keys and OIDC Discovery Provider. Register each as a separate federation issuer and bind rules to the issuer that owns the SPIFFE IDs they match.
## Next steps
[Use WIF with Okta](manage-claude-wif-providers-okta.md)
Federate Okta service application identities to the Claude API with Workload Identity Federation.
[Workload Identity Federation](manage-claude-workload-identity-federation.md)
Authenticate workloads to the Claude API with short-lived identity tokens from your own identity provider instead of long-lived static API keys.
[WIF reference](manage-claude-wif-reference.md)
Environment variables, validation rules, profile configuration, and error reference for Workload Identity Federation.
[Use WIF with Kubernetes](manage-claude-wif-providers-kubernetes.md)
Authenticate to the Claude API from self-managed Kubernetes clusters using projected service account tokens.
---
## WIF reference - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/wif-reference
Path: 04-API-Reference/Other/manage-claude-wif-reference.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fwif-reference)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Authentication
# WIF reference
Copy page
Environment variables, validation rules, profile configuration, and error reference for Workload Identity Federation.
Copy page
This page collects the configuration surfaces, validation constraints, and error mappings for [Workload Identity Federation](manage-claude-workload-identity-federation.md). For setup walkthroughs, see the [provider guides](manage-claude-workload-identity-federation.md#identity-providers).
## Token exchange request
`POST /v1/oauth/token` accepts a JSON body using the [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) `jwt-bearer` grant. The SDK builds this request for you from the [environment variables](#environment-variables); the cURL examples on each provider guide show the raw body.
| Field | Required | Description |
|:---------------------|:------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `grant_type` | Yes | Always `urn:ietf:params:oauth:grant-type:jwt-bearer`. |
| `assertion` | Yes | The OIDC JWT issued by your identity provider. |
| `federation_rule_id` | Yes | Tagged ID (`fdrl_...`) of the federation rule to evaluate. |
| `organization_id` | Yes | UUID of your Anthropic organization. |
| `service_account_id` | Yes | Tagged ID (`svac_...`) of the target service account. |
| `workspace_id` | Conditional | Tagged ID (`wrkspc_...`) of the workspace to scope the minted token to. Required when the rule is enabled for more than one workspace. When omitted, the server selects the rule's sole enabled workspace. The literal `default` also works for the organization's Default Workspace but is deprecated; use that workspace's `wrkspc_...` ID. |
## Token exchange response
`POST /v1/oauth/token` returns a standard OAuth 2.0 token response ([RFC 6749 §5.1](https://www.rfc-editor.org/rfc/rfc6749#section-5.1)):
| Field | Type | Description |
|----------------|---------|-----------------------------------------------------------------------------------------------------------|
| `access_token` | string | The short-lived Anthropic token, prefixed `sk-ant-oat01-...`. Pass it as `Authorization: Bearer <token>`. |
| `token_type` | string | Always `Bearer`. |
| `expires_in` | integer | Seconds until the token expires. |
| `scope` | string | The OAuth scope granted by the matched rule. |
## Environment variables
The SDK reads these variables to perform a federated token exchange with no constructor arguments.
| Variable | Required | Description | Example |
|:--------------------------------|:---------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------|
| `ANTHROPIC_FEDERATION_RULE_ID` | Yes | Tagged ID of the federation rule to evaluate. | `fdrl_...` |
| `ANTHROPIC_ORGANIZATION_ID` | Yes | UUID of your Anthropic organization. Find it in the Claude Console under **Settings \> Organization**. | `00000000-0000-0000-0000-000000000000` |
| `ANTHROPIC_IDENTITY_TOKEN_FILE` | One of `_TOKEN_FILE` or `_TOKEN` | Filesystem path to the JWT issued by your identity provider (IdP). The SDK re-reads this file on every exchange so that projected tokens that rotate on disk are always current. | `/var/run/secrets/anthropic.com/token` |
| `ANTHROPIC_IDENTITY_TOKEN` | One of `_TOKEN_FILE` or `_TOKEN` | The literal JWT as a string. Use when your platform injects the token as an environment variable rather than a file. | `eyJhbGciOiJSUzI1NiIs...` |
| `ANTHROPIC_SERVICE_ACCOUNT_ID` | Yes | Tagged ID of the target Anthropic service account that the issued access token acts as. | `svac_...` |
| `ANTHROPIC_WORKSPACE_ID` | Conditional | Tagged ID of the workspace to scope the minted token to. Required when the federation rule is enabled for more than one workspace; optional when the rule is bound to a single workspace. The minted token is scoped to this workspace at exchange time, so switching workspaces requires a new exchange. The literal `default` also works for the Default Workspace but is deprecated; use its `wrkspc_...` ID. | `wrkspc_...` |
| `ANTHROPIC_PROFILE` | No | Name of a [configuration profile](#profile-configuration-file) to load. Takes precedence over the federation environment variables in this table. | `staging-profile` |
The direct environment-variable federation path activates only when `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, and one of `ANTHROPIC_IDENTITY_TOKEN_FILE` or `ANTHROPIC_IDENTITY_TOKEN` are all set. `ANTHROPIC_WORKSPACE_ID` is read alongside but does not gate activation.
A variable that is set to an empty string still occupies its slot in the credential precedence chain. If `ANTHROPIC_API_KEY=""` is exported, the SDK selects the API-key path with an empty key rather than falling through to federation. Unset unused credential variables rather than blanking them.
### Credential precedence
The SDK resolves credentials in this order. The first source that yields a credential wins.
| Order | Source | Notes |
|:------|:-----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------|
| 1 | Constructor argument (`api_key=`, `auth_token=`, `credentials=`) | Always overrides everything else. |
| 2 | `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` | Shadows federation entirely. Unset these when migrating from API keys. |
| 3 | `ANTHROPIC_PROFILE` | Loads `<config_dir>/configs/<name>.json`. A missing named profile is an error, not a fall-through. |
| 4 | Federation environment variables | `ANTHROPIC_FEDERATION_RULE_ID` + `ANTHROPIC_ORGANIZATION_ID` + `ANTHROPIC_SERVICE_ACCOUNT_ID` + `ANTHROPIC_IDENTITY_TOKEN[_FILE]`. |
| 5 | Active profile | Resolved from `<config_dir>/active_config`, falling back to a profile named `default`. |
When a profile is loaded, environment variables fill any fields the profile omits but never override fields the profile sets explicitly. For example, `ANTHROPIC_WORKSPACE_ID` fills `workspace_id` only when the active profile does not set it.
## Profile configuration file
A profile is a named configuration file that the SDK and the `ant` CLI both read. Profiles let you ship federation parameters with your container image or switch between environments without changing code.
### Configuration directory
The SDK locates the configuration directory in this order:
1. `$ANTHROPIC_CONFIG_DIR`
2. `~/.config/anthropic` on Linux and macOS
3. `%APPDATA%\Anthropic` on Windows
### Active profile
The active profile name resolves in this order:
1. `$ANTHROPIC_PROFILE`
2. The contents of `<config_dir>/active_config` (a one-line file written by `ant profile activate <name>`)
3. The literal name `default`
Claude Code and the Claude Agent SDK honor this same resolution order, so a federation profile configured here also authenticates those tools without additional setup.
### File layout
| Path | Contents | Sensitivity |
|:------------------------------------------|:-------------------------------------------------------------------------------------------------|:--------------------------------------------------|
| `<config_dir>/configs/<profile>.json` | `version`, the `authentication` block, `organization_id`, `workspace_id`, and `base_url`. | Non-secret. Safe to commit or bake into an image. |
| `<config_dir>/credentials/<profile>.json` | `version`, the cached `access_token`, `expires_at`, and (for interactive login) `refresh_token`. | Secret. Written by the SDK with mode `0600`. |
Both the config file and the credentials file carry a top-level string `version` field in `major.minor` format (currently `"1.0"`). The SDK writes this field automatically so future releases can detect and migrate older formats; omit it when authoring a config by hand and the SDK treats the file as the current version.
### Federation profile example
configs/production.json
```python
{
"version": "1.0",
"authentication": {
"type": "oidc_federation",
"federation_rule_id": "fdrl_...",
"service_account_id": "svac_...",
"identity_token": {
"source": "file",
"path": "/var/run/secrets/anthropic.com/token"
}
},
"organization_id": "00000000-0000-0000-0000-000000000000",
"workspace_id": "wrkspc_...",
"base_url": "https://api.anthropic.com"
}
```
If `authentication.identity_token` is omitted, the SDK falls back to `ANTHROPIC_IDENTITY_TOKEN_FILE` or `ANTHROPIC_IDENTITY_TOKEN` from the environment.
## OAuth scopes
The `oauth_scope` you set on a federation rule determines which Claude API endpoints the minted access token can call.
| Scope | Grants access to |
|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `workspace:developer` | All non-administrative Claude API endpoints in the rule's workspace: [Messages](../Endpoints/messages.md) (including streaming and token counting), [Models](../Endpoints/models-list.md), [Managed Agents](managed-agents-overview.md) and their sessions, [Files](../Guides/build-with-claude-files.md), and [Skills](../Guides/build-with-claude-skills-guide.md). This matches the access a workspace API key in the same workspace has. |
| `workspace:inference` | The inference endpoints in the rule's workspace: [Messages](../Endpoints/messages.md) (including streaming and token counting), [Models](../Endpoints/models-list.md), and the [OpenAI-compatible chat endpoint](cli-sdks-libraries-libraries-openai-sdk.md). Use this for workloads that only need to call Claude and never need to manage Files, Skills, or other resources. |
| `workspace:manage_tunnels` | The [MCP tunnels API](../Agents-Tools/agents-and-tools-mcp-tunnels-reference.md#tunnels-api): create, list, and get tunnels, register and archive CA certificates, reveal and rotate the tunnel token, and archive tunnels. The Console's create-tunnel modal window locks this scope when you create a rule from it. |
| `org:admin` | Full access to the [Admin API](manage-claude-admin-api.md) (organization members, invites, workspaces, API keys, and the rest). An OAuth `org:admin` token can only create or modify rules scoped to `workspace:developer` or `workspace:inference`, and cannot update an issuer that backs a rule with any other scope; see the [constraints](manage-claude-wif-admin-api.md#permissions-and-constraints). |
A request to an endpoint outside the token's scope returns HTTP 403. Finer-grained scopes (per resource, or read versus write) are not currently available.
### Permission boundaries
A federation rule's `oauth_scope` is a ceiling: the minted token can never exceed it. The target service account's `organization_role` (`developer` or `admin`) determines which scopes are grantable, so a rule that grants `org:admin` must target a service account with `organization_role=admin`. Effective permissions are the intersection of the rule's scope and the service account's role.
| Rule `oauth_scope` | Service account `organization_role` | Effective permissions |
|:----------------------|:------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `workspace:developer` | `admin` | Claude API access in the rule's workspace only. The scope caps the token below the role. |
| `org:admin` | `admin` | Full Admin API access (organization members, invites, workspaces, API keys, and the rest), minus the OAuth-caller carve-outs; see [constraints](manage-claude-wif-admin-api.md#permissions-and-constraints). |
## Validation rules
Anthropic enforces these constraints when you create or update issuers and rules, and when verifying an incoming JWT at exchange time.
For complete parameter details and response schemas, see the [Service accounts API reference](../Admin/beta-organization-service-accounts.md), [Federation issuers API reference](../Admin/beta-organization-federation-issuers.md), and [Federation rules API reference](../Admin/beta-organization-federation-rules.md).
### Resource fields
| Field | Constraint |
|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Issuer, rule, and service account `name` | Must match `^[a-z0-9-]+$`, length 1 to 255 characters. |
| `workspace_id` | Required on create unless `applies_to_all_workspaces` is true. The workspace (`wrkspc_...`) whose quota, billing, and rate limits apply to tokens minted under this rule. Must be a workspace in the same organization, and the target service account must be a member of that workspace. |
| `applies_to_all_workspaces` | Boolean. Set `true` to enable the rule in every workspace in the organization instead of naming one; either this or `workspace_id` is required on create. |
| `token_lifetime_seconds` | Integer between `60` and `86400` (1 minute to 24 hours). Default `3600`. Values outside this range are rejected at request time. See [Token lifetime and refresh](manage-claude-workload-identity-federation.md#token-lifetime-and-refresh). |
### URL fields
The `issuer_url`, `jwks.discovery_base`, and `jwks.url` fields are validated:
| Constraint | Detail |
|:-----------|:-------------------------------------------------------------------------------------------------------------------------|
| Scheme | Must be `https`. |
| Port | Must be `443` (explicit or default). |
| Host | Must be a public DNS hostname for your OIDC provider. Must resolve to public IP addresses; IP literals are not accepted. |
URL validation failures return `400 invalid_request_error` with the field name as a prefix on the error message (for example, `issuer_url: url must use https scheme`).
URL constraints apply only to URLs that Anthropic dials. In `explicit_url` and `inline` JWKS modes, and in `discovery` mode when `jwks.discovery_base` is set, the `issuer_url` is compared against the JWT `iss` claim as a string and is never fetched, so it may reference an internal hostname or non-standard port.
### JWT verification
| Constraint | Detail |
|:------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Maximum size | The `assertion` JWT must be at most 16 KiB. |
| Signing algorithm | Only asymmetric algorithms (RSA and ECDSA families: ES256, ES384, ES512, RS256, RS384, RS512, PS256, PS384, PS512) are accepted. HMAC (`HS256`, `HS384`, `HS512`) and `none` are rejected. |
| Key ID | The JWT header must carry a `kid` that matches a key in the issuer's JWKS. Tokens without `kid` are rejected. |
| Required claims | `sub` must be present. `iat` must be present and not in the future. `exp` must be present and in the future. |
| Single use | An assertion that carries a `jti` claim can be exchanged only once per issuer: repeating an exchange with the same `jti` is rejected as a replay. The issuer's `check_jti` field (enabled by default) controls this check; assertions without a `jti` claim are not subject to it. See the [Federation issuers API reference](../Admin/beta-organization-federation-issuers.md). |
| Maximum lifetime | The token's lifetime (`exp` minus `iat`) must not exceed the issuer's configured maximum (1 hour by default, configurable for each issuer in the Claude Console). |
| Clock skew | A 30-second leeway is applied to `exp`, `nbf`, and `iat`. |
## Rule matching semantics
A federation rule's `match` block determines whether an incoming JWT is accepted. All populated fields are evaluated with AND semantics: the JWT must satisfy every populated matcher. At least one of `subject_prefix`, `claims`, or `condition` must be set; a `match` block that contains only `audience` (or no matchers at all) is rejected. This guards against rules that would accept every token from an issuer.
| Matcher | Type | Semantics |
|:-----------------|:----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `subject_prefix` | string | Exact match against the JWT `sub` claim. A trailing `*` makes it a prefix match (the `sub` value must begin with the characters before the `*`). Case-sensitive. |
| `audience` | string | The JWT `aud` claim must contain this exact string. When `aud` is an array, any element matching exactly satisfies the check. |
| `claims` | map\<string, string\> | Each key is a top-level claim name and each value is the required exact string value. For nested, numeric, boolean, or complex claims like lists and maps, use `condition` with a CEL expression instead. |
| `condition` | string (CEL) | A [CEL](https://cel.dev/) expression that must evaluate to `true`. |
### CEL evaluation environment
The `condition` expression has access to a single variable:
| Variable | Type | Contents |
|:---------|:-----|:------------------------------------------------------------------------------|
| `claims` | map | The full decoded JWT claim set. Nested objects are accessible as nested maps. |
Example:
``` block
claims.sub.startsWith("repo:acme-corp/") && claims.ref in ["refs/heads/main", "refs/heads/release"]
```
CEL conditions are security boundaries. An expression that evaluates to `true` for more inputs than intended grants broader access than intended. Prefer the static matchers when they express your constraint.
## Errors
### Token exchange errors
`POST /v1/oauth/token` returns errors in the standard [API error shape](../Endpoints/errors.md). The SDK wraps exchange failures in a typed `WorkloadIdentityError` that exposes the HTTP status, the response body, and the `request_id`.
| Status | Error | Cause | Resolution |
|:-------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 400 | `invalid_request_error` | `federation_rule_id` is malformed or a required request field is missing. | Verify the `fdrl_` ID and that the request body includes all required fields. |
| 400 | `invalid_request_error` | `workspace_id` is present but is not a well-formed `wrkspc_...` ID or the literal `default`. | Fix the `workspace_id` value; the response message names the expected format. |
| 401 | `authentication_error` | The JWT `iss` claim does not equal the registered `issuer_url` exactly. | Compare byte-for-byte, including trailing slashes and scheme: `jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson | .iss' <<< "$JWT"`. |
| 401 | `authentication_error` | JWKS fetch failed, JWKS is stale, or the JWT was signed with a key not in the JWKS. | For `inline` mode, update the issuer with the rotated keys. For `discovery` and `explicit_url`, confirm the JWKS endpoint is reachable on port 443; if the issuer recently rotated its signing key, see [Key rotation and caching](#key-rotation-and-caching). |
| 401 | `authentication_error` | The JWT `exp` claim is in the past (beyond the 30-second skew window). | Confirm your identity provider is projecting a fresh token and the SDK is re-reading the token file. |
| 401 | `authentication_error` | The JWT was verified but its claims do not satisfy the rule's `match` block. | Decode the JWT and compare each claim against the rule. `subject_prefix` is case-sensitive. `audience` requires an exact element match. |
| 401 | `authentication_error` | The `federation_rule_id` does not exist, is archived, or the JWT is not authorized for it (consolidated to prevent enumeration). | Confirm the rule ID in the Claude Console and that the rule has not been archived. |
| 401 | `authentication_error` | The federation rule is enabled for more than one workspace and the request omits `workspace_id`. The authentication history entry shows reason `workspace_id_required`. | Set `ANTHROPIC_WORKSPACE_ID` (or the `workspace_id` body field on a raw request) to the `wrkspc_...` ID you want the token scoped to. See [Token exchange request](#token-exchange-request). |
Every assertion denial returns the same opaque `401` `authentication_error` with the fixed message `Authentication failed`, regardless of which check failed; a distinguishable error would let a caller probe rule configuration. The deny reason is recorded on the attempt's entry in the [authentication history](https://platform.claude.com/settings/workload-identity-federation?tab=history), for example `match_subject_prefix` when the `sub` claim fails the rule's `subject_prefix`, or `workspace_id_required` when the rule spans multiple workspaces and the request names none. Requests rejected before the rule's organization is corroborated (the `400 invalid_request_error` family above) leave no history entry; their response messages name the problem directly. A `401` with no matching history entry usually means the `federation_rule_id` itself was not recognized.
### Common SDK-side failures
| Symptom | Cause | Resolution |
|:-------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------|
| SDK reports "no credentials" instead of exchanging | One of `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, or `ANTHROPIC_IDENTITY_TOKEN[_FILE]` is unset and no profile is active. | Set all four variables, or configure a profile. |
| SDK authenticates with an API key instead of federating | `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` is set and wins precedence. | Unset the key or token variable. |
| `FileNotFoundError` on first request | The path in `ANTHROPIC_IDENTITY_TOKEN_FILE` does not exist. The SDK opens the file lazily at exchange time. | Confirm the projected-token volume is mounted and the path matches. |
| Token exchange succeeds but a Claude API request returns 403 | The minted token's scope does not grant access to that endpoint. | Check the rule's `oauth_scope` against [OAuth scopes](#oauth-scopes). |
| Authentication fails with empty credential | A credential environment variable is exported but set to an empty string. Empty values still win their precedence slot. | Unset the variable with `unset VAR` rather than `VAR=""`. |
## Troubleshoot a failed exchange
A `401` `authentication_error` response is intentionally opaque and its message is always `Authentication failed`; the deny reason is recorded in the authentication history, not in the response.
Start with the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history) in the Claude Console. Recent exchange attempts surface the issuer and rule that were evaluated, the JWT claims that were inspected, and which validation step failed, which usually short-circuits the following checks.
One common opaque failure is a replayed assertion: an assertion that carries a `jti` claim can be [exchanged only once](#jwt-verification), so a workload that re-sends the same JWT (a retry loop, or a refresh that re-reads an unrotated token) is rejected on the second exchange. The authentication history page shows these attempts with the reason `jti_reused`; the fix is to mint a fresh assertion for each exchange.
If you still need to debug from the JWT itself, work through these checks in order:
1. 1
### Decode the JWT
Decode the assertion you sent so you can compare each claim against your issuer and rule configuration:
cURL
``` shiki
jq -rR 'split(".")[1] | gsub("-";"+") | gsub("_";"/") | @base64d | fromjson' <<< "$JWT"
```
2. 2
### Check iss matches the issuer
The decoded `iss` claim must equal the registered `issuer_url` byte for byte, including scheme, port, and any trailing slash. A mismatch on a single character fails verification.
3. 3
### Check aud matches the rule
The decoded `aud` claim must contain the rule's `audience` value as an exact match. When `aud` is an array, one element must match exactly.
4. 4
### Check sub and each claims entry
Compare `sub` against the rule's `subject_prefix` (case-sensitive; a trailing `*` is a prefix match, anything else is exact). Compare every key in the rule's `claims` map against the same-named top-level claim.
5. 5
### Check exp, nbf, and iat
`exp` must be in the future and `nbf`/`iat` must be in the past, within the 30-second skew window. If the workload host's clock has drifted, an otherwise valid token is rejected.
6. 6
### Check JWKS reachability
For `discovery` mode, fetch `<jwks.discovery_base or issuer_url>/.well-known/openid-configuration` over public HTTPS on port 443 and confirm `jwks_uri` resolves. For `explicit_url`, fetch the JWKS URL directly. For `inline`, confirm the issuer's signing key has not rotated since you registered the keys.
If the issuer rotated its signing key and immediately started signing with it, exchanges can fail for up to a minute while Anthropic's JWKS cache refreshes. See [Key rotation and caching](#key-rotation-and-caching).
## JWKS source modes
When you register a federation issuer, the `jwks` field controls how Anthropic obtains the public keys used to verify JWT signatures from that issuer. It is a discriminated union keyed on `type`:
| `jwks.type` | `jwks` shape | Behavior | Use when |
|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `discovery` (default) | `{ "type": "discovery", "discovery_base": "https://..." }` (`discovery_base` is optional; set it when the discovery URL differs from `issuer_url`) | Anthropic fetches `<discovery_base or issuer_url>/.well-known/openid-configuration`, reads `jwks_uri` from the discovery document, and fetches the JWKS from there. | Your IdP serves a standard OIDC discovery document on the public internet. Most managed providers (EKS, GKE, Cloud Run, GitHub Actions, Entra ID) support this. |
| `explicit_url` | `{ "type": "explicit_url", "url": "https://..." }` | Anthropic fetches the JWKS directly from `url`. The `issuer_url` is used only for string comparison against the JWT `iss` claim and is never dialed. | Your IdP does not serve a discovery document, or discovery is internal-only but the JWKS is publicly reachable. |
| `inline` | `{ "type": "inline", "keys": [...] }` | You supply the array of JWK objects inline (the `keys` array from the JWKS document, not the wrapper object). Anthropic makes no outbound request. The `issuer_url` is used only for `iss` comparison. | Air-gapped environments, self-managed Kubernetes clusters with cluster-internal issuer URLs, or when you want explicit control over key rotation. |
The discriminated union makes the companion fields mutually exclusive by construction. Both `discovery` and `explicit_url` also accept an optional `ca_cert_pem` string for issuers that serve TLS from a private CA.
### Key rotation and caching
In `discovery` and `explicit_url` modes, Anthropic caches the fetched JWKS. If your identity provider publishes a new signing key and immediately starts signing tokens with it, exchanges that present those tokens may fail with a signature error for up to 1 minute while the cache refreshes.
To avoid this window, publish a new signing key in the JWKS at least 15 minutes before your identity provider starts signing tokens with it, and keep the superseded key in the JWKS until tokens it signed have expired. Managed identity providers typically follow this discipline on their own. If you operate your own issuer (a self-managed Kubernetes cluster, a SPIRE OIDC discovery provider, or an Okta custom authorization server with a configured rotation cadence), confirm that your rotation policy publishes new keys ahead of first use.
In `inline` mode there is no automatic key refresh. When your identity provider rotates its signing keys, you must update the issuer configuration with the new JWKS or all token exchanges will fail signature verification.
---
## Workload Identity Federation - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/workload-identity-federation
Path: 04-API-Reference/Other/manage-claude-workload-identity-federation.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fworkload-identity-federation)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Authentication
# Workload Identity Federation
Copy page
Authenticate workloads to the Claude API with short-lived identity tokens from your own identity provider instead of long-lived static API keys.
Copy page
Workload Identity Federation (WIF) lets your workloads authenticate to the Claude API with short-lived OpenID Connect (OIDC) tokens instead of long-lived `sk-ant-...` API keys. The tokens come from an identity provider (IdP) you already operate: AWS IAM, Google Cloud, or any standards-compliant OIDC issuer such as GitHub Actions, Kubernetes, SPIFFE, Microsoft Entra ID, or Okta.
Your workload presents a signed JWT from your identity provider. Anthropic validates it against trust rules you configure in the Claude Console and returns a short-lived Anthropic access token bound to a service account in your organization. There are no static secrets to mint, store in CI, rotate, or leak.
Workload Identity Federation strengthens your security posture by replacing static API keys with tokens that expire in minutes rather than never. It is not a complete security story on its own: federated authentication is only as strong as the upstream identity provider that signs the JWT. Pair Workload Identity Federation with the controls your IdP already supports (workload identity binding, conditional access, audit logging) for defense in depth.
## Concepts
You configure three resources in the Claude Console before any workload can federate. Together they express "tokens signed by issuer X, with claims that look like Y, may act as service account Z."
### Service accounts
A **service account** (`svac_...`) is a named, non-human identity inside your Anthropic organization. It is the principal that a [service account key](manage-claude-authentication.md#key-types) or a federated token acts as. Service accounts live at the organization level and become active in a workspace when you add them as members of that workspace. At exchange time, Anthropic checks that the federation rule's workspace matches one of the service account's workspace memberships; the minted token then follows that workspace's rate limits and usage attribution, the same as an API key. Unlike a human user, a service account has no email, no password, and no Console login. Every service account is implicitly a member of your organization's default workspace; add explicit memberships for any other workspace it should act in. To let an all-workspaces service account key act in a workspace, add the service account to that workspace.
The key distinction versus a workspace API key: a workspace API key *is* a credential, while a service account *has* credentials. You can more easily audit which workloads acted as which service account.
### Federation issuers
A **federation issuer** (`fdis_...`) registers an OIDC identity provider with your organization. Registering an issuer tells Anthropic "JWTs signed by this provider may assert workload identity for my org."
An issuer has two pieces of configuration:
- **Issuer URL:** The exact `iss` claim value that appears in the provider's JWTs, for example `https://token.actions.githubusercontent.com` or `https://oidc.eks.us-west-2.amazonaws.com/id/EXAMPLE`.
- **JWKS source:** How Anthropic fetches the public keys to verify JWT signatures. Use `discovery` (the default) for any provider that serves `/.well-known/openid-configuration` at its issuer URL. Use `explicit_url` to point at a JWKS endpoint directly, or `inline` to upload the key set for issuers that are not reachable from the public internet (for example, a private Kubernetes cluster).
Issuer and JWKS URLs must be `https`, on port 443, and use a public DNS hostname that resolves to public IP addresses; IP literals are not accepted. These constraints apply only to URLs Anthropic fetches; in `explicit_url` and `inline` modes the `issuer_url` is compared as a string and may reference an internal hostname.
You typically register one issuer per environment: your production EKS cluster, your staging cluster, and GitHub Actions are three separate issuers.
### Federation rules
A **federation rule** (`fdrl_...`) is the bridge between an issuer and a service account: "when a JWT from issuer X has claims that look like Y, mint a token for service account Z with scope S."
A rule defines match conditions, a target, and the authorization scope and token lifetime that apply when the rule matches:
- **Match:** The conditions an incoming JWT must satisfy. You can match on a `subject_prefix` (for example, `system:serviceaccount:prod:worker`, or with a trailing `*` for a prefix match), an exact `audience`, a map of exact claim values, a [CEL](https://cel.dev/) `condition` expression for complex logic, or any combination. At least one of `subject_prefix`, `claims`, or `condition` must be set, and all configured matchers must pass for the JWT to be accepted.
- **Target:** The service account the matched JWT maps to.
- **Authorization:** The OAuth `scope` granted on the minted token. The default is `workspace:developer`, which grants the same access as a workspace API key. Some products lock the scope when you create a rule from their flow; for example, the [MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md) create-tunnel modal creates rules scoped to `workspace:manage_tunnels`. See [OAuth scopes](manage-claude-wif-reference.md#oauth-scopes). The rule also sets `token_lifetime_seconds` (60 to 86400, default 3600).
A single issuer can have many rules: one per team, namespace, or permission level. Rules are evaluated by ID: the client specifies which rule to use in the exchange request, and Anthropic verifies the JWT satisfies that rule's match criteria. There is no implicit rule search.
## How it works
1. **Your IdP issues a JWT to the workload.** On most platforms this is ambient: a Kubernetes projected service-account token, the Google Cloud metadata server, Azure IMDS, or the GitHub Actions OIDC endpoint. The JWT's `iss` claim identifies the provider, and its `sub` and other claims identify the specific workload.
2. **The SDK exchanges the JWT for an Anthropic access token.** The SDK posts the JWT to `POST /v1/oauth/token` using the [RFC 7523](https://www.rfc-editor.org/rfc/rfc7523) `jwt-bearer` grant. Anthropic verifies the JWT against the issuer's JWKS and the federation rule's match conditions, then returns a short-lived `sk-ant-oat01-...` token that acts on behalf of the rule's target service account.
3. **The SDK sends the token on every request and refreshes it before it expires.** Your application code constructs the client with no API key and calls the API as usual. The SDK re-runs the exchange before the token expires.
## Set up federation
You need the admin, owner, or primary owner role in your Anthropic organization, an OIDC-capable identity provider with a reachable JWKS endpoint (or a JWKS document you can paste, for air-gapped clusters), and a workload that can obtain an identity token from that provider.
The **Connect workload** wizard creates all three resources (the issuer, the service account, and the federation rule) in one guided flow, then verifies the connection end to end.
1. 1
### Open Connect workload
In the Claude Console, go to **Settings → Workload identity** and select **Connect workload**.
2. 2
### Choose your provider
Select the tile for your identity provider: GitHub Actions, AWS, Google Cloud, Microsoft Entra ID, or Kubernetes. Each tile prefills the issuer URL pattern and the match fields that provider's JWTs support. For any other standards-compliant provider (such as SPIFFE or Okta), select **Custom OIDC**.
3. 3
### Fill in the guided fields
The wizard walks you through the provider-specific fields: the issuer configuration, the match conditions for incoming JWTs, and names for the service account and federation rule it creates. The wizard prefills `oauth_scope=workspace:developer` and `token_lifetime_seconds=600` (the API default when `token_lifetime_seconds` is omitted is 3600); adjust these if your workload needs a different scope or lifetime.
4. 4
### Verify the issuer
Optionally select **Verify issuer** to dry-run the issuer configuration before anything is created. Verification confirms Anthropic can fetch and parse the JWKS from the URLs you entered, which catches reachability and configuration mistakes early.
5. 5
### Test the connection
The wizard creates the issuer, service account, and federation rule, then listens for a successful token exchange for 15 minutes. Trigger an exchange from your workload within that window (see [Authenticate from your workload](#authenticate-from-your-workload)) to confirm the setup works. If the window elapses, the resources persist; you can re-run the test from the federation rule's detail page. Note the rule's ID (`fdrl_...`) and the service account ID (`svac_...`) the wizard creates: your workload passes both, along with your organization ID (and your workspace ID when the rule covers more than one workspace), in every token-exchange request.
To manage these resources programmatically, see [Manage WIF with the Admin API](manage-claude-wif-admin-api.md) for the curl walkthrough, or see the [Service accounts API reference](../Admin/beta-organization-service-accounts.md), [Federation issuers API reference](../Admin/beta-organization-federation-issuers.md), and [Federation rules API reference](../Admin/beta-organization-federation-rules.md) for complete parameter details and response schemas.
## Authenticate from your workload
With federation configured, your workload exchanges its IdP-issued JWT for an Anthropic token at runtime. The SDK handles the exchange and refresh loop for you. The cURL tab shows the underlying HTTP exchange for shell scripts, debugging, or languages without SDK support.
### Construct the SDK client
You can construct the client with explicit credentials or with no arguments. With no arguments, the SDK resolves credentials from environment variables or the active profile, as described under [Credential precedence](#credential-precedence). The zero-argument form is the recommended pattern for production workloads: ship the same container image everywhere and inject `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_SERVICE_ACCOUNT_ID`, `ANTHROPIC_WORKSPACE_ID`, and `ANTHROPIC_IDENTITY_TOKEN_FILE` per environment.
cURL
Python
TypeScript
Go
Java
C#
PHP
Ruby
```python
from anthropic import Anthropic, WorkloadIdentityCredentials, IdentityTokenFile
client = Anthropic(
credentials=WorkloadIdentityCredentials(
identity_token_provider=IdentityTokenFile(
"/var/run/secrets/anthropic.com/token"
),
federation_rule_id="fdrl_...",
organization_id="00000000-0000-0000-0000-000000000000",
service_account_id="svac_...",
workspace_id="wrkspc_...",
),
)
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
print(next(block.text for block in message.content if block.type == "text"))
```
The token-exchange response follows [RFC 6749 §5.1](https://www.rfc-editor.org/rfc/rfc6749#section-5.1). See [Token exchange response](manage-claude-wif-reference.md#token-exchange-response) for the field reference.
## Credential precedence
Every SDK resolves credentials in the same five-tier order: constructor arguments, then `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN`, then an explicit `ANTHROPIC_PROFILE`, then the federation environment variables, then the implicit active profile. The first source that yields a credential wins.
`ANTHROPIC_API_KEY` sits above the federation tiers, so a leftover key in the environment silently shadows federation. When migrating a workload from API keys to Workload Identity Federation, confirm `ANTHROPIC_API_KEY` is unset everywhere that workload runs (container env, CI secrets, shell profiles). The CLI's [`ant auth status`](cli-sdks-libraries-cli-authentication.md#check-authentication-status) command reports which source won.
For the full precedence table, the per-tier semantics, and the profile file schema, see [Credential precedence in the WIF reference](manage-claude-wif-reference.md#credential-precedence).
## Migrate from API keys
To switch an existing workload from a static API key to federation without downtime:
1. **Configure federation in parallel.** Complete the [setup walkthrough](#set-up-federation) and confirm the federation rule matches your workload's token. Leave the existing `ANTHROPIC_API_KEY` in place for now.
2. **Smoke-test which credential wins.** Run `ant auth status` from inside the workload (or inspect SDK debug logs). Because `ANTHROPIC_API_KEY` sits above the federation tiers in the precedence chain, the API key still wins at this stage.
3. **Unset `ANTHROPIC_API_KEY` everywhere it is injected.** Remove it from CI secrets, container environment, and shell profiles (see the preceding warning). Re-run `ant auth status` and confirm the federation source is now selected.
4. **Delete the API key.** Once the workload is running on the federated token, delete the key in the Claude Console under **Settings → API keys**.
## Token lifetime and refresh
The minted Anthropic token's lifetime is the lesser of (a) the rule's `token_lifetime_seconds` (default 3,600 seconds) and (b) twice the remaining lifetime of the IdP JWT you presented. The result is never less than 60 seconds. The second bound prevents an Anthropic token from outliving the upstream identity it was derived from by more than a small margin.
The SDK caches the token and refreshes it on a two-tier schedule modeled on `botocore`:
- **Advisory refresh** at expiry minus 120 seconds. The SDK attempts a new exchange. If the token endpoint is unreachable, the SDK continues serving the cached token, which is still valid for roughly 90 more seconds.
- **Mandatory refresh** at expiry minus 30 seconds. A failed exchange at this point raises an error. The cached token is too close to expiry to be safe.
Because the SDK re-reads `ANTHROPIC_IDENTITY_TOKEN_FILE` on every exchange, it transparently picks up rotated projected tokens (Kubernetes service-account tokens, for example, rotate well before their `exp`).
By default, identity tokens that carry a `jti` claim are single-use: each exchange must present a JWT that has not been exchanged before, and re-presenting one fails with the reason `jti_reused` on the [authentication history page](https://platform.claude.com/settings/workload-identity-federation?tab=history). If your workload fetches its own tokens from your identity provider, mint a fresh JWT for each exchange instead of reusing a cached one (retry loops are the common culprit). The same applies to a token read from `ANTHROPIC_IDENTITY_TOKEN_FILE`: the SDK re-reads the file on every exchange, so the file must hold a new token before each refresh. A refresh that re-reads an unrotated token, or a restarted process that re-presents a token it already exchanged, is rejected the same way. Rotating the token well within the minted token's lifetime keeps the file ahead of the refresh schedule; if your token source cannot rotate that often, you can disable `check_jti` for that issuer as a last resort (this removes replay protection for every rule on the issuer). See [JWT verification](manage-claude-wif-reference.md#jwt-verification) for details.
## Identity providers
Each guide covers where the JWT comes from on that platform, what its claims look like, and the issuer and rule configuration to register.
[AWS](manage-claude-wif-providers-aws.md)
STS web identity tokens, or EKS IRSA projected tokens.
[Google Cloud](manage-claude-wif-providers-gcp.md)
Google-signed identity tokens from the metadata server.
[Microsoft Entra ID](manage-claude-wif-providers-azure.md)
Managed Identity (IMDS) and Entra Workload ID on AKS.
Keyless CI authentication with the Actions OIDC token.
[Kubernetes](manage-claude-wif-providers-kubernetes.md)
Self-managed and on-premises clusters using projected service-account tokens.
[SPIFFE](manage-claude-wif-providers-spiffe.md)
Workloads with SPIFFE JWT-SVIDs from SPIRE or another conformant issuer.
[Okta](manage-claude-wif-providers-okta.md)
Okta service applications using client-credentials flow.
## See also
- [Manage WIF with the Admin API](manage-claude-wif-admin-api.md): create issuers, service accounts, and rules from infrastructure as code
- [WIF reference](manage-claude-wif-reference.md): environment variables, profile file schema, validation rules, and error codes
- [Authentication](manage-claude-authentication.md): all the SDK's authentication options
- [Admin API reference](../Admin/http-beta-organization.md): generated request and response schemas for every Admin API endpoint
---
## Workspaces - Claude Platform Docs
Source: https://platform.claude.com/docs/en/manage-claude/workspaces
Path: 04-API-Reference/Other/manage-claude-workspaces.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanage-claude%2Fworkspaces)
SearchCtrlK
Organization
[Admin API](manage-claude-admin-api.md)[User management](manage-claude-user-management.md)[Plugins API](manage-claude-plugins-api.md)[Workspaces](manage-claude-workspaces.md)
Authentication
[Overview](manage-claude-authentication.md)[Create an Admin API key](manage-claude-admin-api-keys.md)[App Attest](manage-claude-app-attest.md)[Workload Identity Federation](manage-claude-workload-identity-federation.md)[Manage WIF via API](manage-claude-wif-admin-api.md)[WIF reference](manage-claude-wif-reference.md)
Identity providers
Monitoring
[Usage and Cost API](manage-claude-usage-cost-api.md)[Rate Limits API](manage-claude-rate-limits-api.md)[Analytics APIs](manage-claude-analytics-api.md)[Claude Code Analytics API](manage-claude-claude-code-analytics-api.md)[Spend Limits API](manage-claude-spend-limits-api.md)
Data & compliance
[Data residency](manage-claude-data-residency.md)[API and data retention](../Guides/build-with-claude-api-and-data-retention.md)
[Access Transparency](manage-claude-access-transparency.md)
[Encryption keys (CMEK)](manage-claude-cmek.md)
[Inference hooks](manage-claude-inference-hooks.md)
Compliance API
[Overview](manage-claude-compliance-api.md)[Set up the Compliance API](manage-claude-compliance-api-access.md)[Activity Feed](manage-claude-compliance-activity-feed.md)[Chats, files, and projects](manage-claude-compliance-content-data.md)[Session transcripts](manage-claude-compliance-sessions.md)[Organizations, users, roles, groups, and settings](manage-claude-compliance-org-data.md)[Design your integration](manage-claude-compliance-integration-patterns.md)[Errors](manage-claude-compliance-errors.md)[FAQ](manage-claude-compliance-faq.md)
[Console](usage-limits.md)
[Admin](manage-claude-admin-api.md)Organization
# Workspaces
Copy page
Organize API keys, manage team access, and control costs with workspaces.
Copy page
Workspaces provide a way to organize your API usage within an organization. Use workspaces to separate different projects, environments, or teams while maintaining centralized billing and administration.
## How workspaces work
Every organization has a **Default Workspace** that cannot be renamed, archived, or deleted. When you create additional workspaces, you can assign members, service accounts, API keys, and resource limits to each one.
Key characteristics:
- **Workspace identifiers** use the `wrkspc_` prefix (for example, `wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ`)
- **Maximum 100 workspaces** per organization by default (archived workspaces don't count); contact your account team if you need more
- **Default Workspace** has a `wrkspc_` ID like any other workspace (returned in the [`anthropic-workspace-id` response header](#identify-the-workspace-behind-an-api-response) and accepted by [Get Workspace](../Admin/beta-organization-workspaces-retrieve.md)), but it appears in [List Workspaces](../Admin/beta-organization-workspaces-list.md) results only when you pass `include_default=true`, and API keys, usage reports, and cost reports show `null` for its `workspace_id`, as do all-workspaces API keys (an API key's `scope` field tells them apart; for a key bound to the Default Workspace it carries the real ID)
- **API keys** can be scoped to a single workspace. In this case, they can only access resources within that workspace. Some API keys can be granted permissions across multiple workspaces, and provide a [workspace ID header](manage-claude-authentication.md#select-a-workspace) to access resources within that workspace
### Claude Code workspace
When a member of your organization first signs in to [Claude Code](../../02-Claude-Code-CLI/code-home.md) with their Claude Console account, Anthropic automatically creates a **Claude Code** workspace in the organization and adds that member to it. Every subsequent member who signs in to Claude Code is added the same way.
The Claude Code workspace keeps Claude Code traffic separate from your other API workloads:
- Claude Code mints a per-user API key in this workspace at sign-in. You cannot create keys in it manually from the Console.
- A Claude Code key stops working if its owner is removed from the workspace or organization, unlike a workspace key.
- Claude Code usage is rate-limited separately, and admins can cap its share of the organization's limits under [Settings \> Workspaces](playground.md).
- It is the only workspace that supports per-user monthly spend limits.
Archiving the Claude Code workspace disables Claude Code sign-in through Console billing for the whole organization.
## Workspace roles and permissions
Members can have different roles in each workspace, allowing fine-grained access control.
| Role | Permissions |
|-----------------------------|-------------------------------------------------------------------------------------------------|
| Workspace User | Use playground only |
| Workspace Limited Developer | Create and manage API keys, use the API. Cannot access session tracing views or download files. |
| Workspace Developer | Create and manage API keys, use the API |
| Workspace Admin | Full control over workspace settings and members |
| Workspace Billing | View workspace billing information (inherited from organization billing role) |
### Role inheritance
- **Organization admins** automatically receive Workspace Admin access to all workspaces
- **Organization billing members** automatically receive Workspace Billing access to all workspaces
- **Organization users and developers** must be explicitly added to each workspace
- **Service accounts** are added to workspaces in [Settings \> Service accounts](https://platform.claude.com/settings/service-accounts): select **Add to workspace** in the account's menu or on its own page. To see the accounts in one workspace, filter the list by workspace.
The Workspace Billing role cannot be manually assigned. It's inherited from having the organization billing role.
## Managing workspaces
Only organization admins can create workspaces. Organization users and developers must be added to workspaces by an admin.
### Using the Console
Create and manage workspaces in the [Claude Console](playground.md).
#### Create a workspace
1. 1
### Open workspace settings
In the Claude Console, go to **Settings \> Workspaces**.
2. 2
### Create a workspace
Click **Create workspace**.
3. 3
### Configure the workspace
Enter a workspace name and select a color for visual identification.
4. 4
### Create the workspace
Click **Create** to finalize.
To switch between workspaces in the Console, use the **Workspaces** selector in the top-left corner.
#### Edit workspace details
To modify a workspace's name or color:
1. Select the workspace from the list.
2. Click the ellipsis menu (**...**) and choose **Edit details**.
3. Update the name or color and save your changes.
The Default Workspace cannot be renamed or deleted.
#### Add members to a workspace
1. Navigate to the workspace's **Members** tab.
2. Click **Add to Workspace**.
3. Select an organization member and assign them a [workspace role](#workspace-roles-and-permissions).
4. Confirm the addition.
To remove a member, click the trash icon next to their name.
Organization admins and billing members cannot be removed from workspaces while they hold those organization roles.
#### Set workspace limits
Each workspace's settings split these across two tabs:
- **Rate limits:** On the **Rate limits** tab, set limits per model tier for requests per minute, input tokens, or output tokens
- **Spend limits:** On the **Spend limits** tab, cap monthly spending and configure alerts when spending reaches certain thresholds
#### Archive a workspace
To archive a workspace, click the ellipsis menu (**...**) and select **Archive**. Archiving:
- Preserves historical data for reporting
- Deactivates the workspace and archives every API key created for it
- Cannot be undone
Archiving a workspace archives every API key created for that workspace within seconds (they remain listed in the Admin API as archived), and multi-workspace keys can no longer act in it. This action cannot be undone. If you archive the [Claude Code workspace](#claude-code-workspace), members of your organization can no longer sign in to Claude Code through Console billing.
### Using the Admin API
Programmatically manage workspaces using the [Admin API](manage-claude-admin-api.md).
Admin API endpoints accept an [Admin API key](manage-claude-admin-api-keys.md), an `org:admin` OAuth token, or a personal or service account key that isn't scoped to a specific workspace. Workspace keys don't work there. See [Authentication](manage-claude-admin-api.md#authentication).
The following SDK and CLI examples construct the default client, which reads the Admin API key from the `ANTHROPIC_API_KEY` environment variable; the SDKs expose these endpoints under `client.beta.organization.workspaces`. SDK list methods fetch further pages on demand, so `limit` sets the page size; the PHP, Ruby, and curl examples return one page.
Create a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
workspace = client.beta.organization.workspaces.create(name="Production")
print(f"id: {workspace.id}")
print(f"name: {workspace.name}")
```
List workspaces:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
workspaces = client.beta.organization.workspaces.list(limit=10, include_archived=False)
for workspace in workspaces:
print(f"{workspace.id}: {workspace.name}")
```
Archive a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
workspace = client.beta.organization.workspaces.archive(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ"
)
print(f"id: {workspace.id}")
print(f"archived_at: {workspace.archived_at}")
```
For complete parameter details and response schemas, see the [Workspaces API reference](../Admin/beta-organization-workspaces-retrieve.md).
### Managing workspace members
Add a member to a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.add(
"wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
user_id="user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_role="workspace_developer",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
```
Update a member's role:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
member = client.beta.organization.workspaces.members.update(
"user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
workspace_role="workspace_admin",
)
print(f"user_id: {member.user_id}")
print(f"workspace_role: {member.workspace_role}")
```
Remove a member from a workspace:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
removed_member = client.beta.organization.workspaces.members.remove(
"user_01XyDMpzjS89pFZXqSFUBDr6",
workspace_id="wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ",
)
print(f"user_id: {removed_member.user_id}")
```
For complete parameter details, see the [Workspace Members API reference](../Admin/beta-organization-workspaces-members-retrieve.md).
## API keys and resource scoping
Every request runs in exactly one workspace and can only access resources within that workspace. Which workspace depends on the [key type](manage-claude-authentication.md#key-types):
- A **workspace key** (a legacy key without an owner) belongs to the workspace it was created in and always runs there.
- A **personal key** or **service account key** acts as its user or service account. A single-workspace key always runs in the workspace chosen when it was created. A multi-workspace key runs in the workspace named by each request's `anthropic-workspace-id` header. Accounts must have access to the workspace to use it.
Resources scoped to workspaces include:
- **Files** created through the [Files API](../Guides/build-with-claude-files.md)
- **Message Batches** created through the [Batch API](../Guides/build-with-claude-batch-processing.md)
- **Skills** created through the [Skills API](../Guides/build-with-claude-skills-guide.md)
Some resources are managed differently:
- **[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)** are managed with a `workspace:manage_tunnels` OAuth token obtained through [Workload Identity Federation](manage-claude-workload-identity-federation.md), not an API key. Tunnels are created in a workspace, and the Console **MCP tunnels** list and the Managed Agent server picker show tunnels in the current workspace only; the cap of 10 active tunnels applies organization-wide. Tunnel management requires a role with tunnel management permissions; organization developers can view but not change them.
- **Workspaces** themselves and **organization members** are managed at the organization level through the [Admin API](manage-claude-admin-api.md), using an Admin API key, an `org:admin` OAuth token, or a personal or service account key that isn't scoped to a specific workspace.
To look up your organization's workspace IDs, call the [List Workspaces](../Admin/beta-organization-workspaces-list.md) endpoint (pass `include_default=true` to include the Default Workspace) or find them in the [Claude Console](playground.md).
[Prompt caches](../Guides/build-with-claude-prompt-caching.md) are also isolated per workspace on the Claude API, [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md), and [Microsoft Foundry](../Guides/build-with-claude-claude-in-microsoft-foundry.md). On Amazon Bedrock and Google Cloud, prompt caches are isolated per organization.
## Identify the workspace behind an API response
Claude API responses include an `anthropic-workspace-id` header alongside the `request-id` and `anthropic-organization-id` [response headers](../Endpoints/overview.md#response-headers). Its value is the `wrkspc_`-prefixed ID of the workspace that the request's API key or access token resolved to, including when that workspace is the Default Workspace. For example, a successful response includes headers like these:
```python
HTTP/1.1 200 OK
request-id: req_018EeWyXxfu5pfWkrYcMdjWG
anthropic-organization-id: 0d0e7a3b-52f1-4c7e-9a51-3f6f2f7c1b9e
anthropic-workspace-id: wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
```
The header is absent when the credential doesn't resolve to a workspace (for example, on Admin API requests) or when the request fails before authentication completes, such as a 401 error.
The following examples send a Messages API request and print the workspace ID from the response headers:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client = anthropic.Anthropic()
response = client.messages.with_raw_response.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello, Claude"}],
)
workspace_id = response.headers.get("anthropic-workspace-id")
print(f"Workspace ID: {workspace_id}")
```
Output
``` block
Workspace ID: wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ
```
The same accessors read the header from other Claude API endpoints too, including the [Claude Managed Agents](managed-agents-overview.md) APIs. For example, read `anthropic-workspace-id` from the response that [creates a session](managed-agents-sessions.md) to record which workspace the session belongs to.
With the workspace ID from a response, you can:
- Confirm which workspace's usage, cost, and [rate limits](../Endpoints/rate-limits.md) the request counted toward
- Match it against the `workspace_id` field in [Usage and Cost API](manage-claude-usage-cost-api.md) reports and on [Admin API](manage-claude-admin-api.md) objects such as API keys (both report `null` for the Default Workspace, as API keys also do for all-workspaces keys; an API key's `scope` field tells the two apart and, for a key bound to one workspace, carries that workspace's real ID)
- Check whether it's your Default Workspace's ID by passing it to [Get Workspace](../Admin/beta-organization-workspaces-retrieve.md) with an [Admin API key](manage-claude-admin-api-keys.md): the Default Workspace comes back with `"name": "Default"`, even though [List Workspaces](../Admin/beta-organization-workspaces-list.md) omits it unless you pass `include_default=true`
- Open that workspace in the [Console](playground.md) to find the request's resources, such as sessions, files, message batches, and skills
## Workspace limits
You can set custom spend and rate limits for each workspace to protect against overuse and ensure fair resource distribution.
### Setting workspace limits
You can set workspace limits lower than (but not higher than) your organization's limits:
- **Spend limits:** Cap monthly spending for a workspace. Set these on the workspace's **Spend limits** settings tab in the [Claude Console](playground.md).
- **Rate limits:** Limit requests per minute, input tokens per minute, or output tokens per minute. Set these on the workspace's **Rate limits** settings tab in the [Claude Console](playground.md).
- You cannot set limits on the Default Workspace
- If not set, workspace limits match the organization's limits
- Organization-wide limits always apply, even if workspace limits add up to more
For detailed information on rate limits and how they work, see [Rate limits](../Endpoints/rate-limits.md). You can also read your current organization and workspace rate limits programmatically with the [Rate Limits API](manage-claude-rate-limits-api.md).
## Usage and cost tracking
Track usage and costs by workspace using the [Usage and Cost API](manage-claude-usage-cost-api.md):
cURL
```python
curl "https://api.anthropic.com/v1/organizations/usage_report/messages?\
starting_at=2025-01-01T00:00:00Z&\
ending_at=2025-01-08T00:00:00Z&\
workspace_ids[]=wrkspc_01JwQvzr7rXLA5AGx3HKfFUJ&\
group_by[]=workspace_id&\
bucket_width=1d" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY"
```
Usage and costs attributed to the Default Workspace have a `null` value for `workspace_id`.
## Common use cases
### Environment separation
Create separate workspaces for development, staging, and production:
| Workspace | Purpose |
|-------------|----------------------------------------------------|
| Development | Testing and experimentation with lower rate limits |
| Staging | Pre-production testing with production-like limits |
| Production | Live traffic with full rate limits and monitoring |
### Team or department isolation
Assign workspaces to different teams for cost allocation and access control:
- **Engineering team** with developer access
- **Data science team** with their own API keys
- **Support team** with limited access for customer tools
### Project-based organization
Create workspaces for specific projects or products to track usage and costs separately.
## Best practices
1. 1
### Plan your workspace structure
Consider how you'll organize workspaces before creating them. Think about billing, access control, and usage tracking needs.
2. 2
### Use meaningful names
Name workspaces clearly to indicate their purpose (for example, "Production - Customer Chatbot" or "Dev - Internal Tools").
3. 3
### Set appropriate limits
Configure spend and rate limits to prevent unexpected costs and ensure fair resource distribution.
4. 4
### Audit access regularly
Review workspace membership periodically to ensure only appropriate users have access.
5. 5
### Monitor usage
Use the [Usage and Cost API](manage-claude-usage-cost-api.md) to track workspace-level consumption.
## FAQ
### What's the Default Workspace?
Every organization has a "Default Workspace" that cannot be renamed, archived, or deleted. Like every workspace, it has a `wrkspc_` ID: the API returns it in the [`anthropic-workspace-id` response header](#identify-the-workspace-behind-an-api-response), and you can pass it to [Get Workspace](../Admin/beta-organization-workspaces-retrieve.md) and [Update Workspace](../Admin/beta-organization-workspaces-update.md). It has no member list of its own, because access to it follows each member's organization role. It appears in [List Workspaces](../Admin/beta-organization-workspaces-list.md) results only when you pass `include_default=true`, and API keys, usage reports, and cost reports that belong to it show `null` for `workspace_id`, as do all-workspaces API keys; an API key's `scope` field tells the two apart and, for a key that belongs to the Default Workspace, carries its real ID.
### What's the Claude Code workspace?
Anthropic creates the Claude Code workspace automatically the first time a member of your organization signs in to Claude Code with their Console account. It isolates Claude Code's API keys, usage, and rate limits from your other workloads. See [Claude Code workspace](#claude-code-workspace) for details.
### Are there limits on workspaces?
Yes. Each organization can have up to 100 workspaces by default, and archived workspaces don't count toward this limit. If you need more, contact your account team.
### How do organization roles affect workspace access?
Organization admins automatically get the Workspace Admin role in all workspaces. Organization billing members automatically get the Workspace Billing role. Organization users and developers must be manually added to each workspace.
### Which roles can be assigned in workspaces?
Organization users and developers can be assigned Workspace Admin, Workspace Developer, Workspace Limited Developer, or Workspace User roles. The Workspace Billing role cannot be manually assigned; it's inherited from having the organization `billing` role.
### Can organization admin or billing members' workspace roles be changed?
Organization admins and billing members cannot have their workspace roles changed or be removed from workspaces while they hold those organization roles (with one exception: billing members can be upgraded to a Workspace Admin role). For everyone else covered by this constraint, change their organization role first to change their workspace access.
### What happens to workspace access when organization roles change?
If an organization admin or billing member is demoted to user or developer, they lose access to all workspaces except ones where they were manually assigned roles. When users are promoted to admin or billing roles, they gain automatic access to all workspaces.
### What happens to API keys when a user is removed from a workspace?
Behavior depends on the [key type](manage-claude-authentication.md#key-types).
A personal or service account key stops working in a workspace shortly after its user or service account is removed from it. A service account key keeps working even if the user who created it is removed. Workspace API keys continue to work. In the [Claude Code workspace](#claude-code-workspace), each key is bound to the member who created it and stops working when that member is removed.
Personal keys are archived when their user is removed from the organization. If the user is re-invited, they need to create new keys; archived keys are not restored.
## See also
- [Admin API](manage-claude-admin-api.md)
- [Admin API reference](../Admin/http-beta-organization.md)
- [Rate limits](../Endpoints/rate-limits.md)
- [Usage and Cost API](manage-claude-usage-cost-api.md)
---
## Define your agent - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/agent-setup
Path: 04-API-Reference/Other/managed-agents-agent-setup.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fagent-setup)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# Define your agent
Copy page
Create a reusable, versioned agent configuration.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
An agent is a reusable, versioned configuration that defines persona and capabilities. It bundles the model, system prompt, tools, MCP servers, and skills that shape how Claude behaves during a session.
Create the agent once as a reusable resource and reference it by ID each time you [start a session](managed-agents-sessions.md). Agents are versioned and easier to manage across many sessions.
## Agent configuration fields
| Field | Description |
|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | Required. A human-readable name for the agent. |
| `model` | Required. The Claude [model](../../20-Models/about-claude-models-overview.md) that powers the agent. Accepts a model ID string or an object, for example `{"id": "claude-opus-5"}`. Claude 4.5 and later models are supported. The object form also accepts `speed`, `effort`, and `inference_geo` fields; see the tips under [Create an agent](#create-an-agent), [Effort levels](../Guides/build-with-claude-effort.md#effort-levels), and [Pin the inference geo](#pin-the-inference-geo). |
| `system` | A [system prompt](../../10-Prompting-Guides/build-with-claude-prompt-engineering-claude-prompting-best-practices.md#give-claude-a-role) that defines the agent's behavior and persona. The system prompt is distinct from [user messages](managed-agents-reference.md#event-types), which should describe the work to be done. |
| `tools` | The tools available to the agent. Combines [pre-built agent tools](managed-agents-tools.md), [MCP tools](managed-agents-mcp-connector.md), and [custom tools](managed-agents-tools.md#custom-tools). |
| `mcp_servers` | [MCP servers](managed-agents-mcp-connector.md) that provide standardized third-party capabilities. |
| `skills` | [Skills](managed-agents-skills.md) that supply domain-specific context with progressive disclosure. |
| `multiagent` | A coordinator declaration listing the agents this agent can delegate to. See [Multiagent orchestration](managed-agents-multiagent-orchestration.md). |
| `description` | A description of what the agent does. |
| `metadata` | Arbitrary key-value pairs for your own tracking. |
You can also override `model`, `system`, `tools`, `mcp_servers`, and `skills` for a single session without changing the agent. A `model` override replaces the agent's `model` object in full, so the agent's own `effort` isn't carried over. To run the session at a specific effort level, set `effort` inside the override's `model` object. See [Override agent configuration for a session](managed-agents-sessions.md#override-agent-configuration-for-a-session).
## Create an agent
The following example defines a coding agent that uses Claude Opus 5 with access to the pre-built agent toolset. The toolset lets the agent write code, read files, search the web, and more. See the [agent tools reference](managed-agents-tools.md) for the full list of supported tools.
The examples use curl, the `ant` CLI, or one of the SDKs. If you haven't set one up, the [quickstart](managed-agents-quickstart.md#install-the-cli) covers installation and client setup.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply coding-assistant.md
```
coding-assistant.md
```python
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
---
You are a helpful coding agent.
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) creates the agent from `coding-assistant.md`, prints its ID, and records it in `claude-lock.json`. Commit `claude-lock.json` so the next `ant apply` updates this agent instead of creating a second one.
The response echoes your configuration and adds `id`, `type`, `version`, `created_at`, `updated_at`, and `archived_at` fields, and fills in `model` fields you omit, such as `effort`, with their defaults. The `version` starts at 1 and increments each time an update changes the agent.
```python
{
"id": "agent_01HqR2k7vXbZ9mNpL3wYcT8f",
"type": "agent",
"name": "Coding Assistant",
"model": {
"id": "claude-opus-5-5",
"effort": { "type": "high" },
"speed": "standard"
},
"system": "You are a helpful coding agent.",
"description": null,
"tools": [
{
"type": "agent_toolset_20260401",
"default_config": {
"permission_policy": { "type": "always_allow" }
}
}
],
"skills": [],
"mcp_servers": [],
"multiagent": null,
"metadata": {},
"version": 1,
"created_at": "2026-04-03T18:24:10.412Z",
"updated_at": "2026-04-03T18:24:10.412Z",
"archived_at": null
}
```
The `default_config` on the toolset shows its default [permission policy](managed-agents-permission-policies.md), `always_allow`, which applies unless you configure one.
To use Claude Opus 5.5, Claude Opus 5, or Claude Opus 4.8 with [fast mode](../Guides/build-with-claude-fast-mode.md), pass `model` as an object, for example: `{"id": "claude-opus-5", "speed": "fast"}`. See the fast mode page's [supported models](../Guides/build-with-claude-fast-mode.md#supported-models).
To set the model's effort level, pass `model` as an object, for example: `{"id": "claude-opus-5", "effort": "high"}`. The `effort` field accepts a level string (`low`, `medium`, `high`, `xhigh`, or `max`) or an object such as `{"type": "high"}`. See [Effort levels](../Guides/build-with-claude-effort.md#effort-levels) for what each level does.
### Pin the inference geo
Like `speed` and `effort`, `inference_geo` is set through the object form of `model`: pass `model` as an object and set `inference_geo` alongside `id`. The field accepts `"us"` or `"global"`. When it's unset, each model request follows the workspace's default inference geo at the time it's served. See [Data residency](manage-claude-data-residency.md) for the workspace-level geo controls and pricing.
The following example pins an agent to US inference and prints the `inference_geo` value from the agent's `model` object:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply geo-pinned-assistant.md
```
geo-pinned-assistant.md
```python
---
name: Geo-pinned assistant
model:
id: claude-opus-5-5
inference_geo: us
---
You are a helpful assistant.
```
An `inference_geo` pin is validated against the workspace's [`allowed_inference_geos`](manage-claude-data-residency.md#workspace-level-restrictions) when the agent is saved, when a session is created from it, and on every turn the session serves. If the workspace allowlist narrows so a pin is no longer allowed, new sessions can't be created from the agent and running sessions refuse further turns; pins are never exempted, because workspaces rely on them for compliance and data residency.
Setting `inference_geo` on a model that doesn't support geographic inference pinning returns a 400 error; see [Model availability](manage-claude-data-residency.md#model-availability) for the models that do. In a `multiagent` configuration, the coordinator's pin and every roster member's must all be set to the same value or all be unset; see [Multiagent orchestration](managed-agents-multiagent-orchestration.md). To change or clear the pin later, update the agent's `model` object; supplying `model` without `inference_geo` clears it, as described under [Update semantics](#update-semantics).
## Update an agent
Updating an agent generates a new version when the configuration changes. The `version` field is optional: supply it for optimistic concurrency (a mismatch returns a 409), or omit it to apply the update unconditionally (last write wins). Updates to archived agents are rejected.
With the CLI, edit the agent's file and run `ant apply` again; apply supplies `version` for you.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply coding-assistant.md
```
coding-assistant.md
```python
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
---
You are a helpful coding agent. Always write tests.
```
The preceding example supplies `version` from the create response, so the update only applies if nothing else has changed the agent since you read it. To apply an update unconditionally, omit `version` from the request:
cURL
```python
updated_agent=$(curl -fsSL "https://api.anthropic.com/v1/agents/$AGENT_ID" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"description": "Writes and reviews code."
}')
echo "New version: $(jq -r '.version' <<< "$updated_agent")"
```
### Update semantics
- **`version`** is optional and must be at least 1 when supplied. When supplied, the request returns a 409 if it doesn't match the agent's current version, even when the fields you send already match the stored values; re-read the agent and retry. When omitted, the update applies unconditionally and the most recent update silently replaces any concurrent one, with no error to either caller. Supplying `version` is the recommended default for interactive callers, and omitting it fits declarative apply loops, such as a CI job that syncs checked-in agent definitions, where the loop owns the agent.
- **Omitted fields are preserved.** You only need to include the fields you want to change.
- **Scalar fields** (`model`, `system`, `name`, `description`) are replaced with the new value. `system` and `description` can be cleared by passing `null`. `model` and `name` are mandatory and cannot be cleared. Within a `model` object you supply, `effort` is the sole exception: if the model `id` is unchanged, omitting `effort` leaves the stored effort level unchanged. If you change the model `id`, an omitted `effort` resets to the new model's default. Other `model` fields are replaced along with the object: supplying `model` without `inference_geo` clears the agent's inference geo pin.
- **Array fields** (`tools`, `mcp_servers`, `skills`) are fully replaced by the new array. To clear an array field entirely, pass `null` or an empty array.
- **`multiagent`** is replaced as a whole, including its `agents` roster. Pass `null` to clear it.
- **Metadata** is merged at the key level. Keys you provide are added or updated. Keys you omit are preserved. To delete a specific key, set its value to `null`.
- **No-op detection.** If the update produces no change relative to the current version, no new version is created and the existing version is returned.
- **Coordinator rosters are not updated.** Coordinators that reference this agent in their `multiagent.agents` roster keep the version that was pinned when the coordinator was created or last updated, even if the reference omits `version`. To delegate to the new version, [update the coordinator](managed-agents-multiagent-orchestration.md#configure-the-coordinator) so its roster references it.
## Agent lifecycle
| Operation | Behavior |
|-------------------|-----------------------------------------------------------------------------------------------------|
| **Update** | Generates a new agent version when the configuration changes. |
| **List versions** | Returns the full version history so you can track changes over time. |
| **Archive** | Makes the agent read-only. New sessions cannot reference it, but existing sessions continue to run. |
### List versions
Fetch the full version history to track how an agent has changed over time. Results are paginated, and the SDK examples fetch every page automatically.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for version in client.beta.agents.versions.list(agent.id):
print(f"Version {version.version}: {version.updated_at.isoformat()}")
```
### Archive an agent
Archiving makes the agent read-only and cannot be undone. Existing sessions continue to run, but new sessions cannot reference the agent. The response sets `archived_at` to the archive timestamp.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
archived = client.beta.agents.archive(agent.id)
print(f"Archived at: {archived.archived_at.isoformat()}")
```
## Next steps
[Tools](managed-agents-tools.md)
Configure tools available to your agent.
[Skills](managed-agents-skills.md)
Attach reusable, filesystem-based expertise to your agent for domain-specific workflows.
[Start a session](managed-agents-sessions.md)
Create a session to run your agent and begin executing tasks.
[Reference](managed-agents-reference.md)
Event types, self-hosted worker CLI flags, supported MCP server types, rate limits, and branding guidelines for Claude Managed Agents.
---
## Session budgets - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/budgets
Path: 04-API-Reference/Other/managed-agents-budgets.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fbudgets)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Event deltas](managed-agents-event-deltas.md)[Inspect and track usage](managed-agents-session-observability.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Session budgets
Copy page
Cap a session's spend with a hard dollar budget enforced at public list rates.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
A session budget is an optional hard spend ceiling you set when you [create a session](managed-agents-sessions.md). The platform continuously prices everything the session consumes at public list rates (the session's **list cost**) and stops issuing new model requests once that cost reaches the budget. The request in flight when the cap is crossed still finishes, so the final list cost can land [a fraction past the budget](#when-a-session-reaches-its-budget). A session at its budget pauses and goes [idle](managed-agents-session-operations.md#session-statuses) rather than terminating; changing or removing the budget resumes its work automatically. Deployments accept the same budget and apply it to each session they start; see [Budgets on deployments](#budgets-on-deployments).
## Set a budget at session creation
Pass the optional `budget` field when you create the session:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
budget={
"type": "limit",
"max_list_cost": {"amount": "125", "currency": "USD"},
},
)
print(session.id, session.budget.max_list_cost.amount) # sesn_01... 125
```
The `budget` object has two fields:
- `type` is always `"limit"`.
- `max_list_cost` is the cap itself: `amount` is a whole number of US cents written as a string with no leading zeros (`"125"` is \$1.25 and `"50"` is 50 cents) and must be greater than zero. Decimal forms such as `"25.00"` are rejected. The amount is a string rather than a number so no float rounding is ever applied to it. `currency` is an uppercase ISO-4217 currency code; `USD` is the only supported currency.
A budget can only be attached when the session is created. Adding a budget to an existing session that doesn't have one is rejected with a 400 error. A budgeted session's cap can be [changed](#change-the-budget) or [removed](#remove-the-budget) at any time.
## How list cost is measured
The platform prices what the session consumes, continuously, at public list rates:
- **Model tokens**, at each served model's list price
- **Web searches**, at \$10 per 1,000 searches
- **Session running time**, at \$0.08 per hour
This running dollar total is the session's **list cost**, and it is what the budget compares against. List cost is not your contracted price: if your organization has negotiated discounts, the session reaches its cap when the list-price total does, and your billed spend might be lower than the cap.
Enforcement uses the exact, unrounded list cost. The `list_cost` figures reported on the session and its events are whole cents, rounded to the nearest cent, so a reported figure can read up to half a cent either side of the exact amount enforcement uses.
## When a session reaches its budget
The cap is enforced between model requests, not mid-request. Before each model request, the platform checks the session's consumed list cost, and once that total reaches the cap every thread pauses before its next request. The request that carried the total past the cap was admitted while the session was still under it and runs to completion, so a paused session's recorded `list_cost` reads at or a fraction past `max_list_cost`: a session capped at `"50"` (50 cents) can pause with a `list_cost` of `"53"`. This is expected, not a billing error, and the overshoot is bounded by one model request per thread. Treat the budget as a bound on new work rather than an exact stopping point, and size the cap with that one-request margin in mind.
A session that reaches its budget goes idle with a `stop_reason` of `budget_reached`; it is not terminated, and its history and sandbox are preserved like any other idle session's. On the [event stream](managed-agents-events-and-streaming.md) you'll see, in order:
1. A `session.thread_status_idle` event with a `stop_reason` of `budget_reached` as each thread pauses.
2. A [`session.usage`](#monitor-spend) event with the session's cumulative usage and list cost.
3. A `session.status_idle` event with a `stop_reason` of `budget_reached`. The usage event always immediately precedes this idle event.
A thread whose final request both crosses the cap and completes its turn reports `end_turn` on its own `session.thread_status_idle` event while the session still reports `budget_reached`; treat the session-level `stop_reason` as the signal that the session paused at its budget.
### Events accepted at the cap
While the session is at or over its budget, it accepts only events that settle work already in progress:
- `user.tool_confirmation`
- `user.tool_result`
- `user.custom_tool_result`
- `user.interrupt`
Any event that would start new work, such as `user.message`, is rejected with a 400 error naming this list. Settled results are recorded without triggering a new model request; the session stays paused at its budget.
A `user.interrupt` sent while the session is paused at its budget (all threads paused at the cap) is accepted and ignored: it does not appear in the event list and changes nothing. Change or remove the budget to continue.
## Resume a session at its budget
Change or remove the budget with a session update. An accepted update resumes the session's paused work automatically; no further client action is needed.
### Change the budget
Update the session with a new `max_list_cost`. The new value can be higher or lower than the current cap, but it must be strictly greater than the session's consumed list cost; otherwise the update is rejected with a 400 error: `budget.max_list_cost must be greater than the session's consumed list cost`. Because the consumed cost usually sits [a fraction past the old cap](#when-a-session-reaches-its-budget) when the session pauses, base the new value on the session's reported `usage.list_cost`, not on the old `max_list_cost`. Set it a cent or more above that figure: the reported value is rounded and can sit a fraction below the exact consumed cost the check uses.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
updated_session = client.beta.sessions.update(
session.id,
budget={
"type": "limit",
"max_list_cost": {"amount": "500", "currency": "USD"},
},
)
print(updated_session.budget.max_list_cost.amount) # 500
```
### Remove the budget
Set `budget` to `null` to remove the cap entirely. The session's paused work resumes, and the resulting `session.updated` event carries `budget` set to `null`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
unbudgeted_session = client.beta.sessions.update(session.id, budget=None)
print(unbudgeted_session.budget) # None
```
Removing a session's budget is one-way: a session whose budget has been removed cannot be given a new one. To keep a cap on the session, change the budget instead.
## Monitor spend
The session object carries its `budget` and a `usage` object with the tracked spend: `usage.list_cost` is the session's consumed list cost, and `usage.active_seconds` is the running time its runtime cost is priced on. On a session paused at `budget_reached`, expect `usage.list_cost` to read at or a fraction past `max_list_cost`: the [request that crossed the cap](#when-a-session-reaches-its-budget) finished before the pause. Session-level `active_seconds` counts overlapping activity from concurrent threads once. Thread retrieval responses carry the same two fields on the thread's own `usage`, priced per thread. Per-thread figures are rounded independently and exclude the session's running-time cost, so they don't sum exactly to the session's `list_cost`; the session figure is the one the budget is enforced against.
The `session.usage` event is a snapshot of the session's cumulative usage and tracked list cost. It carries the session's token totals, `list_cost`, `active_seconds`, `server_tool_use` request counts (`web_search_requests`, priced into list cost per request, and `web_fetch_requests`, which reads `0` because web fetch requests carry no per-request charge and aren't metered), and an echo of the session's `budget`, or `null` when the session has none. It appears in the events list and the session stream. The session emits one immediately before it goes idle, whatever the stop reason, so a session that reaches its budget always emits one immediately before the budget-reached idle event.
To read usage from the stream and the session object, see [Track usage](managed-agents-session-observability.md#track-usage).
## Budgets in multiagent sessions
A [multiagent](managed-agents-multiagent-orchestration.md) session has a single budget shared across all of its threads; there are no per-thread caps. Each thread's consumption is priced at its own served model, and threads pause independently as the shared cap is reached. [Advisor](managed-agents-multiagent-orchestration.md#give-the-session-an-advisor) consultations count against the same budget, priced at the advisor model's rates. One thread can pause at `budget_reached` while another finishes its in-flight request.
A pending ask outranks the cap: a session with one thread waiting on `requires_action` and another paused at `budget_reached` reports `requires_action` at the session level. The pending request still needs an answer, and answering it is a [settle event](#events-accepted-at-the-cap) the budget doesn't block.
## Budgets on deployments
A [deployment](managed-agents-scheduled-deployments.md) accepts the same `budget` object when you create or update it:
```python
{
"budget": {
"type": "limit",
"max_list_cost": { "amount": "2000", "currency": "USD" }
}
}
```
The cap is copied onto each session the deployment starts, so it bounds each run separately rather than the deployment's cumulative spend. Changing the deployment's budget applies to sessions the deployment starts afterward, not to sessions already running. Unlike a session, a deployment's budget can be cleared with `null` and set again later. See [Set a budget on each run](managed-agents-scheduled-deployments.md#set-a-budget-on-each-run).
## Models without a list price
A budget can only track consumption the platform can price. Creating a budgeted session whose agent, or any agent or advisor on its [multiagent roster](managed-agents-multiagent-orchestration.md), uses a model with no public list price is rejected with a 400 error stating that no list price is available for the model.
If a budgeted session's usage comes to include a model with no list price, the budget can no longer measure the session's spend: the session can pause with a `stop_reason` of `budget_reached`, and changing the budget is rejected. Remove the budget to resume the session.
## Error reference
Budget-related requests are rejected in the following cases:
| Condition | Status |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|
| A work-starting event (for example, `user.message`) is sent while the session is at or over its budget; the error names the [accepted settle events](#events-accepted-at-the-cap) | 400 |
| The budget is set to a value at or below the session's consumed list cost | 400 |
| A budget is added to a session created without one, or re-added after removal | 400 |
| `amount` is not a whole number of cents (for example, `"25.00"`), is zero or negative, or `currency` is not `USD` | 400 |
| A budgeted create references a model with [no public list price](#models-without-a-list-price) | 400 |
Session budgets are hard caps in US dollars (written in cents) on a single session, enforced by the platform. They are distinct from the Messages API's [task budgets](../Guides/build-with-claude-task-budgets.md), which are advisory, token-denominated budgets the model uses to self-regulate within one agentic loop.
---
## Container reference
Source: https://platform.claude.com/docs/en/managed-agents/cloud-containers
Path: 04-API-Reference/Other/managed-agents-cloud-containers.md
# Container reference
Pre-installed packages, databases, and utilities available in cloud containers.
---
Cloud containers in Claude Managed Agents come pre-installed with a comprehensive set of programming languages, databases, and utilities. The agent can use these immediately without any installation steps.
<Note>
All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically.
</Note>
## Programming languages
| Language | Version | Package manager |
|----------|---------|-----------------|
| Python | 3.12+ | pip, uv |
| Node.js | 20+ | npm, yarn, pnpm |
| Go | 1.22+ | go modules |
| Rust | 1.77+ | cargo |
| Java | 21+ | maven, gradle |
| Ruby | 3.3+ | bundler, gem |
| PHP | 8.3+ | composer |
| C/C++ | GCC 13+ | make, cmake |
## Databases
| Database | Description |
|----------|-------------|
| SQLite | Pre-installed, available immediately |
| PostgreSQL client | `psql` client for connecting to external databases |
| Redis client | `redis-cli` for connecting to external instances |
<Note>
Database servers (PostgreSQL, Redis, etc.) are not running in the container by default. The container includes client tools for connecting to external database instances. SQLite is fully available for local use.
</Note>
## Utilities
### System tools
- `git` - Version control
- `curl`, `wget` - HTTP clients
- `jq` - JSON processing
- `tar`, `zip`, `unzip` - Archive tools
- `ssh`, `scp` - Remote access (requires network enabled)
- `tmux`, `screen` - Terminal multiplexers
### Development tools
- `make`, `cmake` - Build systems
- `docker` - Container management (limited availability)
- `ripgrep` (`rg`) - Fast file search
- `tree` - Directory visualization
- `htop` - Process monitoring
### Text processing
- `sed`, `awk`, `grep` - Stream editors
- `vim`, `nano` - Text editors
- `diff`, `patch` - File comparison
## Container specifications
| Property | Value |
|----------|-------|
| Operating system | Ubuntu 22.04 LTS |
| Architecture | x86_64 (amd64) |
| Memory | Up to 8 GB |
| Disk space | Up to 10 GB |
| Network | Disabled by default (enable in environment config) |
---
## Cloud sandbox reference - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/cloud-sandboxes-reference
Path: 04-API-Reference/Other/managed-agents-cloud-sandboxes-reference.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fcloud-sandboxes-reference)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Configure agent environment
# Cloud sandbox reference
Copy page
Pre-installed packages, databases, and utilities available in cloud sandboxes.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Cloud sandboxes run as isolated Linux containers on Anthropic-managed infrastructure. They come pre-installed with a comprehensive set of programming languages, databases, and utilities. The agent can use these immediately without any installation steps.
These specifications apply to `cloud` environments. Self-hosted sandboxes run on your infrastructure with whatever your worker provides.
## Programming languages
| Language | Version | Package manager |
|----------|-----------------------------|----------------------|
| Python | 3.10, 3.11, 3.12, and 3.13 | pip, uv, poetry |
| Node.js | 20, 21, and 22 (default) | npm, yarn, pnpm, bun |
| Go | 1.24 (default) and 1.25 | go modules |
| Rust | Stable toolchain (rustup) | cargo |
| Java | OpenJDK 21 | maven, gradle |
| Ruby | 3.1, 3.2, and 3.3 (default) | bundler, gem |
| PHP | 8.3 | composer |
| C/C++ | GCC 13 and Clang | make, cmake, ninja |
Common Python data and document libraries, including NumPy, pandas, Matplotlib, openpyxl, python-docx, python-pptx, and pypdf, are installed for the `python3` interpreter.
## Databases
| Database | Description |
|---------------|-------------------------------------------------------------------------------|
| PostgreSQL 16 | Server and `psql` client are installed. The server is not running by default. |
| Redis 7 | Server and `redis-cli` are installed. The server is not running by default. |
| SQLite | Available through language bindings, such as Python's `sqlite3` module. |
## Utilities
### System tools
- `git` - Version control
- `curl`, `wget` - HTTP clients
- `jq`, `yq` - JSON and YAML processing
- `tar`, `zip`, `unzip` - Archive tools
- `tmux` - Terminal multiplexer
### Development tools
- `make`, `cmake` - Build systems
- `docker` - Container management (limited availability)
- `ripgrep` (`rg`) - Fast file search
### Text processing
- `sed`, `awk`, `grep` - Stream editors
- `vim`, `nano` - Text editors
- `diff`, `patch` - File comparison
### Document and media processing
- `ffmpeg` - Audio and video processing
- ImageMagick (`convert`, `identify`) - Image manipulation
- `pandoc` - Document conversion
- LibreOffice (headless) - Office document conversion
- Poppler utilities (`pdftotext`, `pdftoppm`) and `qpdf` - PDF processing
- `tesseract` - Optical character recognition (English language data)
- TeX Live (`pdflatex`, `xelatex`, `latexmk`) - Typesetting
### Browser automation
- Playwright (Python and Node.js) - Browser automation library
- Chromium (`/opt/pw-browsers/chromium`) - Browser used by Playwright, not on `PATH`
The sandbox sets `PLAYWRIGHT_BROWSERS_PATH` to `/opt/pw-browsers`, so the pre-installed Playwright packages find Chromium there without configuration. The Python package is installed for the `python3` interpreter. Use the pre-installed packages rather than installing another Playwright version, which would look for a browser build that is not present. Firefox and WebKit are not installed.
## Sandbox specifications
| Property | Value |
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Operating system | Ubuntu 24.04 LTS |
| Architecture | x86_64 (amd64) |
| Memory | Up to 8 GB |
| Disk space | Up to 10 GB |
| Network | API-created environments default to [`unrestricted` networking](managed-agents-environments.md#networking); sandboxes provisioned through Claude Studio default to `limited` |
---
## MCP connector
Source: https://platform.claude.com/docs/en/managed-agents/custom-mcp-servers
Path: 04-API-Reference/Other/managed-agents-custom-mcp-servers.md
# MCP connector
Connect MCP servers to your agents for access to external tools and data sources.
---
> Note: The original URL `/docs/en/managed-agents/custom-mcp-servers` returned 404. This content is sourced from the actual MCP connector page at `/docs/en/managed-agents/mcp-connector`.
Claude Managed Agents supports connecting [Model Context Protocol (MCP)](https://modelcontextprotocol.io) servers to your agents. This gives the agent access to external tools, data sources, and services through a standardized protocol.
MCP configuration is split across two steps:
1. **Agent creation** declares which MCP servers the agent connects to, by name and URL.
2. **Session creation** supplies auth for those servers by referencing a pre-registered [vault](managed-agents-vaults.md).
This separation keeps secrets out of reusable agent definitions while letting each session authenticate with its own credentials.
> All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically.
## Declare MCP servers on the agent
Specify MCP servers in the `mcp_servers` array when creating an agent. Each server needs a `type`, a unique `name`, and a `url`. No auth tokens are provided at this stage.
The `name` you assign in the MCP server array is used to reference the `mcp_toolset` entries in the tools array.
```python
agent = client.beta.agents.create(
name="GitHub Assistant",
model="claude-sonnet-4-6",
mcp_servers=[
{
"type": "url",
"name": "github",
"url": "https://api.githubcopilot.com/mcp/",
},
],
tools=[
{"type": "agent_toolset_20260401"},
{"type": "mcp_toolset", "mcp_server_name": "github"},
],
)
```
```typescript
const agent = await client.beta.agents.create({
name: "GitHub Assistant",
model: "claude-sonnet-4-6",
mcp_servers: [
{
type: "url",
name: "github",
url: "https://api.githubcopilot.com/mcp/",
},
],
tools: [
{ type: "agent_toolset_20260401" },
{ type: "mcp_toolset", mcp_server_name: "github" },
],
});
```
```bash
agent=$(curl -fsSL https://api.anthropic.com/v1/agents \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @- <<'EOF'
{
"name": "GitHub Assistant",
"model": "claude-sonnet-4-6",
"mcp_servers": [
{
"type": "url",
"name": "github",
"url": "https://api.githubcopilot.com/mcp/"
}
],
"tools": [
{"type": "agent_toolset_20260401"},
{"type": "mcp_toolset", "mcp_server_name": "github"}
]
}
EOF
)
agent_id=$(jq -r '.id' <<<"$agent")
```
> The MCP toolset defaults to a permission policy of `always_ask`, which requires user approval before each tool call. See [permission policies](managed-agents-permission-policies.md) to configure this behavior.
## Provide auth at session creation
When starting a session, pass `vault_ids` to provide credentials for your MCP servers. Vaults are collections of credentials that you register once and reference by ID. See [Authenticate with vaults](managed-agents-vaults.md) for how to create vaults and manage credentials.
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
vault_ids=[vault.id],
)
```
```typescript
const session = await client.beta.sessions.create({
agent: agent.id,
environment_id: environment.id,
vault_ids: [vault.id],
});
```
```bash
session=$(curl -sS --fail-with-body https://api.anthropic.com/v1/sessions \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @- <<EOF
{
"agent": "$agent_id",
"environment_id": "$environment_id",
"vault_ids": ["$vault_id"]
}
EOF
)
session_id=$(jq -r '.id' <<<"$session")
```
If the authorization credentials supplied in the vault are invalid, session creation will succeed and interaction is still possible. A `session.error` event is emitted describing the MCP auth failure. You can decide whether to block further interactions on this error, trigger a credential update, or allow the session to continue without the MCP. Authentication retries will happen on the following `session.status_idle` to `session.status_running` transition. See [Session event stream](managed-agents-events-and-streaming.md) for details on consuming `session.error` and other events.
## Supported MCP server types
Claude Managed Agents connects to [remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md) that expose an HTTP endpoint. The server must support the MCP protocol's streamable HTTP transport.
For more information on MCP and building MCP servers, see the [MCP documentation](https://modelcontextprotocol.io).
---
## Define outcomes - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/define-outcomes
Path: 04-API-Reference/Other/managed-agents-define-outcomes.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fdefine-outcomes)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Define outcomes
Copy page
Tell the agent what 'done' looks like, and let it iterate until it gets there.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
An outcome tells the session what the end result should look like and how to measure its quality. The agent works toward that target, self-evaluating and iterating until the outcome is met.
When you define an outcome, the harness automatically provisions a *grader* to evaluate the artifact against a rubric. The grader uses a separate context window to avoid being influenced by the main agent's implementation choices.
The grader returns an explanation summarizing which criteria passed or failed, or confirming that the artifact satisfies the rubric. That feedback is handed back to the agent for the next iteration.
## Create a rubric
A rubric is a markdown document describing per-criterion scoring. The rubric is required.
### Tips for writing effective rubrics
Structure the rubric as explicit, gradeable criteria, such as "The CSV contains a price column with numeric values" rather than "The data looks good." The grader scores each criterion independently, so vague criteria produce noisy evaluations.
If you don't have a rubric on hand, try giving Claude an example of a known-good artifact and asking it to analyze what makes that content good, then turn that analysis into a rubric. This middle-ground approach often produces better results than writing criteria from scratch.
Example rubric:
```python
# DCF Model Rubric
## Revenue Projections
- Uses historical revenue data from the last 5 fiscal years
- Projects revenue for at least 5 years forward
- Growth rate assumptions are explicitly stated and reasonable
## Cost Structure
- COGS and operating expenses are modeled separately
- Margins are consistent with historical trends or deviations are justified
## Discount Rate
- WACC is calculated with stated assumptions for cost of equity and cost of debt
- Beta, risk-free rate, and equity risk premium are sourced or justified
## Terminal Value
- Uses either perpetuity growth or exit multiple method (stated which)
- Terminal growth rate does not exceed long-term GDP growth
## Output Quality
- All figures are in a single .xlsx file with clearly labeled sheets
- Key assumptions are on a separate "Assumptions" sheet
- Sensitivity analysis on WACC and terminal growth rate is included
```
Pass the rubric as inline text on `user.define_outcome` (see [Create a session with an outcome](#create-a-session-with-an-outcome)), or upload it through the Files API for reuse across sessions.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import time
from pathlib import Path
from anthropic import Anthropic
client = Anthropic()
RUBRIC = """# DCF Model Rubric
## Revenue Projections
- Uses historical revenue data from the last 5 fiscal years
- Projects revenue for at least 5 years forward
## Output Quality
- All figures are in a single .xlsx file with clearly labeled sheets
"""
Path("/tmp/rubric.md").write_text(RUBRIC)
rubric = client.files.upload(file=Path("/tmp/rubric.md"))
print(f"Uploaded rubric: {rubric.id}")
```
## Create a session with an outcome
The following examples create a [session](managed-agents-sessions.md) for an existing [agent](managed-agents-agent-setup.md) and [environment](managed-agents-environments.md) (both created separately), then send a `user.define_outcome` event. The agent begins work immediately. No additional user message event is required.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Create a session
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
title="Financial analysis on Costco",
)
# Define the outcome — agent starts working on receipt
client.beta.sessions.events.send(
session_id=session.id,
events=[
{
"type": "user.define_outcome",
"description": "Build a DCF model for Costco in .xlsx",
"rubric": {"type": "text", "content": RUBRIC},
# or: "rubric": {"type": "file", "file_id": rubric.id},
"max_iterations": 5, # optional; default 3, max 20
}
],
)
```
You can also define the outcome in the create request itself: pass a single `user.define_outcome` event in [`initial_events`](managed-agents-sessions.md#seed-the-session-with-initial-events) to create the session and start work toward the outcome in one call.
## Outcome events
Progress on an outcome-oriented session is surfaced on the events [stream](managed-agents-events-and-streaming.md).
- `agent.*` events (such as messages and tool use) show progress toward the outcome.
- `span.outcome_evaluation_*` events are only emitted for outcome-oriented sessions and show the number of iteration loops and the grader's feedback process.
- You can also send `user.message` [events](managed-agents-reference.md#event-types) to an outcome-oriented session to direct the agent's work as it progresses, but it isn't required: the agent works toward the outcome on its own, iterating until it succeeds or runs out of iterations.
- A `user.interrupt` event pauses work on the current outcome and marks the `span.outcome_evaluation_end.result` as `interrupted`, allowing you to kick off a new outcome.
- After the final outcome evaluation, the session can be continued as a conversational session, or a new outcome can be started. The session retains history of the prior outcome.
### Define outcome user event
Only one outcome is supported at a time, but you may chain outcomes in sequence. To do this, send a new `user.define_outcome` event after the terminal `span.outcome_evaluation_end` event of the previous outcome.
This is the event you send to initiate an outcome. It is echoed back on receipt, including a `processed_at` timestamp and `outcome_id`.
```python
{
"type": "user.define_outcome",
"description": "Build a DCF model for Costco in .xlsx",
"rubric": { "type": "file", "file_id": "file_01..." },
"max_iterations": 5
}
```
### Outcome evaluation start
Emitted once the grader starts an evaluation over one iteration loop. The `iteration` field is a 0-indexed revision counter: `0` is the first evaluation, `1` is the re-evaluation after the first revision, and so on.
```python
{
"type": "span.outcome_evaluation_start",
"id": "sevt_01def...",
"outcome_id": "outc_01a...",
"iteration": 0,
"processed_at": "2026-03-25T14:01:45Z"
}
```
### Outcome evaluation ongoing
Heartbeat emitted while the grader runs. The grader's internal reasoning is opaque: you see that it's working, not what it's thinking.
```python
{
"type": "span.outcome_evaluation_ongoing",
"id": "sevt_01ghi...",
"outcome_id": "outc_01a...",
"iteration": 0,
"processed_at": "2026-03-25T14:02:10Z"
}
```
### Outcome evaluation end
Emitted when an outcome evaluation cycle ends: after the grader finishes evaluating one iteration, or when the session is interrupted while an outcome is active. The `result` field indicates what happens next.
| Result | Next |
|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `satisfied` | Session transitions to `idle`. |
| `needs_revision` | Agent starts a new iteration cycle. |
| `max_iterations_reached` | One final acknowledgment turn follows before the session transitions to `idle`. No further evaluation runs. |
| `failed` | Session transitions to `idle`. Returned when the rubric does not apply to the deliverables, for example if the description and rubric contradict each other. |
| `interrupted` | Emitted when the session is interrupted while an outcome is active, even if evaluation hadn't started yet. If no `outcome_evaluation_start` fired before the interrupt, `outcome_evaluation_start_id` is an empty string. |
```python
{
"type": "span.outcome_evaluation_end",
"id": "sevt_01jkl...",
"outcome_evaluation_start_id": "sevt_01def...",
"outcome_id": "outc_01a...",
"result": "satisfied",
"explanation": "All 12 criteria met: revenue projections use 5 years of historical data, WACC assumptions are stated, sensitivity table is included...",
"iteration": 0,
"usage": {
"input_tokens": 2400,
"output_tokens": 350,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1800
},
"processed_at": "2026-03-25T14:03:00Z"
}
```
## Check outcome status
You can either listen on the [event stream](managed-agents-events-and-streaming.md) for `span.outcome_evaluation_end`, or poll `GET /v1/sessions/{session_id}` and read `outcome_evaluations[].result`. Until an evaluation completes, `result` reports `pending`, `running`, or `evaluating`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.retrieve(session.id)
for outcome in session.outcome_evaluations:
print(f"{outcome.outcome_id}: {outcome.result}")
# outc_01a...: satisfied
```
## Retrieve deliverables
The agent writes output files to `/mnt/session/outputs/` inside the sandbox. To retrieve them, list files through the [Files API](../Guides/build-with-claude-files.md) with the session ID as the `scope_id`, then download them by ID. Filtering by `scope_id` requires the `managed-agents-2026-04-01` beta header on the list request, so the SDK and CLI examples make that call through the `beta` namespace and pass the header explicitly. Files appear in the list shortly after the agent finishes writing them, sometimes a few seconds after the session goes idle. If a file you expect is not listed yet, list again after a short delay; once it appears in the list, its upload has finished.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List files produced by this session
# scope_id filtering requires the managed-agents beta on the files request
files = client.beta.files.list(scope_id=session.id, betas=["managed-agents-2026-04-01"])
for file in files:
print(file.id, file.filename)
# Download a file
if files.data:
content = client.files.download(files.data[0].id)
content.write_to_file("/tmp/output.txt")
```
## Next steps
[Authenticate with vaults](managed-agents-vaults.md)
Register per-user credentials when creating sessions.
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
[Adding files](managed-agents-files.md)
Upload files and mount them in your sandbox for reading and processing.
---
## Dreams - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/dreams
Path: 04-API-Reference/Other/managed-agents-dreams.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fdreams)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
[Memory stores](managed-agents-memory.md)[Dreams](managed-agents-dreams.md)
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Build persistent memory
# Dreams
Copy page
Let Claude reflect on past sessions to curate an agent's memory and surface new insights.
Copy page
Dreaming is a research preview feature. [Request access](https://claude.com/form/claude-managed-agents) to try it.
Agents write to their [memory stores](managed-agents-memory.md) as they work, but these writes are local and incremental: over many sessions a memory store accumulates duplicates, contradictions, and stale entries.
**Dreams** let Claude clean that up. A dream reads an existing memory store alongside past session transcripts, then produces a new, reorganized memory store: duplicates merged, stale or contradicted entries replaced with the latest value, and new insights surfaced.
The input store is never modified, so you can review the output and discard it if you don't like the result.
Dream endpoints are gated by the `dreaming-2026-04-21` beta header; the `managed-agents-2026-04-01` header on its own doesn't grant access to dreams. The dream-endpoint examples on this page send both headers; session and memory-store calls need only `managed-agents-2026-04-01`. The SDK sets these automatically.
## How it works
A **dream** is an asynchronous job that takes:
- a pre-existing **memory store:** the store Claude verifies, deduplicates, and reorganizes, and
- 1 to 100 **sessions:** past transcripts Claude mines for patterns and insights to fold into the output.
The dream produces another **output memory store**, separate from the input. The output store ID appears in the dream's `outputs[]` shortly after the dream starts `running`, once the workflow has cloned the input store; a `running` dream can briefly report an empty `outputs[]`.
## Create a dream
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
dream = client.beta.dreams.create(
inputs=[
{"type": "memory_store", "memory_store_id": store_id},
{"type": "sessions", "session_ids": [session_a, session_b]},
],
model="claude-opus-4-8",
instructions="Focus on coding-style preferences; ignore one-off debugging notes.",
)
print(dream.id) # drm_01...
```
Dreaming inputs include the pre-existing memory store and an array of sessions. The selected model runs the dreaming pipeline. During the research preview, `claude-opus-5`, `claude-fable-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-sonnet-5`, and `claude-sonnet-4-6` are supported. You can optionally pass `instructions` to steer the dreaming process. See [Steer with instructions](#steer-with-instructions).
The response is the full `dream` resource with `status: "pending"`:
```python
{
"type": "dream",
"id": "drm_01AbCDefGhIjKlMnOpQrStUv",
"status": "pending",
"inputs": [
{ "type": "memory_store", "memory_store_id": "memstore_01Hx..." },
{ "type": "sessions", "session_ids": ["sesn_01...", "sesn_02..."] }
],
"outputs": [],
"model": { "id": "claude-opus-4-8" },
"instructions": "Focus on coding-style preferences; ignore one-off debugging notes.",
"session_id": null,
"created_at": "2026-04-29T17:04:10Z",
"ended_at": null,
"archived_at": null,
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"cache_read_input_tokens": 0,
"cache_creation_input_tokens": 0
},
"error": null
}
```
If you only have session transcripts and no existing store, [create an empty memory store](managed-agents-memory.md#create-a-memory-store) first and pass it as the `memory_store` input.
### Steer with instructions
The optional `instructions` field steers what the dreaming pipeline synthesizes. It is applied throughout the pipeline: what to read closely, what to merge or drop, and how to structure the output store.
Use `instructions` for high-level synthesis guidance such as focus areas ("focus on coding-style preferences"), content to preserve unchanged, or output conventions you want applied across the store. The pipeline is a synthesis pass over the inputs, not an editor applied to the text of the store, so imperative directives that target specific lines ("change sentence X to Y", "fix the count in section Z") generally produce no change. To make targeted edits to individual memories, use the [Memory Stores API](managed-agents-memory.md#view-and-edit-memories) on the output store directly.
## Track progress
Dreams run asynchronously and typically take minutes to a few hours, driven by the number of input transcripts. Poll the dream by ID to check status:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
while dream.status in ("pending", "running"):
time.sleep(10)
dream = client.beta.dreams.retrieve(dream.id)
print(f"status={dream.status} input_tokens={dream.usage.input_tokens}")
```
### Lifecycle
| `status` | Meaning |
|-------------|-------------------------------------------------------------------------------------------------------------------|
| `pending` | Dream successfully created and queued. |
| `running` | The pipeline is processing. `usage` updates as work progresses. |
| `completed` | Finished successfully. The `outputs[]` value is the new memory store. |
| `failed` | Dreaming run ended with an error. The output memory store is left as-is with whatever was written before failure. |
| `canceled` | Dreaming run canceled. The output memory store is left as-is. |
### Watch the pipeline run
Once a dream is `running`, its `session_id` field points at the underlying [session](managed-agents-sessions.md) running the pipeline. You can stream that session's [events](managed-agents-events-and-streaming.md) to observe what the dream is reading and writing in real time. The session is archived (not deleted) when the dream reaches a terminal state, so the transcript remains available afterward.
## Use the output
When `status` reaches `completed`, the `memory_store` entry in `outputs[]` references a fully populated store. It's an ordinary memory store in your workspace. Review it with the [Memory Stores API](managed-agents-memory.md#view-and-edit-memories) or in the Console, then either:
- **Leverage it:** attach it to future sessions as a `memory_store` resource in place of (or alongside) the input memory store, or
- **Discard it:** [delete the memory store](../Endpoints/beta-memory-stores-delete.md) or [archive the memory store](../Endpoints/beta-memory-stores-archive.md).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# After the dream ends, the output holds the rebuilt memory store
output_store_id = next(
output.memory_store_id for output in dream.outputs if output.type == "memory_store"
)
session = client.beta.sessions.create(
agent=agent_id,
environment_id=environment_id,
resources=[
{"type": "memory_store", "memory_store_id": output_store_id},
],
)
```
The dream itself never deletes or modifies its inputs. On `failed` or `canceled` the output store persists with partial contents so you can inspect what was produced before stopping; clean it up through the Memory Stores API if you don't need it.
While a dream is `pending` or `running`, the 400 guard applies to archiving the dream itself, not its stores. Archiving or deleting an *input* memory store mid-run (or deleting an input session) will cause the dream to fail with `input_memory_store_unavailable` or `input_session_unavailable`.
## Cancel a dream
Cancel moves a `pending` or `running` dream to `canceled` immediately. Canceling an already-`canceled` dream is an idempotent no-op; canceling a `completed` or `failed` dream returns 400.
After cancellation, the dream's `usage` fields might continue to update for a few seconds while in-flight work winds down. Poll the dream until `usage` stabilizes if you need the final count.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.dreams.cancel(dream.id)
```
## Archive a dream
Archive sets `archived_at` on a dream that has reached a terminal state (`completed`, `failed`, or `canceled`); `status` is left unchanged. Archived dreams are excluded from default list responses but remain readable by ID. Archiving an already-archived dream is an idempotent no-op. Archiving a `pending` or `running` dream returns 400; cancel it first. There is no unarchive.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.dreams.archive(dream.id)
```
Archiving a dream does not touch its output memory store; manage that separately through the [Memory Stores API](managed-agents-memory.md#view-and-edit-memories).
## List dreams
Returns all non-archived dreams in the workspace, newest first. Use `limit` (default 20, max 100) and the `page` cursor to paginate. Pass `include_archived=true` to include archived dreams.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for listed_dream in client.beta.dreams.list(limit=20):
print(listed_dream.id, listed_dream.status)
```
## Errors
A non-exhaustive list of possible dreaming errors follows.
| `error.type` | When |
|-----------------------------------|-------------------------------------------------------------------------------------------------|
| `timeout` | The pipeline exceeded its runtime budget. |
| `internal_error` | Unclassified pipeline failure. |
| `memory_store_org_limit_exceeded` | Your organization hit its memory-store cap while the pipeline was provisioning working storage. |
| `input_memory_store_too_large` | The input memory store exceeds the pipeline's size limit. |
| `input_memory_store_unavailable` | The input memory store was archived or deleted after the dream was created. |
| `input_session_unavailable` | An input session was deleted after the dream was created. |
## Billing
Dreams are billed at standard API token rates for the model you select; `usage` on the resource reports the exact totals. Cost scales roughly linearly with the number and length of input sessions. Start with a small batch of sessions and scale up once you're satisfied with the curation quality.
## Limits
| Limit | Value |
|-----------------------|-----------------------------------------------------------------------------------------------------------------|
| Sessions per dream | 100 |
| `instructions` length | 4,096 characters |
| Supported models | `claude-opus-5`, `claude-fable-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-sonnet-5`, `claude-sonnet-4-6` |
Default rate limits apply to dream creation while this feature is in research preview. [Contact support](https://support.claude.com) if you need higher limits.
---
## Cloud environment setup - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/environments
Path: 04-API-Reference/Other/managed-agents-environments.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fenvironments)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Configure agent environment
# Cloud environment setup
Copy page
Customize cloud sandboxes for your sessions.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Environments define the sandbox configuration where your agent runs. You create an environment once, then reference its ID each time you start a session. Multiple sessions can share the same environment, but each session gets its own isolated sandbox (a fresh Linux container).
This page covers `type: cloud` environments. To run sandboxes on your own infrastructure, see [Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md).
## Create an environment
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply environment.yaml
```
environment.yaml
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: python-dev
config:
type: cloud
networking:
type: limited
allow_package_managers: true
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) creates the environment from `environment.yaml`, prints its ID, and records it in `claude-lock.json`. Commit `claude-lock.json` so the next `ant apply` updates this environment instead of trying to create it again.
Use a unique, descriptive `name` so you can tell environments apart. This example uses `limited` [networking](#networking) with package managers allowed, so the sandbox can reach the package registries and code hosts. To let it reach other hosts, add them to `allowed_hosts`.
## Use the environment in a session
Pass the environment ID as a string when [creating a session](managed-agents-sessions.md).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
)
```
## Configuration options
### Packages
The `packages` field pre-installs packages into the sandbox before the agent starts. Packages are installed by their respective package managers and cached across sessions that share the same environment. When multiple package managers are specified, they run in alphabetical order (apt, cargo, gem, go, npm, pip). You can optionally pin specific versions. Unpinned packages install the latest version. If the environment uses `limited` [networking](#networking), also set `networking.allow_package_managers` to `true`; otherwise the request is rejected with a 400 error.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply environment.yaml
```
environment.yaml
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: data-analysis
config:
type: cloud
packages:
pip:
- pandas
- numpy
- scikit-learn
npm:
- express
networking:
type: limited
allow_package_managers: true
```
Supported package managers:
| Field | Package manager | Example |
|---------|---------------------------|---------------------------------------------|
| `apt` | System packages (apt-get) | `"graphviz"` |
| `cargo` | Rust (cargo) | `"hyperfine@1.18.0"` |
| `gem` | Ruby (gem) | `"rails:7.1.0"` |
| `go` | Go modules | `"golang.org/x/tools/cmd/goimports@latest"` |
| `npm` | Node.js (npm) | `"express@4.18.0"` |
| `pip` | Python (pip) | `"sqlalchemy==2.0.30"` |
### Networking
The `networking` field controls the sandbox's outbound network access. It does not affect the `web_search` or `web_fetch` tools, which run on Anthropic's servers; to restrict the sites those tools can reach, set `allowed_domains` or `blocked_domains` on the tool's entry in the agent toolset. See [Restrict web search and web fetch domains](managed-agents-tools.md#restrict-web-search-and-web-fetch-domains).
| Mode | Description |
|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `limited` | Restricts sandbox network access to the hosts in `allowed_hosts`. Set `allow_package_managers` and `allow_mcp_servers` to `true` to allow additional access. Use this mode unless the agent must reach sites you cannot list in advance. |
| `unrestricted` | Full outbound network access, except for a general safety blocklist. Before you use it, read [Risks of unrestricted networking](#risks-of-unrestricted-networking). |
Set `networking` explicitly in API requests; a create request that omits it gets `unrestricted`. The Claude Console's form for creating an environment starts with **Limited** selected and nothing else allowed.
The following example creates an environment with `limited` networking:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply environment.yaml
```
environment.yaml
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: api-access
config:
type: cloud
networking:
type: limited
allowed_hosts:
- api.example.com
allow_mcp_servers: true
allow_package_managers: true
```
Use `limited` networking with an explicit `allowed_hosts` list. Follow the principle of least privilege by granting only the minimum network access your agent requires, and regularly audit your allowed domains.
With `limited` networking and no other fields set, no hosts are allowed. Files, memory stores, and GitHub repositories that you attach to the session stay available. When a request from the sandbox on port 80 or 443 is refused because its host is not allowed, the response is a 403 that names the blocked host.
When using `limited` networking:
- `allowed_hosts` specifies domains the sandbox can reach. Specify bare hostnames or wildcard patterns (such as `*.example.com`). Do not include a URL scheme, port, or path.
- `allow_mcp_servers` allows outbound access to MCP server endpoints configured on the agent, beyond those listed in the `allowed_hosts` array. Defaults to `false`. While it is `false`, session creation fails with a 400 error if the agent declares an MCP server whose host is not in `allowed_hosts`. The same applies to [an agent it can delegate to](managed-agents-multiagent-orchestration.md). To fix it, add the host to `allowed_hosts` or set `allow_mcp_servers` to `true`.
- `allow_package_managers` allows outbound access to a set of public package registries and code hosts beyond those listed in the `allowed_hosts` array. See [Package manager hosts](#package-manager-hosts) for the list. Defaults to `false`. Set it to `true` whenever the environment specifies `packages`; otherwise the request is rejected with a 400 error, even if the registry hosts are listed in `allowed_hosts`.
#### Package manager hosts
When `allow_package_managers` is `true`, the sandbox can reach the following hosts in addition to those in `allowed_hosts`. Anthropic maintains this list and can change it.
| Ecosystem | Hosts |
|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Code hosting | `github.com`, `api.github.com`, `codeload.github.com`, `raw.githubusercontent.com`, `objects.githubusercontent.com`, `release-assets.githubusercontent.com`, `gitlab.com`, `bitbucket.org` |
| Node.js | `registry.npmjs.org`, `registry.yarnpkg.com`, `nodejs.org` |
| Python | `pypi.org`, `files.pythonhosted.org` |
| Rust | `crates.io`, `index.crates.io`, `static.crates.io`, `static.rust-lang.org` |
| Go | `proxy.golang.org`, `sum.golang.org` |
| Java | `repo1.maven.org`, `repo.maven.apache.org`, `services.gradle.org`, `plugins.gradle.org`, `plugins-artifacts.gradle.org` |
| Ruby | `rubygems.org`, `index.rubygems.org` |
| PHP | `packagist.org`, `repo.packagist.org` |
| Ubuntu (apt) | `archive.ubuntu.com`, `security.ubuntu.com`, `ppa.launchpad.net` |
| Containers | `registry-1.docker.io`, `auth.docker.io`, `production.cloudflare.docker.com`, `download.docker.com`, `ghcr.io` |
Network access is granted per host, not per operation. The sandbox can send any request to an allowed host, including uploads such as `git push` and package publishing, with any credential the command supplies. If the agent processes untrusted input (repository files, fetched web content, or third-party tool output), a successful prompt injection could use an allowed host to copy files out of the sandbox. To reduce this risk, set the `bash` tool's [permission policy](managed-agents-permission-policies.md) to `always_ask` or `auto`. If the environment does not specify `packages`, you can instead leave `allow_package_managers` set to `false` and list only the hosts your agent needs in `allowed_hosts`.
#### Risks of unrestricted networking
With `unrestricted` networking, code in the sandbox can send requests to any host on the internet, except for hosts on a general safety blocklist. Before you choose this mode, consider what the agent can do with that access:
- **The agent can change things on external sites, not only read them:** The `bash` tool can send any request. The agent can post data, submit forms, call APIs, and run scripts that change data on external sites. Even a request that only fetches a URL can change data on some sites.
- **Nothing pauses these requests by default:** The agent toolset's default [permission policy](managed-agents-permission-policies.md) is `always_allow`, so `bash` commands run without approval.
- **Anything in the sandbox can leave it:** This includes files, tool outputs, and any credentials or secrets you put in the sandbox.
- **Fetched content can steer the agent:** Web pages, API responses, and other content the agent reads can contain instructions (prompt injection) that change what it does next.
- **The agent acts on your behalf:** Its actions can violate a site's terms of service, or create accounts and records there.
- **Model behavior is not a security control:** The agent can act on external sites in ways you did not ask for, including retrying in a different way after a site blocks a request. Use network settings and permission policies to limit what it can do.
- **The safety blocklist is not an allowlist:** It does not limit which other sites the agent reaches, or what the agent does on them.
To reduce these risks, use `limited` networking with an explicit list of hosts. The following `networking` value allows `api.example.com`, plus the [package manager hosts](#package-manager-hosts) for an agent that installs packages:
```python
{
"type": "limited",
"allowed_hosts": ["api.example.com"],
"allow_package_managers": true
}
```
An agent that only uses the `web_search` and `web_fetch` tools does not need `unrestricted` networking if you can list the sites it needs. [Networking](#networking) says when `allowed_hosts` applies to those tools. Where it does, list those sites in `allowed_hosts`. Listing them in `web_search`'s `allowed_domains` too makes it search those sites. A host that you add to `allowed_hosts` is also open to the sandbox. To restrict the tools further, see [Restrict web search and web fetch domains](managed-agents-tools.md#restrict-web-search-and-web-fetch-domains).
Use `unrestricted` only when the agent must reach sites you cannot list in advance. In that case, keep secrets and sensitive files out of the sandbox, and give the agent only the credentials the task needs. Consider setting the `bash` tool's permission policy to `always_ask` or `auto`, and [watch the session's events](managed-agents-events-and-streaming.md).
## Environment lifecycle
- Environments persist until explicitly archived or deleted.
- Each session gets its own sandbox instance, even when multiple sessions reference the same environment. Sessions do not share filesystem state.
- Environments are not versioned. If you update an environment frequently, keep your own record of the changes so you can tell which configuration each session used.
## Manage environments
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List environments
environments = client.beta.environments.list()
# Retrieve a specific environment
env = client.beta.environments.retrieve(environment.id)
# Archive an environment (read-only, existing sessions continue)
client.beta.environments.archive(environment.id)
# Delete an environment (only if no sessions reference it)
client.beta.environments.delete(environment.id)
```
## Pre-installed runtimes
Cloud sandboxes include common language runtimes, databases, and command-line tools out of the box. See [Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md) for the full list.
## Next steps
[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
Pre-installed packages, databases, and utilities available in cloud sandboxes.
[Start a session](managed-agents-sessions.md)
Create a session to run your agent and start running tasks.
---
## Preview responses with event deltas - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/event-deltas
Path: 04-API-Reference/Other/managed-agents-event-deltas.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fevent-deltas)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Event deltas](managed-agents-event-deltas.md)[Inspect and track usage](managed-agents-session-observability.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Preview responses with event deltas
Copy page
Render the agent's response text as a live preview while the model is still generating it.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
By default, the agent's response text reaches the [session event stream](managed-agents-events-and-streaming.md) as buffered `agent.message` events. Each one is emitted only after the model request that produced it finishes. Event deltas let you render that text incrementally, as a live preview, while the model is still generating it.
Previews are a best-effort display aid, and the buffered `agent.message` is always the authoritative record. A client that ignores previews still receives a complete, correct stream.
## Opt in to previews
Previews are opt-in per stream connection. Add the `event_deltas[]` query parameter to the stream you're reading, and repeat it once for each event type you want previewed. The accepted values are `agent.message` and `agent.thinking`. Any other value returns a 400 error, as does a request with more than 100 values.
Both stream endpoints accept the parameter:
- **Session-level stream:** `GET /v1/sessions/{session_id}/events/stream`
- **Session thread stream:** `GET /v1/sessions/{session_id}/threads/{thread_id}/stream`
A subagent's previews appear on [that subagent's own thread stream](#preview-session-thread-events).
`[]` is a shell glob pattern, so quote the URL whenever you build the request in a shell. The examples percent-encode the brackets as `%5B%5D`, which also works.
### Preview events
When a previewed event begins, the stream emits an `event_start` carrying the upcoming event's type and `id`:
```python
{
"type": "event_start",
"event": {
"type": "agent.message",
"id": "sevt_01abc..."
}
}
```
For `agent.message`, the start is followed by `event_delta` events carrying incremental text. Each delta names the event it extends in `event_id` and the content block it extends in `delta.index`:
```python
{
"type": "event_delta",
"event_id": "sevt_01abc...",
"delta": {
"type": "content_delta",
"index": 0,
"content": {
"type": "text",
"text": "Here is the summary"
}
}
}
```
For `agent.thinking`, only the `event_start` is emitted, as a signal that a thinking block has started. No `event_delta` events follow. The buffered `agent.thinking` event that concludes the preview is a progress signal and carries no thinking content.
Unlike persisted events, `event_start` and `event_delta` have no `id` or `processed_at` of their own. The only identifier they carry is the `id` of the event they preview. Their type strings are also the exception to the `{domain}.{action}` naming convention of persisted events.
Event deltas use a different wire format from [Streaming messages](../Guides/build-with-claude-streaming.md), and the difference is intentional. A previewed `agent.message` gets a single `event_start` followed only by `event_delta` events. There are no per-content-block start or stop events and no stop event for the previewed event itself. The delta type is `content_delta`, not `content_block_delta`. Accumulator code written for the Messages API does not carry over unchanged.
## Accumulate and reconcile
Every SDK that supports event deltas includes an [accumulator helper](#sdk-accumulator-helpers) that handles the `index` bookkeeping for you. The manual pattern in this section works in every language when you need custom bookkeeping. Apply it to the generated event types.
In the manual pattern, hold preview text in a temporary map keyed by `(event_id, index)`, and treat the buffered event as the record. Reconcile the two per model request.
A turn opens with a single `session.status_running` event. On a turn that completes normally, each model request then produces these events, in order:
1. `span.model_request_start`
2. `event_start`
3. The `event_delta` events
4. The buffered `agent.message`
5. [`span.model_request_end`](managed-agents-reference.md#event-types) (in the Span events tab)
On the wire, this is the previewed portion of that sequence, interleaved with the connection's other buffered events:
``` block
event_start {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message {"id": "sevt_01abc...", "content": [...]}
```
The `event_delta` line repeats once per text fragment. Process each event as it arrives:
1. On `event_start`, note the announced `id`. The identifiers always line up: `event_start.event.id`, every `event_delta.event_id`, and the buffered `agent.message`'s `id` are the same value.
2. On each `event_delta`, append `delta.content.text` to the entry at `(event_id, delta.index)` and render the running text. The first delta for an `index` creates that entry.
3. When the buffered `agent.message` arrives, match it by `id`, discard the accumulated preview, and render the message's content instead.
4. On `span.model_request_end`, close any preview that has not been reconciled by its buffered event. No more deltas are coming for it. If the turn errors or is interrupted, the buffered event might never arrive, but `span.model_request_end` still does.
The pattern relies on two guarantees:
- Concatenating a preview's deltas in arrival order, keyed by `(event_id, index)`, gives a prefix of `content[index].text` in the buffered event. It is not necessarily the whole text, because deltas might be [shed under load](#limitations).
- A connection emits at most one `event_start` per `event_id`, and the buffered event is the last thing that connection delivers for that `id`.
### SDK accumulator helpers
Each SDK's helper handles the `index` bookkeeping. The Go, Java, Ruby, and C# helpers also key the accumulating preview by the event's `id`. With the Python, TypeScript, and PHP helpers, keep that map yourself and fold each delta into the entry for its `id`.
The following examples opt in to `agent.message` previews and reconcile them with the buffered event:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Preview snapshots, keyed by event id. accumulate_managed_agents_event folds each
# event_start / event_delta into an agent.message snapshot; the buffered
# agent.message replaces it.
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}
# Opt in to agent.message previews on this connection
with client.beta.sessions.events.stream(
session.id, event_deltas=["agent.message"]
) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "Describe the repo in one sentence."}],
},
],
)
for event in stream:
match event.type:
case "event_start":
snapshot = accumulate_managed_agents_event(None, event)
if snapshot is not None:
previews[event.event.id] = snapshot
print(f"event_start {event.event.type} {event.event.id}")
case "event_delta":
preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
if preview is not None:
previews[event.event_id] = preview
text = "".join(block.text for block in preview.content)
print(f"event_delta preview: {text!r}")
case "agent.message":
# The buffered event is the record: it replaces and closes the preview
preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
text = "".join(block.text for block in preview.content)
print(f"agent.message {event.id} {text!r}")
case "span.model_request_end":
# No more deltas are coming. Close any preview whose
# buffered event never arrived.
for event_id in previews:
print(f"span.model_request_end closing preview for {event_id}")
previews.clear()
case "session.status_idle":
break
```
## Preview session thread events
In a [multiagent](managed-agents-multiagent-orchestration.md) session, every session thread has its own event stream. It takes the same `event_deltas[]` parameter with the same values.
A connection previews only the thread it's reading. The session-level stream previews the primary thread, and a child thread's previews are never cross-posted to it. To watch a subagent's text as the model generates it, open that subagent's thread stream.
A thread stream's path ends in `/threads/{thread_id}/stream`. `/events/stream` exists only at the session level, so there is no `/threads/{thread_id}/events/stream` endpoint.
`event_start` and `event_delta` have the same shape on a thread stream as on the session-level stream, and the [accumulate and reconcile](#accumulate-and-reconcile) pattern applies as written. Run one accumulator instance per stream connection.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List the session's threads and pick a child: child threads carry a non-null
# parent_thread_id, and the primary thread's parent_thread_id is null.
child_thread = next(
thread
for thread in client.beta.sessions.threads.list(session.id)
if thread.parent_thread_id is not None
)
# The child thread's stream takes the same event_deltas parameter as the
# session stream.
with client.beta.sessions.threads.events.stream(
child_thread.id,
session_id=session.id,
event_deltas=["agent.message"],
) as stream:
for event in stream:
match event.type:
case "event_delta":
print(event.delta.content.text, end="")
case "agent.message":
# The buffered event is the authoritative record; render its content
print()
for block in event.content:
if block.type == "text":
print(block.text, end="")
print()
case "session.thread_status_idle":
break
```
The read loop exits on [`session.thread_status_idle`](managed-agents-reference.md#event-types), the event emitted when the session thread's turn finishes and the thread goes idle.
## Limitations
- **Best effort:** Under load, the server might shed deltas for an event. When it does, you receive a contiguous prefix of the text and then no further deltas for that event. The buffered `agent.message` still arrives complete. Never treat an accumulated preview as final.
- **No replay on reconnect:** Deltas are delivered only to the connection that opted in, while it is open. This applies to the session-level stream and to each session thread stream alike. A connection opened after a model request started receives no deltas for that in-flight event. There is no way to re-request missed deltas.
- **One thread, text only:** Previews cover assistant text on [the thread the connection is reading](#preview-session-thread-events). Tool use, tool results, and MCP results are never previewed.
- **Never persisted:** `event_start` and `event_delta` exist only on the live stream. They do not appear in the session's event history (`GET /v1/sessions/{session_id}/events`) or in any session thread's event history.
## Troubleshoot previews
| You see | What it means |
|---------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| A stream with buffered events but no `event_start` or `event_delta` | The connection you're reading didn't opt in, or the turn never touched the thread you're streaming. `event_deltas[]` applies per connection, not per session. To find which thread ran, list the session's threads (`GET /v1/sessions/{session_id}/threads`). |
| A stream that drops during a preview | Deltas are not replayed. Follow the [reconnect procedure](managed-agents-events-and-streaming.md#reconnect-without-missing-events): reopen the stream and list the event history. The history includes any buffered events emitted while you were disconnected, including the `agent.message` your preview was waiting for. |
| A 404 on the stream URL | The path or an ID is wrong, or the request carries no managed-agents beta header at all. The thread endpoints are beta-gated, so without the header they don't exist. |
| A 400 naming `event_deltas` | Only `agent.message` and `agent.thinking` are accepted. |
## Next steps
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)
Coordinate multiple agents within a single session.
---
## Session event stream - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/events-and-streaming
Path: 04-API-Reference/Other/managed-agents-events-and-streaming.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fevents-and-streaming)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Session event stream
Copy page
Send events, stream responses, and interrupt or redirect your session mid-execution.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Communication with Claude Managed Agents is event-based. You send user events to the agent, and receive agent and session events back to track status.
## Event types
Events flow in two directions.
- **User events** and **system events** are what you send to the agent: `user.*` events start a session and steer it as it progresses; `system.message` appends system-level context that applies to the accompanying turn and all subsequent turns.
- **Session events**, **span events**, and **agent events** are sent to you for observability into your session state and agent progress. Stream connections that opt in also receive [event deltas](#event-deltas).
Session, span, agent, user, and system event type strings follow a `{domain}.{action}` naming convention. The stream-only delta preview events (`event_start`, `event_delta`) are the exception. See [Event types](managed-agents-reference.md#event-types) in the reference for the full catalog. [Webhook event types](managed-agents-webhooks.md#supported-event-types) are separate, and some of their names differ from the stream's (for example, `session.status_idled` rather than `session.status_idle`).
Every persisted event includes a `processed_at` timestamp set when the event finishes processing. On events you send, `processed_at` is null while the event is still queued behind earlier events. The exceptions are `user.define_outcome`, `user.custom_tool_result`, and `user.tool_result`, which are processed on receipt and echoed back with `processed_at` already populated.
## Integrating events
Sending events
Streaming events
Listing past events
Send a `user.message` event to start or continue the agent's work:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Analyze the performance of the sort function in utils.py",
},
],
},
],
)
```
Send a `user.interrupt` event to stop the agent mid-execution, then follow up with a `user.message` event to redirect it:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Agent is currently analyzing a file...
# Interrupt with a new direction:
client.beta.sessions.events.send(
session.id,
events=[
{"type": "user.interrupt"},
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Instead, focus on fixing the bug in line 42.",
},
],
},
],
)
```
The call returns as soon as the events are queued, and the interrupt's `processed_at` stays null until the agent applies it. A model response in progress stops immediately. The interrupt can take longer to apply while tool calls are running, and the session stays `running` until it does. The `user.interrupt` event then appears on the stream, and the interrupted turn ends with a `session.status_idle` event. Its `stop_reason` is `end_turn`, the same value as a turn that finishes on its own; there is no stop reason specific to interruption. The agent starts its next turn with the `user.message` you sent after the interrupt.
## Event deltas
By default, the agent's response text reaches the stream as buffered `agent.message` events, each emitted only after the model request that produced it finishes. Event deltas let you render that text incrementally, as a live preview, while the model is still generating it. A preview is not the response: previews are a best-effort display aid, and the buffered `agent.message` is always the authoritative record. A client that ignores previews still receives a complete, correct stream.
### Opt in to previews
Previews are opt-in per stream connection. Add the `event_deltas[]` query parameter to the stream you're reading, repeating it once for each event type you want previewed. Because `[]` is a shell glob pattern, quote the URL whenever you build the request in a shell; the examples percent-encode the brackets as `%5B%5D`, which also works. Both stream endpoints accept the parameter: the session-level stream at `GET /v1/sessions/{session_id}/events/stream`, and each [session thread](managed-agents-multiagent-orchestration.md)'s own stream at `GET /v1/sessions/{session_id}/threads/{thread_id}/stream`. The accepted values are `agent.message` and `agent.thinking`; any other value returns a 400 error, as does a request with more than 100 values. A subagent's previews appear on [that subagent's own thread stream](#preview-session-thread-events).
When a previewed event begins, the stream emits an `event_start` carrying the upcoming event's type and `id`:
```python
{
"type": "event_start",
"event": {
"type": "agent.message",
"id": "sevt_01abc..."
}
}
```
For `agent.message`, the start is followed by `event_delta` events carrying incremental text. Each delta names the event it extends in `event_id` and the content block it extends in `delta.index`:
```python
{
"type": "event_delta",
"event_id": "sevt_01abc...",
"delta": {
"type": "content_delta",
"index": 0,
"content": {
"type": "text",
"text": "Here is the summary"
}
}
}
```
When an `agent.thinking` event is previewed, only the `event_start` is emitted. No `event_delta` events follow, and the buffered `agent.thinking` event that concludes the preview carries no thinking content; it is a progress signal, not a content carrier.
Unlike persisted events, `event_start` and `event_delta` have no `id` or `processed_at` of their own. The only identifier they carry is the `id` of the event they preview.
Event deltas use a different wire format from [Streaming messages](../Guides/build-with-claude-streaming.md), and the difference is intentional. A previewed `agent.message` gets a single `event_start` followed only by `event_delta` events. There are no per-content-block start or stop events and no stop event for the previewed event itself. The delta type is `content_delta`, not `content_block_delta`. Accumulator code written for the Messages API does not carry over unchanged.
### Accumulate and reconcile
Every SDK that supports event deltas includes an accumulator helper that handles the `index` bookkeeping for you. The Go, Java, Ruby, and C# helpers also key the accumulating preview by the event's `id`; with the Python, TypeScript, and PHP helpers you keep that map yourself and fold each delta into the entry for its `id`. The manual pattern also works in every language when you need custom bookkeeping: apply it to the generated event types.
In the manual pattern, treat the preview as a scratch buffer and the buffered event as the record. Key the buffer by `(event_id, index)`. Reconcile per model request: a turn opens with a single `session.status_running` event, then on a turn that completes normally each model request produces, in order, `span.model_request_start`, `event_start`, the `event_delta` events, the buffered `agent.message`, and finally [`span.model_request_end`](managed-agents-reference.md#event-types) (in the Span events tab). On the wire, this is the previewed portion of that sequence, interleaved with the connection's other buffered events:
``` block
event_start {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message {"id": "sevt_01abc...", "content": [...]}
```
The `event_delta` line repeats once per text fragment. Process each event as it arrives:
1. On `event_start`, note the announced `id`. The identifiers always line up: `event_start.event.id`, every `event_delta.event_id`, and the buffered `agent.message`'s `id` are the same value.
2. On each `event_delta`, append `delta.content.text` to the entry at `(event_id, delta.index)` and render the running text. The first delta for an `index` creates that entry.
3. When the buffered `agent.message` arrives, match it by `id`, discard the accumulated preview, and render the message's content instead.
4. On `span.model_request_end`, close any preview that has not been reconciled by its buffered event. No more deltas are coming for it. If the turn errors or is interrupted, the buffered event might never arrive; `span.model_request_end` still does.
Guarantees the pattern relies on:
- Concatenating a preview's deltas in arrival order, keyed by `(event_id, index)`, gives a prefix of `content[index].text` in the buffered event (a prefix, not necessarily the whole text, because deltas might be shed under load).
- A connection emits at most one `event_start` per `event_id`, and the buffered event is the last thing that connection delivers for that `id`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Preview snapshots, keyed by event id. accumulate_managed_agents_event folds each
# event_start / event_delta into an agent.message snapshot; the buffered
# agent.message replaces it.
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}
# Opt in to agent.message previews on this connection
with client.beta.sessions.events.stream(
session.id, event_deltas=["agent.message"]
) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "Describe the repo in one sentence."}],
},
],
)
for event in stream:
match event.type:
case "event_start":
snapshot = accumulate_managed_agents_event(None, event)
if snapshot is not None:
previews[event.event.id] = snapshot
print(f"event_start {event.event.type} {event.event.id}")
case "event_delta":
preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
if preview is not None:
previews[event.event_id] = preview
text = "".join(block.text for block in preview.content)
print(f"event_delta preview: {text!r}")
case "agent.message":
# The buffered event is the record: it replaces and closes the preview
preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
text = "".join(block.text for block in preview.content)
print(f"agent.message {event.id} {text!r}")
case "span.model_request_end":
# No more deltas are coming. Close any preview whose
# buffered event never arrived.
for event_id in previews:
print(f"span.model_request_end closing preview for {event_id}")
previews.clear()
case "session.status_idle":
break
```
### Preview session thread events
In a [multiagent](managed-agents-multiagent-orchestration.md) session, every session thread has its own event stream at `GET /v1/sessions/{session_id}/threads/{thread_id}/stream`, and it takes the same `event_deltas[]` parameter with the same values. Previews are thread-scoped by design: a connection previews only the thread it's reading. A child thread's previews are delivered on that child's own stream and are never cross-posted to the session-level stream, whose previews stay scoped to the primary thread. To watch a subagent's text as the model generates it, open that subagent's thread stream.
The thread stream's path is easy to get wrong: it is `/threads/{thread_id}/stream`, not `/events/stream` (which exists only at the session level), and there is no `/threads/{thread_id}/events/stream` endpoint.
The preview events themselves don't change. `event_start` and `event_delta` have the same shape on a thread stream as on the session-level stream, and the [accumulate and reconcile](#accumulate-and-reconcile) pattern applies as written. The one adjustment is bookkeeping: run one accumulator instance per stream connection.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List the session's threads and pick a child: child threads carry a non-null
# parent_thread_id, and the primary thread's parent_thread_id is null.
child_thread = next(
thread
for thread in client.beta.sessions.threads.list(session.id)
if thread.parent_thread_id is not None
)
# The child thread's stream takes the same event_deltas parameter as the
# session stream.
with client.beta.sessions.threads.events.stream(
child_thread.id,
session_id=session.id,
event_deltas=["agent.message"],
) as stream:
for event in stream:
match event.type:
case "event_delta":
print(event.delta.content.text, end="")
case "agent.message":
# The buffered event is the authoritative record; render its content
print()
for block in event.content:
if block.type == "text":
print(block.text, end="")
print()
case "session.thread_status_idle":
break
```
The read loop exits on [`session.thread_status_idle`](managed-agents-reference.md#event-types), the event emitted when the session thread's turn finishes and the thread goes idle.
### Limitations
Previews are tuned for responsiveness. Build against these constraints:
- **Best effort:** Under load, the server might shed deltas for an event. When it does, you receive a contiguous prefix of the text and then no further deltas for that event. The buffered `agent.message` still arrives complete. Never treat an accumulated preview as final.
- **No replay on reconnect:** Deltas are delivered only to the connection that opted in, while it is open. This applies to the session-level stream and to each session thread stream alike, and a connection opened after a model request started receives no deltas for that in-flight event. If the stream drops, follow the [reconnect procedure](#integrating-events) in the Streaming events tab: reopen the stream and list the event history. The history includes any buffered events emitted while you were disconnected, including the `agent.message` your preview was waiting for. There is no way to re-request missed deltas.
- **One thread, text only:** Previews cover assistant text on the thread the connection is reading. Tool use, tool results, MCP results, and activity on any other [session thread](managed-agents-multiagent-orchestration.md) are never previewed on that connection.
- **Start-only `agent.thinking`:** An `agent.thinking` preview emits only the `event_start` as a signal that a thinking block has started; no `event_delta` events follow it.
- **Never persisted:** `event_start` and `event_delta` exist only on the live stream. They do not appear in the session's event history (`GET /v1/sessions/{session_id}/events`) or in any session thread's event history.
### Troubleshoot previews
If the stream doesn't behave as you expect:
| You see | What it means |
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| A stream with buffered events but no `event_start` or `event_delta` | The connection you're reading didn't opt in (`event_deltas[]` applies per connection, not per session), or the turn never touched the thread you're streaming. Previews are thread-scoped, so list the session's threads (`GET /v1/sessions/{session_id}/threads`) to find which one ran. |
| A 404 on the stream URL | The path or an ID is wrong, or the request carries no managed-agents beta header at all. The thread endpoints are beta-gated, so without the header they don't exist. |
| A 400 naming `event_deltas` | Only `agent.message` and `agent.thinking` are accepted. |
## Additional scenarios
### Handling custom tool calls
When the agent invokes a [custom tool](managed-agents-tools.md#custom-tools):
1. The session emits an `agent.custom_tool_use` event containing the tool name and input.
2. The session pauses with a `session.status_idle` event containing `stop_reason: requires_action`. The blocking event IDs are in the `stop_reason.event_ids` array.
3. Execute the tool in your system and send a `user.custom_tool_result` event for each, passing the event ID in the `custom_tool_use_id` parameter along with the result content.
4. Once all blocking events are resolved, the session transitions back to `running`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# Look up the custom tool use event and execute it
tool_event = events_by_id[event_id]
result = call_tool(tool_event.name, tool_event.input)
# Send the result back
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event_id,
"content": [{"type": "text", "text": result}],
},
],
)
case "end_turn":
break
```
### Tool confirmation
A tool call waits for your confirmation under an `always_ask` [permission policy](managed-agents-permission-policies.md), or under `auto` when the server reaches no determination. When that happens:
1. The session emits an `agent.tool_use` or `agent.mcp_tool_use` event.
2. The session pauses with a `session.status_idle` event whose `stop_reason.type` is `requires_action`. The blocking event IDs are in the `stop_reason.event_ids` array.
3. Send a `user.tool_confirmation` event for each, passing the event ID in the `tool_use_id` parameter. Set `result` to `"allow"` or `"deny"`. Use `deny_message` to explain a denial.
4. Once all blocking events are resolved, the session transitions back to `running`.
Each `agent.tool_use` and `agent.mcp_tool_use` event carries `evaluated_permission` (`allow`, `ask`, or `deny`), and only events whose `evaluated_permission` is `"ask"` wait for a confirmation. Most events also carry an `evaluation` object that records which policy produced that outcome, described under [See how each call was evaluated](managed-agents-permission-policies.md#see-how-each-call-was-evaluated). For example, a `bash` call paused under an `always_ask` policy appears on the stream as follows:
```python
{
"type": "agent.tool_use",
"id": "sevt_01def...",
"name": "bash",
"input": {
"command": "pip install -r requirements.txt"
},
"evaluated_permission": "ask",
"evaluation": {
"type": "always_ask"
},
"processed_at": "2026-03-25T14:01:45Z"
}
```
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
with client.beta.sessions.events.stream(session.id) as stream:
for event in stream:
if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
match stop_reason.type:
case "requires_action":
for event_id in stop_reason.event_ids:
# Approve the pending tool call
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
},
],
)
case "end_turn":
break
```
### Resuming an idle session
Sessions persist between interactions. Conversation history is preserved unless the session is explicitly deleted. When a session goes idle, its sandbox is checkpointed, preserving the full sandbox state, including the filesystem, installed packages, and any files the agent created. This allows you to resume cleanly from inactivity.
While session history is persisted until deleted, sandbox state is only preserved for 30 days after the sandbox is created. Activity does not extend this window: after 30 days the sandbox state (files, installed tools, and so on) is unrecoverable, and a resumed session starts from a fresh sandbox. If your workflow depends on sandbox contents, have the agent write important artifacts to [outputs](managed-agents-define-outcomes.md#retrieving-deliverables) before the window ends.
To resume a session, send a `user.message` event to it as usual:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Resume a previously created session by sending it a new user.message event.
# In production, pass the stored ID of the session you want to resume.
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Now run the tests against the changes you made earlier.",
},
],
},
],
)
```
### Reaching a session budget
A session created with a [budget](managed-agents-budgets.md) pauses instead of overspending. When the session's tracked list cost reaches the cap, the platform pauses each thread before its next model request, and the session goes idle with a `stop_reason` of `budget_reached` rather than terminating. The request that carried the total past the cap runs to completion, so the `list_cost` reported by the `session.usage` snapshot can read [at or a fraction past the cap](managed-agents-budgets.md#when-a-session-reaches-its-budget). On the stream, the pause arrives as three events, in order:
1. `session.thread_status_idle` with `stop_reason: budget_reached`, for each thread as it pauses.
2. `session.usage`, a snapshot of the session's cumulative usage and tracked list cost.
3. `session.status_idle` with `stop_reason: budget_reached`. The `session.usage` event always immediately precedes this idle.
A thread whose final request both crosses the cap and completes its turn reports `end_turn` on its own `session.thread_status_idle` event while the session still reports `budget_reached`; key on the session-level `stop_reason` to detect the pause.
While the session is at its cap, it accepts only the events that settle work already in flight: `user.tool_confirmation`, `user.tool_result`, `user.custom_tool_result`, and `user.interrupt`. Any event that would start new work, including `user.message`, is rejected with a 400 error naming that list. When a session has both a thread waiting on a tool ask and a thread paused at the cap, the session-level `stop_reason` is `requires_action`, not `budget_reached`: settling the ask doesn't trigger a model request, so respond to it as usual.
No event resumes a session paused at its cap. Instead, update the session's budget: changing the cap to any value above the consumed list cost, or removing the budget by updating the session with `"budget": null`, resumes the paused work automatically. See [Session budgets](managed-agents-budgets.md) for how list cost is tracked and the full budget update semantics.
### Sending system messages
`system.message` is supported by Claude Fable 5.1, Claude Mythos 5.1, Claude Fable 5, Claude Mythos 5, Claude Opus 5.5, Claude Opus 5, Claude Opus 4.8, and Claude Sonnet 5.5. If the agent's primary model does not support mid-conversation system injection, the event is rejected with a `model_does_not_support_mid_conversation_system` validation error. Subagent models are not checked, because `system.message` lands on the primary thread only.
Send a `system.message` event to give the agent privileged system-level context that applies to the accompanying turn and all subsequent turns. Unlike the `system` field on the agent definition (which sets the top-level system prompt), `system.message` content is appended to the session's system context as a `role: "system"` turn rather than replacing that prompt. Use it when the agent needs updated system-level guidance mid-session: a different persona, revised constraints, or context fetched at runtime that should shape the model's behavior going forward.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "system.message",
"content": [
{
"type": "text",
"text": "The user's current timezone is America/New_York.",
},
],
},
],
)
```
While the session is idle with `stop_reason: requires_action`, a `system.message` is accepted only when it trails a tool result event in the same request; sent on its own or with a `user.message`, it is rejected until the pending tool events are resolved. `content` accepts 1–1000 text items.
### Tracking usage
The session object includes a `usage` field with the session's cumulative usage: token counts, server tool use, active time, and the tracked list cost. Fetch the session after it goes idle to read the latest totals.
```python
{
"id": "sesn_01...",
"status": "idle",
"usage": {
"input_tokens": 5000,
"output_tokens": 3200,
"cache_read_input_tokens": 20000,
"cache_creation": {
"ephemeral_5m_input_tokens": 2000,
"ephemeral_1h_input_tokens": 0
},
"list_cost": {
"amount": "187",
"currency": "USD"
},
"active_seconds": 342.5,
"server_tool_use": {
"web_search_requests": 3,
"web_fetch_requests": 0
}
}
}
```
`input_tokens` reports uncached input tokens and `output_tokens` reports total output tokens across all model calls in the session. The `cache_read_input_tokens` field reports tokens read from the prompt cache, and the `cache_creation` object breaks down cache-creation tokens by cache lifetime (`ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens`). Cache entries use a 5-minute TTL by default, so back-to-back turns within that window benefit from cache reads, which reduce per-token cost.
`list_cost` is the session's cumulative consumption priced at public list rates, as a whole number of cents in a string, with a currency code. `active_seconds` is the cumulative time during which the session had at least one thread running; overlapping activity from concurrent threads is counted once, unlike the `active_seconds` in the session's `stats` object, which sums each thread's own active time. This deduplicated figure is the duration the session's runtime cost is priced on. `server_tool_use` counts server-executed tool requests for pricing: web search requests are priced into list cost per request, and web fetch requests carry no per-request charge and aren't metered, so `web_fetch_requests` reads `0`. Each [session thread](managed-agents-multiagent-orchestration.md)'s own `usage` carries `list_cost` and `active_seconds` too. Per-thread figures are rounded independently and exclude the session's running-time cost, so they don't sum exactly to the session's `list_cost`; the session figure is the authoritative one.
You don't have to poll the session to observe these totals. The `session.usage` event carries the same cumulative snapshot (the `usage` object, plus the session's `budget`, which is `null` when the session has none) on the session stream and in the event history. It is emitted on idle transitions rather than on a timer: the session emits one immediately before it goes idle, whatever the stop reason, and one when a thread pauses at a [session budget](managed-agents-budgets.md). A stream reader therefore sees the final cost of a turn, or of the work that hit a budget, without an extra fetch.
To enforce a spend limit, set a [session budget](managed-agents-budgets.md) rather than polling usage and stopping the session yourself. The platform prices the session's consumption continuously and pauses each thread before its next model request once the session's list cost reaches the cap; see [Reaching a session budget](#reaching-a-session-budget) for what that looks like on the stream.
## Console observability
The Claude Console includes a session viewer for inspecting what an agent did without writing any code. In the Console sidebar, under **Managed Agents**, select **Sessions** to see every session in the workspace with its status, agent, token usage, cost, and creation time, then select a session to open it. The session viewer is only accessible to Developers and Admins. It shows:
- **Timeline minimap:** A zoomable overview of the session's activity over time, with one lane per thread in [multiagent](managed-agents-multiagent-orchestration.md) sessions. Select a lane to view that thread, or select a mark to jump to its event.
- **Transcript:** The conversation grouped by model request, including thinking, tool calls with their inputs and results, and message text as it streams. You can filter the events and copy or download them as JSON.
- **Inspector:** A resizable side panel with details about the session, in five tabs:
- **Session** shows the session's details and metadata, its cumulative cost over time, and spend against the session's [budget](managed-agents-budgets.md) when one is set.
- **Events** lists every raw event on the current thread in the order the server sent it; select an event to see its JSON. A message that streamed while the page was open also has a **Deltas** view of its [event deltas](#event-deltas).
- **Tools** lists the tools the session's agents are configured with, along with call counts, failures, and median duration; select a tool to see its calls and jump to one in the transcript.
- **Resources** lists mounted [files](managed-agents-files.md), [repositories](managed-agents-github.md), and [memory stores](managed-agents-memory.md) at their container paths, including the memories in each store and the changes this session made to them, plus files the agent wrote to `/mnt/session/outputs` and the [skills](managed-agents-skills.md) attached to the session's agents.
- **Threads** lists every thread with its status, context size, and cost. Select a thread to view its details, such as the agent, model, context usage, and cost.
Append `?event={event_id}` to a session URL to open the session at a specific event.
With `ant beta:sessions connect`, you can open the same viewer from the `ant` CLI or follow the session in your terminal. See [Connect to a Managed Agents session from your terminal](cli-sdks-libraries-cli-sessions-connect.md).
## Debugging tips
- **Check session events:** Session errors are conveyed through the `session.error` event
- **Review tool results:** Tool execution failures often explain unexpected agent behavior
- **Track token usage:** Monitor token consumption to optimize prompts and reduce costs
- **Use system prompts:** Add logging instructions to the system prompt so the agent summarizes what it did and what it found
- **Troubleshoot previews:** If a stream that opts in to event deltas doesn't behave as you expect, see [Troubleshoot previews](#troubleshoot-previews)
---
## Adding files - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/files
Path: 04-API-Reference/Other/managed-agents-files.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Ffiles)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Manage agent context
# Adding files
Copy page
Upload files and mount them in your sandbox for reading and processing.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
You can provide files to your agent by uploading them through the Files API and mounting them in the session's sandbox.
## Uploading files
First, upload a file using the [Files API](../Guides/build-with-claude-files.md):
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
file = client.files.upload(file=Path("data.csv"))
print(f"File ID: {file.id}")
```
## Mounting files in a session
Mount uploaded files into the sandbox by adding them to the `resources` array when creating a session:
The `mount_path` is optional, but make sure the uploaded file has a descriptive name so the agent can identify it.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
resources=[
{
"type": "file",
"file_id": file.id,
"mount_path": "/data.csv",
},
],
)
```
With the preceding `mount_path`, the agent reads the file at `/mnt/session/uploads/data.csv` (see [File paths](#file-paths)).
A new `file_id` is created that references the instance of the file in the session. These copies do not count against your [storage limits](../Guides/build-with-claude-files.md).
## Multiple files
Mount multiple files by adding entries to the `resources` array:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
resources = [
{"type": "file", "file_id": "file_abc123", "mount_path": "/data.csv"},
{"type": "file", "file_id": "file_def456", "mount_path": "/config.json"},
{"type": "file", "file_id": "file_ghi789", "mount_path": "/src/main.py"},
]
```
A maximum of 500 files is supported per session.
## Managing files on a running session
You can add or remove files from a session after creation using the session resources API. Each resource has an `id` returned when it is added (or listed), which you use for deletes.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
resource = client.beta.sessions.resources.add(
session.id,
type="file",
file_id=file.id,
)
print(resource.id) # "sesrsc_01ABC..."
```
List all resources on a session with `client.beta.sessions.resources.list()`. To remove a file, call `client.beta.sessions.resources.delete()` with the resource ID:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
listed = client.beta.sessions.resources.list(session.id)
for entry in listed.data:
print(entry.id, entry.type)
client.beta.sessions.resources.delete(resource.id, session_id=session.id)
```
## Listing and downloading session files
Use the [Files API](../Guides/build-with-claude-files.md) to list files scoped to a session and download them. Files the agent writes to `/mnt/session/outputs/` appear in the list shortly after the agent finishes writing them, sometimes a few seconds after the session goes idle. If an output file you expect is missing, list again after a short delay; once it appears in the list, its upload has finished.
Filtering by `scope_id` requires the `managed-agents-2026-04-01` beta header, so the list examples call `client.beta.files.list()` and pass that header explicitly.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List files associated with a session
files = client.beta.files.list(
scope_id="sesn_abc123",
betas=["managed-agents-2026-04-01"],
)
for file in files:
print(file.id, file.filename)
# Download a file
content = client.files.download(files.data[0].id)
content.write_to_file("output.txt")
```
## Supported file types
The agent can work with any file type, including:
- Source code (`.py`, `.js`, `.ts`, `.go`, `.rs`, and others)
- Data files (`.csv`, `.json`, `.xml`, `.yaml`)
- Documents (`.txt`, `.md`)
- Archives (`.zip`, `.tar.gz`) - the agent can extract these using bash
- Binary files - the agent can process these with appropriate tools
## File paths
Files mounted in the sandbox are read-only copies. The agent can read them but cannot modify the original uploaded file. To work with modified versions, the agent writes to new paths within the sandbox.
- The path you specify is rooted under the session's uploads directory: a `mount_path` of `/data.csv` places the file at `/mnt/session/uploads/data.csv` in the sandbox
- If you omit `mount_path`, the file is placed at `/mnt/session/uploads/<file_id>`
- Parent directories are created automatically
- Paths should be absolute (starting with `/`)
- Files the agent writes to `/mnt/session/outputs/` become available through the Files API, scoped to the session; see [Listing and downloading session files](#listing-and-downloading-session-files)
---
## Accessing GitHub - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/github
Path: 04-API-Reference/Other/managed-agents-github.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fgithub)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Manage agent context
# Accessing GitHub
Copy page
Connect your agent to GitHub repositories for cloning, reading, and creating pull requests.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
You can mount a GitHub repository to your session sandbox and connect to the GitHub MCP for making pull requests.
## GitHub MCP and session resources
First, create an agent that declares the GitHub MCP server. The agent definition holds the server URL but no authentication token:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply code-reviewer.md
```
code-reviewer.md
```python
---
name: Code Reviewer
model: claude-opus-5-5
mcp_servers:
- type: url
name: github
url: https://api.githubcopilot.com/mcp/
tools:
- type: agent_toolset_20260401
- type: mcp_toolset
mcp_server_name: github
---
You are a code review assistant with access to GitHub.
```
Then create a session that mounts the GitHub repository. A `limited` [environment](managed-agents-environments.md#networking) blocks an agent's MCP servers unless its networking sets `allow_mcp_servers: true` or lists each server's host in `allowed_hosts`. With neither set, session creation fails with a 400 error.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
resources=[
{
"type": "github_repository",
"url": "https://github.com/org/repo",
"mount_path": "/workspace/repo",
"authorization_token": "ghp_your_github_token",
},
],
)
```
A `github_repository` resource accepts the following fields:
| Field | Description |
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Required. Must be `"github_repository"`. |
| `url` | Required. The repository's HTTPS URL in the form `https://github.com/<owner>/<repo>`, without a `.git` suffix. Other forms, including SSH URLs, are rejected with an `invalid_request_error`. |
| `authorization_token` | Required. The GitHub token used to clone the repository. It is not echoed in API responses. See [Token permissions](#token-permissions). |
| `mount_path` | Optional. The directory under `/workspace` to clone the repository into. Defaults to `/workspace/<repo-name>`. |
| `checkout` | Optional. A branch (`{"type": "branch", "name": "main"}`) or commit (`{"type": "commit", "sha": "..."}`) to check out. Defaults to the repository's default branch. |
Mounting a repository also loads any skills stored in its root `.claude/skills` directory. Skills are discovered once per session, from the repository state checked out at session start. See [Load skills from a GitHub repository](managed-agents-skills.md#load-skills-from-a-github-repository).
## Token permissions
When providing a GitHub token, use the minimum required permissions:
| Action | Required scopes |
|---------------------|-----------------------------------|
| Clone private repos | `repo` |
| Create PRs | `repo` |
| Read issues | `repo` (private) or `public_repo` |
| Create issues | `repo` (private) or `public_repo` |
Use fine-grained personal access tokens with minimum required permissions. Avoid using tokens with broad access to your GitHub account.
## Multiple repositories
Mount multiple repositories by adding entries to the `resources` array:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
resources = [
{
"type": "github_repository",
"url": "https://github.com/org/frontend",
"mount_path": "/workspace/frontend",
"authorization_token": "ghp_your_github_token",
},
{
"type": "github_repository",
"url": "https://github.com/org/backend",
"mount_path": "/workspace/backend",
"authorization_token": "ghp_your_github_token",
},
]
```
## Managing repositories on a running session
After a session is created, you can list its repository resources and rotate their authorization tokens. Each resource has an `id` returned at session creation time (or through `client.beta.sessions.resources.list()`) that you use for updates. Repositories are attached for the lifetime of the session; to change which repositories are mounted, create a new session.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# List resources on the session
listed = client.beta.sessions.resources.list(session.id)
repo_resource_id = listed.data[0].id
print(repo_resource_id) # "sesrsc_01ABC..."
# Rotate the authorization token
client.beta.sessions.resources.update(
repo_resource_id,
session_id=session.id,
authorization_token="ghp_your_new_github_token",
)
```
## Creating pull requests
With the GitHub MCP server, the agent can create branches, commit changes, and push them:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Fix the type error in src/utils.ts, commit it to a new branch, and push it.",
},
],
},
],
)
```
## Next steps
[Session event stream](managed-agents-events-and-streaming.md)
Stream events and steer the agent while it opens the pull request
[MCP connector](managed-agents-mcp-connector.md)
Connect more MCP servers to give the agent additional tools
[Adding files](managed-agents-files.md)
Mount files in the sandbox alongside your repositories
---
## Session event stream
Source: https://platform.claude.com/docs/en/managed-agents/how-managed-agents-work
Path: 04-API-Reference/Other/managed-agents-how-managed-agents-work.md
# Session event stream
Send events, stream responses, and interrupt or redirect your session mid-execution.
---
> Note: The original URL `/docs/en/managed-agents/how-managed-agents-work` returned 404. This content is sourced from the events-and-streaming page, which covers how managed agents work through their event-based communication model.
Communication with Claude Managed Agents is event-based. You send user events to the agent, and receive agent and session events back to track status.
> All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically.
## Event types
Events flow in two directions.
- **User events** are what you send to the agent to kick off a session and steer it as it progresses.
- **Session events**, **span events**, and **agent events** are sent to you for observability into your session state and agent progress.
Event type strings follow a `{domain}.{action}` naming convention.
### User events
| Type | Description |
| --- | --- |
| `user.message` | A user message with text content. |
| `user.interrupt` | Stop the agent mid-execution. |
| `user.custom_tool_result` | Response to a custom tool call from the agent. |
| `user.tool_confirmation` | Approve or deny an agent or MCP tool call when a permission policy requires confirmation. |
| `user.define_outcome` | Define an [outcome](managed-agents-define-outcomes.md) for the agent to work toward. |
### Agent events
| Type | Description |
| --- | --- |
| `agent.message` | Agent response containing text content blocks. |
| `agent.thinking` | Agent thinking content, emitted separately from messages. |
| `agent.tool_use` | Agent invoked a pre-built agent tool (bash, file operations, and so on). |
| `agent.tool_result` | Result of a pre-built agent tool execution. |
| `agent.mcp_tool_use` | Agent invoked an MCP server tool. |
| `agent.mcp_tool_result` | Result of an MCP tool execution. |
| `agent.custom_tool_use` | Agent invoked one of your custom tools. Respond with a `user.custom_tool_result` event. |
| `agent.thread_context_compacted` | Conversation history was compacted to fit the context window. |
| `agent.thread_message_sent` | Agent sent a message to another [multiagent](managed-agents-multi-agent.md) thread. |
| `agent.thread_message_received` | Agent received a message from another [multiagent](managed-agents-multi-agent.md) thread. |
### Session events
| Type | Description |
| --- | --- |
| `session.status_running` | Agent is actively processing. |
| `session.status_idle` | Agent finished its current task and is waiting for input. Includes a `stop_reason` indicating why the agent stopped. |
| `session.status_rescheduled` | A transient error occurred and the session is retrying automatically. |
| `session.status_terminated` | Session ended due to an unrecoverable error. |
| `session.error` | An error occurred during processing. Includes a typed `error` object with a `retry_status`. |
| `session.outcome_evaluated` | An [outcome](managed-agents-define-outcomes.md) evaluation has reached a terminal status. |
| `session.thread_created` | The coordinator spawned a new [multiagent](managed-agents-multi-agent.md) thread. |
| `session.thread_idle` | A [multiagent](managed-agents-multi-agent.md) thread finished its current work. |
### Span events
Span events are observability markers that wrap activity for timing and usage tracking.
| Type | Description |
| --- | --- |
| `span.model_request_start` | A model inference call has started. |
| `span.model_request_end` | A model inference call has completed. Includes `model_usage` with token counts. |
| `span.outcome_evaluation_start` | [Outcome](managed-agents-define-outcomes.md) evaluation has started. |
| `span.outcome_evaluation_ongoing` | Heartbeat during an ongoing [outcome](managed-agents-define-outcomes.md) evaluation. |
| `span.outcome_evaluation_end` | [Outcome](managed-agents-define-outcomes.md) evaluation has completed. |
Every event includes a `processed_at` timestamp indicating when the event was recorded server-side. If `processed_at` is null, it means the event has been queued by the harness and will be handled after preceding events finish processing.
See the [session events API reference](../Endpoints/beta-sessions-events-stream.md) for the full schema of each event type.
## Integrating events
### Sending events
Send a `user.message` event to start or continue the agent's work:
```python
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Analyze the performance of the sort function in utils.py",
},
],
},
],
)
```
```typescript
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [
{
type: "text",
text: "Analyze the performance of the sort function in utils.py"
}
]
}
]
});
```
Send a `user.interrupt` event to stop the agent mid-execution, then follow up with a `user.message` event to redirect it:
```python
# Agent is currently analyzing a file...
# Interrupt with a new direction:
client.beta.sessions.events.send(
session.id,
events=[
{"type": "user.interrupt"},
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Instead, focus on fixing the bug in line 42.",
},
],
},
],
)
```
The agent will acknowledge the interruption and switch to the new task.
### Streaming responses
Stream events from the session to receive real-time updates as the agent works. Only events emitted after the stream is opened are delivered, so open the stream before sending events to avoid a race condition.
```python
# Open the stream first, then send the user message
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{"type": "text", "text": "Summarize the repo README"},
],
},
],
)
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
if block.type == "text":
print(block.text, end="")
case "agent.tool_use":
print(f"\n[Tool: {event.name}]")
case "session.status_idle":
print("\nDone.")
break
```
```typescript
const stream = await client.beta.sessions.events.stream(session.id);
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [
{ type: "text", text: "Summarize the repo README" }
]
}
]
});
for await (const event of stream) {
if (event.type === "agent.message") {
for (const block of event.content) {
if (block.type === "text") process.stdout.write(block.text);
}
} else if (event.type === "agent.tool_use") {
console.log(`\n[Tool: ${event.name}]`);
} else if (event.type === "session.status_idle") {
console.log("\nDone.");
break;
}
}
```
### Handling custom tool calls
When the agent calls a custom tool, you receive an `agent.custom_tool_use` event and must respond with a `user.custom_tool_result` event:
```python
for event in stream:
if event.type == "agent.custom_tool_use":
# Execute the tool in your application
result = execute_custom_tool(event.name, event.input)
# Send the result back to the agent
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"tool_use_id": event.id,
"content": [{"type": "text", "text": result}],
},
],
)
```
### Fetching event history
Retrieve the full event history for a session:
```python
events = client.beta.sessions.events.list(session.id)
for event in events:
print(f"{event.type}: {event.processed_at}")
```
```typescript
const events = await client.beta.sessions.events.list(session.id);
for (const event of events.data) {
console.log(`${event.type}: ${event.processed_at}`);
}
```
---
## MCP connector - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/mcp-connector
Path: 04-API-Reference/Other/managed-agents-mcp-connector.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fmcp-connector)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# MCP connector
Copy page
Connect MCP servers to your agents for access to external tools and data sources.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Claude Managed Agents supports connecting [Model Context Protocol (MCP)](https://modelcontextprotocol.io) servers to your agents. This gives the agent access to external tools, data sources, and services through a standardized protocol.
MCP configuration is split across two steps:
1. **Agent creation** declares which MCP servers the agent connects to, by name and URL.
2. **Session creation** supplies authentication for those servers by referencing a pre-registered vault (see [Authenticate with vaults](managed-agents-vaults.md)).
This separation keeps secrets out of reusable agent definitions while letting each session authenticate with its own credentials.
## Declare MCP servers on the agent
Specify MCP servers in the `mcp_servers` array when creating an agent. Each server needs a `type`, a unique `name`, and a `url`. No authentication tokens are provided at this stage.
Each declared server also needs a matching `mcp_toolset` entry in the `tools` array. The toolset's `mcp_server_name` must match the server's `name`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply github-assistant.md
```
github-assistant.md
```python
---
name: GitHub Assistant
model: claude-opus-5-5
mcp_servers:
- type: url
name: github
url: https://api.githubcopilot.com/mcp/
tools:
- type: agent_toolset_20260401
- type: mcp_toolset
mcp_server_name: github
---
```
The MCP toolset defaults to a permission policy of `always_ask`, which requires user approval before each tool call. See [permission policies](managed-agents-permission-policies.md) to configure this behavior.
### `mcp_servers` field reference
Each entry in the `mcp_servers` array defines one connection.
| Field | Description |
|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Required. Must be `"url"`. |
| `name` | Required. A unique name for this server within the agent (1–255 characters). Used as the `mcp_server_name` in the `tools` array and surfaced on MCP tool events in the [session event stream](managed-agents-events-and-streaming.md). |
| `url` | Required. The endpoint of the remote MCP server (up to 2,048 characters). See [Supported MCP server types](managed-agents-reference.md#supported-mcp-server-types) for transport requirements. |
Constraints:
- An agent can declare up to 20 MCP servers. Server names must be unique within the array.
- Every `mcp_servers` entry must be referenced by an `mcp_toolset` in the `tools` array, and every `mcp_toolset` must reference a declared server. The API rejects agent definitions with unreferenced servers or dangling toolsets.
## Configure which MCP tools are available
The `mcp_toolset` entry supports a `default_config` object and a `configs` array, applied to the tools the MCP server exposes. Each `configs` entry accepts only `name`, `enabled`, and `permission_policy`. Unlike entries in the built-in agent toolset, MCP tool entries do not take a `type` field, and the [web settings](managed-agents-tools.md#restrict-web-search-and-web-fetch-domains) available on `web_search` and `web_fetch` do not apply to MCP tools. The `name` in each `configs` entry is the bare tool name as reported by the server.
By default all tools exposed by the MCP server are enabled. To enable only specific tools, set `default_config.enabled` to `false` and explicitly enable the tools you want:
```python
{
"type": "mcp_toolset",
"mcp_server_name": "github",
"default_config": { "enabled": false },
"configs": [
{ "name": "get_issue", "enabled": true },
{ "name": "list_issues", "enabled": true },
{ "name": "add_issue_comment", "enabled": true }
]
}
```
This pattern is useful when a server exposes many tools but the agent only needs a few, or when you want tools added by the server operator to stay off until you review them.
To disable specific tools while keeping the rest enabled, omit `default_config` and set `enabled: false` on individual entries:
```python
{
"type": "mcp_toolset",
"mcp_server_name": "github",
"configs": [{ "name": "delete_repository", "enabled": false }]
}
```
See [configuring the toolset](managed-agents-tools.md#configuring-the-toolset) for the general `default_config` / `configs` pattern, and [MCP toolset permissions](managed-agents-permission-policies.md#mcp-toolset-permissions) for setting `permission_policy` on MCP tools and handling confirmation requests.
### MCP tool output handling
When an MCP tool output exceeds 100,000 characters (about 25,000 tokens), it is automatically written to a file in the sandbox. The model receives a truncated preview with the file path and can read the full content from there.
## Provide authentication at session creation
When starting a session, pass `vault_ids` to provide credentials for your MCP servers. Vaults are collections of credentials that you register once and reference by ID. See [Authenticate with vaults](managed-agents-vaults.md) for how to create vaults and manage credentials.
A `limited` [environment](managed-agents-environments.md#networking) blocks an agent's MCP servers unless its networking sets `allow_mcp_servers: true` or lists each server's host in `allowed_hosts`. With neither set, session creation fails with a 400 error.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
vault_ids=[vault.id],
)
```
Credentials are matched by URL, so the vault must contain a credential whose `mcp_server_url` refers to the same server as the `url` declared in `mcp_servers`. Both URLs are normalized before matching (scheme and host lowercased, default ports and trailing slashes stripped), so differences in host casing, a default port, or a trailing slash don't prevent a match; a different path, subdomain, or non-default port does. If none matches, the connection is attempted unauthenticated. See [Add a credential](managed-agents-vaults.md#add-a-credential) for the `static_bearer` and `mcp_oauth` credential types.
### Handle connection and authentication failures
Session creation does not validate MCP connectivity or credentials. If an MCP server is unreachable or rejects the supplied credential, the session still starts and interaction remains possible. A [`session.error`](managed-agents-events-and-streaming.md) event is emitted with the `mcp_server_name` of the affected server and a `retry_status`:
| Error type | Meaning |
|-----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `mcp_connection_failed_error` | The MCP server could not be reached (network error, timeout, or non-authentication HTTP failure). |
| `mcp_authentication_failed_error` | Authentication with the MCP server failed: the server rejected the credential from the attached vault, required authentication when no matching credential was configured, or an OAuth token refresh failed. |
You can decide whether to block further interaction on this error, trigger a credential rotation, or let the session continue without the affected server's tools. The connection is retried on the next `session.status_idle` to `session.status_running` transition.
## Next steps
[Permission policies](managed-agents-permission-policies.md)
Control when agent and MCP tools run.
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
[Supported MCP server types](managed-agents-reference.md#supported-mcp-server-types)
Transport requirements for remote MCP servers.
---
## Using agent memory - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/memory
Path: 04-API-Reference/Other/managed-agents-memory.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fmemory)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
[Memory stores](managed-agents-memory.md)[Dreams](managed-agents-dreams.md)
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Build persistent memory
# Using agent memory
Copy page
Give your agents persistent memory that survives across sessions using memory stores.
Copy page
Using agent memory
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
agent-memory-2026-07-22
Each Managed Agents session starts with a fresh context by default. When a session ends, any state the agent built up is gone. Memory stores let the agent carry information across sessions: user preferences, project conventions, prior mistakes, and domain context.
Don't combine `agent-memory-2026-07-22` with `managed-agents-2026-04-01` on a memory store request: sending both returns a `400` error. If your code sets beta headers explicitly, replace `managed-agents-2026-04-01` with `agent-memory-2026-07-22` on memory store calls rather than adding a second value. Session endpoints, including attaching a memory store to a session, still use `managed-agents-2026-04-01`.
`GET /v1/memory_stores/{memory_store_id}/memories` behaves the same under either header: results come back in a stable, server-defined order, and `path_prefix` and `depth` apply the same way.
## Overview
A **memory store** is a workspace-scoped collection of text documents optimized for Claude. When you attach a store to a session, it is mounted as a directory inside the session's sandbox. The agent reads and writes it with the same file tools it uses for the rest of the filesystem, and a note describing each mount is automatically added to the system prompt, telling the agent where to look. The [agent toolset](managed-agents-tools.md) is required for these interactions; make sure to enable it during [agent creation](managed-agents-agent-setup.md). On [self-hosted sandboxes](managed-agents-self-hosted-sandboxes-memory.md), that directory is not a live mount. Instead, your environment worker downloads each attached store into your sandbox before the agent's tools run and keeps that copy in sync with the store.
Each **memory** in a store is addressed by a path and can be read and edited directly through the API or the Claude Console, allowing for tuning, importing, and exporting.
Every change to a memory creates an immutable **memory version**, giving you an audit trail and point-in-time recovery for everything the agent writes.
## Create a memory store
Give the store a `name` and a `description`. The description is passed to the agent, telling it what the store contains.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply memory_store.yaml
```
memory_store.yaml
```python
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/memory_store.json
name: User Preferences
description: Per-user preferences and project context.
```
The memory store `id` (`memstore_...`) is what you pass when attaching the store to a session.
### Seed it with content (optional)
Pre-load a store with reference material before any agent runs:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.memories.create(
store.id,
path="/formatting_standards.md",
content="All reports use GAAP formatting. Dates are ISO-8601...",
)
```
Individual memories within the store are capped at 100 kB (~25k tokens). A store holds a maximum of 10,000 memories. Structure memory as many small focused files, not a few large ones.
## Attach a memory store to a session
Memory stores are attached in the session's `resources[]` array when the [session is created](managed-agents-sessions.md#creating-a-session). Unlike file resources, memory stores can only be attached at session creation time; adding or removing one from a running session is not supported. You attach memory stores the same way for sessions on cloud and [self-hosted environments](managed-agents-self-hosted-sandboxes-memory.md); self-hosted environments accept only `memory_store` resources.
Optionally include `instructions` to provide session-specific guidance for how the agent should use this store. It is shown to the agent alongside the store's `name` and `description`, and is capped at 4,096 characters.
You can configure `access` as well. It defaults to `read_write` (shown explicitly in the following example), but `read_only` is also supported.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
resources=[
{
"type": "memory_store",
"memory_store_id": store.id,
"access": "read_write",
"instructions": "User preferences and project context. Check before starting any task.",
}
],
)
```
Memory stores attach with `read_write` access by default. If the agent processes untrusted input (user-supplied prompts, fetched web content, or third-party tool output), a successful prompt injection could write malicious content into the store. Later sessions then read that content as trusted memory. Use `read_only` for reference material, shared lookups, and any store the agent does not need to modify.
A maximum of **8 memory stores** are supported per session. Attach multiple stores when different parts of memory have different owners or access rules. Common reasons:
- **Shared reference material:** one read-only store attached to many sessions (standards, conventions, domain knowledge), kept separate from each session's own read-write store.
- **Mapping to your product's structure:** one store per end user, per team, or per project, while sharing a single agent configuration.
- **Different lifecycles:** a store that outlives any single session, or one you want to archive on its own schedule.
### How the agent accesses memory
Each attached store is mounted inside the session's sandbox as a directory under `/mnt/memory/`. The directory name is the store's display name sanitized to a filesystem-safe slug (lowercased; non-alphanumeric runs become a single hyphen), so a store named "Demo Memory" mounts at `/mnt/memory/demo-memory/`. The exact path is returned in the `mount_path` field on the session's memory-store resource; read it from there rather than constructing it yourself. The agent reads and writes the store with the standard [agent toolset](managed-agents-tools.md). Writes under the mount path are persisted back to the store and stay in sync across sessions that share it; writes to any other path under `/mnt/memory/` fail, because the sandbox mounts that parent directory read-only. A short description of each mount (display name, mount path, access mode, store `description`, and any `instructions`) is automatically added to the system prompt.
`access` is enforced at the filesystem level: a `read_only` mount rejects writes, while writes to a `read_write` mount produce [memory versions](#audit-memory-changes) attributed to the session.
On [self-hosted sandboxes](managed-agents-self-hosted-sandboxes-memory.md), each store's directory is a local copy that the worker manages rather than a live mount. The worker reconciles each copy with its store after tool calls, at most once per sync interval (15 seconds by default), and once more when the session ends. The agent's `write` and `edit` tools change only the local copy; the worker uploads those changes at its next sync, so another session running on a self-hosted sandbox sees a change only after both workers have synced. Paths under `/mnt/memory/` outside the store directories are not scratch space there: the worker's file tools refuse to write to them, and anything a shell command writes there is never synced to a store.
For a `read_only` store, the worker's `write` and `edit` tools refuse changes under that directory and the worker never uploads anything from it. To learn how the worker resolves write conflicts, and what the `bash` tool can still change in a read-only store's local copy, see [Read-only stores and conflicts](managed-agents-self-hosted-sandboxes-memory.md#read-only-stores-and-conflicts).
The agent's reads and writes appear in the [event stream](managed-agents-events-and-streaming.md) as ordinary `agent.tool_use` and `agent.tool_result` events for whichever tool touched the mount.
## View and edit memories
Memory stores can be managed directly through the API. Use this for building review workflows, correcting bad memories, or seeding stores before any session runs.
### List memories
List the memories in a store. Results are returned in a stable, server-defined order.
- `path_prefix` scopes the list to one directory. It must end with `/` and matches whole path segments, so `path_prefix=/notes/` returns `/notes/todo.md` but not `/notes-archive/todo.md`.
- `depth` controls how deep the listing goes below `path_prefix`: omit it (or pass `0`) to list the whole subtree, or pass `1` to list only the immediate children. Other values return a `400` error.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
page = client.beta.memory_stores.memories.list(
store.id,
path_prefix="/",
)
for item in page.data:
print(item.type, item.path)
```
See the [List memories reference](../Endpoints/beta-memory-stores-memories-list.md) for full parameters and response schema.
### Read a memory
Fetching an individual memory returns the full content.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
retrieved = client.beta.memory_stores.memories.retrieve(
mem.id,
memory_store_id=store.id,
)
print(retrieved.content)
```
See the [Retrieve a memory reference](../Endpoints/beta-memory-stores-memories-retrieve.md) for full parameters and response schema.
### Create a memory
`client.beta.memory_stores.memories.create()` creates a memory at a given `path`. Create does not overwrite; to change an existing memory, [update it](#update-a-memory) with `client.beta.memory_stores.memories.update()`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
mem = client.beta.memory_stores.memories.create(
store.id,
path="/preferences/formatting.md",
content="Always use tabs, not spaces.",
)
```
See the [Create a memory reference](../Endpoints/beta-memory-stores-memories-create.md) for full parameters and response schema.
### Update a memory
`client.beta.memory_stores.memories.update()` modifies an existing memory by ID. You can change `content`, `path` (a rename), or both. The example renames a memory to an archive path:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.memories.update(
mem.id,
memory_store_id=store.id,
path="/archive/2026_q1_formatting.md",
)
```
See the [Update a memory reference](../Endpoints/beta-memory-stores-memories-update.md) for full parameters and response schema.
#### Safe content edits (optimistic concurrency)
To avoid clobbering a concurrent write, pass a `content_sha256` precondition. The update only applies if the stored content hash still matches the one you read; on mismatch, re-read the memory and retry against the fresh state.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.memories.update(
memory_id=mem.id,
memory_store_id=store.id,
content="CORRECTED: Always use 2-space indentation.",
precondition={"type": "content_sha256", "content_sha256": mem.content_sha256},
)
```
### Delete a memory
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.memories.delete(
mem.id,
memory_store_id=store.id,
)
```
See the [Delete a memory reference](../Endpoints/beta-memory-stores-memories-delete.md) for full parameters and response schema.
## Audit memory changes
Every mutation to a memory creates an immutable **memory version** (`memver_...`). Use the version endpoints to audit who changed what and when, to inspect or restore a prior snapshot, and to scrub sensitive content out of history with redact.
Versions belong to the store (not the individual memory) and are not deleted when the memory itself is deleted, so the audit trail also covers deleted memories, subject to the retention described below. Versions are retained for 30 days after they are written; however, the recent versions of a live memory are always kept regardless of age, so memories that change infrequently might retain history beyond 30 days. The live `client.beta.memory_stores.memories.retrieve()` call always returns the latest version; the version endpoints give you the retained history.
There is no dedicated restore endpoint; to roll back, retrieve the version you want and write its `content` back with `client.beta.memory_stores.memories.update()` (or `client.beta.memory_stores.memories.create()` if the parent memory has been deleted, provided the version you want is still retained).
Past memory versions might be deleted after 30 days. To preserve memory history for longer, export versions through the API.
### List versions
List version history for a store, newest first. The example filters to a single memory's history:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
versions = client.beta.memory_stores.memory_versions.list(
store.id,
memory_id=mem.id,
)
for version in versions:
print(f"{version.id}: {version.operation}")
version_id = versions.data[1].id
```
See the [List memory versions reference](../Endpoints/beta-memory-stores-memory-versions-list.md) for full parameters and response schema.
### Retrieve a version
Fetching an individual version returns the same fields as the list response plus the full `content` body.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
version = client.beta.memory_stores.memory_versions.retrieve(
version_id,
memory_store_id=store.id,
)
print(version.content)
```
See the [Retrieve a memory version reference](../Endpoints/beta-memory-stores-memory-versions-retrieve.md) for full parameters and response schema.
### Redact a version
Redact scrubs content out of a historical version while preserving the audit trail (who did what, when). Use it for compliance workflows such as removing leaked secrets, PII, or user deletion requests.
A version that is the current head of a live memory cannot be redacted. Write a new version first (or delete the memory), then redact the old one.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.memory_versions.redact(
version_id,
memory_store_id=store.id,
)
```
See the [Redact a memory version reference](../Endpoints/beta-memory-stores-memory-versions-redact.md) for full parameters and response schema.
## Manage memory stores
In addition to [`create`](../Endpoints/beta-memory-stores-create.md), memory stores support [`retrieve`](../Endpoints/beta-memory-stores-retrieve.md), [`update`](../Endpoints/beta-memory-stores-update.md), [`list`](../Endpoints/beta-memory-stores-list.md), [`archive`](../Endpoints/beta-memory-stores-archive.md), and [`delete`](../Endpoints/beta-memory-stores-delete.md).
### List stores
List stores in the workspace. Archived stores are excluded by default; pass `include_archived: true` to include them.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for memory_store in client.beta.memory_stores.list(include_archived=True):
print(memory_store.id, memory_store.name, memory_store.archived_at)
```
See the [List memory stores reference](../Endpoints/beta-memory-stores-list.md) for full parameters and response schema.
### Archive a store
Archiving makes a store read-only and prevents it from being attached to new sessions. Archiving is one-way; there is no unarchive.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.memory_stores.archive(store.id)
```
See the [Archive a memory store reference](../Endpoints/beta-memory-stores-archive.md) for full parameters and response schema.
To [permanently remove a store](../Endpoints/beta-memory-stores-delete.md) along with all of its memories and versions, call `client.beta.memory_stores.delete()`.
## Best practices for memory management
When a store reaches its 10,000-memory limit, writes to new memories fail: both direct `client.beta.memory_stores.memories.create()` calls and the agent's file writes to unmapped paths. Existing memories remain readable and editable. The following practices help you stay well under the limit and recover gracefully if you reach it.
- **Use focused stores.** Rather than one large general-purpose store, use smaller purpose-built stores: one per user, one for shared domain knowledge, and one for project-specific context. Each store has its own 10,000-memory limit, so keeping stores scoped reduces the chance any single one fills up.
- **Condense or prune before the store fills up.** Delete stale or redundant memories with `client.beta.memory_stores.memories.delete()`. You can also run a [dreaming session](managed-agents-dreams.md), which consolidates fragmented content into a separate new output store rather than modifying the original. Switch your sessions over to that output store, then archive or delete the original.
- **Attach a new store when it makes sense.** If a store has grown beyond its useful scope, attach a fresh one for new content and attach the original with `read_only` access. The agent can read from both while only writing to the new one.
- **Limit write access where appropriate.** Sessions that only read shared reference material don't need `read_write`. Keeping write access scoped to sessions that actually add new memories makes it easier to track where growth is coming from.
---
## Migration - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/migration
Path: 04-API-Reference/Other/managed-agents-migration.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fmigration)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)First steps
# Migration
Copy page
Move an existing agent built on the Messages API or the Claude Agent SDK to Claude Managed Agents.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Claude Managed Agents replaces your hand-written agent loop with managed infrastructure. This page covers what changes when you migrate from a custom loop built on the [Messages API](../Guides/build-with-claude-working-with-messages.md) or from the [Claude Agent SDK](../../05-Agent-SDK/agent-sdk-overview.md).
## From a Messages API agent loop
If you built an agent by calling `client.messages.create()` in a loop, running tool calls yourself, and appending results to the conversation history, most of that code goes away.
### What you stop managing
| Before | After |
|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|
| You maintain the conversation history array and pass it back on every turn. | The session stores history server-side. Send events, receive events. |
| You iterate `tool_use` content blocks, run each tool, and loop back with `tool_result` messages. | Pre-built tools run inside the sandbox automatically. You only handle custom tools through `agent.custom_tool_use` events. |
| You provision your own sandbox for running agent-generated code. | The session sandbox handles code execution, file operations, and bash. |
| You decide when the loop is done. | The session emits `session.status_idle` when the agent has nothing more to do. |
### Code comparison
**Before** (Messages API loop, simplified):
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
messages = [{"role": "user", "content": task}]
while True:
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=messages,
tools=tools,
)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason == "end_turn":
break
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
messages.append(
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": block.id,
"content": result,
}
],
}
)
```
**After** (Claude Managed Agents):
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
agent = client.beta.agents.create(
name="Task Runner",
model="claude-opus-5-5",
tools=[{"type": "agent_toolset_20260401"}],
)
session = client.beta.sessions.create(
agent={"type": "agent", "id": agent.id, "version": agent.version},
environment_id=environment.id,
)
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(
session.id,
events=[{"type": "user.message", "content": [{"type": "text", "text": task}]}],
)
for event in stream:
if event.type == "session.status_idle":
break
```
### What you still control
- **System prompt and model:** Same fields, now on the agent definition.
- **Custom tools:** Still declared with JSON Schema. Execution moves from inline handling to responding to `agent.custom_tool_use` events. See [Session event stream](managed-agents-events-and-streaming.md).
- **Web search and web fetch settings:** Same `allowed_domains`, `blocked_domains`, `max_content_tokens`, and `user_location` fields, now set once on the `web_search` and `web_fetch` entries of the agent toolset's `configs` array instead of on every request. The `max_uses`, `citations`, and `cache_control` fields are not available. See [Restrict web search and web fetch domains](managed-agents-tools.md#restrict-web-search-and-web-fetch-domains).
- **Context:** You can still inject context through the system prompt, [file resources](managed-agents-files.md), or [skills](managed-agents-skills.md).
## From the Claude Agent SDK
If you built with the [Claude Agent SDK](../../05-Agent-SDK/agent-sdk-overview.md), you're already working with agents, tools, and sessions as concepts. The difference is where they run: the SDK runs in a process you operate, while Managed Agents runs in Anthropic's infrastructure. Most of the migration is mapping SDK configuration objects to their API-side equivalents.
### What changes
| Agent SDK | Managed Agents |
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ClaudeAgentOptions(...)` constructed per run | `client.beta.agents.create(...)` once; the Agent is persisted and versioned server-side. See [Agent setup](managed-agents-agent-setup.md). |
| `async with ClaudeSDKClient(...)` or `query(...)` | `client.beta.sessions.create(...)` then send and receive [events](managed-agents-events-and-streaming.md). |
| Functions defined with `@tool`, dispatched automatically by the SDK | Declare as `{"type": "custom", ...}` on the Agent; your client handles `agent.custom_tool_use` events and replies with `user.custom_tool_result`. See [Tools](managed-agents-tools.md). |
| Built-in tools run in your process against your filesystem | `{"type": "agent_toolset_20260401"}` runs the same tools inside the session sandbox against `/workspace`. |
| `cwd`, `add_dirs` point at local paths | Upload or mount [files](managed-agents-files.md) as session resources. |
| `system_prompt` and the `CLAUDE.md` hierarchy | A single `system` string on the Agent. Each update that changes the agent produces a new server-side version; pin sessions to a specific version to promote or roll back without a deploy. See [Agent setup](managed-agents-agent-setup.md). |
| `mcp_servers` configured and authenticated in one place | Declare servers on the Agent; provide credentials through a [Vault](managed-agents-vaults.md) on the Session. |
| `permission_mode`, `can_use_tool` | Per-tool [`permission_policy`](managed-agents-permission-policies.md) (`always_allow`, `always_ask`, or `auto`); send `user.tool_confirmation` events for calls that pause for your approval. |
### Code comparison
**Before** (Agent SDK):
Python
TypeScript
```python
from claude_agent_sdk import (
ClaudeAgentOptions,
ClaudeSDKClient,
create_sdk_mcp_server,
tool,
)
@tool("get_weather", "Get the current weather for a city.", {"city": str})
async def get_weather(args: dict) -> dict:
return {"content": [{"type": "text", "text": f"{args['city']}: 18°C, clear"}]}
options = ClaudeAgentOptions(
model="claude-opus-5-5",
system_prompt="You are a concise weather assistant.",
mcp_servers={
"weather": create_sdk_mcp_server("weather", "1.0", tools=[get_weather])
},
)
async with ClaudeSDKClient(options=options) as agent:
await agent.query("What's the weather in Tokyo?")
async for msg in agent.receive_response():
print(msg)
```
**After** (Managed Agents):
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
from anthropic import Anthropic
client = Anthropic()
agent = client.beta.agents.create(
name="weather-agent",
model="claude-opus-5-5",
system="You are a concise weather assistant.",
tools=[
{
"type": "custom",
"name": "get_weather",
"description": "Get the current weather for a city.",
"input_schema": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
}
],
)
environment = client.beta.environments.create(
name="weather-env",
config={
"type": "cloud",
"networking": {"type": "limited", "allow_package_managers": True},
},
)
session = client.beta.sessions.create(
agent={"type": "agent", "id": agent.id, "version": agent.version},
environment_id=environment.id,
)
def get_weather(city: str) -> str:
return f"{city}: 18°C, clear"
with client.beta.sessions.events.stream(session.id) as stream:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": "What's the weather in Tokyo?"}],
}
],
)
for event in stream:
match event.type:
case "agent.message":
print(
"".join(
block.text for block in event.content if block.type == "text"
)
)
case "agent.custom_tool_use":
result = get_weather(**event.input)
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event.id,
"content": [{"type": "text", "text": result}],
}
],
)
case "session.status_idle":
if event.stop_reason and event.stop_reason.type == "end_turn":
break
```
The Agent and Environment are created once and reused across sessions. The tool function still runs in your process; the difference is that you read the `agent.custom_tool_use` event and send the result explicitly instead of the SDK dispatching it for you.
### Features that move to your client
The tradeoff for Anthropic running the agent loop is that a few things the SDK handled automatically become your client's responsibility.
| SDK feature | Managed Agents approach |
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Plan mode | Run a planning-only session first, then a second session to run the plan. |
| Output styles, slash commands | Apply in your client before sending `user.message` or after receiving `agent.message`. |
| `PreToolUse` / `PostToolUse` hooks | Your client already sees every `agent.custom_tool_use` event before responding; put the logic there. For built-in tools, use `permission_policy: always_ask` to review every call. [`auto`](managed-agents-permission-policies.md#let-the-server-evaluate-each-call-with-auto) lets the server evaluate each call instead, but if the server evaluates a call as safe, it runs without reaching your client. |
| `max_turns` | Count turns client-side. |
## Migration checklist
1. [Create an environment](managed-agents-environments.md) with the networking and runtimes your agent needs.
2. Port your system prompt and tool selection to an [agent definition](managed-agents-agent-setup.md).
3. Replace your loop: [create a session](managed-agents-sessions.md) with `client.beta.sessions.create()` and [stream its events](managed-agents-events-and-streaming.md) with `client.beta.sessions.events.stream()`.
4. For any local files the agent reads, upload them through the [Files API](managed-agents-files.md) and mount them as `resources`.
5. For any custom tool handlers, move execution into your event loop as responses to `agent.custom_tool_use` events.
6. Verify with a test session before pointing production traffic at the new flow.
## Migrating between model versions
When a new Claude model is released, migrating a Claude Managed Agents integration is typically a one-field change: update `model` on your [agent definition](managed-agents-agent-setup.md) and the change takes effect on the next session you create.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Task Runner
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
---
You are a task automation agent. Complete the task you are given end to end.
```
Most model-level behavior changes documented in the [Messages API migration guide](../../20-Models/about-claude-models-migration-guide.md) do not require action on your side:
- **Request parameter changes** (`max_tokens` defaults, `thinking` configuration) are handled by the Claude Managed Agents runtime. These fields are not exposed on the agent definition.
- **Assistant message prefilling** does not exist in the event-based session model, so its removal on newer models is a no-op.
- **Tool argument JSON escaping** is parsed by the runtime before you receive `agent.custom_tool_use` events. You see structured data, not raw strings.
The behavior descriptions in the Messages API guide (what the model does differently) still apply. The migration steps (how to change your request code) do not.
---
## Multiagent sessions
Source: https://platform.claude.com/docs/en/managed-agents/multi-agent
Path: 04-API-Reference/Other/managed-agents-multi-agent.md
# Multiagent sessions
Coordinate multiple agents within a single session.
---
<Tip>
Multiagent is a Research Preview feature. [Request access](https://claude.com/form/claude-managed-agents) to try it.
</Tip>
Multi-agent orchestration lets one agent coordinate with others to complete complex work. Agents can act in parallel with their own isolated context, which helps improve output quality and improve time to completion.
<Note>
All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. An additional beta header is needed for research preview features. The SDK sets these beta headers automatically.
</Note>
## How it works
All agents share the same container and filesystem, but each agent runs in its own session **thread**, a context-isolated event stream with its own conversation history. The coordinator reports activity in the **primary thread** (which is the same as the session-level event stream); additional threads are spawned at runtime when the coordinator decides to delegate.
Threads are persistent: the coordinator can send a follow-up to an agent it called earlier, and that agent retains everything from its previous turns.
Each agent uses its own configuration (model, system prompt, tools, MCP servers, and skills) as defined when that agent was created. Tools and context are not shared.
### What to delegate
Multiagent sessions work best when there are multiple well-scoped, specialized tasks in an overall goal:
- **Code review:** A reviewer agent with a focused system prompt and read-only tools.
- **Test generation:** A test agent that writes and runs tests without touching production code.
- **Research:** A search agent with web tools that summarizes findings back to the coordinator.
## Declare callable agents
When [defining your agent](managed-agents-agent-setup.md), list additional IDs of agents it is permitted to call:
<CodeGroup defaultLanguage="CLI">
```bash curl
orchestrator=$(curl -fsS https://api.anthropic.com/v1/agents \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @- <<EOF
{
"name": "Engineering Lead",
"model": "claude-opus-4-7",
"system": "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
"tools": [
{
"type": "agent_toolset_20260401"
}
],
"callable_agents": [
{"type": "agent", "id": "$REVIEWER_AGENT_ID", "version": $REVIEWER_AGENT_VERSION},
{"type": "agent", "id": "$TEST_WRITER_AGENT_ID", "version": $TEST_WRITER_AGENT_VERSION}
]
}
EOF
)
```
```bash CLI
ant beta:agents create <<YAML
name: Engineering Lead
model: claude-opus-4-7
system: You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.
tools:
- type: agent_toolset_20260401
callable_agents:
- type: agent
id: $REVIEWER_AGENT_ID
version: $REVIEWER_AGENT_VERSION
- type: agent
id: $TEST_WRITER_AGENT_ID
version: $TEST_WRITER_AGENT_VERSION
YAML
```
```python Python
orchestrator = client.beta.agents.create(
name="Engineering Lead",
model="claude-opus-4-7",
system="You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
tools=[
{"type": "agent_toolset_20260401"},
],
callable_agents=[
{"type": "agent", "id": reviewer_agent.id, "version": reviewer_agent.version},
{
"type": "agent",
"id": test_writer_agent.id,
"version": test_writer_agent.version,
},
],
)
```
```typescript TypeScript
const orchestrator = await client.beta.agents.create({
name: "Engineering Lead",
model: "claude-opus-4-7",
system:
"You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
tools: [{ type: "agent_toolset_20260401" }],
callable_agents: [
{ type: "agent", id: reviewerAgent.id, version: reviewerAgent.version },
{ type: "agent", id: testWriterAgent.id, version: testWriterAgent.version }
]
});
```
```csharp C#
var orchestrator = await client.Beta.Agents.Create(new()
{
Name = "Engineering Lead",
Model = BetaManagedAgentsModel.ClaudeOpus4_7,
System = "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
Tools =
[
new BetaManagedAgentsAgentToolset20260401Params
{
Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
},
],
CallableAgents =
[
new BetaManagedAgentsCallableAgentParams
{
Type = BetaManagedAgentsCallableAgentParamsType.Agent,
ID = reviewerAgent.ID,
Version = reviewerAgent.Version,
},
new BetaManagedAgentsCallableAgentParams
{
Type = BetaManagedAgentsCallableAgentParamsType.Agent,
ID = testWriterAgent.ID,
Version = testWriterAgent.Version,
},
],
});
```
```go Go
orchestrator, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
Name: "Engineering Lead",
Model: anthropic.BetaManagedAgentsModelConfigParams{ID: anthropic.BetaManagedAgentsModelClaudeOpus4_7},
System: anthropic.String("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent."),
Tools: []anthropic.BetaAgentNewParamsToolUnion{{
OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
},
}},
CallableAgents: []anthropic.BetaManagedAgentsCallableAgentParams{
{Type: "agent", ID: reviewerAgent.ID, Version: reviewerAgent.Version},
{Type: "agent", ID: testWriterAgent.ID, Version: testWriterAgent.Version},
},
})
if err != nil {
panic(err)
}
```
```java Java
var orchestrator = client.beta().agents().create(
AgentCreateParams.builder()
.name("Engineering Lead")
.model(BetaManagedAgentsModel.CLAUDE_OPUS_4_7)
.system("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.")
.addTool(
BetaManagedAgentsAgentToolset20260401Params.builder()
.type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
.build()
)
.addCallableAgent(
BetaManagedAgentsAgentParams.builder()
.type(BetaManagedAgentsAgentParams.Type.AGENT)
.id(reviewerAgent.id())
.version(reviewerAgent.version())
.build()
)
.addCallableAgent(
BetaManagedAgentsAgentParams.builder()
.type(BetaManagedAgentsAgentParams.Type.AGENT)
.id(testWriterAgent.id())
.version(testWriterAgent.version())
.build()
)
.build()
);
```
```php PHP
$orchestrator = $client->beta->agents->create(
name: 'Engineering Lead',
model: 'claude-opus-4-7',
system: 'You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.',
tools: [
['type' => 'agent_toolset_20260401'],
],
callableAgents: [
['type' => 'agent', 'id' => $reviewerAgent->id, 'version' => $reviewerAgent->version],
['type' => 'agent', 'id' => $testWriterAgent->id, 'version' => $testWriterAgent->version],
],
);
```
```ruby Ruby
orchestrator = client.beta.agents.create(
name: "Engineering Lead",
model: "claude-opus-4-7",
system: "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
tools: [
{type: "agent_toolset_20260401"}
],
callable_agents: [
{type: "agent", id: reviewer_agent.id, version: reviewer_agent.version},
{type: "agent", id: test_writer_agent.id, version: test_writer_agent.version}
]
)
```
</CodeGroup>
Each entry in `callable_agents` must be the ID of an existing agent. Only one level of delegation is supported: the coordinator can call other agents, but those agents cannot call agents of their own.
Then create a session referencing the orchestrator:
<CodeGroup>
```bash curl
session=$(curl -fsS https://api.anthropic.com/v1/sessions \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{"agent": "'$ORCHESTRATOR_ID'", "environment_id": "'$ENVIRONMENT_ID'"}')
```
```bash CLI nocheck
ant beta:sessions create \
--agent "$ORCHESTRATOR_ID" \
--environment-id "$ENVIRONMENT_ID"
```
```python Python
session = client.beta.sessions.create(
agent=orchestrator.id,
environment_id=environment.id,
)
```
```typescript TypeScript
const session = await client.beta.sessions.create({
agent: orchestrator.id,
environment_id: environment.id
});
```
</CodeGroup>
The callable agents are resolved from the orchestrator's configuration. You don't need to reference them at session creation.
## Session threads
The **session-level event stream** (`/v1/sessions/:id/stream`) is considered the **primary thread**, containing an condensed view of all activity across all threads. You won't see called agents' individual traces, but you will see the start and end of their work. **Session threads** are where you drill into a specific agent's reasoning and tool calls.
The session status also is an aggregation of all agent activity; if at least one thread is `running`, then the overall session status will be `running` as well.
List all threads in a session as follows:
<CodeGroup>
```bash curl
curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
| jq -r '.data[] | "[\(.agent_name)] \(.status)"'
```
```bash CLI nocheck
ant beta:sessions:threads list --session-id "$SESSION_ID"
```
```python Python
for thread in client.beta.sessions.threads.list(session.id):
print(f"[{thread.agent_name}] {thread.status}")
```
```typescript TypeScript
for await (const thread of client.beta.sessions.threads.list(session.id)) {
console.log(`[${thread.agent_name}] ${thread.status}`);
}
```
```csharp C#
await foreach (var thread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
{
Console.WriteLine($"[{thread.AgentName}] {thread.Status}");
}
```
```go Go
threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionThreadListParams{})
for threads.Next() {
thread := threads.Current()
fmt.Printf("[%s] %s\n", thread.AgentName, thread.Status)
}
if err := threads.Err(); err != nil {
panic(err)
}
```
```java Java
for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
IO.println("[" + thread.agentName() + "] " + thread.status());
}
```
```php PHP
foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
echo "[{$thread->agentName}] {$thread->status}\n";
}
```
```ruby Ruby
client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
puts "[#{thread.agent_name}] #{thread.status}"
end
```
</CodeGroup>
Stream events from a specific thread:
<CodeGroup>
```bash curl
curl -fsSN "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" |
while IFS= read -r line; do
[[ $line == data:* ]] || continue
json=${line#data: }
case $(jq -r '.type' <<<"$json") in
agent.message)
printf '%s' "$(jq -j '.content[] | select(.type == "text") | .text' <<<"$json")"
;;
session.thread_idle)
break
;;
esac
done
```
```bash CLI nocheck
ant beta:sessions:threads stream \
--session-id "$SESSION_ID" \
--thread-id "$THREAD_ID"
```
```python Python
with client.beta.sessions.threads.stream(
thread.id,
session_id=session.id,
) as stream:
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
if block.type == "text":
print(block.text, end="")
case "session.thread_idle":
break
```
```typescript TypeScript
const stream = await client.beta.sessions.threads.stream(thread.id, {
session_id: session.id
});
for await (const event of stream) {
if (event.type === "agent.message") {
for (const block of event.content) {
if (block.type === "text") {
process.stdout.write(block.text);
}
}
} else if (event.type === "session.thread_idle") {
break;
}
}
```
```csharp C#
await foreach (var evt in client.Beta.Sessions.Threads.Stream(thread.ID, new() { SessionID = session.ID }))
{
if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
{
foreach (var block in message.Content)
{
if (block.Type == "text")
{
Console.Write(block.Text);
}
}
}
else if (evt.Value is BetaManagedAgentsSessionThreadIdleEvent)
{
break;
}
}
```
```go Go
stream := client.Beta.Sessions.Threads.StreamEvents(ctx, thread.ID, anthropic.BetaSessionThreadStreamParams{
SessionID: session.ID,
})
defer stream.Close()
loop:
for stream.Next() {
event := stream.Current()
switch event.Type {
case "agent.message":
for _, block := range event.AsAgentMessage().Content {
if block.Type == "text" {
fmt.Print(block.Text)
}
}
case "session.thread_idle":
break loop
}
}
if err := stream.Err(); err != nil {
panic(err)
}
```
```java Java
try (var streamResponse = client.beta().sessions().threads().streamStreaming(
thread.id(),
ThreadStreamParams.builder().sessionId(session.id()).build()
)) {
for (var event : (Iterable<StreamEvents>) streamResponse.stream()::iterator) {
if (event.isAgentMessage()) {
for (var block : event.asAgentMessage().content()) {
IO.print(block.text());
}
} else if (event.isSessionThreadIdle()) {
break;
}
}
}
```
```php PHP
$stream = $client->beta->sessions->threads->stream(
$thread->id,
sessionID: $session->id,
);
foreach ($stream as $event) {
if ($event->type === 'agent.message') {
foreach ($event->content as $block) {
if ($block->type === 'text') {
echo $block->text;
}
}
} elseif ($event->type === 'session.thread_idle') {
break;
}
}
```
```ruby Ruby
client.beta.sessions.threads.stream(thread.id, session_id: session.id).each do |event|
case event.type
when :"agent.message"
event.content.each do |block|
print block.text if block.type == :text
end
when :"session.thread_idle"
break
end
end
```
</CodeGroup>
List past events for a thread:
<CodeGroup>
```bash curl
curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
| jq -r '.data[] | "[\(.type)] \(.processed_at)"'
```
```bash CLI nocheck
ant beta:sessions:threads:events list \
--session-id "$SESSION_ID" \
--thread-id "$THREAD_ID"
```
```python Python
for event in client.beta.sessions.threads.events.list(
thread.id,
session_id=session.id,
):
print(f"[{event.type}] {event.processed_at}")
```
```typescript TypeScript
for await (const event of client.beta.sessions.threads.events.list(thread.id, {
session_id: session.id
})) {
console.log(`[${event.type}] ${event.processed_at}`);
}
```
```csharp C#
var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
await foreach (var evt in page.Paginate())
{
Console.WriteLine($"[{evt.Type}] {evt.ProcessedAt}");
}
```
```go Go
pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, anthropic.BetaSessionThreadEventListParams{
SessionID: session.ID,
})
for pager.Next() {
event := pager.Current()
fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
}
if err := pager.Err(); err != nil {
panic(err)
}
```
```java Java
for (var event : client.beta().sessions().threads().events().list(
thread.id(),
EventListParams.builder().sessionId(session.id()).build()
).autoPager()) {
IO.println("[" + event.type() + "] " + event.processedAt());
}
```
```php PHP
foreach (
$client->beta->sessions->threads->events->list(
$thread->id,
sessionID: $session->id,
)->pagingEachItem() as $event
) {
echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
}
```
```ruby Ruby
client.beta.sessions.threads.events.list(
thread.id,
session_id: session.id
).auto_paging_each do |event|
puts "[#{event.type}] #{event.processed_at}"
end
```
</CodeGroup>
## Multiagent event types
These events surface multiagent activity on the top-level session stream.
| Type | Description |
| --- | --- |
| `session.thread_created` | The coordinator spawned a new thread. Includes the `session_thread_id` and `model`. |
| `session.thread_idle` | An agent thread finished its current work. |
| `agent.thread_message_sent` | An agent sent a message to another thread. Includes `to_thread_id` and `content`. |
| `agent.thread_message_received` | An agent received a message from another thread. Includes `from_thread_id` and `content`. |
## Tool permissions and custom tools in threads
When a `callable_agent` thread needs something from your client ([permission](managed-agents-events-and-streaming.md#tool-confirmation) to run an `always_ask` tool, or the [result of a custom tool](managed-agents-events-and-streaming.md#handling-custom-tool-calls)) the request surfaces on the **session stream** with a `session_thread_id` field. Include the same `session_thread_id` when you post your response so the platform routes it back to the waiting thread.
- **`session_thread_id` is present:** the event originated in a subagent thread. Echo it on your reply.
- **`session_thread_id` is absent:** the event came from the primary thread. Reply without the field.
- Match on `tool_use_id` to pair requests with responses.
The example below extends the [tool confirmation handler](managed-agents-events-and-streaming.md#tool-confirmation) to route replies. The same pattern applies to `user.custom_tool_result`.
<CodeGroup>
```bash curl
while IFS= read -r event_id; do
pending=$(jq -r --arg id "$event_id" '.[$id]' <<<"$events_by_id")
thread_id=$(jq -r '.session_thread_id // empty' <<<"$pending")
jq -n --arg id "$event_id" --arg thread "$thread_id" '
{events: [
{type: "user.tool_confirmation", tool_use_id: $id, result: "allow"}
+ (if $thread != "" then {session_thread_id: $thread} else {} end)
]}' |
curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @-
done < <(jq -r '.stop_reason.event_ids[]' <<<"$data")
```
```bash CLI
while IFS= read -r event_id; do
pending=$(jq -r --arg id "$event_id" '.[$id]' <<<"$events_by_id")
thread_id=$(jq -r '.session_thread_id // empty' <<<"$pending")
event="type: user.tool_confirmation, tool_use_id: $event_id, result: allow"
# Echo session_thread_id when the request came from a subagent thread
if [[ -n $thread_id ]]; then
event+=", session_thread_id: $thread_id"
fi
ant beta:sessions:events send \
--session-id "$SESSION_ID" \
--event "{$event}" \
</dev/null
done < <(jq -r '.stop_reason.event_ids[]' <<<"$data")
```
```python Python
for event_id in stop.event_ids:
pending = events_by_id[event_id]
confirmation = {
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
}
# Echo session_thread_id when the request came from a subagent thread
if pending.session_thread_id is not None:
confirmation["session_thread_id"] = pending.session_thread_id
client.beta.sessions.events.send(session.id, events=[confirmation])
```
```typescript TypeScript
for (const eventId of event.stop_reason.event_ids) {
const pending = eventsById[eventId];
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.tool_confirmation",
tool_use_id: eventId,
result: "allow",
// Echo session_thread_id when the request came from a subagent thread
...(pending.session_thread_id != null && {
session_thread_id: pending.session_thread_id
})
}
]
});
}
```
```csharp C#
foreach (var eventId in requiresAction.EventIds)
{
var pending = eventsById[eventId];
await client.Beta.Sessions.Events.Send(session.ID, new()
{
Events =
[
new BetaManagedAgentsUserToolConfirmationEventParams
{
Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
ToolUseID = eventId,
Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
// Echo session_thread_id when the request came from a subagent thread
SessionThreadID = pending.SessionThreadID,
},
],
});
}
```
```go Go
for _, eventID := range stopReason.EventIDs {
pending := eventsByID[eventID]
params := anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
Type: anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
ToolUseID: eventID,
Result: anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
}
// Echo session_thread_id when the request came from a subagent thread
if pending.SessionThreadID != "" {
params.SessionThreadID = anthropic.String(pending.SessionThreadID)
}
if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
Events: []anthropic.SendEventsParamsUnion{{OfUserToolConfirmation: ¶ms}},
}); err != nil {
panic(err)
}
}
```
```java Java
for (var eventId : stopReason.asRequiresAction().eventIds()) {
var pending = eventsById.get(eventId);
var confirmation = BetaManagedAgentsUserToolConfirmationEventParams.builder()
.toolUseId(eventId)
.result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW);
// Echo session_thread_id when the request came from a subagent thread
pending.sessionThreadId().ifPresent(confirmation::sessionThreadId);
client.beta().sessions().events().send(
session.id(),
EventSendParams.builder().addEvent(confirmation.build()).build()
);
}
```
```php PHP
foreach ($event->stopReason->eventIDs as $eventId) {
$pending = $eventsById[$eventId];
$confirmation = [
'type' => 'user.tool_confirmation',
'tool_use_id' => $eventId,
'result' => 'allow',
];
// Echo session_thread_id when the request came from a subagent thread
if ($pending->sessionThreadID !== null) {
$confirmation['session_thread_id'] = $pending->sessionThreadID;
}
$client->beta->sessions->events->send($session->id, events: [$confirmation]);
}
```
```ruby Ruby
event_ids.each do |event_id|
pending = events_by_id[event_id]
confirmation = {
type: "user.tool_confirmation",
tool_use_id: event_id,
result: "allow"
}
# Echo session_thread_id when the request came from a subagent thread
confirmation[:session_thread_id] = pending.session_thread_id if pending.session_thread_id
client.beta.sessions.events.send_(session.id, events: [confirmation])
end
```
</CodeGroup>
---
## Multiagent orchestration - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/multiagent-orchestration
Path: 04-API-Reference/Other/managed-agents-multiagent-orchestration.md
# Multiagent orchestration
Copy page
Coordinate multiple agents within a single session.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Multiagent orchestration lets one agent coordinate with others to complete complex work. Agents can act in parallel with their own isolated context, which helps improve output quality and can also improve time to completion.
Not sure a multiagent setup fits your problem? See [when to use multiagent systems (and when not to)](https://claude.com/blog/building-multi-agent-systems-when-and-how-to-use-them).
## How it works
All agents share the same sandbox, filesystem, and [vault credentials](managed-agents-vaults.md), but each agent runs in its own **session thread**, a context-isolated event stream with its own conversation history. The coordinator reports activity in the **primary thread** (which is the same as the session-level [event stream](managed-agents-events-and-streaming.md)); additional threads are spawned at runtime when the coordinator delegates work.
Threads are persistent: the coordinator can send a follow-up to an agent it called earlier, and that agent retains everything from its previous turns.
Each agent uses its own configuration: model, system prompt, tools, MCP servers, and skills. Session-level [agent configuration overrides](managed-agents-sessions.md#override-agent-configuration-for-a-session) are the exception; they apply to the coordinator and its `self` copies. Tools, MCP servers, and context are not shared.
### What to delegate
Multiagent coordination is best suited for complex tasks that either require work across a variety of surfaces, or where multiple well-scoped tasks contribute to an overall goal.
Patterns that work well:
- **Parallelization:** Fan out independent subtasks simultaneously (searching multiple sources, analyzing separate files) and have the coordinator synthesize the results.
- **Specialization:** Route to agents with domain-focused system prompts and tools, such as a security agent or a documentation agent, rather than loading a single agent with every capability.
- **Escalation:** Consult a more capable agent or model for a subset of complex subtasks.
## Configure the coordinator
When [defining your agent](managed-agents-agent-setup.md), set `multiagent` to declare the roster of agents the coordinator can delegate to:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply engineering-lead.md reviewer.md test-writer.md
```
engineering-lead.md
```python
---
name: Engineering Lead
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
multiagent:
type: coordinator
agents: # paths: ant apply substitutes {type: agent, id, version}
- ./reviewer.md
- ./test-writer.md
---
You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.
```
reviewer.md
```python
---
name: reviewer
model: claude-haiku-4-5
---
You are a code reviewer.
```
test-writer.md
```python
---
name: test-writer
model: claude-haiku-4-5
---
You write unit tests.
```
`multiagent.agents` can accept any of the following:
- `{"type": "agent", "id": agent.id}` references a previously created `agent` by ID. If no `version` is specified, the reference is pinned to the latest version of that agent at the time the coordinator is created.
- `{"type": "agent", "id": agent.id, "version": agent.version}` pins a specific agent version.
- `{"type": "self"}` allows the coordinator to spawn copies of itself. If the session was created with [agent configuration overrides](managed-agents-sessions.md#override-agent-configuration-for-a-session), those overrides also apply to these copies; roster entries referenced by ID are unaffected.
- `{"type": "advisor", "model": "<model id>"}` gives the session's primary thread an advisor it can consult mid-turn. At most one advisor entry per roster. See [Give the session an advisor](#give-the-session-an-advisor).
In an [`ant apply`](cli-sdks-libraries-cli-apply.md) agent file (the CLI tab), a roster entry can also be the path to another agent's file, such as `./reviewer.md`. Apply creates that agent first and replaces the path with a pinned `{"type": "agent", "id": ..., "version": ...}` reference.
The coordinator's configuration, including its `multiagent.agents` roster, is snapshotted when the coordinator is created or updated. Referenced agents stay pinned to the versions resolved at that time and do not automatically pick up later updates to their definitions. To delegate to a newer version of a referenced agent, [update the coordinator](managed-agents-agent-setup.md#update-an-agent) so its roster references that version.
The coordinator can only delegate to one level of agents; referencing an agent that has its own `multiagent.agents` roster fails the create or update request with a validation error. A maximum of 20 unique agents can be listed in `multiagent.agents`, but the coordinator can call multiple copies of each agent.
When agents pin an [inference geography](manage-claude-data-residency.md) (`model.inference_geo` in the [agent definition](managed-agents-agent-setup.md)), the coordinator's pin and every roster member's pin must either all be set to the same value or all be unset. A mismatched roster is rejected with a 400 validation error, both when the agent is saved and when a [session-create override](managed-agents-sessions.md#override-agent-configuration-for-a-session) changes any of the pins.
### Give the session an advisor
An advisor entry in `multiagent.agents` gives the session's primary thread an **advisor**: a model it can consult mid-turn for strategic guidance, such as planning an approach, getting unstuck, or reviewing work before finishing. The entry has exactly two fields, `type` and `model`:
cURL
```python
curl -fsS https://api.anthropic.com/v1/agents \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d '{
"name": "Backend engineer",
"model": "claude-sonnet-5",
"system": "You implement backend features end to end. Consult the advisor before major backend design decisions.",
"multiagent": {
"type": "coordinator",
"agents": [
{"type": "advisor", "model": "claude-opus-5-5"}
]
}
}'
```
A roster can contain at most one advisor entry, alongside any of the other roster forms. The entry occupies the reserved roster name `anthropic.advisor`: a roster that lists both an advisor entry and a member literally named `anthropic.advisor` is rejected with a 400 validation error. In responses, the advisor entry is echoed last in the roster regardless of the position it was submitted in.
The advisor model must meet a minimum capability bar, and the agent's own model must not be more capable than its advisor; models of equal capability can pair. An invalid pairing is rejected with a 400 validation error when the agent is saved. Valid pairings follow the advisor tool's [model compatibility](../Agents-Tools/agents-and-tools-tool-use-advisor-tool.md#model-compatibility) table.
The advisor is also available as a [server tool on the Messages API](../Agents-Tools/agents-and-tools-tool-use-advisor-tool.md). The Managed Agents surface differs in configuration and delivery: the roster entry has no `max_uses`, `max_tokens`, or `caching` fields, and advice arrives through thread events rather than `advisor_tool_result` blocks.
#### How consultations work
Each consultation runs as a platform-spawned thread named `anthropic.advisor` that terminates itself when the consultation completes, and the advice is delivered to the primary thread as an `agent.thread_message_received` event. A consultation emits the standard thread events, identified by the reserved name `anthropic.advisor` (the thread lifecycle events carry it as `agent_name`, and the advice delivery carries it as `from_agent_name`), typically in this order:
1. `session.thread_created`
2. `session.thread_status_running`
3. `agent.thread_message_received` (the advice)
4. `session.thread_status_idle` (`stop_reason: end_turn`)
5. `session.thread_status_terminated`
No `agent.tool_use` events are emitted for a consultation, and no `agent.thread_message_sent` event appears on the session's event stream, because the consultation input is composed by the platform rather than sent by the agent. If you list the advisor thread's own events, the advice also appears there as an `agent.thread_message_sent` event. The advice delivery (event 3) is not guaranteed to arrive before the advisor thread's idle and terminated events, so don't treat those as a signal that the advice has already been delivered.
Whether your client can read the advice is the advisor model's policy, and it mirrors the [result variants](../Agents-Tools/agents-and-tools-tool-use-advisor-tool.md#result-variants) split on the Messages API advisor tool. Advisor models that return plaintext results there deliver the advice as readable text content here; advisor models that return redacted results there deliver a `[{"type": "redacted"}]` placeholder as the message content on every client surface, while the agent itself still reads the full advice server-side. In the preceding example, Claude Opus 5 is a redacted-result advisor, so your client sees the placeholder while the agent reads the full advice; choose Claude Opus 4.8 as the advisor instead if you want the advice readable on the event stream. Advisor thinking is never surfaced. Clients cannot send `redacted` blocks themselves; an event containing one is rejected with a 400 validation error.
A failed or interrupted consultation never fails the agent's turn: the agent continues after a generic notice that the consultation failed. A session-level `user.interrupt` during a consultation terminates the advisor thread with no advice delivered; a `user.interrupt` with the advisor thread's `session_thread_id` abandons only that consultation.
#### Advisor threads
The advisor is not a roster agent: it is invisible to the coordinator's `list_agents` tool, it cannot be messaged with `send_to_agent`, and only the session's primary thread can consult it. Roster agents cannot.
Advisor threads are exempt from the concurrent-thread limit. They appear in the session's [thread list](#threads) with `agent` set to the advisor form exactly as configured (`{"type": "advisor", "model": ...}`) and `parent_thread_id` set to the primary thread.
Prompt caching on the advisor's side is automatic; there is nothing to configure. Consultations are billed at the advisor model's rates, and their tokens appear in the advisor thread's usage and in the session's usage totals.
#### Removing the advisor
To remove the advisor, [update the agent](managed-agents-agent-setup.md#update-an-agent) with a roster that no longer includes the advisor entry. If the advisor is the roster's only entry, clear the roster entirely by setting `"multiagent": null`.
## Create the session
Create a session referencing the coordinator. The coordinator delegates to the agents in its roster as needed.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=coordinator.id,
environment_id=environment.id,
)
```
## Connect agents to MCP servers
MCP servers are agent-scoped (each agent definition declares its own servers and tools), while vault credentials are session-scoped (`vault_ids` passed at session creation apply to every thread). Two implications for your integration:
- To authenticate MCP servers, include a vault credential for every MCP server used across all agents.
- To limit an agent's access, declare only the servers it needs in its agent definition.
[Agent configuration overrides](managed-agents-sessions.md#override-agent-configuration-for-a-session) at session creation can replace the coordinator's MCP servers and those of its `self` copies.
A `limited` [environment](managed-agents-environments.md#networking) blocks an agent's MCP servers unless its networking sets `allow_mcp_servers: true` or lists each server's host in `allowed_hosts`. With neither set, session creation fails with a 400 error. The check covers every agent the coordinator can delegate to.
Create the researcher, which declares the GitHub MCP server, and the coordinator that delegates to the researcher:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply coordinator.md researcher.md
```
coordinator.md
```python
---
name: coordinator
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
multiagent:
type: coordinator
agents: # path: ant apply substitutes {type: agent, id, version}
- ./researcher.md
---
```
researcher.md
```python
---
name: researcher
model: claude-haiku-4-5
mcp_servers:
- type: url
name: github
url: https://api.githubcopilot.com/mcp/
tools:
- type: mcp_toolset
mcp_server_name: github
---
```
Then create the session with the vault that holds the GitHub credential:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=coordinator.id,
environment_id=environment.id,
vault_ids=[vault.id],
)
print(session.id)
```
In this example, only the researcher declares the GitHub MCP server, so the coordinator does not have access. The session's `vault_ids` supply the GitHub credential to the researcher's thread.
If an agent's MCP calls fail to authenticate after you declare the server, confirm the credential's `mcp_server_url` refers to the same server as the agent's `mcp_servers[].url`. Both URLs are normalized before matching (scheme and host lowercased, default ports and trailing slashes stripped), so differences in host casing, a default port, or a trailing slash don't prevent a match; a different path, subdomain, or non-default port does.
## Threads
The **session-level event stream** (`/v1/sessions/{session_id}/events/stream`) is considered the **primary thread**, containing a condensed view of all activity across all threads. You don't see the full activity from subagents, but you do see the start and end of their work, and blocking events such as tool permission requests.
**Session threads** are where you drill into a specific agent's activity.
The session `status` is an aggregation of all agent activity; if at least one thread is `running`, then the overall session status is `running` as well.
A [session budget](managed-agents-budgets.md) is a single shared cap across all of a session's threads. As the cap is reached, threads pause independently, and each thread's cost is priced at the thread's own served model.
A maximum of 25 concurrent threads is supported. The coordinator can call multiple copies of a single agent in the roster, creating multiple threads associated with one `agent`. [Advisor](#give-the-session-an-advisor) consultation threads are exempt from this limit.
List threads
Interrupt a session thread
Archive a session thread
List all threads associated with a session as follows:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for thread in client.beta.sessions.threads.list(session.id):
print(f"[{thread.agent.name}] {thread.status}")
```
The full list includes the primary thread. `parent_thread_id` is null for the primary thread.
### Primary thread events
These events surface multiagent activity on the primary thread at `/v1/sessions/{session_id}/events/stream`. Message-direction events are named relative to the thread whose stream they appear on: `agent.thread_message_received` means a message arrived on this thread from another thread, and `agent.thread_message_sent` means this thread sent one. The task the coordinator delegates, for example, arrives on the child's own stream as an `agent.thread_message_received` event.
| Type | Description |
|------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `session.thread_created` | A thread was created. Includes `session_thread_id` and `agent_name`. |
| `session.thread_status_running` | A thread started activity. |
| `session.thread_status_idle` | The agent associated with the thread is awaiting input. Includes a `stop_reason` indicating why the agent stopped. |
| `session.thread_status_terminated` | A thread was archived or encountered a terminal error. |
| `agent.thread_message_received` | On the primary thread, an agent sent a report or question to the coordinator. Includes `from_session_thread_id`, `from_agent_name`, and `content`. |
| `agent.thread_message_sent` | On the primary thread, the coordinator sent a task or follow-up message to another agent. Includes `to_session_thread_id`, `to_agent_name`, and `content`. |
Advisor consultations emit these same thread events under the reserved name `anthropic.advisor` (as `agent_name` on the thread lifecycle events and `from_agent_name` on the advice delivery); see [Give the session an advisor](#give-the-session-an-advisor) for the sequence.
### Session thread events
Critical events are proxied to the primary thread. However, you might still want to investigate a specific agent's reasoning and tool calls. To do so, stream or list the events from the associated session thread.
Each session thread has its own event stream at `/v1/sessions/{session_id}/threads/{thread_id}/stream`, and it accepts the same `event_deltas[]` parameter as the session-level stream, so you can preview a subagent's text as the model generates it. A connection previews only the thread it's reading: a child thread's previews never appear on the session-level stream, so to watch a subagent live, open its own thread stream. See [Preview session thread events](managed-agents-events-and-streaming.md#preview-session-thread-events) for opting in, accumulating, and reconciling previews.
Stream session thread events
List session thread events
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
with client.beta.sessions.threads.events.stream(
thread.id,
session_id=session.id,
) as stream:
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
if block.type == "text":
print(block.text, end="")
case "session.thread_status_idle":
break
```
### Tool permissions and custom tools
If a subagent needs something from your client, such as [permission](managed-agents-events-and-streaming.md#tool-confirmation) to run a tool call or the [result of a custom tool](managed-agents-events-and-streaming.md#handling-custom-tool-calls), the event is cross-posted to the **primary thread** with `session_thread_id` identifying the originating session thread. A tool call needs your permission under `always_ask`, or under [`auto`](managed-agents-permission-policies.md#let-the-server-evaluate-each-call-with-auto) when the server reaches no determination.
```python
{
"type": "session.thread_status_idle",
"id": "sevt_01ABC...",
"session_thread_id": "sth_01DEF...",
"agent_name": "code-reviewer",
"stop_reason": {
"type": "requires_action",
"event_ids": ["sevt_01XYZ..."]
}
}
```
Post `user.tool_confirmation` (with `tool_use_id`) or `user.custom_tool_result` (with `custom_tool_use_id`); the server routes the response to the correct thread automatically.
Under `auto`, your `user.message` events can lead the server to allow a call it would otherwise deny. Nothing in a subagent's thread counts as your intent: your client posts no messages there, and the coordinator's messages to the subagent do not count. When the server denies a call under `auto`, nothing is cross-posted: the event and the error tool result appear only on the subagent's own [thread stream](#session-thread-events), and the subagent keeps running.
The following example extends the [tool confirmation handler](managed-agents-events-and-streaming.md#tool-confirmation) to route replies. The same pattern applies to `user.custom_tool_result`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for event_id in stop.event_ids:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
}
],
)
```
---
## Session tracing
Source: https://platform.claude.com/docs/en/managed-agents/observability
Path: 04-API-Reference/Other/managed-agents-observability.md
# Session tracing
Monitor and debug your sessions using the Console timeline and raw event views.
---
Claude Managed Agents provides observability tools in the [Claude Console](usage-limits.md) to help you monitor, debug, and understand your agent sessions.
## Console observability
The Console provides a visual timeline view of your agent sessions. Navigate to the Claude Managed Agents section in the Console to see:
- **Session list** - All sessions with their status, creation time, and model
- **Tracing view** - A chronological view of events (content, timestamps, token usage) within a session. These are only accessible to Developers and Admins.
- **Tool execution** - Details of each tool call and its result
## Raw events
For programmatic debugging, retrieve raw events via the API:
<CodeGroup>
```bash curl
curl -fsSL "https://api.anthropic.com/v1/sessions/$SESSION_ID/events" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
| jq -r '
.data[]
| "Type: \(.type)",
"Processed: \(.processed_at)",
( if .type | IN("user.message", "agent.message") then
.content[]
| " Block: \(.type)",
(select(.type == "text") | " Text: \(.text[:100])...")
elif .type | IN("agent.tool_use", "agent.custom_tool_use", "agent.mcp_tool_use") then
" Tool: \(.name)"
else empty end ),
"---"
'
```
```bash CLI nocheck
ant beta:sessions:events list --session-id "$SESSION_ID"
```
```python Python
events = client.beta.sessions.events.list(session.id)
for event in events:
print(f"Type: {event.type}")
print(f"Processed: {event.processed_at}")
match event.type:
case "user.message" | "agent.message":
for block in event.content:
print(f" Block: {block.type}")
if block.type == "text":
print(f" Text: {block.text[:100]}...")
case "agent.tool_use" | "agent.custom_tool_use" | "agent.mcp_tool_use":
print(f" Tool: {event.name}")
print("---")
```
```typescript TypeScript
for await (const event of client.beta.sessions.events.list(session.id)) {
console.log(`Type: ${event.type}`);
console.log(`Processed: ${event.processed_at}`);
switch (event.type) {
case "user.message":
case "agent.message":
for (const block of event.content) {
console.log(` Block: ${block.type}`);
if (block.type === "text") {
console.log(` Text: ${block.text.slice(0, 100)}...`);
}
}
break;
case "agent.tool_use":
case "agent.custom_tool_use":
case "agent.mcp_tool_use":
console.log(` Tool: ${event.name}`);
break;
}
console.log("---");
}
```
```csharp C#
var events = await client.Beta.Sessions.Events.List(session.ID);
await foreach (var evt in events.Paginate())
{
Console.WriteLine($"Type: {evt.Json.GetProperty("type").GetString()}");
Console.WriteLine($"Processed: {evt.ProcessedAt}");
switch (evt.Value)
{
case BetaManagedAgentsUserMessageEvent userMessage:
foreach (var block in userMessage.Content)
{
Console.WriteLine($" Block: {block.Json.GetProperty("type").GetString()}");
if (block.Value is BetaManagedAgentsTextBlock textBlock)
{
Console.WriteLine($" Text: {textBlock.Text[..Math.Min(100, textBlock.Text.Length)]}...");
}
}
break;
case BetaManagedAgentsAgentMessageEvent agentMessage:
foreach (var block in agentMessage.Content)
{
Console.WriteLine($" Block: {block.Type.Raw()}");
Console.WriteLine($" Text: {block.Text[..Math.Min(100, block.Text.Length)]}...");
}
break;
case BetaManagedAgentsAgentToolUseEvent or BetaManagedAgentsAgentCustomToolUseEvent or BetaManagedAgentsAgentMcpToolUseEvent:
Console.WriteLine($" Tool: {evt.Name}");
break;
}
Console.WriteLine("---");
}
```
```go Go
events := client.Beta.Sessions.Events.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionEventListParams{})
for events.Next() {
event := events.Current()
fmt.Printf("Type: %s\n", event.Type)
fmt.Printf("Processed: %s\n", event.ProcessedAt)
switch event.Type {
case "user.message":
for _, block := range event.AsUserMessage().Content {
fmt.Printf(" Block: %s\n", block.Type)
if block.Type == "text" {
fmt.Printf(" Text: %s...\n", block.Text[:min(100, len(block.Text))])
}
}
case "agent.message":
for _, block := range event.AsAgentMessage().Content {
fmt.Printf(" Block: %s\n", block.Type)
if block.Type == "text" {
fmt.Printf(" Text: %s...\n", block.Text[:min(100, len(block.Text))])
}
}
case "agent.tool_use", "agent.custom_tool_use", "agent.mcp_tool_use":
fmt.Printf(" Tool: %s\n", event.Name)
}
fmt.Println("---")
}
if err := events.Err(); err != nil {
panic(err)
}
```
```java Java
var events = client.beta().sessions().events()
.list(EventListParams.builder().sessionId(session.id()).build());
for (var event : events.autoPager()) {
var json = (Map<String, JsonValue>) event._json().orElseThrow().asObject().orElseThrow();
var type = json.get("type").asStringOrThrow();
IO.println("Type: " + type);
IO.println("Processed: " + json.get("processed_at"));
if (event.isUserMessage() || event.isAgentMessage()) {
for (var block : (List<JsonValue>) json.get("content").asArray().orElseThrow()) {
var blockJson = (Map<String, JsonValue>) block.asObject().orElseThrow();
var blockType = blockJson.get("type").asStringOrThrow();
IO.println(" Block: " + blockType);
if (blockType.equals("text")) {
var text = blockJson.get("text").asStringOrThrow();
IO.println(" Text: " + text.substring(0, Math.min(100, text.length())) + "...");
}
}
} else if (event.isAgentToolUse() || event.isAgentCustomToolUse() || event.isAgentMcpToolUse()) {
IO.println(" Tool: " + json.get("name").asStringOrThrow());
}
IO.println("---");
}
```
```php PHP
foreach ($client->beta->sessions->events->list($session->id)->pagingEachItem() as $event) {
echo "Type: {$event->type}\n";
$processedAt = ($event->processedAt ?? null)?->format(DATE_RFC3339) ?? 'pending';
echo "Processed: {$processedAt}\n";
if (in_array($event->type, ['user.message', 'agent.message'], true)) {
foreach ($event->content as $block) {
echo " Block: {$block->type}\n";
if ($block->type === 'text') {
echo ' Text: ' . mb_substr($block->text, 0, 100) . "...\n";
}
}
} elseif (in_array($event->type, ['agent.tool_use', 'agent.custom_tool_use', 'agent.mcp_tool_use'], true)) {
echo " Tool: {$event->name}\n";
}
echo "---\n";
}
```
```ruby Ruby
client.beta.sessions.events.list(session.id).auto_paging_each do |event|
puts "Type: #{event.type}"
puts "Processed: #{event.processed_at}"
case event.type
when :"user.message", :"agent.message"
event.content.each do |block|
puts " Block: #{block.type}"
puts " Text: #{block.text[0, 100]}..." if block.type == :text
end
when :"agent.tool_use", :"agent.custom_tool_use", :"agent.mcp_tool_use"
puts " Tool: #{event.name}"
end
puts "---"
end
```
</CodeGroup>
Use the same event stream to surface errors and track token consumption:
<CodeGroup>
```bash curl
curl -fsSL "https://api.anthropic.com/v1/sessions/$SESSION_ID/events" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
| jq -r '
(.data[] | select(.type == "session.error") | "[\(.error.type)] \(.error.message)"),
(reduce (.data[] | select(.type == "span.model_request_end") | .model_usage) as $u
({input: 0, output: 0}; .input += $u.input_tokens | .output += $u.output_tokens)
| "Total input tokens: \(.input), output tokens: \(.output)")
'
```
```bash CLI nocheck
ant beta:sessions:events list \
--session-id "$SESSION_ID" \
--format jsonl \
| jq -sr '
(.[] | select(.type == "session.error") | "[\(.error.type)] \(.error.message)"),
(reduce (.[] | select(.type == "span.model_request_end") | .model_usage) as $u
({input: 0, output: 0}; .input += $u.input_tokens | .output += $u.output_tokens)
| "Total input tokens: \(.input), output tokens: \(.output)")
'
```
```python Python
events = client.beta.sessions.events.list(session.id)
input_tokens, output_tokens = 0, 0
for event in events:
match event.type:
case "session.error":
print(f"[{event.error.type}] {event.error.message}")
case "span.model_request_end":
input_tokens += event.model_usage.input_tokens
output_tokens += event.model_usage.output_tokens
print(f"Total input tokens: {input_tokens}, output tokens: {output_tokens}")
```
```typescript TypeScript
let inputTokens = 0;
let outputTokens = 0;
for await (const event of client.beta.sessions.events.list(session.id)) {
switch (event.type) {
case "session.error":
console.log(`[${event.error?.type}] ${event.error?.message}`);
break;
case "span.model_request_end":
inputTokens += event.model_usage.input_tokens;
outputTokens += event.model_usage.output_tokens;
break;
}
}
console.log(`Total input tokens: ${inputTokens}, output tokens: ${outputTokens}`);
```
```csharp C#
var events = await client.Beta.Sessions.Events.List(session.ID);
var inputTokens = 0;
var outputTokens = 0;
await foreach (var evt in events.Paginate())
{
switch (evt.Value)
{
case BetaManagedAgentsSessionErrorEvent { Error: { } error }:
Console.WriteLine($"[{error.Json.GetProperty("type").GetString()}] {error.Message}");
break;
case BetaManagedAgentsSpanModelRequestEndEvent spanEnd:
inputTokens += spanEnd.ModelUsage.InputTokens;
outputTokens += spanEnd.ModelUsage.OutputTokens;
break;
}
}
Console.WriteLine($"Total input tokens: {inputTokens}, output tokens: {outputTokens}");
```
```go Go
events := client.Beta.Sessions.Events.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionEventListParams{})
var inputTokens, outputTokens int64
for events.Next() {
event := events.Current()
switch event.Type {
case "session.error":
e := event.AsSessionError().Error
fmt.Printf("[%s] %s\n", e.Type, e.Message)
case "span.model_request_end":
usage := event.AsSpanModelRequestEnd().ModelUsage
inputTokens += usage.InputTokens
outputTokens += usage.OutputTokens
}
}
if err := events.Err(); err != nil {
panic(err)
}
fmt.Printf("Total input tokens: %d, output tokens: %d\n", inputTokens, outputTokens)
```
```java Java
var events = client.beta().sessions().events()
.list(EventListParams.builder().sessionId(session.id()).build());
var inputTokens = 0;
var outputTokens = 0;
for (var event : events.autoPager()) {
if (event.isError()) {
var json = (Map<String, JsonValue>) event._json().orElseThrow().asObject().orElseThrow();
var error = (Map<String, JsonValue>) json.get("error").asObject().orElseThrow();
IO.println("[" + error.get("type").asStringOrThrow() + "] "
+ error.get("message").asStringOrThrow());
} else if (event.isSpanModelRequestEnd()) {
var usage = event.asSpanModelRequestEnd().modelUsage();
inputTokens += usage.inputTokens();
outputTokens += usage.outputTokens();
}
}
IO.println("Total input tokens: " + inputTokens + ", output tokens: " + outputTokens);
```
```php PHP
$inputTokens = 0;
$outputTokens = 0;
foreach ($client->beta->sessions->events->list($session->id)->pagingEachItem() as $event) {
match ($event->type) {
'session.error' => printf("[%s] %s\n", $event->error->type, $event->error->message),
'span.model_request_end' => [
$inputTokens += $event->modelUsage->inputTokens,
$outputTokens += $event->modelUsage->outputTokens,
],
default => null,
};
}
echo "Total input tokens: {$inputTokens}, output tokens: {$outputTokens}\n";
```
```ruby Ruby
input_tokens, output_tokens = 0, 0
client.beta.sessions.events.list(session.id).auto_paging_each do |event|
case event.type
when :"session.error"
puts "[#{event.error.type}] #{event.error.message}"
when :"span.model_request_end"
input_tokens += event.model_usage.input_tokens
output_tokens += event.model_usage.output_tokens
end
end
puts "Total input tokens: #{input_tokens}, output tokens: #{output_tokens}"
```
</CodeGroup>
## Debugging tips
- **Check session events** - Session errors are conveyed through the `session.error` event
- **Review tool results** - Tool execution failures often explain unexpected agent behavior
- **Track token usage** - Monitor token consumption to optimize prompts and reduce costs
- **Use system prompts** - Add logging instructions to the system prompt to make the agent explain its reasoning
---
## Build in Console - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/onboarding
Path: 04-API-Reference/Other/managed-agents-onboarding.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fonboarding)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)First steps
# Build in Console
Copy page
Create, test, and iterate on agents visually in Console, then run them from your code with the API.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
[Console](https://platform.claude.com/workspaces/default/agent-quickstart/) provides a visual interface for creating and configuring agents. It lets you iterate on configuration interactively before writing code.
## How to build an agent
The [visual interface](https://platform.claude.com/workspaces/default/agent-quickstart/) walks you through each field of an agent definition:
- **Model and system prompt:** Pick a model and write the system prompt in a full-width editor.
- **MCP servers:** Add remote MCP servers by URL and authenticate your agent to take action on your behalf.
- **Tools:** Extend your agent's capabilities using a pre-built agent toolset and MCP tools.
- **Skills:** Attach Anthropic or custom skills from your organization's library.
As you configure, Console shows the equivalent API request so you can copy it into your code once you're satisfied.
## Testing an agent
Console includes an inline session runner. After configuring your agent, you can start a test session directly, send messages, and watch the event stream without leaving the page. This is the fastest way to check that your system prompt and tool selection produce the behavior you expect.
## From Console to your codebase
Once your agent works as expected:
1. Copy the agent ID and [environment ID](managed-agents-environments.md) from Console.
2. Reference them in your code when [creating sessions](managed-agents-sessions.md):
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent="agent_01J8XkN5uT3vHpLqRfWdY2",
environment_id="env_01K2mPsT7hNwR4jXuLvCqD8",
title="My first session",
)
```
---
## Define outcomes
Source: https://platform.claude.com/docs/en/managed-agents/output-patterns
Path: 04-API-Reference/Other/managed-agents-output-patterns.md
# Define outcomes
Tell the agent what 'done' looks like, and let it iterate until it gets there.
---
> Note: The original URL `/docs/en/managed-agents/output-patterns` returned 404. This content is sourced from the define-outcomes page, which covers output patterns for managed agents.
> Outcomes is a Research Preview feature. [Request access](https://claude.com/form/claude-managed-agents) to try it.
The `outcome` elevates a session from *conversation* to *work*. You define what the end result should look like and how to measure quality. The agent works toward that target, self-evaluating and iterating until the outcome is met.
When you define an outcome, the harness automatically provisions a *grader* to evaluate the artifact against a rubric. It leverages a separate context window to avoid being influenced by the main agent's implementation choices.
The grader returns a per-criterion breakdown: either confirmation that the artifact satisfies the rubric, or the specific gaps between the current work and the requirements. That feedback is handed back to the agent for the next iteration.
> All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. Research preview features additionally require `managed-agents-2026-04-01-research-preview`. The SDK sets these beta headers automatically.
## Create a rubric
A rubric is a markdown document describing per-criterion scoring. The rubric is required.
### Tips for writing effective rubrics
Structure the rubric as explicit, gradeable criteria, such as "The CSV contains a price column with numeric values" rather than "The data looks good." The grader scores each criterion independently, so vague criteria produce noisy evaluations.
If you don't have a rubric on hand, try giving Claude an example of a known-good artifact and asking it to analyze what makes that content good, then turn that analysis into a rubric. This middle-ground approach often produces better results than writing criteria from scratch.
Example rubric:
```markdown
# DCF Model Rubric
## Revenue Projections
- Uses historical revenue data from the last 5 fiscal years
- Projects revenue for at least 5 years forward
- Growth rate assumptions are explicitly stated and reasonable
## Cost Structure
- COGS and operating expenses are modeled separately
- Margins are consistent with historical trends or deviations are justified
## Discount Rate
- WACC is calculated with stated assumptions for cost of equity and cost of debt
- Beta, risk-free rate, and equity risk premium are sourced or justified
## Terminal Value
- Uses either perpetuity growth or exit multiple method (stated which)
- Terminal growth rate does not exceed long-term GDP growth
## Output Quality
- All figures are in a single .xlsx file with clearly labeled sheets
- Key assumptions are on a separate "Assumptions" sheet
- Sensitivity analysis on WACC and terminal growth rate is included
```
Pass the rubric as inline text on `user.define_outcome`, or upload it via the Files API for reuse across sessions.
## Create a session with an outcome
After creating a session, send a `user.define_outcome` event. The agent begins work immediately; no additional user message event is required.
```python
# Create a session
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
title="Financial analysis on Costco",
)
# Define the outcome -- agent starts working on receipt
client.beta.sessions.events.send(
session_id=session.id,
events=[
{
"type": "user.define_outcome",
"description": "Build a DCF model for Costco in .xlsx",
"rubric": {"type": "text", "content": RUBRIC},
# or: "rubric": {"type": "file", "file_id": rubric.id},
"max_iterations": 5, # optional; default 3, max 20
}
],
)
```
```typescript
// Create a session
const session = await client.beta.sessions.create({
agent: agent.id,
environment_id: environment.id,
title: "Financial analysis on Costco"
});
// Define the outcome -- agent starts working on receipt
await client.beta.sessions.events.send(session.id, {
events: [
{
type: "user.define_outcome",
description: "Build a DCF model for Costco in .xlsx",
rubric: { type: "text", content: RUBRIC },
// or: rubric: { type: "file", file_id: rubric.id },
max_iterations: 5 // optional; default 3, max 20
}
]
});
```
## Outcome events
Progress on an outcome-oriented session is surfaced on the events [stream](managed-agents-events-and-streaming.md).
- `agent.*` events (messages, tool use, etc.) show progress towards the outcome.
- `span.outcome_evaluation_*` events are only emitted for outcome-oriented sessions and show the number of iteration loops and the grader's feedback process.
- You can also send `user.message` [events](managed-agents-events-and-streaming.md#user-events) to an outcome-oriented session, to direct the agent's work as it progresses, but these are not as necessary; the agent knows to work until it has exhausted its iterations or achieved the outcome.
- A `user.interrupt` event will pause work on the current outcome and mark the `span.outcome_evaluation_end.result` as `interrupted`, allowing you to kick off a new outcome.
- After the final outcome evaluation, the session can be continued as a conversational session, or a new outcome can be kicked off. The session will retain history of the prior outcome.
### Define outcome user event
> Only one outcome supported at a time, but you may chain together outcomes in sequence. To do this, send a new `user.define_outcome` event after the terminal event of the previous outcome.
This is the event you send to initiate an outcome. It is echoed back on receipt, including a `processed_at` timestamp and `outcome_id`.
```json
{
"type": "user.define_outcome",
"description": "Build a DCF model for Costco in .xlsx",
"rubric": { "type": "file", "file_id": "file_01..." },
"max_iterations": 5
}
```
### Outcome evaluation start
Emitted once the grader starts an evaluation over one iteration loop. The `iteration` field is a 0-indexed revision counter: `0` is the first evaluation, `1` is the re-evaluation after the first revision, and so on.
```json
{
"type": "span.outcome_evaluation_start",
"id": "sevt_01def...",
"outcome_id": "outc_01a...",
"iteration": 0,
"processed_at": "2026-03-25T14:01:45Z"
}
```
### Outcome evaluation ongoing
Heartbeat emitted while the grader runs. The grader's internal reasoning is opaque: you see that it's working, not what it's thinking.
```json
{
"type": "span.outcome_evaluation_ongoing",
"id": "sevt_01ghi...",
"outcome_id": "outc_01a...",
"processed_at": "2026-03-25T14:02:10Z"
}
```
### Outcome evaluation end
Emitted after the grader finishes evaluating one iteration. The `result` field indicates what happens next.
| Result | Next |
| --- | --- |
| `satisfied` | Session transitions to `idle`. |
| `needs_revision` | Agent starts a new iteration cycle. |
| `max_iterations_reached` | No further evaluation cycles. The agent may run one final revision before the session transitions to `idle`. |
| `failed` | Session transitions to `idle`. Returned when the rubric fundamentally does not match the task, for example if the description and rubric contradict each other. |
| `interrupted` | Only emitted if `outcome_evaluation_start` already fired before the interrupt. |
```json
{
"type": "span.outcome_evaluation_end",
"id": "sevt_01jkl...",
"outcome_evaluation_start_id": "sevt_01def...",
"outcome_id": "outc_01a...",
"result": "satisfied",
"explanation": "All 12 criteria met: revenue projections use 5 years of historical data, WACC assumptions are stated, sensitivity table is included...",
"iteration": 0,
"usage": {
"input_tokens": 2400,
"output_tokens": 350,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1800
},
"processed_at": "2026-03-25T14:03:00Z"
}
```
## Checking on outcome status
You can either listen on the [event stream](managed-agents-events-and-streaming.md) for `span.outcome_evaluation_end`, or poll `GET /v1/sessions/:id` and read `outcome_evaluations[].result`:
```python
session = client.beta.sessions.retrieve(session.id)
for outcome in session.outcome_evaluations:
print(f"{outcome.outcome_id}: {outcome.result}")
# outc_01a...: satisfied
```
```typescript
const retrieved = await client.beta.sessions.retrieve(session.id);
for (const outcome of retrieved.outcome_evaluations) {
console.log(`${outcome.outcome_id}: ${outcome.result}`);
// outc_01a...: satisfied
}
```
## Retrieving deliverables
The agent writes output files to `/mnt/session/outputs/` inside the container. Once the session is idle, fetch them via the [Files API](../Guides/build-with-claude-files.md) scoped to the session:
```python
files = client.beta.files.list(scope_id=session.id)
for f in files.data:
print(f"{f.id}: {f.filename} ({f.size_bytes} bytes)")
content = client.beta.files.download(files.data[0].id)
content.write_to_file("costco_dcf.xlsx")
```
```bash
# List files produced by this session
curl -fsSL "https://api.anthropic.com/v1/files?scope_id=$session_id" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: files-api-2025-04-14,managed-agents-2026-04-01-research-preview" \
| jq '.data[] | {id, filename, size_bytes}'
# Download by file_id
curl -fsSL "https://api.anthropic.com/v1/files/$file_id/content" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: files-api-2025-04-14" \
-o costco_dcf.xlsx
```
---
## Claude Managed Agents overview - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/overview
Path: 04-API-Reference/Other/managed-agents-overview.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Foverview)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
Managed AgentsFirst steps
# Claude Managed Agents overview
Copy page
Pre-built, configurable agent harness that runs in managed infrastructure. Best for long-running tasks and asynchronous work.
Copy page
Managed Agents
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Anthropic offers two ways to build with Claude, each suited to different use cases:
| | Messages API | Claude Managed Agents |
|----------------|---------------------------------------------|---------------------------------------------------------------------------|
| **What it is** | Direct model prompting access | Pre-built, configurable agent harness that runs in managed infrastructure |
| **Best for** | Custom agent loops and fine-grained control | Long-running tasks and asynchronous work |
Claude Managed Agents provides the harness and infrastructure for running Claude as an autonomous agent. Instead of building your own agent loop, tool execution, and runtime, you get a fully managed environment where Claude can read files, run commands, browse the web, and run code securely. The harness supports built-in prompt caching, compaction, and other performance optimizations for high-quality, efficient agent outputs. To build your own agent loop with direct model access instead, see [Using the Messages API](../Guides/build-with-claude-working-with-messages.md).
Claude Managed Agents is also available on Claude Platform on AWS, with some differences in feature availability and session behavior. See [Claude Managed Agents](../Guides/build-with-claude-claude-platform-on-aws.md#claude-managed-agents) in the Claude Platform on AWS guide.
[Quickstart](managed-agents-quickstart.md)
Create your first agent session
[Start a session](managed-agents-sessions.md)
Create a session and send your first event
[Reference](managed-agents-reference.md)
Event types, rate limits, CLI flags, and other lookup tables
## Core concepts
Claude Managed Agents is built around four concepts:
| Concept | Description |
|-----------------|-------------------------------------------------------------------------------------------------------------------------------|
| **Agent** | The model, system prompt, tools, MCP servers, and skills |
| **Environment** | Configuration for where sessions run: an Anthropic-managed cloud sandbox, or a self-hosted sandbox on your own infrastructure |
| **Session** | A running agent instance within an environment, performing a specific task and generating outputs |
| **Events** | Messages exchanged between your application and the agent (user turns, tool results, status updates) |
## How it works
1. 1
### Create an agent
Define the model, system prompt, tools, MCP servers, and skills. Create the agent once and reference it by ID across sessions.
2. 2
### Create an environment
Configure where the agent runs: a cloud sandbox, or a [self-hosted sandbox](managed-agents-self-hosted-sandboxes.md) on your own infrastructure.
3. 3
### Start a session
Launch a session that references your agent and environment configuration.
4. 4
### Send events and stream responses
Send user messages as events. Claude autonomously runs tools and streams back results through server-sent events (SSE). Event history is persisted server-side and can be fetched in full.
5. 5
### Steer or interrupt
Send additional user events to guide the agent mid-execution, or interrupt it to change direction.
## When to use Claude Managed Agents
Claude Managed Agents is best for workloads that need:
- **Long-running execution:** Tasks that run for minutes or hours with multiple tool calls
- **Cloud infrastructure:** Secure sandboxes with pre-installed packages and network access
- **Self-hosted execution:** Sandboxes on infrastructure you control for compliance or data-residency requirements
- **Minimal infrastructure:** No need to build your own agent loop, sandbox, or tool execution layer
- **Stateful sessions:** Persistent filesystems and conversation history across multiple interactions
- **Scheduled execution:** Recurring agent runs on a cron schedule through [scheduled deployments](managed-agents-scheduled-deployments.md)
## Supported tools
Claude Managed Agents gives Claude access to a set of built-in tools:
- **Bash:** Run shell commands in the sandbox
- **File operations:** Read, write, edit, glob, and grep files in the sandbox
- **Web search and fetch:** Search the web and retrieve content from URLs, optionally restricted to an allowlist or blocklist of domains
- **MCP servers:** Connect to external tool providers
See [Tools](managed-agents-tools.md) for the full list and configuration options.
## Beta access
Claude Managed Agents is in beta. All Managed Agents endpoints require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically. Behaviors may be refined between releases to improve outputs.
To get started, you need:
1. A [Claude API key](playground.md)
2. The `managed-agents-2026-04-01` beta header on all requests
3. Access to Claude Managed Agents (enabled by default for all API accounts)
Within the beta, [MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md) and [dreaming](managed-agents-dreams.md) are in a more limited research preview. [Request access](https://claude.com/form/claude-managed-agents) to enable them.
Claude Managed Agents is stateful by design: sessions are long-running, resume cleanly after pauses, and store conversation history, sandbox state, and outputs server-side. Because of this, Managed Agents is not currently eligible for [Zero Data Retention](../Guides/build-with-claude-api-and-data-retention.md#zero-data-retention-zdr-scope) or HIPAA Business Associate Agreement (BAA) coverage. You retain control over this data: you can [delete sessions](managed-agents-session-operations.md#deleting-a-session), and separately delete any [files](../Guides/build-with-claude-files.md#delete-a-file) you uploaded, at any time through the API. For eligibility across all features, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md#feature-eligibility).
See [Rate limits](managed-agents-reference.md#rate-limits) and [Branding guidelines](managed-agents-reference.md#branding-guidelines) in the reference.
---
## Permission policies - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/permission-policies
Path: 04-API-Reference/Other/managed-agents-permission-policies.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fpermission-policies)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# Permission policies
Copy page
Control when agent and MCP tools execute.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Permission policies control whether server-executed tools (the pre-built agent toolset and MCP toolset) run automatically, wait for your approval, or have each call evaluated by the server. Custom tools are executed by your application and controlled by you, so they are not governed by permission policies.
## Permission policy types
| Policy | Behavior |
|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `always_allow` | The tool executes automatically with no confirmation. |
| `always_ask` | The session pauses and waits for your approval before executing. See [Respond to confirmation requests](#respond-to-confirmation-requests) for the event flow. |
| `auto` | The server evaluates each call and runs it, denies it, or pauses for your approval. See [Let the server evaluate each call with `auto`](#let-the-server-evaluate-each-call-with-auto). |
Each toolset kind has its own default: the agent toolset defaults to `always_allow`, and MCP toolsets default to `always_ask`.
A permission policy controls when an enabled tool runs. To remove a tool from the agent entirely, disable it instead. See [Disabling specific tools](managed-agents-tools.md#disabling-specific-tools).
## Set a policy for a toolset
You set permission policies in the agent's `tools` configuration when you create the agent, and you can change them later by [updating the agent](managed-agents-agent-setup.md#update-an-agent). Running sessions keep the toolset configuration they were created with. Updates apply to sessions created afterward.
### Agent toolset permissions
When creating an agent, you can apply a policy to every tool in `agent_toolset_20260401` using `default_config.permission_policy`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
default_config:
permission_policy:
type: always_ask
---
```
`default_config` is optional. If you omit it, the agent toolset is enabled with the default permission policy, `always_allow`.
### MCP toolset permissions
MCP toolsets default to `always_ask`. This ensures that new tools added to an MCP server do not execute in your application without approval. To auto-approve tools from a trusted MCP server, set `default_config.permission_policy` on the `mcp_toolset` entry.
The `mcp_server_name` must match the `name` of a server in the `mcp_servers` array.
This example connects a GitHub MCP server and allows its tools to run without confirmation:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Dev Assistant
model: claude-opus-5-5
mcp_servers:
- type: url
name: github
url: https://mcp.example.com/github
tools:
- type: agent_toolset_20260401
- type: mcp_toolset
mcp_server_name: github
default_config:
permission_policy:
type: always_allow
---
```
## Override an individual tool policy
Use the `configs` array to override the default for individual tools. The `name` values for the agent toolset are listed in [Available tools](managed-agents-tools.md#available-tools). This example allows the full agent toolset by default but requires confirmation before any bash command runs:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
default_config:
permission_policy:
type: always_allow
configs:
- name: bash
permission_policy:
type: always_ask
---
```
Pass this `tools` configuration in the agent create request (the CLI tab shows the complete command). MCP toolsets support the same per-tool overrides, with `name` set to the tool name reported by the MCP server. See [Configure which MCP tools are available](managed-agents-mcp-connector.md#configure-which-mcp-tools-are-available).
## Let the server evaluate each call with `auto`
With the `auto` permission policy, the server evaluates each call before it runs. Because the evaluation considers the tool, the call's input, and the session's content up to that point, the server can treat two calls to the same tool differently. Each call has one of three outcomes:
- **The call runs.** When the server determines that the call is safe, the tool runs as it would under `always_allow`.
- **The call is denied.** When the server evaluates the call as high-risk, the tool does not run. The agent receives an error tool result with the content `Permission to use {tool_name} has been denied.` and `is_error: true`. The session keeps running, and your client cannot override the denial.
- **The call pauses for your approval.** When the server reaches no determination, the session pauses as it does under `always_ask`. See [Respond to confirmation requests](#respond-to-confirmation-requests).
To turn on `auto`, set `permission_policy` to `{"type": "auto"}`. It goes in the same two places as the other policies: a toolset's [`default_config`](#set-a-policy-for-a-toolset) for the whole toolset, or a [`configs` entry](#override-an-individual-tool-policy) for one tool. The agent toolset and MCP toolsets both accept it. No toolset uses `auto` by default.
The following example sets `auto` as the default for the agent toolset and for the `github` MCP toolset, and overrides `bash` to `always_ask`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Ops Agent
model: claude-opus-5-5
mcp_servers:
- type: url
name: github
url: https://mcp.example.com/github
tools:
- type: agent_toolset_20260401
default_config:
permission_policy:
type: auto
configs:
- name: bash
permission_policy:
type: always_ask
- type: mcp_toolset
mcp_server_name: github
default_config:
permission_policy:
type: auto
---
```
What you post in `user.message` events counts as your intent, and it can lead the server to allow a call it would otherwise deny. The server does not read intent from a tool result, a fetched webpage, an MCP server's response, or a message between [session threads](managed-agents-multiagent-orchestration.md#tool-permissions-and-custom-tools). It assesses that content but does not take instructions from it. The server evaluates some calls as high-risk no matter who asks. If you relay untrusted end-user input in `user.message` events, the server reads that input as your intent too, and it can get a call allowed. Configure `always_ask` on the tools you would not let that end user run without review.
`auto` is not a human checkpoint. If the server determines that a call is safe, the call runs before anyone sees it, and its effects might not be reversible. If a person must review a tool's calls before they run, configure `always_ask` on that tool.
## See how each call was evaluated
Under any permission policy, each `agent.tool_use` and `agent.mcp_tool_use` event carries `evaluated_permission`, the outcome of the call's permission check: `"allow"`, `"ask"`, or `"deny"`. Most events also carry an `evaluation` object whose `type` names the policy that produced that outcome. Under `auto`, the object also records the server's determination, plus a `reason_code` when the outcome is `ask` or `deny`.
For example, when `bash` is under `auto` and the server evaluates a call as high-risk, the denied call appears on the event stream as follows:
```python
{
"type": "agent.tool_use",
"id": "sevt_01pqr...",
"name": "bash",
"input": {
"command": "rm -rf /workspace/reports"
},
"evaluated_permission": "deny",
"evaluation": {
"type": "auto",
"evaluated_permission": {
"type": "deny",
"reason_code": "high_risk"
}
},
"processed_at": "2026-03-25T14:05:12Z"
}
```
The `evaluation` object takes one of the forms in the following table.
| `evaluation` | Top-level `evaluated_permission` | Meaning |
|---------------------------------------------------------------------------------------------|----------------------------------|------------------------------------------------------------------------------------------|
| `{"type": "always_allow"}` | `"allow"` | The resolved policy is `always_allow`, so the call ran. |
| `{"type": "always_ask"}` | `"ask"` | The resolved policy is `always_ask`, so the call paused for your approval. |
| `{"type": "auto", "evaluated_permission": {"type": "allow"}}` | `"allow"` | Under `auto`, the server determined that the call was safe, and it ran. |
| `{"type": "auto", "evaluated_permission": {"type": "ask", "reason_code": "indeterminate"}}` | `"ask"` | Under `auto`, the server reached no determination, so the call paused for your approval. |
| `{"type": "auto", "evaluated_permission": {"type": "deny", "reason_code": "high_risk"}}` | `"deny"` | Under `auto`, the server evaluated the call as high-risk and denied it. |
When `evaluation.type` is `"auto"`, its nested `evaluated_permission.type` repeats the event's top-level `evaluated_permission`, so you can read the outcome from either field. A `reason_code` is a value for your client to branch on and keep in audit records, not text to display to end users.
`evaluation` is absent in two cases. When the agent names a tool that is not enabled in the session, the server denies the call without evaluating a policy: the event carries `evaluated_permission: "deny"` and no `evaluation`. Events recorded before `evaluation` was introduced also omit it: read those as `always_allow` when `evaluated_permission` is `"allow"` and as `always_ask` when it is `"ask"`.
Write your client to tolerate an `evaluation.type` or `reason_code` it does not recognize. `agent.custom_tool_use` events carry neither field, because permission policies do not govern [custom tools](#custom-tools).
## Respond to confirmation requests
A tool call evaluates to `ask` under an `always_ask` policy, or under `auto` when the server reaches no determination. When that happens:
1. The session emits an `agent.tool_use` or `agent.mcp_tool_use` event.
2. The session pauses with a `session.status_idle` event whose `stop_reason.type` is `requires_action`. The blocking event IDs are in the `stop_reason.event_ids` array. The session waits indefinitely for a response.
3. Send a `user.tool_confirmation` event for each blocking event, passing the event ID in the `tool_use_id` parameter. Set `result` to `"allow"` or `"deny"`. Use `deny_message` to explain a denial. You can send several confirmations in a single `events` request.
4. Once all blocking events are resolved, the session transitions back to `running`. Allowed tools execute. Denied tools do not run, and the agent receives a tool result saying the call was rejected, including your `deny_message`.
If you send a `user.tool_confirmation` for an event whose `evaluated_permission` is not `ask`, the API rejects it with a 400 error. That includes calls the server denied under `auto`: your client cannot override them.
To answer interactively instead, use `ant beta:sessions connect`, which shows the waiting call and sends this event when you allow or deny it. See [Connect to a Managed Agents session from your terminal](cli-sdks-libraries-cli-sessions-connect.md#follow-and-steer-the-session).
In the following examples, the tool-use event IDs come from the `stop_reason.event_ids` array of the `session.status_idle` event. Learn more about receiving events in the [Session event stream](managed-agents-events-and-streaming.md#integrating-events) guide, or [subscribe to webhooks](managed-agents-webhooks.md) to be notified when a session pauses for input.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Allow the tool to execute
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": agent_tool_use_event.id,
"result": "allow",
},
],
)
# Or deny it with an explanation
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": mcp_tool_use_event.id,
"result": "deny",
"deny_message": "Don't create issues in the production project. Use the staging project.",
},
],
)
```
## Custom tools
Permission policies do not apply to custom tools. When the agent invokes a custom tool, your application receives an `agent.custom_tool_use` event and is responsible for deciding whether to execute it before sending back a `user.custom_tool_result`. See [Session event stream](managed-agents-events-and-streaming.md#handling-custom-tool-calls) for the full flow.
## Next steps
[Skills](managed-agents-skills.md)
Attach reusable, filesystem-based expertise to your agent for domain-specific workflows.
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
---
## Get started with Claude Managed Agents - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/quickstart
Path: 04-API-Reference/Other/managed-agents-quickstart.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fquickstart)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)First steps
# Get started with Claude Managed Agents
Copy page
Create your first autonomous agent.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
This guide walks you through creating an agent, setting up an environment, starting a session, and streaming agent responses.
**Prefer an interactive walkthrough?** Run `/claude-api managed-agents-onboard` in the latest version of [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md) for a guided setup and interactive question-answering.
## Core concepts
| Concept | Description |
|-----------------|-------------------------------------------------------------------------------------------------------------------------------|
| **Agent** | The model, system prompt, tools, MCP servers, and skills |
| **Environment** | Configuration for where sessions run: an Anthropic-managed cloud sandbox, or a self-hosted sandbox on your own infrastructure |
| **Session** | A running agent instance within an environment, performing a specific task and generating outputs |
| **Events** | Messages exchanged between your application and the agent (user turns, tool results, status updates) |
## Prerequisites
- A [Claude Console account](usage-limits.md)
- An [API key](playground.md)
## Install the CLI
Homebrew (macOS)
curl (Linux/WSL)
Go
```python
brew install anthropics/tap/ant
```
Check the installation:
```python
ant --version
```
## Install the SDK
Python
TypeScript
Java
Go
C#
Ruby
PHP
```python
pip install anthropic
```
Set your API key as an environment variable:
```python
export ANTHROPIC_API_KEY="your-api-key-here"
```
## Create your first session
1. 1
### Create an agent
Create an agent that defines the model, system prompt, and available tools.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
ant apply coding-assistant.md
```
coding-assistant.md
``` shiki
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
---
You are a helpful coding assistant. Write clean, well-documented code.
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) prints the agent's ID and records it in `claude-lock.json`. You'll reference it in every session you create.
The `agent_toolset_20260401` tool type enables the full set of pre-built agent tools (bash, file operations, web search, and more). See [Tools](managed-agents-tools.md) for the complete list and per-tool configuration options.
2. 2
### Create an environment
An environment defines the sandbox where your agent runs. This one uses `limited` [networking](managed-agents-environments.md#networking) with package managers allowed, so code running in the sandbox can reach public [package registries and code hosts](managed-agents-environments.md#package-manager-hosts) and no other sites. Web search and web fetch run outside the sandbox and are not affected.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
ant apply environment.yaml
```
environment.yaml
``` shiki
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: quickstart-env
config:
type: cloud
networking:
type: limited
allow_package_managers: true
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) records the environment's ID in `claude-lock.json` too. To create the agent and the environment with one command, pass both files: `ant apply coding-assistant.md environment.yaml`.
To run the sandbox on your own infrastructure instead of a cloud sandbox, see [Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md).
3. 3
### Start a session
Create a session that references your agent and environment.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
title="Quickstart session",
)
print(f"Session ID: {session.id}")
```
4. 4
### Send a message and stream the response
Open a stream, send a user event, then process events as they arrive:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
with client.beta.sessions.events.stream(session.id) as stream:
# Send the user message after the stream opens
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Create a Python script that generates the first 20 Fibonacci numbers and saves them to fibonacci.txt",
},
],
},
],
)
# Process streaming events
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
if block.type == "text":
print(block.text, end="")
case "agent.tool_use":
print(f"\n[Using tool: {event.name}]")
case "session.status_idle":
print("\n\nAgent finished.")
break
```
The agent writes a Python script, runs it in the sandbox, and verifies the output file was created. Your output looks similar to this:
``` block
I'll create a Python script that generates the first 20 Fibonacci numbers and saves them to a file.
[Using tool: write]
[Using tool: bash]
The script ran successfully. Let me verify the output file.
[Using tool: bash]
fibonacci.txt contains the first 20 Fibonacci numbers (0 through 4181).
Agent finished.
```
## What's happening
When you send a user event, Claude Managed Agents:
1. **Provisions a sandbox:** Your environment configuration determines how it's built.
2. **Runs the agent loop:** Claude determines which tools to use based on your message.
3. **Runs tools:** File writes, bash commands, and other tool calls run inside the sandbox.
4. **Streams events:** You receive real-time updates as the agent works.
5. **Goes idle:** The agent emits a `session.status_idle` event when it has nothing more to do.
## Build a complete app
Each of these quickstarts pairs Claude Managed Agents with a popular chat framework to make a complete, runnable application. In each one, the framework renders the chat surface while a managed session runs the agent loop server-side: the session holds the transcript, runs tools in a sandbox, and streams events that the front end renders.
[Chat SDK](https://github.com/anthropics/claude-quickstarts/tree/main/managed-agents/chat-sdk)
A research analyst in a browser chat built with Vercel's Chat SDK. Each conversation is one persistent session that streams its reply while a live feed shows the tool calls. Swapping the Chat SDK adapter moves the same handler to Slack, Teams, Discord, or WhatsApp.
[assistant-ui](https://github.com/anthropics/claude-quickstarts/tree/main/managed-agents/assistant-ui)
A spreadsheet analyst in a chat built from assistant-ui primitives. Sessions are the thread list, one reducer turns the session event log into messages and tool cards, and each bash command renders an inline Allow/Deny gate before it runs.
[CopilotKit (AG-UI)](https://github.com/anthropics/claude-quickstarts/tree/main/managed-agents/copilot-kit-ag-ui)
A personal finance assistant in a CopilotKit chat. The AG-UI adapter for Claude Managed Agents maps each chat thread to a managed session and streams replies token by token, and custom tools render interactive charts inline in the conversation.
## Next steps
[Define your agent](managed-agents-agent-setup.md)
Create reusable, versioned agent configurations
[Configure environments](managed-agents-environments.md)
Customize networking and sandbox settings
[Agent tools](managed-agents-tools.md)
Enable specific tools for your agent
[Session event stream](managed-agents-events-and-streaming.md)
Handle events and steer the agent mid-execution
[Scheduled deployments](managed-agents-scheduled-deployments.md)
Run your agent on a recurring cron schedule
[Knowledge wiki quickstart](https://github.com/anthropics/claude-quickstarts/tree/main/managed-agents/knowledge-wiki)
Distill a document corpus once into a knowledge wiki, then answer repeated questions from it at a fraction of the cost
---
## Reference - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/reference
Path: 04-API-Reference/Other/managed-agents-reference.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Freference)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Reference
# Reference
Copy page
Event types, self-hosted worker CLI flags, supported MCP server types, rate limits, and branding guidelines for Claude Managed Agents.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
This page collects reference material for Claude Managed Agents. For task-oriented guides, follow the links in each section. For the operations on the session resource, see [Session operations](managed-agents-session-operations.md).
## Event types
Persisted event type strings follow a `{domain}.{action}` naming convention; the stream-only event deltas (see the Event deltas tab) are the exception. See [Session event stream](managed-agents-events-and-streaming.md) for sending, streaming, and listing events. Webhook event types are listed separately in [Subscribe to webhooks](managed-agents-webhooks.md#supported-event-types), and some of their names differ from the stream's (for example, `session.status_idled` rather than `session.status_idle`).
User events
Agent events
Session events
Span events
System events
Event deltas
| Type | Description |
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `user.message` | A user message with text, image, or document content. |
| `user.interrupt` | Stop the agent mid-execution. |
| `user.custom_tool_result` | Response to a custom tool call from the agent. |
| `user.tool_confirmation` | Approve or deny an agent or MCP tool call when a permission policy requires confirmation. |
| `user.define_outcome` | Define an [outcome](managed-agents-define-outcomes.md) for the agent to work toward. |
| `user.tool_result` | For sessions with `self_hosted` [environments](managed-agents-self-hosted-sandboxes.md) only, your integration is responsible for providing `agent_toolset` results. The SDK helpers and CLI do this automatically. |
## Self-hosted worker
See [Self-hosted worker reference](managed-agents-self-hosted-sandboxes-reference.md) for the `ant beta:worker` CLI flags, environment variables, filesystem paths, and SDK helper options of the pre-built workers that drive a `self_hosted` environment.
## Supported MCP server types
Claude Managed Agents connects to [remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md) that expose an HTTP endpoint, or to private MCP servers through [MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md). The server should support the MCP protocol's streamable HTTP transport; servers that only support the deprecated SSE transport still work through an automatic fallback. See [MCP connector](managed-agents-mcp-connector.md) for declaring servers on an agent.
For more information on MCP and building MCP servers, see the [MCP documentation](https://modelcontextprotocol.io).
## Rate limits
Managed Agents endpoints are rate-limited per organization:
| Operation | Limit |
|---------------------------------------------------------------|---------------------------|
| Create endpoints (such as agents, sessions, and environments) | 300 requests per minute |
| Read endpoints (such as retrieve, list, and stream) | 1,200 requests per minute |
Organization-level [spend limits and usage-tier rate limits](../Endpoints/rate-limits.md) also apply.
## Branding guidelines
For partners integrating Claude Managed Agents, use of Claude branding is optional. When referencing Claude in your product:
**Allowed:**
- "Claude Agent" (preferred for dropdown menus)
- "Claude" (when within a menu already labeled "Agents")
- "{YourAgentName} Powered by Claude" (if you have an existing agent name)
**Not permitted:**
- "Claude Code" or "Claude Code Agent"
- "Claude Cowork" or "Claude Cowork Agent"
- Claude Code-branded ASCII art or visual elements that mimic Claude Code
Your product should maintain its own branding and not appear to be Claude Code, Claude Cowork, or any other Anthropic product. For questions about branding compliance, contact the Anthropic [sales team](https://www.anthropic.com/contact-sales).
---
## Scheduled deployments - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/scheduled-deployments
Path: 04-API-Reference/Other/managed-agents-scheduled-deployments.md
# Scheduled deployments
Copy page
Create and manage deployments with the Claude API: run an agent on a recurring cron schedule and inspect its run history.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
A **scheduled deployment** allows an [agent](managed-agents-agent-setup.md) to start [sessions](managed-agents-sessions.md) autonomously, enabling task completion over a predictable cadence. You create and manage deployments with the Deployments API, part of the Claude API.
For the launch context and examples of what teams run on schedules, see [scheduled deployments and vaults in Claude Managed Agents](https://claude.com/blog/whats-new-in-claude-managed-agents) on the blog.
## Create a scheduled deployment
When creating a deployment, you pass the [session configurations](managed-agents-sessions.md) required for execution, in addition to a `schedule`.
- Deployments require [agent configuration](managed-agents-agent-setup.md) and [environment configuration](managed-agents-environments.md), and optionally accept [files](managed-agents-files.md), [GitHub](managed-agents-github.md), [memory stores](managed-agents-memory.md), and [vaults](managed-agents-vaults.md). A deployment that targets a [self-hosted environment](managed-agents-self-hosted-sandboxes-memory.md) can attach memory stores; `file` and `github_repository` resources require a cloud environment. The Claude Console deployment form does not currently offer memory stores for self-hosted environments; attach them through the API or an SDK instead.
- Deployments also require at least one initial event, a `user.message` or `user.define_outcome`, that starts each session's work. In a deployment file for `ant apply`, the text below the frontmatter becomes that `user.message`.
- In the `schedule`, you define a cron `expression` and a `timezone`. Maximum granularity supported is at the minute level.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply deployment.md
```
deployment.md
```python
---
name: Weekly compliance scan
agent: agent_011CYm1BLqPXpQRk5khsSXrs
environment_id: env_01595EKxaaTTGwwY3kyXdtbs
schedule:
type: cron
expression: "0 20 * * 5"
timezone: America/New_York
---
Run the weekly compliance scan.
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) prints the new deployment's ID and records it in `claude-lock.json`. To see the deployment object, run `ant beta:deployments retrieve`.
The response includes a deployment object with a populated `schedule.upcoming_runs_at` with the next upcoming fire times, to confirm your schedule was set correctly.
```python
{
"id": "depl_01xyz",
"status": "active",
"paused_reason": null,
"schedule": {
"type": "cron",
"expression": "0 20 * * 5",
"timezone": "America/New_York",
"last_run_at": null,
"upcoming_runs_at": [
"2026-05-09T00:00:00Z",
"2026-05-16T00:00:00Z",
"2026-05-23T00:00:00Z"
]
}
}
```
The upcoming run timestamps reflect the exact schedule configured. However, to distribute load, actual execution applies jitter of up to 15% of the interval between runs, with a minimum of 5 seconds and a maximum of 9 minutes.
A maximum of **1,000 scheduled deployments** is supported per organization. Contact Anthropic support if you need more.
See the [Create Deployment reference](../Endpoints/beta-deployments-create.md) for full parameters and response schema.
### Cron and timezone semantics
- **Expression:** Standard POSIX cron (`minute hour day-of-month month day-of-week`). You can generate and validate these cron expressions in the [Claude Console](https://platform.claude.com/workspaces/default/deployments).
- **Timezone:** IANA timezone identifier (for example, `"America/Los_Angeles"`).
- **DST:** Cron schedules use literal wall-clock matching, so `"0 20 * * *"` in `America/New_York` fires at 8:00 PM local time regardless of whether EST or EDT is in effect.
Wall-clock times that do not exist on a spring-forward day (such as 2 AM) are not triggered. Wall-clock times that occur twice on a fall-back day fire twice. Schedule outside the 1–3 AM local window, or use UTC, when missed or duplicate executions are unacceptable.
### Set a budget on each run
Pass the optional `budget` object when you create or update the deployment. It takes the same shape as a [session budget](managed-agents-budgets.md). The deployment copies the cap onto each session it starts, so the budget bounds every run separately rather than acting as a cumulative ceiling across runs: a deployment with a `"2000"` cap can spend up to about \$20 on every run.
A session started by the deployment behaves exactly like any other budgeted session: it pauses with `budget_reached` when its own list cost [reaches the cap](managed-agents-budgets.md#when-a-session-reaches-its-budget). Changing the deployment's budget applies to runs started afterward; a session already running keeps the cap it started with, which you can [change through the session itself](managed-agents-session-operations.md#updating-the-session-budget). Unlike a session budget, a deployment's budget can be removed with `"budget": null` and set again later.
The following example sets a budget on an existing deployment:
cURL
```python
curl --fail-with-body -sS "https://api.anthropic.com/v1/deployments/$DEPLOYMENT_ID?beta=true" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @- <<'EOF'
{
"budget": {
"type": "limit",
"max_list_cost": {"amount": "2000", "currency": "USD"}
}
}
EOF
```
## Deployment runs
Deployments can fail to trigger for a variety of reasons: for example, if the `environment` resource has been archived, or if session creation is rate-limited. Each attempt at executing a deployment generates a **deployment run** record, allowing you to track successes and failures independent of the session lifecycle.
Successful deployments generate active sessions, and a successful deployment run contains the associated `session_id`. To follow a session's lifecycle, track the session events through the [event stream](managed-agents-events-and-streaming.md) or [webhooks](managed-agents-webhooks.md). Deployment lifecycle changes and the outcome of each scheduled run are also delivered as webhook events, listed in the Deployment events and Deployment run events tabs of [Supported event types](managed-agents-webhooks.md#supported-event-types).
List all deployment runs for a deployment as follows:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for run in client.beta.deployment_runs.list(
deployment_id=deployment.id,
):
print(run.created_at, run.session_id or run.error.type)
```
You can additionally filter on deployment runs with errors:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for run in client.beta.deployment_runs.list(
deployment_id=deployment.id,
has_error=True,
):
print(run.created_at, run.error.type, run.error.message)
```
A failed run includes an `error` with a `type` describing why session creation was rejected (for example, `environment_archived_error`, `agent_archived_error`, or `session_rate_limited_error`). See the [List Deployment Runs reference](../Endpoints/beta-deployment-runs-list.md) for all filter parameters and the response schema.
```python
{
"type": "deployment_run",
"id": "drun_01abc124",
"deployment_id": "depl_01xyz",
"trigger_context": { "type": "schedule", "scheduled_at": "2026-05-09T00:00:00Z" },
"session_id": null,
"error": {
"type": "environment_archived_error",
"message": "environment `env_01abc` is archived"
},
"agent": { "type": "agent", "id": "agent_01ghi789", "version": 3 },
"created_at": "2026-05-09T00:00:01Z"
}
```
To retrieve a single run by ID, call [`GET /v1/deployment_runs/{deployment_run_id}`](../Endpoints/beta-deployment-runs-retrieve.md). A [`deployment_run` webhook event](managed-agents-webhooks.md#supported-event-types) carries the run ID as its `data.id`.
## Managing deployment lifecycle
Each lifecycle change emits a [webhook event](managed-agents-webhooks.md#supported-event-types), so you can react to a paused, unpaused, or archived deployment without polling; see the Deployment events tab.
**Pause** suppresses scheduled triggers on a go-forward basis; running sessions from a prior deployment run continue to execute. Manual runs through the `run` endpoint are still allowed while paused. Pausing sets `paused_reason` to `{"type": "manual"}`; unpausing clears it.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.deployments.pause(deployment.id)
```
**Unpause** resumes the schedule from the next scheduled occurrence. Missed triggers are not backfilled.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.deployments.unpause(deployment.id)
```
**Archive**, unlike **pause**, is terminal: the schedule terminates and the deployment cannot be modified.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.deployments.archive(deployment.id)
```
### Failure behavior
Session creation rate-limit responses are recorded immediately as a `session_rate_limited_error` run without retry; the schedule attempts again at the next scheduled occurrence. Rate limits on underlying API calls within a session are handled by the session itself.
If a deployment's agent has been archived, the deployment is automatically archived in the same operation. If the agent has been deleted, the next scheduled trigger detects the missing agent and automatically archives the deployment. In both cases no deployment run is recorded. If a subagent referenced by the agent has been archived, the next trigger records a failed run with `error.type: "agent_archived_error"` and the deployment is automatically paused so you can update the agent and resume. Other unrecoverable session-creation errors, such as an archived environment or vault, behave the same way: the trigger records a failed run and the deployment is automatically paused. The deployment's `paused_reason.error.type` mirrors the failed run's `error.type`.
## Trigger a manual run
To run a deployment outside its schedule, call the [`run` endpoint](../Endpoints/beta-deployments-run.md). This creates a session immediately and writes a deployment run with `trigger_context.type: "manual"`. This allows you to test a deployment before committing to the schedule.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
run = client.beta.deployments.run(deployment.id)
```
---
## Custom tools in self-hosted sandboxes - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-custom-tools
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-custom-tools.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-custom-tools)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Custom tools in self-hosted sandboxes
Copy page
Serve custom tools from a self-hosted sandbox worker, and wrap an MCP server inside your network as custom tools without running a tunnel.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
[Custom tools](managed-agents-tools.md#custom-tools) are tools your own code executes: the agent emits an `agent.custom_tool_use` event and waits for a matching `user.custom_tool_result`. Your worker can be that code. Because it runs inside your sandbox, the tool reaches the internal services, credentials, and network egress you configured for the sandbox, and nothing more.
The environment key authorizes posting custom tool results, so your Claude API key stays off the worker host.
Serving custom tools requires the SDK worker. The `ant` CLI worker has no way to register a custom tool implementation. In the sandbox-per-session pattern, [run the SDK worker inside the sandbox](managed-agents-self-hosted-sandboxes-workers.md#run-the-sdk-worker-inside-the-sandbox).
## Serve a custom tool
1. 1
### Declare the tool on the agent
Add a `custom` entry to the agent's `tools` whose `name` matches the tool your worker registers. See [Custom tools](managed-agents-tools.md#custom-tools) for the full declaration shape.
``` shiki
{
"type": "custom",
"name": "get_order_status",
"description": "Look up an order in the internal fulfillment system by order ID.",
"input_schema": {
"type": "object",
"properties": {
"order_id": { "type": "string", "description": "The order ID" }
},
"required": ["order_id"]
}
}
```
2. 2
### Register the implementation with the worker
Pass the tool through the worker's `tools` factory (see [`EnvironmentWorker`](managed-agents-self-hosted-sandboxes-reference.md#environment-worker)), alongside the built-in toolset:
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
import asyncio
import os
from anthropic import AsyncAnthropic, beta_async_tool
from anthropic.lib.environments import EnvironmentWorker
from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401
@beta_async_tool
async def get_order_status(order_id: str) -> str:
"""Look up an order in the internal fulfillment system by order ID."""
# Runs on the worker host: call anything the sandbox can reach.
return f"Order {order_id}: shipped"
async def main() -> None:
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
async with AsyncAnthropic(auth_token=environment_key) as client:
await EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
tools=lambda env: [*beta_agent_toolset_20260401(env), get_order_status],
).run()
asyncio.run(main())
```
The worker answers only the tools registered with it. If a tool is declared on the agent but no worker or client serves it, the session pauses with a `requires_action` stop reason. It stays paused until something posts the result. See [Handling custom tool calls](managed-agents-events-and-streaming.md#handling-custom-tool-calls) for the event flow.
## Wrap an MCP server as custom tools
The [MCP connector](managed-agents-mcp-connector.md) connects to MCP servers from Anthropic's side. A server must therefore expose an HTTP endpoint that Anthropic can reach, directly or through an [MCP tunnel](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md).
To use a server that only your network can reach, make the worker the MCP client instead and declare the server's tools as custom tools. The MCP server needs no inbound connectivity from outside your network. Anthropic receives the tool definitions you declare on the agent, each call's input, and the result your worker posts back.
At runtime the model calls a wrapped tool like any other custom tool:
1. The agent emits an `agent.custom_tool_use` event.
2. The worker, inside your sandbox, forwards the call over its open MCP session to the server on your network.
3. The worker posts the server's response as the `user.custom_tool_result`.
### Install an MCP SDK
The SDK's [Client-side MCP helpers](../Agents-Tools/agents-and-tools-mcp-connector.md#client-side-mcp-helpers) convert the server's tools into the runnable tools the worker accepts. Install an MCP SDK alongside the Anthropic SDK: `pip install "anthropic[mcp]" "mcp>=1.24"`.
The examples connect without authentication. To send credentials, configure the `http_client` you hand to the MCP transport.
### Declare and serve the tools
1. 1
### Declare the server's tools on the agent
List the MCP server's tools and declare each one as a `custom` tool. The MCP `name`, `description`, and `inputSchema` map one to one onto the custom tool's fields. If the server paginates its tool list, declare every page; the worker must list the same pages.
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
import asyncio
from typing import Any, cast
from anthropic import AsyncAnthropic
from anthropic.types.beta import BetaManagedAgentsCustomToolParams
from mcp import ClientSession, types
# Requires mcp >= 1.24, which renamed streamablehttp_client to streamable_http_client.
from mcp.client.streamable_http import streamable_http_client
MCP_SERVER_URL = "http://mcp.internal.example.com:8000/mcp"
def to_custom_tool(tool: types.Tool) -> BetaManagedAgentsCustomToolParams:
# The MCP fields map one to one onto a custom tool declaration. The cast
# hands the schema dictionary to the SDK's typed parameter unchanged.
return {
"type": "custom",
"name": tool.name,
"description": tool.description or tool.name,
"input_schema": cast(Any, tool.inputSchema),
}
async def main() -> None:
# Run this wherever you create agents, not on the worker host: it
# authenticates with your Claude API key (ANTHROPIC_API_KEY).
async with (
streamable_http_client(MCP_SERVER_URL) as (read, write, _),
ClientSession(read, write) as mcp_session,
AsyncAnthropic() as client,
):
await mcp_session.initialize()
listed = await mcp_session.list_tools()
agent = await client.beta.agents.create(
name="Internal tools agent",
model="claude-opus-5-5",
tools=[
{"type": "agent_toolset_20260401"},
*[to_custom_tool(tool) for tool in listed.tools],
],
)
print(agent.id)
asyncio.run(main())
```
2. 2
### Serve the tools from the worker
Connect to the same MCP server at startup, convert its tools with `async_mcp_tool`, and register them alongside `beta_agent_toolset_20260401`. Keep one MCP session open for the life of the worker.
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
import asyncio
import os
from datetime import timedelta
from anthropic import AsyncAnthropic
from anthropic.lib.environments import EnvironmentWorker
from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401
from anthropic.lib.tools.mcp import async_mcp_tool
from mcp import ClientSession
# Requires mcp >= 1.24, which renamed streamablehttp_client to streamable_http_client.
from mcp.client.streamable_http import streamable_http_client
MCP_SERVER_URL = "http://mcp.internal.example.com:8000/mcp"
async def main() -> None:
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
# Connect to the MCP server once at startup and keep the session open for
# the life of the worker. The timeout turns a hung tool call into an error
# result instead of a stalled call.
async with (
streamable_http_client(MCP_SERVER_URL) as (read, write, _),
ClientSession(read, write, read_timeout_seconds=timedelta(seconds=60)) as mcp_session,
AsyncAnthropic(auth_token=environment_key) as client,
):
await mcp_session.initialize()
listed = await mcp_session.list_tools()
mcp_tools = [async_mcp_tool(tool, mcp_session) for tool in listed.tools]
await EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
tools=lambda env: [*beta_agent_toolset_20260401(env), *mcp_tools],
).run()
asyncio.run(main())
```
## Limits and behavior
### Tools are declared, not discovered at runtime
The worker lists the MCP server's tools once at startup and cannot add tools to a running session. When the server's tools change:
1. Declare them again, on the agent or on an idle session through [Updating the agent configuration](managed-agents-session-operations.md#updating-the-agent-configuration).
2. Restart the worker.
### Declarations must fit the Managed Agents API
The MCP helpers keep the server's names and descriptions, and most schemas pass through unchanged. Rename, trim, or inline where a declaration breaks one of these rules:
| Field | Rule |
|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `name` | Unique per agent. Letters, digits, underscores, and hyphens, 1–128 characters. Cannot match a built-in agent tool such as `bash` or `read`, or use the reserved `mcp__` prefix. |
| `description` | Required and non-empty. |
| `input_schema` | Accepts the JSON Schema keywords MCP servers commonly emit, such as `additionalProperties` and `title`. Rejects reference keywords such as `$ref` anywhere, and top-level `oneOf`, `anyOf`, and `allOf`. Property names use letters, digits, underscores, dots, and hyphens, 1–64 characters. |
| The agent's `tools` array | At most 128 entries. Each wrapped tool is one entry, and the built-in toolset is one more. |
Two cases need extra work:
- **Two servers expose the same tool name:** Define the wrapper yourself under a prefixed name and have it call the server's original tool name.
- **A generator such as pydantic factors schemas into `$defs`:** Inline those schemas before you declare the tool.
### Tool failures surface as error tool results
When the MCP server reports a tool error, the worker posts an error tool result the model can react to. MCP content with no tool result equivalent, such as audio blocks and resource links, also surfaces as an error.
Set a timeout on the MCP client for a faster and clearer failure, as the Python worker example does with `read_timeout_seconds`. See [A wrapped MCP tool call hangs](managed-agents-self-hosted-sandboxes-operations.md#a-wrapped-mcp-tool-call-hangs) for what happens without one.
### Wrap only servers you operate or trust
A wrapped tool's name, description, and results enter the model's context like any other tool's. They are untrusted input that can influence what the agent does with its other tools, including `bash` on the worker host. Declare only the tools you intend the agent to use.
### Permission policies do not apply
[Permission policies](managed-agents-permission-policies.md#custom-tools) govern the built-in and MCP toolsets. The worker executes every wrapped tool call the model makes, so put any approval step in your own tool code.
---
## Memory stores in self-hosted sandboxes - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-memory
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-memory.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-memory)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Memory stores in self-hosted sandboxes
Copy page
Attach memory stores to Claude Managed Agents sessions that run in self-hosted sandboxes: prepare the host, configure sync, and handle read-only stores and conflicts.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Sessions on a self-hosted environment attach [memory stores](managed-agents-memory.md) exactly as sessions on cloud environments do. List them in `resources` when you create the session, as shown in [Attach a memory store to a session](managed-agents-memory.md#attach-a-memory-store-to-a-session). A session accepts up to 8 memory stores.
The difference is who materializes the store. On a self-hosted environment your worker, rather than Anthropic's infrastructure, downloads each store into the sandbox and syncs the agent's changes back.
## Requirements
- **A worker that mounts memory stores:** Use `ant` CLI 1.33.0 or later, or `EnvironmentWorker` from the Python, TypeScript, or Go SDK.
- **A POSIX filesystem:** Windows hosts are not supported, because the worker requires `O_NOFOLLOW` when it opens memory files. A case-sensitive filesystem is recommended, so that memory paths that differ only in case do not collide.
- **A writable `/mnt/memory` directory:** See [Prepare the host](#prepare-the-host).
- **The work item's secret:** If your own code launches the worker, [forward the work item's secret](managed-agents-self-hosted-sandboxes-workers.md#forward-the-work-items-secret) to it.
Memory stores cannot be attached to sessions on self-hosted environments on [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md).
## Prepare the host
Before you start the worker, create the parent directory and make it writable by the user the worker runs as:
```python
sudo mkdir -p /mnt/memory && sudo chown "$USER" /mnt/memory
```
Do not create the per-store directories yourself. The worker creates each store's `mount_path` directory (for example, `/mnt/memory/user-preferences`) when a session starts and removes it when the session ends. If something already exists at that path, the worker refuses to start the session's work.
In the [sandbox-per-session pattern](managed-agents-self-hosted-sandboxes-workers.md#run-one-sandbox-per-session), the sandbox image needs a writable `/mnt/memory`. You don't need to bind-mount the memory directories to the host, because the worker uploads their contents to the store before the sandbox exits.
### Isolate sessions that share a store
Two sessions cannot mount the same store on one host at the same time, because both need the same path. If your sessions attach the same store, run one session per filesystem. Giving each session [its own sandbox](managed-agents-self-hosted-sandboxes-workers.md#run-one-sandbox-per-session) satisfies this rule.
## How the worker handles memory
When the worker claims a work item whose session has memory stores attached, it:
1. **Downloads each store to its `mount_path`.** This is the same directory under `/mnt/memory/` that cloud sessions use, and the session's system prompt describes it to the agent. For example, a store named "User Preferences" lands at `/mnt/memory/user-preferences/`.
2. **Opens those directories to the file tools.** The agent works on memories with the same file tools it uses in the working directory.
3. **Reconciles changes after tool calls,** at most once per sync interval (15 seconds by default). Memories that changed in the store are written to disk, and files the agent changed are uploaded to the store.
4. **Runs a final sync when the session ends.** It flushes any uploads still pending for up to 30 seconds, then removes the directories it created.
The memory store on Anthropic's side remains the source of truth. [Memory versions](managed-agents-memory.md#audit-memory-changes), redaction, and viewing or editing memories in the Console work as they do for cloud sessions. The agent's memory reads and writes appear in the [event stream](managed-agents-events-and-streaming.md) as ordinary tool events.
Because each worker syncs on an interval, a change written in one session becomes visible to another running session only after both have synced. That is typically well under a minute at the default interval. Sessions on cloud sandboxes see each other's changes almost immediately.
Each store directory contains a marker file named `.anthropic-memory-store` that ties the directory to its store. Leave it in place: the worker does not sync a directory whose marker is missing or altered.
A worker that is killed rather than stopped runs no teardown, so unsynced edits are lost and the store directories stay behind. See [Stop workers gracefully](managed-agents-self-hosted-sandboxes-operations.md#stop-workers-gracefully).
## Configure sync
Two `EnvironmentWorker` options control memory behavior. Set them wherever you construct the worker, including in a webhook handler. The `ant` CLI worker always uses the defaults.
### Sync interval
`memory_sync_interval` sets how often attached stores reconcile with the server while the session runs.
| Setting | Value |
|------------------------|------------|
| Default | 15 seconds |
| Minimum | 5 seconds |
| Example (10 seconds) | `10` |
| Disable memory support | `None` |
A shorter interval narrows the window in which another session sees stale memories, at the cost of more memory store requests.
Disable memory support only on workers whose sessions attach no memory stores. A disabled worker neither downloads nor syncs stores, so a session with stores attached runs without them even though its system prompt still describes them.
While memory support is enabled, a work item that arrives without a `secret` for a session with attached stores fails rather than running without memory. See [Memory stores fail to mount](managed-agents-self-hosted-sandboxes-operations.md#memory-stores-fail-to-mount).
### Deletions
`memory_sync_deletions` sets whether a file the agent deletes locally is also deleted from the store. Uploads and downloads are unaffected.
| Value | Behavior |
|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| `"enabled"` (default) | Deletes the memory from the store once a later sync confirms the file is still gone. |
| `"log_only"` | Runs the same checks but only logs what it would have deleted. Use it to watch what your workers would delete before you trust the enabled mode. |
| `"disabled"` | Never deletes from the store. |
For example, to sync every 10 seconds and only log the deletes the worker would have made:
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
memory_sync_interval=10, # seconds
memory_sync_deletions="log_only",
)
```
## Read-only stores and conflicts
For a store attached with `access: "read_only"`, the `write` and `edit` tools refuse to change files inside its directory. The worker never uploads anything from it.
Changes made through `bash`, or through a custom tool or MCP server you serve from the sandbox, are not blocked locally. They are never synced to the store, and the next remote change to that memory overwrites them. If the local copy itself must stay unchanged during the session:
- Disable the `bash` tool for that agent, and give it no custom tool that writes to the sandbox's filesystem.
- Do not mount the store path read-only. The worker itself must create the directory and write the downloaded memories into it.
Conflicts resolve in favor of the store. Suppose the agent changes a memory file that also changed in the store since the session last synced it. At the next sync, the worker keeps the store's version, overwrites the local file with it, and logs a warning. The `write` and `edit` tools themselves succeed and no error reaches the agent. If the agent's change still applies, it can re-read the file after the sync and make the change again.
## Troubleshooting
See [Memory stores fail to mount](managed-agents-self-hosted-sandboxes-operations.md#memory-stores-fail-to-mount) for the worker's log messages and their fixes.
---
## Monitor and troubleshoot self-hosted workers - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-operations
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-operations.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-operations)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Monitor and troubleshoot self-hosted workers
Copy page
Read queue depth, stop sessions and workers without losing work, and fix common self-hosted sandbox failures.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
The monitoring calls on this page run from your monitoring or operations tooling, authenticated with your Claude API key. The worker helpers handle the claim and keep-alive loop, so you don't call those endpoints directly.
These endpoints accept either your organization API key or the environment key. Call them from outside the worker host with your organization API key. Setting `ANTHROPIC_API_KEY` on the worker host exposes an organization-scoped credential to agent tool calls.
## Read queue depth
`client.beta.environments.work.stats()` returns the queue state for an environment:
| Field | Meaning | Use it to |
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|
| `depth` | Items waiting to be claimed. | Scale your worker fleet or alert on backlog. |
| `pending` | Items claimed by a worker but not yet acknowledged. The worker helpers acknowledge each item before processing it, so this stays near zero in normal operation. | Detect a worker that stalled between claiming and acknowledging: alert on a sustained non-zero value. |
| `oldest_queued_at` | Timestamp of the oldest item still in the queue, either waiting to be claimed or claimed but not yet acknowledged. `null` when there is none. | See how long the oldest item has waited. |
| `workers_polling` | Workers that have polled in the last 30 seconds. | Alert on liveness. |
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import os
import anthropic
client = anthropic.Anthropic()
stats = client.beta.environments.work.stats(os.environ["ANTHROPIC_ENVIRONMENT_ID"])
print(f"depth={stats.depth} pending={stats.pending}")
```
``` block
{
"type": "work_queue_stats",
"depth": 0,
"pending": 0,
"oldest_queued_at": null,
"workers_polling": 0
}
```
## Stop a session gracefully
Use `client.beta.environments.work.stop()` to ask the worker handling a specific session to shut it down.
By default the work item moves to `stopping`. The worker notices on its next lease heartbeat, cancels the session's in-flight tool call, and confirms the shutdown. The work item then becomes `stopped`.
Pass `force=True` to mark the work item `stopped` immediately instead of waiting for the worker's confirmation.
Because these calls run from your operations tooling rather than the worker host, `ANTHROPIC_WORK_ID` isn't set automatically. Set it to the target work item's ID before running the following examples. To find a work item's ID, list the environment's work items through the [Environments Work endpoints](../Endpoints/beta-environments-work.md).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import os
import anthropic
client = anthropic.Anthropic()
work = client.beta.environments.work.stop(
os.environ["ANTHROPIC_WORK_ID"],
environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
)
print(work.state)
```
## Stop workers gracefully
A worker that is cancelled while a session runs stops its in-flight work before it exits. If the session has [memory stores](managed-agents-self-hosted-sandboxes-memory.md) attached, the worker skips the final sync but still uploads changed files and removes the store directories.
A killed process runs no teardown. To stop a worker cleanly:
1. **Make sure SIGTERM and SIGINT cancel the worker.** How depends on the worker:
| Worker | What to do |
|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| `ant` CLI | Nothing. The CLI handles both signals itself: it cancels any in-flight tool call, posts its error result, and releases the work item. |
| SDK worker that is its own process | `EnvironmentWorker` installs no signal handlers. Cancel the worker from a signal handler, as the standalone worker examples do. |
| SDK worker inside a webhook server | Cancel the worker from the server's own shutdown hook, as the webhook examples do. The worker must not take over the server's signals. |
2. **Stop the worker with SIGTERM, and allow at least 30 seconds before any hard kill.** The final upload can take that long. Docker sends SIGKILL 10 seconds after the stop signal by default. Raise that limit with `--stop-timeout` on `docker run`, or with your orchestrator's termination grace period.
If a worker is killed before its teardown runs, any memory edits that had not synced are lost. On a long-lived host, also remove the leftover store directory under `/mnt/memory/` before the next session that attaches that store. A sandbox that serves one session and is then discarded needs no cleanup.
## Troubleshooting
### The worker doesn't connect
If `workers_polling` stays at 0, the worker isn't reaching the queue. Confirm that `ANTHROPIC_ENVIRONMENT_KEY` and `ANTHROPIC_ENVIRONMENT_ID` are set on the worker host.
### A session stays queued
No worker is claiming work. A queued session waits rather than failing. Check `workers_polling` and `depth` in [Read queue depth](#read-queue-depth).
### Memory stores fail to mount
The worker logs mount and background sync failures rather than reporting them to the session. Only read-only refusals reach the agent, as tool errors (see [Read-only stores and conflicts](managed-agents-self-hosted-sandboxes-memory.md#read-only-stores-and-conflicts)).
If the worker cannot mount a memory store when it claims a session, it fails the work item. The session emits no error event and stays idle.
| Symptom | Cause | Fix |
|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| The worker log contains `the work item carried no sessions token` (in Go, the `ErrSessionMemoryNoToken` error) and the work item fails. | The work item's per-session `secret` did not reach the worker. Either your code did not forward it, or memory stores on self-hosted sandboxes are not enabled for your organization. | [Forward the work item's secret](managed-agents-self-hosted-sandboxes-workers.md#forward-the-work-items-secret). If the worker polls and runs sessions in one process and still logs this, contact support. |
| The worker log contains `something already exists at the memory store's path`. | A directory left over from a previous session, usually one whose worker was killed before its teardown ran. | Remove the leftover directory that the log line names. Edits in it that had not synced are lost. |
| The worker log contains `cannot create the memory store's folder` and `the worker host must make this mount path writable`. | The user the worker runs as cannot create directories under `/mnt/memory`. | Create `/mnt/memory` and `chown` it to that user. See [Prepare the host](managed-agents-self-hosted-sandboxes-memory.md#prepare-the-host). |
| The session sits `idle` with a `requires_action` stop reason and no error event shortly after a worker claimed it. | The worker failed the work item because it could not mount a memory store, for one of the preceding reasons. | Fix the cause on the host, then send a [`user.interrupt`](managed-agents-events-and-streaming.md#integrating-events) event. The session's work is queued again, and the next worker that claims it retries the mount. |
### A custom tool call never returns
If the session sits paused with a `requires_action` stop reason, no worker or client serves that tool. See [Serve a custom tool](managed-agents-self-hosted-sandboxes-custom-tools.md#serve-a-custom-tool).
### A wrapped MCP tool call hangs
Without a timeout on the MCP client, a hung call to a [wrapped MCP server](managed-agents-self-hosted-sandboxes-custom-tools.md#wrap-an-mcp-server-as-custom-tools) becomes an error tool result only when a backstop fires:
| SDK | Backstop | Fires after |
|------------|----------------------------------------------------------------|------------------------------|
| Python | The worker's own tool call limit | About two and a half minutes |
| TypeScript | The MCP SDK's default request timeout | About a minute |
| Go | The worker cancels a tool call that outlives its default limit | 120 seconds |
---
## Self-hosted worker reference - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-reference
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-reference.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-reference)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Self-hosted worker reference
Copy page
Reference for self-hosted sandbox workers: ant CLI flags, environment variables, host requirements, filesystem paths, and SDK helper options.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
This page documents the pre-built workers that serve a `self_hosted` environment. For task-oriented guides, start with [Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md) and [Deploy self-hosted workers](managed-agents-self-hosted-sandboxes-workers.md).
## CLI commands and flags
| Command | Description |
|------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| `ant beta:worker poll` | Claims work items from the environment's queue and runs each session in process. With `--on-work`, calls your script for each work item instead. |
| `ant beta:worker run` | Handles one claimed session and exits. Use it as the entrypoint of a per-session sandbox. |
| Flag | Description |
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `--environment-id` | The environment to poll for work. Also reads from `ANTHROPIC_ENVIRONMENT_ID`. |
| `--environment-key` | Authenticates the worker with this environment. Also reads from `ANTHROPIC_ENVIRONMENT_KEY`. |
| `--workdir` | Directory where skills are downloaded and tools read and write files. Defaults to `.` (the current directory). |
| `--on-work` | Script to call for each claimed work item instead of running tools in-process. Receives session details as environment variables and the work item as JSON on standard input. |
| `--max-idle` | How long to wait after the session goes idle with an `end_turn` [stop reason](../Guides/build-with-claude-handling-stop-reasons.md) before shutting down. Defaults to `60s`. |
| `--log-format` | Log output format. Use `json` for structured log ingestion. Defaults to `text`. |
## Environment variables
| Variable | Description | Set by |
|---------------------------------|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ANTHROPIC_ENVIRONMENT_ID` | The environment whose queue the worker serves. | You, on the worker host. The poller passes it to the `--on-work` script. |
| `ANTHROPIC_ENVIRONMENT_KEY` | Authenticates the worker to its queue. | You, on the worker host. The poller passes it to the `--on-work` script. |
| `ANTHROPIC_SESSION_ID` | The session that a claimed work item represents. | The poller, for the `--on-work` script. |
| `ANTHROPIC_WORK_ID` | The claimed work item. | The poller, for the `--on-work` script. |
| `ANTHROPIC_WORK_SECRET` | The work item's per-session secret. | You. The poller does not set it. See [Forward the work item's secret](managed-agents-self-hosted-sandboxes-workers.md#forward-the-work-items-secret). |
| `ANTHROPIC_BASE_URL` | Overrides the default API endpoint. Optional. | You, on the worker host. |
| `ANTHROPIC_WEBHOOK_SIGNING_KEY` | Verifies incoming webhook payloads. | You, on a webhook handler host. |
## Host requirements
| Worker | Requirement |
|--------------------|--------------------------------------------------------------------------------------------------------------------------|
| All workers | A Linux host with `/bin/bash` at that exact path. The worker's bash tool invokes it directly, without consulting `PATH`. |
| TypeScript SDK | `unzip` and `tar` on the `PATH`, and Node.js 22 or later. |
| Python and Go SDKs | No additional binaries. These SDKs use their standard libraries for archive extraction. |
[Memory stores](managed-agents-self-hosted-sandboxes-memory.md#requirements) add their own requirements.
## Sandbox filesystem
| Path | Contents |
|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `/workspace` | The system default working directory for tool execution and skill download. If you use a different working directory, update your agent's system prompt so Claude can locate the skill files. |
| `<workdir>/skills/<name>/` | The agent's downloaded skills. |
| `/mnt/memory/<store>/` | One directory per attached memory store, at the store's `mount_path` (for example, `/mnt/memory/user-preferences/`). The worker creates these directories when it claims the session and removes them when the session ends. |
On self-hosted environments the session's system prompt omits the `/mnt/session/outputs` instruction used on Anthropic-managed sandboxes. Final deliverables land wherever the agent writes them in your sandbox filesystem, typically under the working directory.
Skills can include executables that the agent may run directly. The CLI and SDK workers preserve the executable permissions recorded in the skill bundle when they extract it. If you implement skills download manually, you are responsible for setting executable permissions.
## SDK helpers
The Python, TypeScript, and Go SDKs provide three helpers at different levels of control:
| Helper | What it does | Use it when |
|---------------------------------------------------------------------|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
| [`EnvironmentWorker`](#environment-worker) | Handles polling, setup, and execution end to end. | Most cases. |
| [`work.poller()`](#work-poller) | Polls the work queue and gives you each claimed session. | You determine what happens for each session, for example launching a sandbox rather than running tools in-process. |
| [`client.beta.sessions.events.tool_runner()`](#session-tool-runner) | Runs tool calls for a single session, given the session ID and a tool list. | You've already claimed the work and only need the execution layer. |
### EnvironmentWorker
| Method | Description |
|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `run()` | Runs indefinitely, picking up sessions as they arrive. |
| `handle_item()` | Handles a single claimed work item and returns. Pass the work, session, and environment identifiers and the `work_secret` explicitly, or let it read the [`ANTHROPIC_*` variables](#environment-variables). |
| Option | Description |
|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `tools` | A factory that receives the session's `AgentToolContext` and returns the tool list. Defaults to the standard agent toolset. |
| `memory_sync_interval` | How often attached memory stores reconcile with the server while the session runs. See [Sync interval](managed-agents-self-hosted-sandboxes-memory.md#sync-interval). |
| `memory_sync_deletions` | Whether files the agent deletes locally are also deleted from the store. See [Deletions](managed-agents-self-hosted-sandboxes-memory.md#deletions). |
`EnvironmentWorker` manages the `AgentToolContext` and the toolset automatically. Pass a `tools` factory to customize the tool list:
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
EnvironmentWorker(client, ..., tools=lambda env: [beta_bash_tool(env), my_custom_tool])
```
### Work poller
| Option | Description |
|-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `drain` | Whether to stop polling once the queue is empty rather than waiting for new work. |
| `block_ms` | How long each poll waits for work to arrive before returning, in milliseconds. Must be between 1 and 999; the helper re-polls automatically. Pass `None` for a non-blocking check. Defaults to a 999 ms long-poll. |
| `reclaim_older_than_ms` | Re-claims work items that were claimed but never acknowledged within this many milliseconds. |
| `auto_stop` | Whether to post a stop signal for each work item once your loop body finishes with it. Set it to `False` when whatever runs the work item posts the stop itself. `handle_item()` does, and so does a sandbox you launch that owns the stop call. |
For a complete example, see [Launch sandboxes from the SDK poller](managed-agents-self-hosted-sandboxes-workers.md#launch-sandboxes-from-the-sdk-poller).
### Session tool runner
`client.beta.sessions.events.tool_runner()` takes a tool list as `tools`. To build that list, set up `AgentToolContext` yourself and call `beta_agent_toolset_20260401(env)`:
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
from anthropic.lib.tools.agent_toolset import (
AgentToolContext,
beta_agent_toolset_20260401,
)
async with AgentToolContext(
workdir="/workspace", client=client, session_id=work.data.id
) as env:
# skills downloaded to /workspace/skills/<name>/
tools = beta_agent_toolset_20260401(env)
```
### AgentToolContext and the agent toolset
`AgentToolContext` is the execution context for tool calls. It defines the working directory and path policy, and can download the session's skills.
| Option | Description |
|-------------------|-----------------------------------------------------------------------------------------------------------------------------|
| `allowed_roots` | Directories, in addition to the working directory, that the file tools (`read`, `write`, `edit`, `glob`, `grep`) can reach. |
| `read_only_roots` | Directories under which `write` and `edit` refuse paths. |
`EnvironmentWorker` adds the session's memory store directories to `allowed_roots` itself, and the directories of stores attached with `access: "read_only"` to `read_only_roots`.
The confinement is a guardrail for the file tools only, not a sandbox. It does not constrain `bash`.
`beta_agent_toolset_20260401(env)` takes an `AgentToolContext` and returns the standard tool implementations (`bash`, `read`, `write`, `edit`, `glob`, `grep`).
---
## Security model - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-security
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-security.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-security)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Security model
Copy page
Shared responsibility model for self-hosted sandbox environments.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Anthropic secures the control plane across all environments: session and work queue integrity, multitenant isolation, and agent-context minimization. When you self-host, the following responsibilities fall to you.
## What you own
- **Sandbox image quality and runtime hardening.** Anthropic does not inspect or verify your sandbox image. Follow best practices such as dropping unnecessary Linux capabilities, running as a non-root user, and using a read-only root filesystem.
- **Network egress controls.** Your sandbox's network access is determined by your VPC and firewall rules. Without egress restrictions, a compromised tool execution can reach arbitrary external hosts. Restrict outbound traffic to only the endpoints your tools require.
- **Service key storage and rotation.** The environment service key (`ANTHROPIC_ENVIRONMENT_KEY`) authorizes polling your environment's work queue and submitting results back to sessions. Store it in a secrets manager, not in environment files or sandbox images. Rotate it immediately if you suspect exposure.
- **Isolating untrusted workloads.** The environment service key is scoped to one environment's work queue. If you run untrusted code inside your sandbox, consider provisioning a separate workspace and environment for each trust boundary. This limits each key to a single user's sessions instead of a shared pool.
- **Per-session credentials.** Each work item your worker claims can carry a per-session `secret`, which the worker uses in place of the environment service key. Access to [memory stores](managed-agents-memory.md) requires the `secret`: the memory store endpoints reject the environment key (see [Forward the work item's secret](managed-agents-self-hosted-sandboxes-workers.md#forward-the-work-items-secret)). Pass the `secret` only into the sandbox that serves that session, keep it out of images and shared volumes, and never log it.
- **Tool-execution blast radius.** Tools run inside your sandbox with whatever permissions your process has. Apply least privilege to the process user and mount only the directories your tools require.
- **Log retention and session content.** Conversation content and tool outputs pass through your worker and stay in your environment. You are responsible for retaining, redacting, or deleting that data in compliance with your own policies. Anthropic has no visibility into what your worker does with session content once delivered.
- **Memory store contents.** [Memory stores](managed-agents-memory.md) remain hosted by Anthropic, including their version history. When a session attaches one, the worker keeps a working copy under `/mnt/memory/` in your sandbox for the session's duration and syncs changes back. The worker deletes that copy when the session ends, but a worker that exits without running its teardown leaves it behind. Cleaning up leftover copies, the permissions on that path, and isolation between sessions that share a filesystem are your responsibility.
- **Read-only memory stores.** A store attached with `read_only` access is protected from upload, not from local modification. The worker's `write` and `edit` tools refuse to write under its directory, nothing there syncs back, and the memory store endpoints reject writes to it made with the session's `secret`. Other processes in the sandbox can still change the local copy: commands the agent runs through the `bash` tool, and [custom tools](managed-agents-self-hosted-sandboxes-custom-tools.md) or MCP servers you serve from the sandbox, which run with the worker's permissions. Later tool calls in that session read the changed copy until that memory next changes in the store. If the agent must not be able to alter even its local view of such a store, disable the `bash` tool for that agent and give it no custom tool that writes to the sandbox's filesystem.
## What Anthropic cannot do for you
- **Know that your key leaked.** Anthropic can detect anomalous usage patterns, but cannot know your key was compromised. If you suspect `ANTHROPIC_ENVIRONMENT_KEY` leaked, revoke it and generate a replacement immediately. Revocation is validated on every request, so it takes effect on the worker's next call.
- **Verify your worker build.** Anthropic does not inspect your sandbox image or runtime. A supply-chain compromise in your image is not detectable from the control plane.
- **Isolate tools inside your sandbox.** Anthropic's security boundary stops at the sandbox. How you isolate individual tool executions from each other inside that boundary is entirely your responsibility.
- **Enforce data retention in your environment.** Once session content reaches your worker, it is outside Anthropic's data lifecycle controls.
---
## Deploy self-hosted workers - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes-workers
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes-workers.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes-workers)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Deploy self-hosted workers
Copy page
Choose how self-hosted sandbox workers claim work and where sessions run: always-on or webhook-triggered, in one process or one sandbox per session.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
The [quickstart](managed-agents-self-hosted-sandboxes.md#quickstart) runs one `ant` CLI worker that polls continuously and runs every session in one process. This page covers the other ways to run a worker and how to choose between them.
## Choose a deployment pattern
When deploying workers, you need to make two choices: how the worker claims work, and where each session runs.
**How the worker claims work:**
- **Always-on:** A long-running process polls the queue continuously and needs only outbound HTTPS. This is the simplest setup.
- **Webhook-triggered:** A handler wakes on `session.status_run_started` and starts polling. This avoids an idle poller, but requires a [webhook](managed-agents-webhooks.md) endpoint that Anthropic can reach.
**Where each session runs:**
- **In process:** The worker that claims a session also runs its tool calls, in one shared working directory.
- **Sandbox per session:** A poller launches a fresh sandbox for each claimed session. Choose this for stronger isolation: a fresh filesystem, resource limits, or per-session network controls.
The CLI and SDK workers support different combinations:
| Capability | `ant` CLI | SDK (Python, TypeScript, Go) |
|----------------------------------------------------------------------------|---------------------------------|------------------------------|
| Always-on polling | Yes | Yes |
| Webhook-triggered | No | Yes |
| Sandbox per session | Yes | Yes |
| [Memory stores](managed-agents-self-hosted-sandboxes-memory.md) | Yes, with default sync settings | Yes, with configurable sync |
| [Custom tools](managed-agents-self-hosted-sandboxes-custom-tools.md) | No | Yes |
See [Self-hosted worker reference](managed-agents-self-hosted-sandboxes-reference.md) for every CLI flag and SDK option. For more control, call the [Environments Work endpoints](../Endpoints/beta-environments-work.md) directly and implement your own worker.
## Run an always-on worker
Both workers authenticate with the [environment key](managed-agents-self-hosted-sandboxes.md#run-your-first-session) from the quickstart.
With the `ant` CLI:
```python
ant beta:worker poll --workdir /workspace
```
With the SDK, `EnvironmentWorker` does the same work:
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import asyncio
import contextlib
import os
import signal
from anthropic import AsyncAnthropic
from anthropic.lib.environments import EnvironmentWorker
async def main() -> None:
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
async with AsyncAnthropic(auth_token=environment_key) as client:
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
)
task = asyncio.create_task(worker.run())
# Cancelling the task, rather than killing the process, lets the worker stop its
# in-flight work item and upload changed memory files before it exits.
loop = asyncio.get_running_loop()
for signum in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(signum, task.cancel)
with contextlib.suppress(asyncio.CancelledError):
await task
asyncio.run(main())
```
## Trigger workers from webhooks
1. 1
### Subscribe to session webhooks
In the [Console](https://platform.claude.com/settings/workspaces/default/webhooks), define a webhook endpoint that listens for `session.status_run_started` events. See [Webhooks](managed-agents-webhooks.md) for details.
2. 2
### Export the webhook signing key
Along with the environment ID and key from the [quickstart](managed-agents-self-hosted-sandboxes.md#run-your-first-session), export the webhook signing key on your handler host. The handler uses it to verify incoming payloads.
``` shiki
export ANTHROPIC_WEBHOOK_SIGNING_KEY="whsec_..."
```
3. 3
### Implement the webhook handler
Invoke the worker when `session.status_run_started` fires. The handler drains the queue and hands each claimed work item to `handle_item()`, which downloads skills, executes tool calls, posts results back, and returns.
Python
TypeScript
C#
Go
Java
PHP
Ruby
To verify webhook signatures, install the webhooks extra: `pip install "anthropic[webhooks]"`.
``` shiki
import asyncio
import os
import anthropic
import standardwebhooks # installed by the anthropic[webhooks] extra
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
client = anthropic.AsyncAnthropic(
auth_token=environment_key,
)
# Cancelled by shutdown() so an in-flight work item can upload changed memory files and
# remove its store directories before the process exits.
inflight: set[asyncio.Task[None]] = set()
# Await this from the host's shutdown hook, such as an ASGI lifespan shutdown (the code after
# `yield` in a FastAPI lifespan), which uvicorn runs on SIGTERM. uvicorn lets open requests
# finish before that hook runs, so set --timeout-graceful-shutdown to bound the wait.
async def shutdown() -> None:
for task in inflight:
task.cancel()
await asyncio.gather(*inflight, return_exceptions=True)
async def handle(raw: bytes, headers: dict[str, str]) -> tuple[dict[str, str], int]:
try:
event = client.beta.webhooks.unwrap(raw.decode(), headers=headers)
except standardwebhooks.WebhookVerificationError:
return {"error": "signature verification failed"}, 401
if event.data.type != "session.status_run_started":
return {"status": "ignored"}, 200
task = asyncio.create_task(run_queued_work())
inflight.add(task)
task.add_done_callback(inflight.discard)
try:
# Shielded: a dropped or timed-out delivery must not cancel the item; shutdown() does.
await asyncio.shield(task)
except asyncio.CancelledError:
return {"status": "shutting down"}, 503
return {"status": "ok"}, 200
async def run_queued_work() -> None:
async for work in client.beta.environments.work.poller(
environment_id=environment_id,
environment_key=environment_key,
block_ms=None,
reclaim_older_than_ms=2000,
drain=True,
auto_stop=False,
):
await client.beta.environments.work.worker(workdir="/workspace").handle_item(
work_id=work.id,
environment_id=environment_id,
session_id=work.data.id,
environment_key=environment_key,
# The per-session secret is what lets the worker mount the session's memory stores.
work_secret=work.secret,
)
```
Because the handler claims work itself, it must [forward the work item's secret](#forward-the-work-items-secret), as the `work_secret` argument does here.
This handler runs every claimed item in one process on one host. If your sessions attach the same memory store, see [Isolate sessions that share a store](managed-agents-self-hosted-sandboxes-memory.md#isolate-sessions-that-share-a-store).
## Run one sandbox per session
A poller on the host claims work and calls your script once per work item. The script launches a sandbox for that one session.
1. 1
### Build the sandbox image
Install `ant` and set `ant beta:worker run` as the entrypoint. When a sandbox starts, it reads session details from environment variables, handles that session, and exits. The base image must provide `/bin/bash`; `curl` is only used at build time.
``` shiki
FROM your-base-image
ARG ANT_VERSION=1.37.0
ARG TARGETARCH
RUN ARCH=$([ "$TARGETARCH" = "arm64" ] && echo arm64 || echo amd64) && \
curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${ANT_VERSION}/ant_${ANT_VERSION}_linux_${ARCH}.tar.gz" \
| tar -xz -C /usr/local/bin ant
WORKDIR /workspace
VOLUME /workspace
ENTRYPOINT ["ant", "beta:worker", "run"]
```
2. 2
### Write the spawn script
The script forwards the session details into a fresh sandbox. It requires `jq` on the poller host.
``` shiki
#!/bin/bash
# spawn.sh: called once per claimed work item
# The claimed work item arrives as JSON on stdin.
ANTHROPIC_WORK_SECRET="$(jq -r '.secret // empty')"
export ANTHROPIC_WORK_SECRET
mkdir -p "/host/outputs/$ANTHROPIC_SESSION_ID"
exec docker run --rm \
-e ANTHROPIC_SESSION_ID -e ANTHROPIC_ENVIRONMENT_KEY \
-e ANTHROPIC_WORK_ID -e ANTHROPIC_ENVIRONMENT_ID -e ANTHROPIC_BASE_URL \
-e ANTHROPIC_WORK_SECRET \
-v "/host/outputs/$ANTHROPIC_SESSION_ID":/workspace \
your-image
```
The poller sets the `ANTHROPIC_*` variables that the script forwards, except the secret. See [Environment variables](managed-agents-self-hosted-sandboxes-reference.md#environment-variables).
`/host/outputs` is a host directory you choose. Mounting it at `/workspace` lets you retrieve the session's deliverables after the sandbox exits. The mount also picks up the downloaded `skills/` tree and any intermediate files.
3. 3
### Start the poller
``` shiki
ant beta:worker poll --on-work ./spawn.sh
```
### Forward the work item's secret
Each claimed work item can carry a per-session `secret`, which Anthropic issues. The worker that runs the session needs it to mount [memory stores](managed-agents-self-hosted-sandboxes-memory.md).
A worker that claims and runs sessions in one process (`ant beta:worker poll` without `--on-work`, or `EnvironmentWorker` with `run()`) passes the secret along itself. When your own code sits between the claim and the worker, you forward it:
| You claim work with | The secret arrives as | Pass it to the worker as |
|----------------------------------|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
| `ant beta:worker poll --on-work` | The `secret` field of the work item JSON on your script's standard input | `ANTHROPIC_WORK_SECRET` in the sandbox's environment |
| The SDK's `work.poller()` | The `secret` field of each claimed work item | `ANTHROPIC_WORK_SECRET` in the sandbox's environment, or the `work_secret` argument to `handle_item()` |
Pass the secret only into the sandbox that serves that session, and never log it. See [Security model](managed-agents-self-hosted-sandboxes-security.md) for how it relates to the environment key.
### Launch sandboxes from the SDK poller
To claim work from your own code instead of `ant beta:worker poll --on-work`, use `work.poller()`. It polls the queue and gives you each claimed session, and you launch the sandbox:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import asyncio
import os
from anthropic import AsyncAnthropic
from anthropic.types.beta.environments import BetaSelfHostedWork
SANDBOX_ENV = (
"ANTHROPIC_ENVIRONMENT_ID",
"ANTHROPIC_ENVIRONMENT_KEY",
"ANTHROPIC_WORK_ID",
"ANTHROPIC_SESSION_ID",
"ANTHROPIC_WORK_SECRET",
"ANTHROPIC_BASE_URL", # forwarded only when set on this host
)
async def launch_container(work: BetaSelfHostedWork) -> None:
print(f"claimed session {work.data.id}")
# Replace `docker run` with your own sandbox launcher. Forward the environment
# key (never your API key) and the work item's per-session secret: the worker
# inside needs the secret to mount the session's memory stores.
env = os.environ | {
"ANTHROPIC_WORK_ID": work.id,
"ANTHROPIC_SESSION_ID": work.data.id,
"ANTHROPIC_WORK_SECRET": work.secret or "",
}
forward = [arg for name in SANDBOX_ENV for arg in ("-e", name)]
launcher = await asyncio.create_subprocess_exec(
"docker", "run", "--rm", "--detach", *forward, "your-image", env=env
)
await launcher.wait()
async def main() -> None:
environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
async with AsyncAnthropic(auth_token=environment_key) as client:
async for work in client.beta.environments.work.poller(
environment_id=environment_id,
environment_key=environment_key,
auto_stop=False, # the launched sandbox owns the stop call
):
await launch_container(work)
asyncio.run(main())
```
### Run the SDK worker inside the sandbox
Replace the `ant beta:worker run` entrypoint with an SDK entrypoint when the sandbox must [serve custom tools](managed-agents-self-hosted-sandboxes-custom-tools.md) or use [non-default memory sync settings](managed-agents-self-hosted-sandboxes-memory.md#configure-sync). The entrypoint constructs `EnvironmentWorker` and calls `handle_item()`, which reads the same `ANTHROPIC_*` variables that the spawn script forwards.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
import asyncio
import contextlib
import os
import signal
from anthropic import AsyncAnthropic
from anthropic.lib.environments import EnvironmentWorker
async def main() -> None:
async with AsyncAnthropic(auth_token=os.environ["ANTHROPIC_ENVIRONMENT_KEY"]) as client:
worker = EnvironmentWorker(client, workdir="/workspace")
# With no arguments, handle_item() reads the ANTHROPIC_* variables the spawn
# script forwarded, including ANTHROPIC_WORK_SECRET.
task = asyncio.create_task(worker.handle_item())
# Cancelling the task when the container is stopped lets the worker upload
# changed memory files and remove the store directories before it exits.
loop = asyncio.get_running_loop()
for signum in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(signum, task.cancel)
with contextlib.suppress(asyncio.CancelledError):
await task
asyncio.run(main())
```
## Stage files for a session
Anthropic doesn't mount files or GitHub repositories into self-hosted sandboxes. To make session-specific files available:
1. Pass file references, such as an S3 path or commit SHA, in the session's `metadata` field.
2. In your spawn script or `--on-work` handler, retrieve the session (`GET /v1/sessions/{session_id}`) and read `metadata`. The claimed work item carries the session ID but not the metadata.
3. Stage the files into the working directory before tool execution begins.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
metadata={"input_file": "s3://my-bucket/data.csv"},
)
```
## Next steps
[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)
Read queue depth, stop sessions and workers cleanly, and fix common failures.
[Security model](managed-agents-self-hosted-sandboxes-security.md)
Shared responsibility model for self-hosted sandbox environments.
---
## Self-hosted sandboxes - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes
Path: 04-API-Reference/Other/managed-agents-self-hosted-sandboxes.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fself-hosted-sandboxes)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
[Overview and quickstart](managed-agents-self-hosted-sandboxes.md)[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)[Security model](managed-agents-self-hosted-sandboxes-security.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Self-hosted sandboxes
# Self-hosted sandboxes
Copy page
Run Claude Managed Agents sessions in self-hosted sandboxes, keeping tool execution, files, and network egress in your own infrastructure.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
By default, Managed Agents executes tools and code inside [Anthropic-managed cloud sandboxes](managed-agents-cloud-sandboxes-reference.md). Self-hosted sandboxes keep the orchestration on Anthropic's side but move tool execution into infrastructure you control. The agent's files, processes, and network traffic stay in your environment.
Self-hosting is a good fit when the agent needs to:
- Operate on data that cannot leave your network boundary
- Reach internal services that are not publicly routable
- Run under your organization's own compliance and audit controls
## How it works
A `self_hosted` environment acts as a work queue. You run an **environment worker**, a process on your own infrastructure that serves that queue:
1. You create a [session](managed-agents-sessions.md) that targets the environment. Anthropic enqueues the session as a work item.
2. Your worker claims the work item and downloads the agent's [skills](managed-agents-skills.md) and the session's [memory stores](managed-agents-memory.md).
3. Claude runs on Anthropic's side and requests tool calls. Your worker runs each call locally and posts the result back.
Tool inputs and outputs still flow to Anthropic's control plane, so the model can see results and determine what to do next. Anthropic stores skills and memory stores. Your sandbox holds a copy for the session. See [Security model](managed-agents-self-hosted-sandboxes-security.md) for the full data-flow boundary.
Self-hosted sandboxes support every Claude model available in Managed Agents. The model is configured on the agent, not the environment.
The `ant` CLI and the Python, TypeScript, and Go SDKs ship pre-built workers. Sandbox providers such as Cloudflare, Daytona, and Modal also publish their own [platform guides](#platform-guides).
## Quickstart
This quickstart runs one always-on worker with the `ant` CLI, sends it a session, and confirms that the agent's tool calls ran on your host.
### Prerequisites
- **An agent:** If you don't have one, complete [Get started with Claude Managed Agents](managed-agents-quickstart.md) first and note the agent ID.
- **A Linux host:** The host needs `/bin/bash` at that exact path. The worker needs only outbound HTTPS.
- **A Claude API key:** You use it from your own machine to create sessions and read queue stats. Keep it off the worker host, where agent tool calls could read it.
- **The `ant` CLI on your own machine:** The session and stats commands in this quickstart run there, not on the worker host. Install it the same way the [install step](#run-your-first-session) does for the worker.
On [Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md), the worker authenticates with AWS IAM (SigV4) or an [API key generated in the AWS Console](../Guides/build-with-claude-claude-platform-on-aws.md#api-key-authentication), not an environment key. Attach the [`AnthropicSelfHostedEnvironmentAccess`](../Endpoints/claude-platform-on-aws-iam-actions.md#managed-policies) managed policy to the IAM principal your worker runs as. Environment keys generated in the Claude Console don't work with the Claude Platform on AWS endpoint.
### Run your first session
1. 1
### Create a self-hosted environment
In the [Console](https://platform.claude.com/workspaces/default/environments): **Workspace \> Environments \> New \> Self-hosted**
Or through the API:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
``` shiki
ant apply environment.yaml
```
environment.yaml
``` shiki
# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
name: self-hosted
config:
type: self_hosted
```
2. 2
### Generate an environment key
In the Console, open the environment and click **Generate environment key**. The environment key authenticates the worker to its queue. You can generate one only in the Console, even for an environment you created through the API.
Export the environment ID and key on the worker host:
``` shiki
export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
export ANTHROPIC_ENVIRONMENT_ID="env_..."
```
3. 3
### Install the ant CLI
Run this on the worker host.
curl (Linux/WSL)
Homebrew (macOS)
For Linux environments, download the release binary directly.
``` shiki
VERSION=1.37.0
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
case $(uname -m) in
x86_64) ARCH=amd64 ;;
aarch64) ARCH=arm64 ;;
esac
curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${VERSION}/ant_${VERSION}_${OS}_${ARCH}.tar.gz" \
| sudo tar -xz -C /usr/local/bin ant
```
You can find all releases on the [GitHub releases page](https://github.com/anthropics/anthropic-cli/releases).
4. 4
### Start the worker
Create the working directory, then start the worker. `--workdir` defaults to the current directory, so pass `/workspace` to match the system default.
``` shiki
sudo mkdir -p /workspace && sudo chown "$USER" /workspace
ant beta:worker poll --workdir /workspace
```
The worker reads the two variables you exported and polls until you stop it.
5. 5
### Verify the worker is connected
On your own machine, set `ANTHROPIC_API_KEY` to your Claude API key (not the environment key) and `ANTHROPIC_ENVIRONMENT_ID` to the environment ID. Confirm that `workers_polling` is at least 1:
``` shiki
ant beta:environments:work stats --environment-id "$ANTHROPIC_ENVIRONMENT_ID"
```
If `workers_polling` stays at 0, see [Troubleshooting](managed-agents-self-hosted-sandboxes-operations.md#troubleshooting).
6. 6
### Start a session
Set `AGENT_ID` to your agent's ID. Create a session that targets the environment, then send it a task:
``` shiki
SESSION_ID=$(ant beta:sessions create \
--agent "$AGENT_ID" \
--environment-id "$ANTHROPIC_ENVIRONMENT_ID" \
--transform id --raw-output)
ant beta:sessions:events send --session-id "$SESSION_ID" <<'YAML'
events:
- type: user.message
content:
- type: text
text: Write the output of "uname -a" to hello.txt in your working directory.
YAML
```
The session waits in the environment's queue until a worker claims it. If no worker is connected, the session stays queued rather than failing.
7. 7
### Confirm the tools ran on your host
On the worker host, read the file the agent wrote:
``` shiki
cat /workspace/hello.txt
```
The file describes your own host's kernel, so the agent's tool calls ran there. To follow the agent's work as it happens, see [Session event stream](managed-agents-events-and-streaming.md).
## How it differs from cloud environments
| | Cloud environment | Self-hosted sandbox |
|-------------------------------|----------------------------------------|-------------------------------------------------------|
| Where tools run | Anthropic-managed sandboxes | Your infrastructure |
| Network reach | Anthropic's egress controls | Your network policy |
| File and GitHub repo mounting | Managed by Anthropic | Managed by you |
| Memory stores | Mounted by Anthropic at `/mnt/memory/` | Downloaded to `/mnt/memory/` and synced by the worker |
| Lifecycle | Managed by Anthropic | Managed by you |
For Zero Data Retention and HIPAA BAA eligibility, see [API and data retention](../Guides/build-with-claude-api-and-data-retention.md#feature-eligibility).
### Session resources
Self-hosted sandboxes support `memory_store` resources only. A session on a self-hosted environment that includes a `file` or `github_repository` resource is rejected with a 400 error:
```python
Environment env_... is a self-hosted environment. `resources` are not supported with self-hosted environments.
```
[Deployments](managed-agents-scheduled-deployments.md) that target a self-hosted environment follow the same rule. To give a session its own input files, see [Stage files for a session](managed-agents-self-hosted-sandboxes-workers.md#stage-files-for-a-session).
## Self-hosted sandboxes and MCP tunnels
Self-hosting controls *where the agent's code executes*. [MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md) control *how Anthropic reaches MCP servers in your network*. The two are independent:
- A session in Anthropic's cloud sandboxes can reach private MCP servers through a tunnel.
- A self-hosted session can use either tunneled or public MCP servers.
Use both when you want execution and tool access to stay inside your boundary. To skip the tunnel, [wrap the MCP server as custom tools](managed-agents-self-hosted-sandboxes-custom-tools.md#wrap-an-mcp-server-as-custom-tools) that your worker serves.
## Platform guides
These pages describe how to build a worker on any sandboxing platform. Platform-specific guides are also available:
- [AWS Lambda MicroVMs](https://docs.aws.amazon.com/lambda/latest/dg/microvms-integrations-claude-managed-agents.html)
- [Blaxel](https://docs.blaxel.ai/Tutorials/Claude-Managed-Agents)
- [Cloudflare](https://developers.cloudflare.com/sandbox/claude-managed-agents/)
- [Daytona](https://www.daytona.io/docs/en/guides/claude/claude-managed-agents)
- [E2B](https://e2b.dev/docs/agents/claude-managed-agents)
- [Fly.io](https://docs.sprites.dev/integrations/claude-managed-agents/)
- [GKE Agent Sandbox](https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/tree/main/ai-ml/anthropic-agent-sandbox)
- [Modal](https://github.com/modal-labs/claude-managed-agents-modal-sandbox)
- [Namespace](https://namespace.so/docs/integrations/claude)
- [Superserve](https://docs.superserve.ai/integrations/managed-agents/claude-managed-agents)
- [Vercel](https://vercel.com/kb/guide/run-claude-managed-agent-tools-with-vercel-sandbox)
## Next steps
[Deploy workers](managed-agents-self-hosted-sandboxes-workers.md)
Run the SDK worker, trigger workers from webhooks, or give each session its own sandbox.
[Memory stores](managed-agents-self-hosted-sandboxes-memory.md)
Prepare the host, configure sync, and handle read-only stores and conflicts.
[Custom tools and MCP servers](managed-agents-self-hosted-sandboxes-custom-tools.md)
Serve your own tools from the worker, including tools from an MCP server inside your network.
[Monitor and troubleshoot](managed-agents-self-hosted-sandboxes-operations.md)
Read queue depth, stop sessions and workers cleanly, and fix common failures.
[Worker reference](managed-agents-self-hosted-sandboxes-reference.md)
CLI flags, environment variables, filesystem paths, and SDK helper options.
[Security model](managed-agents-self-hosted-sandboxes-security.md)
Shared responsibility model for self-hosted sandbox environments.
---
## Inspect sessions and track usage - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/session-observability
Path: 04-API-Reference/Other/managed-agents-session-observability.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fsession-observability)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Event deltas](managed-agents-event-deltas.md)[Inspect and track usage](managed-agents-session-observability.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Inspect sessions and track usage
Copy page
Inspect a session in the Claude Console, read its token usage and list cost, and debug unexpected agent behavior.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Use the session viewer in the Claude Console to inspect what an agent did in a session, without writing any code. Use the session's `usage` totals to see what that work consumed.
## Inspect a session in the Console
The session viewer is only accessible to Developers and Admins. To open it, go to the Console sidebar and select **Sessions** under **Managed Agents**. The list shows every session in the workspace with its status, agent, token usage, cost, and creation time. Select a session to open it.
The session viewer shows:
- **Timeline minimap:** A zoomable overview of the session's activity over time, with one lane per thread in [multiagent](managed-agents-multiagent-orchestration.md) sessions. Select a lane to view that thread, or select a mark to jump to its event.
- **Transcript:** The conversation grouped by model request, including thinking, tool calls with their inputs and results, and message text as it streams. You can filter the events and copy or download them as JSON.
- **Inspector:** A resizable side panel with details about the session, in five tabs.
| Inspector tab | What it shows |
|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Session** | The session's details and metadata, its cumulative cost over time, and spend against the session's [budget](managed-agents-budgets.md) when one is set. |
| **Events** | Every raw event on the current thread, in the order the server sent it. Select an event to see its JSON. A message that streamed while the page was open also has a **Deltas** view of its [event deltas](managed-agents-event-deltas.md). |
| **Tools** | The tools the session's agents are configured with, along with call counts, failures, and median duration. Select a tool to see its calls and jump to one in the transcript. |
| **Resources** | Mounted [files](managed-agents-files.md), [repositories](managed-agents-github.md), and [memory stores](managed-agents-memory.md) at their container paths, including the memories in each store and the changes this session made to them. Also lists files the agent wrote to `/mnt/session/outputs` and the [skills](managed-agents-skills.md) attached to the session's agents. |
| **Threads** | Every thread with its status, context size, and cost. Select a thread to view its details, such as the agent, model, context usage, and cost. |
Append `?event={event_id}` to a session URL to open the session at a specific event.
With `ant beta:sessions connect`, you can open the same viewer from the `ant` CLI or follow the session in your terminal. See [Connect to a Managed Agents session from your terminal](cli-sdks-libraries-cli-sessions-connect.md).
## Track usage
The session object includes a `usage` field with the session's cumulative usage: token counts, server tool use, active time, and the tracked list cost. Fetch the session after it goes idle to read the latest totals.
```python
{
"id": "sesn_01...",
"status": "idle",
"usage": {
"input_tokens": 5000,
"output_tokens": 3200,
"cache_read_input_tokens": 20000,
"cache_creation": {
"ephemeral_5m_input_tokens": 2000,
"ephemeral_1h_input_tokens": 0
},
"list_cost": {
"amount": "187",
"currency": "USD"
},
"active_seconds": 342.5,
"server_tool_use": {
"web_search_requests": 3,
"web_fetch_requests": 0
}
}
}
```
| Field | Description |
|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `input_tokens` | Uncached input tokens across all model calls in the session. |
| `output_tokens` | Total output tokens across all model calls in the session. |
| `cache_read_input_tokens` | Tokens read from the prompt cache. |
| `cache_creation` | Cache-creation tokens, broken down by cache lifetime (`ephemeral_5m_input_tokens` and `ephemeral_1h_input_tokens`). |
| `list_cost` | The session's cumulative consumption priced at public list rates, as a whole number of cents in a string, with a currency code. |
| `active_seconds` | The cumulative time during which the session had at least one thread running. Overlapping activity from concurrent threads is counted once. The session's runtime cost is priced on this duration. |
| `server_tool_use` | Counts of server-executed tool requests, for pricing. Web search requests are priced into list cost per request. Web fetch requests carry no per-request charge and aren't metered, so `web_fetch_requests` reads `0`. |
Cache entries use a 5-minute TTL by default, so back-to-back turns within that window benefit from cache reads, which reduce per-token cost.
The session's `stats` object has its own `active_seconds`, which sums each thread's own active time instead of counting overlapping activity once.
### Per-thread usage
Each [session thread](managed-agents-multiagent-orchestration.md)'s own `usage` carries `list_cost` and `active_seconds` too. Per-thread figures are rounded independently and exclude the session's running-time cost, so they don't sum exactly to the session's `list_cost`. The session figure is the authoritative one.
### Read usage from the stream
You don't have to poll the session to observe these totals. The `session.usage` event carries the same cumulative snapshot on the session stream and in the event history. The snapshot holds the `usage` object plus the session's `budget`, which is `null` when the session has none.
The event is emitted on idle transitions rather than on a timer:
- The session emits one immediately before it goes idle, whatever the stop reason.
- The session emits one when a thread pauses at a [session budget](managed-agents-budgets.md).
### Enforce a spend limit
To enforce a spend limit, set a session budget rather than polling usage and stopping the session yourself. The platform prices the session's consumption continuously, and pauses each thread before its next model request once the list cost reaches the cap. See [When a session reaches its budget](managed-agents-budgets.md#when-a-session-reaches-its-budget) for what that looks like on the stream.
## Debugging tips
- **Check session events:** The session reports errors through [`session.error`](managed-agents-reference.md#event-types) events.
- **Review tool results:** Tool execution failures often explain unexpected agent behavior. The Inspector's **Tools** tab shows each tool's failures.
- **Use system prompts:** Add logging instructions to the system prompt so the agent summarizes what it did and what it found.
- **Troubleshoot previews:** If a stream that opts in to event deltas doesn't behave as you expect, see [Troubleshoot previews](managed-agents-event-deltas.md#troubleshoot-previews).
## Next steps
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
[Session budgets](managed-agents-budgets.md)
Cap a session's spend with a hard dollar budget enforced at public list rates.
---
## Session operations - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/session-operations
Path: 04-API-Reference/Other/managed-agents-session-operations.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fsession-operations)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Session operations
Copy page
Retrieve, list, update, archive, and delete Claude Managed Agents sessions.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Once a session exists, use these operations to read, update, archive, or delete it. See [Start a session](managed-agents-sessions.md) for creating a session and sending it work.
## Session statuses
Sessions progress through these statuses. See [Start a session](managed-agents-sessions.md) for the session lifecycle.
| Status | Description |
|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| `idle` | Agent is waiting for input, including user messages or tool confirmations. Sessions created without `initial_events` start in `idle`. |
| `running` | Agent is actively executing. |
| `rescheduling` | Transient error occurred, retrying automatically. |
| `terminated` | Session has ended, either because of an unrecoverable error or because it was archived. A session that finishes its work goes `idle`, not `terminated`. |
## Updating the agent configuration
You can update a session's `agent.tools` and `agent.mcp_servers`, including permission policies and per-tool web settings such as [domain filters](managed-agents-tools.md#restrict-web-search-and-web-fetch-domains), mid-session without creating a new agent version. Updates are session-local and do not propagate back to the underlying agent. Updated `allowed_domains` and `blocked_domains` apply to the rest of the session.
Only the agent's `tools` and `mcp_servers` can change after a session is created. To run a session with `model`, `system`, or `skills` values other than the agent's, use [agent configuration overrides](managed-agents-sessions.md#override-agent-configuration-for-a-session) when you create the session. The agent's model configuration, including its [`inference_geo`](manage-claude-data-residency.md) pin, also can't change mid-session: set the pin when you save the agent, or set or clear it for a single session with a `model` override when you create it. The agent's configured `system` field is fixed for the session's lifetime. On models that support it, you can still append system-level guidance mid-session by sending a [`system.message` event](managed-agents-events-and-streaming.md#sending-system-messages).
The semantics of a `tools` or `mcp_servers` update are full replacement: the provided array is the new value. To preserve existing entries, `GET` the session, modify the array, and `POST` it back.
The session must be `idle` to update the agent. To update the agent while the session is running, send a [`user.interrupt` event](managed-agents-events-and-streaming.md#integrating-events) by itself and wait for the session to become `idle`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.update(
session.id,
agent={
"tools": [
{"type": "agent_toolset_20260401"},
{"type": "mcp_toolset", "mcp_server_name": "linear"},
],
"mcp_servers": [
{"type": "url", "name": "linear", "url": "https://mcp.linear.app/sse"}
],
},
)
```
## Updating the session budget
A session [created with a budget](managed-agents-sessions.md#set-a-session-budget) accepts two kinds of budget update: replacing the cap with a new `max_list_cost`, and removing it by setting `budget` to `null`. Both automatically resume work that paused when the session reached its cap. A replacement cap can be higher or lower than the current one, but it must be strictly greater than the session's consumed list cost, and removal is one-way: a non-null `budget` is accepted only on a session that currently has one, so you can't re-add a removed budget or add one to a session created without it. See [Session budgets](managed-agents-budgets.md#resume-a-session-at-its-budget) for request examples, the error behaviors, and what counts toward list cost.
## Retrieving a session
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
retrieved = client.beta.sessions.retrieve(session.id)
print(f"Status: {retrieved.status}")
```
## Listing sessions
Results from `GET /v1/sessions` are paginated. Use the `limit` query parameter to control the page size. Each response includes a `next_page` cursor; pass it as the `page` parameter on the next request to fetch the following page. `next_page` is `null` when there are no more results.
To go back a page, pass `prev_page` as the `page` parameter. `prev_page` is `null` when you're on the first page.
A `page` cursor is opaque and encodes the `order` of the request that produced it. The `order` query parameter sets the sort direction of the results, `asc` or `desc` by creation time; the default is `desc` (newest first). Reusing a cursor with a different `order` returns a 400 error, as does changing a `created_at` filter so that it excludes the cursor's position. Other query parameters, including the remaining filters and `limit`, can change between paginated requests. For the pagination fields shared across list endpoints, see [Pagination](../Endpoints/overview.md#pagination).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
# Set `limit` low so the results span more than one page.
first_page = client.beta.sessions.list(limit=1, agent_id=agent.id)
# `prev_page` is None on the first page; `next_page` is None on the last.
print(f"prev_page: {first_page.prev_page}")
print(f"next_page: {first_page.next_page}")
# Pass `next_page` back as `page` to fetch the next page.
second_page = client.beta.sessions.list(
limit=1, agent_id=agent.id, page=first_page.next_page
)
for listed_session in second_page.data:
print(f"{listed_session.id}: {listed_session.status}")
# Pass `prev_page` back as `page` to return to the previous page.
previous_page = client.beta.sessions.list(
limit=1, agent_id=agent.id, page=second_page.prev_page
)
for listed_session in previous_page.data:
print(f"{listed_session.id}: {listed_session.status}")
# For forward-only iteration, the page object is also directly iterable.
```
## Archiving a session
Archive a session to prevent new events from being sent while preserving its history. A `running` session cannot be archived; to archive one, send a [`user.interrupt` event](managed-agents-events-and-streaming.md#integrating-events) by itself and wait for the session to become `idle`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.archive(session.id)
```
## Deleting a session
Delete a session to permanently remove its record, events, and associated sandbox. A `running` session cannot be deleted; to delete one, send a [`user.interrupt` event](managed-agents-events-and-streaming.md#integrating-events) by itself and wait for the session to become `idle`.
Memory stores, vaults, skills, environments, and agents are independent resources and are not affected by session deletion. Files you uploaded through the Files API are also unaffected, but files the session itself produced are scoped to it and are permanently deleted along with its filesystem. Download anything you need to keep before deleting the session. An output file written at the end of the last turn can take a few seconds after the session goes idle to appear in the [session's file list](managed-agents-files.md#listing-and-downloading-session-files), so check that the files you expect are listed first.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.delete(session.id)
```
---
## Session threads - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/session-threads
Path: 04-API-Reference/Other/managed-agents-session-threads.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fsession-threads)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Event deltas](managed-agents-event-deltas.md)[Inspect and track usage](managed-agents-session-observability.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Session threads](managed-agents-session-threads.md)[Workflow runs](managed-agents-workflow-runs.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Advanced orchestration
# Session threads
Copy page
List, interrupt, and archive the threads of a multiagent session, read their events, and handle tool permissions across them.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
In a [multiagent session](managed-agents-multiagent-orchestration.md), each agent works in its own **session thread**. This page covers how to list, interrupt, and archive threads, the events they send, and how tool permissions work across them. A [workflow run](managed-agents-workflow-runs.md) creates session threads too.
## Primary thread and session threads
The **session-level event stream** (`/v1/sessions/{session_id}/events/stream`) is considered the **primary thread**, containing a condensed view of all activity across all threads. You don't see the full activity from [subagents](managed-agents-multiagent-orchestration.md#delegate-to-subagents), but you do see the start and end of their work, and blocking events such as tool permission requests.
**Session threads** are where you drill into a specific agent's activity.
The session `status` is an aggregation of all agent activity; if at least one thread is `running`, then the overall session status is `running` as well. A [workflow run](managed-agents-workflow-runs.md) that's running can keep the session `running` too, even while none of its threads is working. When no thread is working and a thread waits on your client, the session is `idle`; see [Know when the work is done](managed-agents-workflow-runs.md#know-when-the-work-is-done).
A [session budget](managed-agents-budgets.md) is a single shared cap across all of a session's threads. As the cap is reached, threads pause independently, and each thread's cost is priced at the thread's own served model.
A session can have at most 25 child threads at a time. Idle threads count until you archive them, and the primary thread doesn't count. The primary thread's agent can call multiple copies of a single subagent, creating multiple threads associated with one `agent`. [Advisor](managed-agents-multiagent-orchestration.md#give-the-session-an-advisor) consultation threads are exempt from this limit. So are a [workflow run](managed-agents-workflow-runs.md)'s threads.
## List threads
List all threads associated with a session as follows:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for thread in client.beta.sessions.threads.list(session.id):
agent = thread.agent
label = agent.type if agent.type == "advisor" else agent.name
print(f"[{label}] {thread.status}")
```
The full list includes the primary thread. `parent_thread_id` is `null` for the primary thread. Every other thread is a child thread. `workflow_run_id` is `null` except on [a run's threads](managed-agents-workflow-runs.md#a-runs-threads).
To list only threads that have certain statuses, add `statuses[]` to the request, and repeat it to give more than one status, as in `?statuses[]=running&statuses[]=idle`. Leave it out to return threads of every status.
## Interrupt a session thread
Send `user.interrupt` with `session_thread_id` to stop a specific thread. Omitting `session_thread_id` interrupts every non-archived thread in the session, including the primary. In a session with dynamic workflows, an interrupt ends no run, and one that names a run's thread stops nothing. An interrupt closes other child threads' pending tool calls, but don't rely on it to close a run thread's. See [Interrupt a session with runs open](managed-agents-workflow-runs.md#interrupt-a-session-with-runs-open).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
)
```
Against a subagent's thread blocked on `requires_action`, the interrupt closes each pending tool call with an error tool result ("Tool execution was interrupted before completion. Please retry.") and re-emits `session.thread_status_idle` with `stop_reason: end_turn` directly; the model is not sampled. Against a child thread that's idle with `end_turn` or `budget_reached`, the interrupt is a no-op. An interrupt that names a terminated thread returns a 400 error. An interrupted child doesn't send the primary thread's agent the report it sends when a turn ends. While that agent waits on the child, it doesn't start another turn until something else reaches it, such as a `user.message` or another thread's report.
## Archive a session thread
Optionally archive a session thread when it has completed its work. Archiving a thread frees its place under the 25-child-thread limit. The server archives a [workflow run](managed-agents-workflow-runs.md)'s threads itself. You don't need to archive them, and you can't while the run is open.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
print(archived.status, archived.archived_at)
```
Archive only succeeds if the thread is `idle`. A thread parked on `requires_action` counts as idle and can be archived directly; only a running thread must be interrupted first:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
)
archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
print(archived.status, archived.archived_at)
```
## Primary thread events
These events surface multiagent activity on the primary thread at `/v1/sessions/{session_id}/events/stream`. Message-direction events are named relative to the thread whose stream they appear on: `agent.thread_message_received` means a message arrived on this thread from another thread, and `agent.thread_message_sent` means this thread sent one. The task that the primary thread's agent delegates, for example, arrives on the child's own stream as an `agent.thread_message_received` event.
| Type | Description |
|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `session.thread_created` | A thread was created. Includes `session_thread_id` and `agent_name`. |
| `session.thread_status_running` | A thread started activity. |
| `session.thread_status_idle` | The agent associated with the thread is awaiting input. Includes a `stop_reason` indicating why the agent stopped. |
| `session.thread_status_terminated` | A thread terminated and accepts no further input, for example because it was archived or encountered an unrecoverable error. An advisor thread also terminates when its consultation ends. |
| `agent.thread_message_received` | On the primary thread, a subagent sent the primary thread's agent a report or question. Includes `from_session_thread_id`, `from_agent_name`, and `content`. |
| `agent.thread_message_sent` | On the primary thread, the primary thread's agent sent a subagent a task or follow-up message. Includes `to_session_thread_id`, `to_agent_name`, and `content`. |
Advisor consultations emit these same thread events under the reserved name `anthropic.advisor` (as `agent_name` on the thread lifecycle events and `from_agent_name` on the advice delivery); see [Give the session an advisor](managed-agents-multiagent-orchestration.md#give-the-session-an-advisor) for the sequence.
A [workflow run](managed-agents-workflow-runs.md)'s threads show on the primary stream as follows:
- **Lifecycle events:** Each run thread sends `session.thread_created`, with the run's `workflow_run_id`, and its `session.thread_status_running`, `session.thread_status_idle`, and `session.thread_status_terminated` events.
- **Message events:** A run thread's prompt, an `agent.thread_message_received` event, stays on its own stream.
- **Run events:** `workflow_run.*` events also arrive on this stream; see [Run events](managed-agents-workflow-runs.md#run-events).
- **Tool calls that wait on you:** A run thread's tool calls that need your client are cross-posted to this stream, as for any child thread. See [Tool permissions and custom tools](#tool-permissions-and-custom-tools).
## Session thread events
Critical events are proxied to the primary thread. However, you might still want to investigate a specific agent's reasoning and tool calls. To do so, stream or list the events from the associated session thread.
Each session thread has its own event stream at `/v1/sessions/{session_id}/threads/{thread_id}/stream`, and it accepts the same `event_deltas[]` parameter as the session-level stream, so you can preview a subagent's text as the model generates it. A connection previews only the thread it's reading: a child thread's previews never appear on the session-level stream, so to watch a subagent live, open its own thread stream. See [Preview session thread events](managed-agents-event-deltas.md#preview-session-thread-events) for opting in, accumulating, and reconciling previews.
In a workflow run, the server runs a workflow: a program that the primary thread's agent writes. On each of the run's threads, the first `agent.thread_message_received` is the prompt that the workflow wrote. Its `from_session_thread_id` is the primary thread's ID, and it has no `from_agent_name`. The API doesn't guarantee the prompt's text, so don't parse it. The thread's `session.thread_status_terminated` event, on the primary thread's stream, tells you the thread is done. No event records the result it returned to the workflow.
A thread's stream doesn't replay earlier events. Right after `session.thread_created`, the event list of a run's thread can be empty, because the server writes the thread's first event after it. So open the thread's stream first, then list the thread's events, and skip each streamed event whose `id` the list returned.
Stream session thread events
List session thread events
Stream session thread events
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
with client.beta.sessions.threads.events.stream(
thread.id,
session_id=session.id,
) as stream:
for event in stream:
match event.type:
case "agent.message":
for block in event.content:
if block.type == "text":
print(block.text, end="")
case "session.thread_status_idle":
break
```
List session thread events
List all past session thread events to pull a complete history.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for event in client.beta.sessions.threads.events.list(
thread.id,
session_id=session.id,
):
print(f"[{event.type}] {event.processed_at}")
```
## Tool permissions and custom tools
If a subagent needs something from your client, such as [permission](managed-agents-events-and-streaming.md#tool-confirmation) to run a tool call or the [result of a custom tool](managed-agents-events-and-streaming.md#handling-custom-tool-calls), the event is cross-posted to the **primary thread** with `session_thread_id` identifying the originating session thread. A tool call needs your permission under `always_ask`, or under [`auto`](managed-agents-permission-policies.md#let-the-server-evaluate-each-call-with-auto) when the server reaches no determination.
```python
{
"type": "session.thread_status_idle",
"id": "sevt_01ABC...",
"session_thread_id": "sthr_01DEF...",
"agent_name": "code-reviewer",
"stop_reason": {
"type": "requires_action",
"event_ids": ["sevt_01XYZ..."]
}
}
```
Post `user.tool_confirmation` (with `tool_use_id`) or `user.custom_tool_result` (with `custom_tool_use_id`); the server routes the response to the correct thread automatically. The response can appear on the primary thread and on the subagent's thread with different `id` values. To match the two copies, compare `type` and `tool_use_id` (or `custom_tool_use_id`), not `id`.
The session goes `idle` only when no thread is `running`, so `session.status_idle` can arrive long after a subagent's call. You don't have to wait for it: send the `user.custom_tool_result` as soon as the cross-posted `agent.custom_tool_use` event arrives.
Under `auto`, your `user.message` events can lead the server to allow a call it would otherwise deny. Nothing in a subagent's thread counts as your intent. Your client posts no messages there, and the messages that the primary thread's agent sends the subagent don't count. When the server denies a call under `auto`, nothing is cross-posted: the event and the error tool result appear only on the subagent's own [thread stream](#session-thread-events), and the subagent keeps running.
The following example goes inside the event loop of the [tool confirmation handler](managed-agents-events-and-streaming.md#tool-confirmation). For each ID in `stop_reason.event_ids`, it sends a `user.tool_confirmation` that allows the call. The same pattern applies to `user.custom_tool_result`.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
for event_id in stop.event_ids:
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
}
],
)
```
The previous pattern answers the calls that an idle event lists. On the primary stream, a subagent's `session.thread_status_idle` event can arrive before the `agent.tool_use` or `agent.mcp_tool_use` events that its `stop_reason.event_ids` lists. A `user.tool_confirmation` for a call whose event hasn't arrived yet can return 400. To avoid that, answer each call whose [`evaluated_permission`](managed-agents-permission-policies.md#see-how-each-call-was-evaluated) is `ask` when its own event arrives on the primary stream.
---
## Start a session - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/sessions
Path: 04-API-Reference/Other/managed-agents-sessions.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fsessions)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Start a session
Copy page
Create a session to run your agent and begin executing tasks.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
A session is an agent instance within an environment. Each session references an [agent](managed-agents-agent-setup.md) and an [environment](managed-agents-environments.md) (both created separately), and maintains conversation history across multiple interactions. Sessions follow a two-step lifecycle: first [create the session](#creating-a-session), then [send a user event](#starting-the-session) to start work. You can also collapse both steps into one call with [`initial_events`](#seed-the-session-with-initial-events).
## Creating a session
A session requires an `agent` ID and an `environment` ID. Agents are versioned resources; passing in the `agent` ID as a string creates the session with the latest agent version.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
)
```
To pin a session to a specific agent version, pass an object. This lets you control exactly which version runs and stage rollouts of new versions independently.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
pinned_session = client.beta.sessions.create(
agent={"type": "agent", "id": agent.id, "version": 1},
environment_id=environment.id,
)
```
### Seed the session with initial events
You can create a session and start its work in one call. `initial_events` is an optional array of initial [events](managed-agents-reference.md#event-types) to send to the session at creation, processed in order. It supports `user.message` and [`user.define_outcome`](managed-agents-define-outcomes.md) events, and accepts a maximum of 50 events. A non-empty list starts the agent loop in the same call: the session is created directly in the `running` status, with no further request.
The following example creates a session with a single `user.message` in `initial_events`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
seeded_session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
initial_events=[
{
"type": "user.message",
"content": [
{"type": "text", "text": "List the files in the working directory."}
],
},
],
)
# initial_events are not echoed on the create response; read them back
# from the session's event list.
for event in client.beta.sessions.events.list(seeded_session.id):
if event.type == "user.message":
for block in event.content:
if block.type == "text":
print(f"Seeded event: {block.text}")
```
No other event type is accepted. Events that respond to an agent turn (`user.tool_confirmation`, `user.tool_result`, and `user.custom_tool_result`) aren't accepted because no agent turn exists yet, and `user.interrupt` isn't accepted because there is no turn to stop. Unlike `initial_events` on a scheduled deployment, a session's `initial_events` don't accept `system.message`.
Each event in `initial_events` is validated and persisted before the create response returns, in list order, with a server-assigned ID, exactly as if you had posted it to the [send events](managed-agents-events-and-streaming.md) endpoint immediately after creation. Per-event content rules are also the same as on that endpoint. An empty list is equivalent to omitting the field. Validation is all-or-nothing: if any event fails validation, the whole request is rejected and no session is created.
The create request is rejected in the following cases:
| Condition | Status |
|--------------------------------------------------------------------------------------------------------------------------------|--------|
| More than one `user.define_outcome` event | 400 |
| A `user.define_outcome` event without a `rubric` | 400 |
| More than 100 file-sourced [`document` content blocks](../Guides/build-with-claude-files.md#document-blocks) across the whole list | 400 |
| A request body over 32 MB | 413 |
A `user.define_outcome` event in `initial_events` is accepted under the same conditions as sending one to an existing session; see [Define outcomes](managed-agents-define-outcomes.md).
### Override agent configuration for a session
You can pass `agent` in three forms: an agent ID string, a pinned-version object (`type: "agent"`), or an overrides object. The overrides form changes parts of the agent's configuration for a single session. Use it to try a different model or grant an extra tool in one session without versioning the agent. For the overrides form, set `type` to `agent_with_overrides` and pass the agent's `id` and optionally a `version` (omit `version` to use the agent's latest version). Then include any of `model`, `system`, `tools`, `mcp_servers`, or `skills` with the values the session should use.
Each overridable field follows the same three rules:
- **Omit the field:** The session inherits the value from the agent version it references.
- **Set the field to `null`, or to an empty array for list fields:** The session runs with that field cleared. This rule applies in full to `system` and `skills`. There are three exceptions:
- `model` is never clearable. A session always needs a model, so `model: null` returns a 400 `agent_model_required` error.
- Clearing `tools` returns a 400 error when the session's effective `skills` is non-empty, because skills require the `read` tool. Otherwise, `tools: null` and `tools: []` clear the field.
- Clearing `mcp_servers` returns a 400 error when the session's effective `tools` still contains an `mcp_toolset` that references one of the agent's servers. Override `tools` in the same request to remove those `mcp_toolset` entries, then clear `mcp_servers`.
- **Set the field to a value:** The value replaces the agent's value in full. Overrides never merge with the agent's configuration, so a `tools` override must list every tool the session should have. Likewise, a `model` override replaces the agent's `model` object in full, so the agent's own `effort` isn't carried over. To run the session at a specific effort level, set `effort` inside the override's `model` object. A level the model doesn't support returns a 400 error, and a `model` override without `effort` runs at that model's default effort level.
Overrides apply only to the session you create. They do not modify the agent resource or create a new agent version, so other sessions that reference the same agent are unaffected.
In the response, the `agent` object reflects the configuration the session runs with after the overrides are applied. Its `id` and `version` still identify the agent and version the overrides are applied to. This lets you trace a session back to its base agent.
The following example starts a session that overrides the model and clears the system prompt:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
override_session = client.beta.sessions.create(
agent={
"type": "agent_with_overrides",
"id": agent.id,
"model": {"id": "claude-sonnet-5"},
"system": None, # clear the agent's system prompt for this session
},
environment_id=environment.id,
)
# The response's agent is the resolved snapshot with the overrides applied.
print(f"Model: {override_session.agent.model.id}")
print(f"System: {override_session.agent.system}")
```
#### Pin the inference geo for a session
Because a `model` override replaces the agent's `model` object in full, it also sets or clears the model's [`inference_geo`](manage-claude-data-residency.md) pin for the session: an override that includes `inference_geo` pins the geography that serves the session's model requests, and one that omits it clears the agent's pin so the session follows the workspace's `default_inference_geo`. The overridden value is validated against the workspace's `allowed_inference_geos` when the session is created.
The following example starts a session from an agent whose model has no geo pin, pins the session's model requests to US inference by including `inference_geo` in the `model` override, and prints the value echoed in the response's `agent.model`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent={
"type": "agent_with_overrides",
"id": agent.id,
# Replaces the agent's `model` in full: restate `id`, add `inference_geo` to pin.
"model": {"id": "claude-opus-5-5", "inference_geo": "us"},
},
environment_id=environment.id,
)
print(f"Inference geo: {session.agent.model.inference_geo}")
```
The agent defines how Claude behaves within the session, including the model, system prompt, tools, and MCP servers. See [Define your agent](managed-agents-agent-setup.md) for details.
### Set a session budget
To cap what a session can spend, pass the optional `budget` object when you create it. A budget is a hard ceiling on the session's list cost: the platform prices everything the session consumes at public list rates, and the session stops issuing new model requests once that running total reaches `max_list_cost`. Set `type` to `limit` and give `max_list_cost` an `amount` and a `currency`. `amount` is a whole number of US cents written as a string, such as `"2500"` for \$25.00; the API takes a string rather than a number so no floating-point rounding is ever applied. `USD` is the only currency currently supported. When the session reaches the cap, it pauses and goes idle with the stop reason `budget_reached`. The cap is enforced between model requests, so the request that crosses it finishes first and the session's final list cost can land [a fraction past the cap](managed-agents-budgets.md#when-a-session-reaches-its-budget). A budget can only be attached at creation: you can [change or remove](managed-agents-session-operations.md#updating-the-session-budget) it later, but you can't add one to a session created without it.
The following example creates a session with a \$25.00 budget; the response echoes the `budget` on the session resource:
cURL
```python
curl -fsSL https://api.anthropic.com/v1/sessions \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: managed-agents-2026-04-01" \
-H "content-type: application/json" \
-d @- <<EOF
{
"agent": "$AGENT_ID",
"environment_id": "$ENVIRONMENT_ID",
"budget": {
"type": "limit",
"max_list_cost": {"amount": "2500", "currency": "USD"}
}
}
EOF
```
See [Session budgets](managed-agents-budgets.md) for how enforcement works, what counts toward list cost, and how budgets behave in multiagent sessions.
## MCP authentication through vaults
If your agent uses MCP tools that require authentication, pass `vault_ids` at session creation to reference a vault containing stored OAuth credentials. Anthropic manages token refresh on your behalf. See [Authenticate with vaults](managed-agents-vaults.md) for how to create vaults and register credentials.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
vault_session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
vault_ids=[vault.id],
)
```
## Starting the session
Creating a session without `initial_events` registers the session but does not start any work; the environment's sandbox begins provisioning as soon as the session is created, so the first tool call does not wait on it. To delegate a task, send events to the session using a [user event](managed-agents-reference.md#event-types). To supply the first event in the create request instead, see [Seed the session with initial events](#seed-the-session-with-initial-events). The session acts as a state machine that tracks progress while events drive the actual execution.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [
{"type": "text", "text": "List the files in the working directory."}
],
},
],
)
```
See [Session event stream](managed-agents-events-and-streaming.md) for how to stream the agent's responses and handle tool confirmations.
See [Session statuses](managed-agents-session-operations.md#session-statuses) for the statuses a session moves through.
## Next steps
[Session operations](managed-agents-session-operations.md)
Retrieve, list, update, archive, and delete Claude Managed Agents sessions.
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
[Scheduled deployments](managed-agents-scheduled-deployments.md)
Create and manage deployments with the Claude API: run an agent on a recurring cron schedule and inspect its run history.
---
## Skills - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/skills
Path: 04-API-Reference/Other/managed-agents-skills.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fskills)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# Skills
Copy page
Attach pre-built or custom skills to an agent in Claude Managed Agents to give it reusable, filesystem-based expertise for domain-specific workflows.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Skills are reusable, filesystem-based resources that give your agent domain-specific expertise: workflows, context, and best practices that turn a general-purpose agent into a specialist. Each skill you add incurs a modest cost on the session's context window, adding instructions and metadata that help the model use the skill. Learn more in the [Agent Skills](../Agents-Tools/agents-and-tools-agent-skills-overview.md) overview.
Skills reach your agent in two ways: attach them through the agent's `skills` array, or [load them from a GitHub repository](#load-skills-from-a-github-repository) mounted on the session. Attached skills come in two types. All skills work the same way: your agent invokes them automatically when they are relevant to the task.
- **Pre-built Anthropic skills:** Common document tasks such as PowerPoint, Excel, Word, and PDF handling (`pptx`, `xlsx`, `docx`, `pdf`).
- **Custom skills:** Skills you author and upload to your workspace.
To learn how to author custom skills, see [Agent Skills](../Agents-Tools/agents-and-tools-agent-skills-overview.md) and [Skill authoring best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md). To upload a custom skill to your workspace, see [Create a custom skill](#create-a-custom-skill).
## Create a custom skill
A custom skill is a directory containing a `SKILL.md` file plus any supporting files, uploaded to your workspace as a zip archive or as individual files. Creating the skill returns the `skill_*` ID you reference when attaching it to an agent. Anthropic pre-built skills are already available in every workspace and don't require this step. To use only pre-built skills, skip to [Attach skills to an agent](#attach-skills-to-an-agent).
These examples omit the optional `display_name` field, so the skill's display name is derived from the `name` field in `SKILL.md`. An explicit `display_name` can be up to 255 characters and doesn't need to be unique within your workspace.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply skills/pr-summary
```
skills/pr-summary/SKILL.md
```python
---
name: pr-summary
description: Summarize a pull request's changes and risks in the team's review format.
---
# PR summary
List what changed, why, and anything a reviewer should look at closely, in three short sections.
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) uploads the `skills/pr-summary` directory, prints the new skill's ID, and records it in `claude-lock.json`. Commit `claude-lock.json` so the next `ant apply` uploads your edits as a new version instead of creating a second skill.
To list, retrieve, delete, and version custom skills, see [Managing custom skills](../Guides/build-with-claude-skills-guide.md#managing-custom-skills). For the full request and response schemas, see the [Create Skill API reference](../Endpoints/skills-create.md). Skill bundles upload directly to the Skills API rather than through the [Files API](../Guides/build-with-claude-files.md).
## Attach skills to an agent
Attach skills when creating an agent. Each [session](managed-agents-sessions.md) supports up to 500 skills, counted as the deduplicated set across every agent in the session (see [Multiagent orchestration](managed-agents-multiagent-orchestration.md)).
Mounting more skills increases the time it takes for the session's sandbox to start. Attach only the skills each agent needs for its task.
Each entry in the `skills` array uses the following fields:
| Field | Description |
|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `type` | Either `anthropic` for pre-built skills or `custom` for workspace-authored skills. |
| `skill_id` | The skill identifier. For Anthropic skills, use the short name (for example, `xlsx`). For custom skills, use the `skill_*` ID returned at creation (see [Create a custom skill](#create-a-custom-skill)). |
| `version` | Pin to a specific version or use `latest`. Optional. Defaults to `latest` when omitted. Applies to both Anthropic and custom skills. |
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Financial Analyst
model: claude-opus-5-5
skills:
- type: anthropic
skill_id: xlsx
- type: custom
skill_id: skill_01AbCdEfGhIjKlMnOpQrStUv
version: latest
---
You are a financial analysis agent.
```
## Load skills from a GitHub repository
Skills can also live in your codebase. When a session mounts a repository through the [`github_repository` resource](managed-agents-github.md), the repository's root `.claude/skills` directory is scanned at session start, and each skill found there becomes available to the agent. No upload and no entry in the agent's `skills` array are required. The agent sees each discovered skill's name, description, and path in the sandbox, and reads the skill's `SKILL.md` when a task matches, including any scripts and resources the skill ships. Discovery relies on the agent's `read` tool from the [agent toolset](managed-agents-tools.md), which is enabled by default; an agent with `read` disabled doesn't load repository skills.
Repository skills are agent instructions, so a mounted repository is part of your agent's trust boundary. Anyone who can commit to the repository (a merged external pull request, a compromised dependency, a contributor) can add or change a skill, the platform loads it at session start without a review step, and session tools such as `bash` and `web_fetch` give those instructions real reach. Mount only repositories you trust, and review `.claude/skills` before mounting a repository that accepts outside contributions.
Repository skill discovery runs in cloud sandboxes. [Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md) don't support GitHub repository resources.
Discovery finds skills at exactly `.claude/skills/<skill-name>/SKILL.md`, one directory level deep at the repository root:
- `your-repo/`
- `.claude/`
- `skills/`
- `code-review/`
- `SKILL.md`
- `release-process/`
- `SKILL.md`
- `scripts/`
- `run_checks.sh`
- `src/`
Locations that don't match this layout aren't discovered at session start:
- `.claude/skills/SKILL.md`: a `SKILL.md` with no skill directory around it
- `.claude/skills/tools/code-review/SKILL.md`: nested more than one directory level deep
- `skills/code-review/SKILL.md`: a `skills` directory outside `.claude`
A `.claude/skills` directory elsewhere in the repository, such as inside a package subdirectory, isn't announced at session start; those skills can still surface when the agent reads files under that subtree.
Repository skills use the same `SKILL.md` format as the custom skills you upload. For the format and authoring guidance, see [Agent Skills](../Agents-Tools/agents-and-tools-agent-skills-overview.md) and [Skill authoring best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md).
To load skills from a repository, create a session that mounts it. This is the same request shown in [Accessing GitHub](managed-agents-github.md#token-permissions); `mount_path` is optional and defaults to `/workspace/<repo-name>`:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
resources=[
{
"type": "github_repository",
"url": "https://github.com/org/repo",
"mount_path": "/workspace/repo",
"authorization_token": "ghp_your_github_token",
},
],
)
```
For private repositories, the resource's `authorization_token` must have access to the repository. This is the same personal access token flow used for any repository mount; see [Accessing GitHub](managed-agents-github.md#token-permissions).
Discovered skills follow the checked-out state of the repository: the `checkout` branch or commit when the resource sets one, otherwise the repository's default branch. The scan runs once, when the session starts. Commits pushed mid-session are not picked up; to load updated skills, start a new session.
Repository skills work alongside skills attached through the agent's `skills` array. If a repository skill shares a name with an attached skill, or with a skill from another mounted repository, both are available; each is announced with its own path.
## Next steps
[Cloud environment setup](managed-agents-environments.md)
Customize cloud sandboxes for your sessions.
[Using Agent Skills with the API](../Guides/build-with-claude-skills-guide.md)
Learn how to use Agent Skills to extend Claude's capabilities through the API.
[Files API](../Guides/build-with-claude-files.md)
Upload files once and reference them across API requests.
[Get started with Agent Skills in the API](../Agents-Tools/agents-and-tools-agent-skills-quickstart.md)
Learn how to use Agent Skills to create documents with the Claude API in under 10 minutes.
---
## Tools
Source: https://platform.claude.com/docs/en/managed-agents/tool-use
Path: 04-API-Reference/Other/managed-agents-tool-use.md
# Tools
Configure tools available to your agent.
---
> Note: The original URL `/docs/en/managed-agents/tool-use` returned 404. This content is sourced from the actual tools page at `/docs/en/managed-agents/tools`.
Claude Managed Agents provides a set of built-in tools that Claude can use autonomously within a session. You control which tools are available by specifying them in the agent configuration.
Custom, user-defined tools are also supported. Your application executes these tools separately and sends the tool results back to Claude; Claude can use the results to continue the task at hand.
> All Managed Agents API requests require the `managed-agents-2026-04-01` beta header. The SDK sets the beta header automatically.
## Available tools
The agent toolset includes the following tools. All are enabled by default when you include the toolset in your agent configuration.
| Tool | Name | Description |
|---|---|---|
| Bash | `bash` | Execute bash commands in a shell session |
| Read | `read` | Read a file from the local filesystem |
| Write | `write` | Write a file to the local filesystem |
| Edit | `edit` | Perform string replacement in a file |
| Glob | `glob` | Fast file pattern matching using glob patterns |
| Grep | `grep` | Text search using regex patterns |
| Web fetch | `web_fetch` | Fetch content from a URL |
| Web search | `web_search` | Search the web for information |
## Configuring the toolset
Enable the full toolset with `agent_toolset_20260401` when creating an agent. Use the `configs` array to disable specific tools or override their settings.
```python
agent = client.beta.agents.create(
name="Coding Assistant",
model="claude-sonnet-4-6",
tools=[
{
"type": "agent_toolset_20260401",
"configs": [
{"name": "web_fetch", "enabled": False},
],
},
],
)
```
```typescript
const agent = await client.beta.agents.create({
name: "Coding Assistant",
model: "claude-sonnet-4-6",
tools: [
{
type: "agent_toolset_20260401",
configs: [{ name: "web_fetch", enabled: false }]
}
]
});
```
### Disabling specific tools
To disable a tool, set `enabled: false` in its config entry:
```json
{
"type": "agent_toolset_20260401",
"configs": [
{ "name": "web_fetch", "enabled": false },
{ "name": "web_search", "enabled": false }
]
}
```
### Enabling only specific tools
To start with everything off and enable only what you need, set `default_config.enabled` to `false`:
```json
{
"type": "agent_toolset_20260401",
"default_config": { "enabled": false },
"configs": [
{ "name": "bash", "enabled": true },
{ "name": "read", "enabled": true },
{ "name": "write", "enabled": true }
]
}
```
## Custom tools
In addition to built-in tools, you can define custom tools. Custom tools are analogous to [user-defined client tools](../Agents-Tools/agents-and-tools-tool-use-how-tool-use-works.md#user-defined-tools-client-executed) in the Messages API.
Custom tools allow you to extend Claude's capabilities to perform a wider variety of tasks. Each tool defines a contract: you specify what operations are available and what they return; Claude decides when and how to call them. The model never executes anything on its own. It emits a structured request, your code runs the operation, and the result flows back into the conversation.
```python
agent = client.beta.agents.create(
name="Weather Agent",
model="claude-sonnet-4-6",
tools=[
{
"type": "agent_toolset_20260401",
},
{
"type": "custom",
"name": "get_weather",
"description": "Get current weather for a location",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
],
)
```
```typescript
const agent = await client.beta.agents.create({
name: "Weather Agent",
model: "claude-sonnet-4-6",
tools: [
{ type: "agent_toolset_20260401" },
{
type: "custom",
name: "get_weather",
description: "Get current weather for a location",
input_schema: {
type: "object",
properties: { location: { type: "string", description: "City name" } },
required: ["location"]
}
}
]
});
```
Once you've defined the tool at the agent level, the agent will invoke the tools through the course of a session. See [Session event stream](managed-agents-events-and-streaming.md#handling-custom-tool-calls) for the full flow.
### Best practices for custom tool definitions
- **Provide extremely detailed descriptions.** This is by far the most important factor in tool performance. Your descriptions should explain what the tool does, when it should be used (and when it shouldn't), what each parameter means and how it affects the tool's behavior, and any important caveats or limitations. The more context you can give Claude about your tools, the better it will be at deciding when and how to use them. Aim for at least 3-4 sentences per tool description, more if the tool is complex.
- **Consolidate related operations into fewer tools.** Rather than creating a separate tool for every action (`create_pr`, `review_pr`, `merge_pr`), group them into a single tool with an `action` parameter. Fewer, more capable tools reduce selection ambiguity and make your tool surface easier for Claude to navigate.
- **Use meaningful namespacing in tool names.** When your tools span multiple services or resources, prefix names with the resource (e.g., `db_query`, `storage_read`). This makes tool selection unambiguous as your library grows.
- **Design tool responses to return only high-signal information.** Return semantic, stable identifiers (e.g., slugs or UUIDs) rather than opaque internal references, and include only the fields Claude needs to reason about its next step. Bloated responses waste context and make it harder for Claude to extract what matters.
---
## Restrict web search and web fetch domains - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/tools-web-restrictions
Path: 04-API-Reference/Other/managed-agents-tools-web-restrictions.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Ftools-web-restrictions)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# Restrict web search and web fetch domains
Copy page
Control which sites an agent's web search and web fetch tools can reach, cap fetched content, and localize search results.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
To control which sites the agent's web tools can reach, set a domain list on the `web_search` and `web_fetch` entries of the [agent toolset](managed-agents-tools.md#configuring-the-toolset). Each of these `configs` entries takes one of two lists:
- **`allowed_domains`:** The tool can reach only these hosts.
- **`blocked_domains`:** The tool can never reach these hosts.
Each tool carries its own list, so `web_search` and `web_fetch` can have different restrictions.
These per-tool lists are the way to restrict what the web tools can reach. Two other settings do not affect these tools:
- **Environment networking:** An environment's [`networking`](managed-agents-environments.md#networking) settings control the sandbox's own outbound traffic. `web_search` and `web_fetch` run on Anthropic's servers, whether the environment is a cloud or self-hosted sandbox.
- **Organization settings:** Organization-level web search and web fetch settings in the Claude Console apply to the Messages API. They do not apply to Managed Agents sessions.
## Set domain lists on an agent
The following example creates an agent that limits `web_search` to two sites and blocks one host for `web_fetch`. It also sets `user_location` and `max_content_tokens`, which [Settings](#settings) describes. The example then prints the `configs` array from the response.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Research Agent
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
configs:
- type: web_search
name: web_search
allowed_domains: [docs.example.com, arxiv.org]
user_location:
type: approximate
country: US
timezone: America/Los_Angeles
- type: web_fetch
name: web_fetch
blocked_domains: [ads.example.com]
max_content_tokens: 50000
---
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) creates the agent and prints its ID, not the `configs` array.
In the Claude Console, set allowed or blocked domains from the `web_search` and `web_fetch` rows of the **Built-in tools** card on the agent form. Set `max_content_tokens` and `user_location` in the **Raw** view of the agent's configuration.
## Settings
In addition to `enabled` and `permission_policy`, the web tool entries accept the following settings:
| Setting | Applies to | Description |
|----------------------|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allowed_domains` | `web_search`, `web_fetch` | The only hosts the tool can reach. See [Domain list rules](#domain-list-rules). |
| `blocked_domains` | `web_search`, `web_fetch` | Hosts the tool cannot reach. See [Domain list rules](#domain-list-rules). |
| `max_content_tokens` | `web_fetch` | Caps the amount of fetched page content included in the context. Must be a positive integer. See [content limits](../Agents-Tools/agents-and-tools-tool-use-web-fetch-tool.md#content-limits). |
| `user_location` | `web_search` | Localizes search results. An object with the same fields as the Messages API [`user_location`](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md#localization) parameter. |
For how the SDKs type these entries, see [Config entry types in the SDKs](managed-agents-tools.md#config-entry-types-in-the-sdks).
## When a domain is not permitted
`web_search` omits results that its domain list does not permit. A `web_fetch` call for a URL that its domain list does not permit returns an error result to the agent. The `agent.tool_result` event has `is_error: true`, and its content names the error code `url_not_allowed`.
## Domain list rules
These rules apply to `allowed_domains` and `blocked_domains` alike. A request that breaks one is rejected, as [Validation errors](#validation-errors) describes.
- **One list per entry:** Set either `allowed_domains` or `blocked_domains` on an entry, not both.
- **List size:** Each list holds 1 to 64 domains, each 1 to 255 characters.
- **No empty lists:** To apply no restriction, omit the field or send `null`.
- **No duplicates:** A domain can appear only once in a list. `www.example.com` and `example.com` count as different domains.
### What a listed domain matches
A listed domain matches that host and all of its subdomains. `example.com` covers `docs.example.com`, but `docs.example.com` does not cover `example.com` or `api.example.com`.
A leading `www.` is a subdomain like any other, so `www.example.com` does not cover `example.com`. List the bare domain to cover both.
Hostnames are compared without regard to case.
### Domain format
Each domain is a registrable domain name, or a subdomain of one, written as a plain hostname. It can contain ASCII letters, digits, hyphens, underscores, and dots. A single trailing `/` is ignored.
| Not accepted | Example | Use instead |
|--------------------------------------------------------------------------------|--------------------------|---------------------------------------|
| A scheme | `https://example.com` | `example.com` |
| A port | `example.com:443` | `example.com` |
| A wildcard | `*.example.com` | `example.com` |
| A path on a `web_fetch` domain | `example.com/*` | `example.com` |
| An IP address in any form, whether IPv4, IPv6, bracketed, or numeric shorthand | `127.1` | The site's domain name |
| A bare top-level domain or registry suffix | `com`, `co.uk`, `gov.uk` | A full domain such as `example.co.uk` |
| A single-label name | `intranet` | A full domain such as `example.co.uk` |
| Non-ASCII characters, as in an internationalized domain name | | The `xn--` (Punycode) form |
A domain is also rejected if it contains credentials or whitespace, or if one of its labels begins or ends with a hyphen. `localhost` and hosts ending in `.localhost`, `.local`, `.internal`, `.localdomain`, or `.invalid` are rejected too.
### Path suffixes on web search domains
A `web_search` domain can carry a path suffix, such as `example.com/blog`. The path cannot contain spaces, `?`, `#`, or any of the characters `$ , | ^ !`.
Prefer plain hostnames for `web_search` too. The search provider matches path suffixes as URL patterns rather than as strict host rules.
## Validation errors
The API validates these settings when you [create an agent](managed-agents-agent-setup.md#create-an-agent) or [update an agent](managed-agents-agent-setup.md#update-an-agent). It also validates them when you create or update a session that supplies `tools`.
Format and limit violations are rejected with a 400 `invalid_request_error`:
| Violation | Error message |
|--------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| An entry sets both lists. | Includes `Only one of allowed_domains or blocked_domains may be set.` |
| A list is empty. | Includes `allowed_domains: Empty list of domains is ambiguous. Provide at least one domain or null.` |
| A domain breaks a format rule. | Names the domain's list and zero-based position. For example, `allowed_domains.0: IP addresses are not supported; provide a plain hostname like "example.com"` |
On the same requests, the API also rejects three settings that depend on the search and fetch providers:
- A domain in `allowed_domains` that Anthropic's crawler is not permitted to access.
- A `user_location.country` that the search provider does not support. The message ends in `user_location.country: not a country the search provider supports`.
- A `user_location.timezone` that is not a valid IANA name.
### When an accepted setting is no longer valid
The session checks the configuration again when it first initializes the tool. If a setting that was accepted earlier is no longer valid at that point, the session emits a [`session.error`](managed-agents-events-and-streaming.md) event. It then returns to `idle` without retrying.
To continue the session:
1. Fix the setting by [updating the session's tools](managed-agents-session-operations.md#updating-the-agent-configuration).
2. Update the agent as well, so that new sessions start with the corrected configuration.
3. Send a new `user.message`.
## Multiagent and outcome-driven sessions
In a [multiagent session](managed-agents-multiagent-orchestration.md), every domain list that applies to a thread is enforced at the same time. An agent in the coordinator's roster is bound by three sets of lists:
- Its own `allowed_domains` and `blocked_domains`
- Those of any agent that called it
- The coordinator's current lists
The settings combine as follows:
| Setting | How it combines |
|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allowed_domains` | The tool can reach a host only if every list covers it. |
| `blocked_domains` | The lists add together. |
| `max_content_tokens`, `user_location` | Not combined. A thread uses the value from its own tool configuration if set. Otherwise it uses the value from the agent that called it, and otherwise the coordinator's current configuration. |
A roster agent can therefore narrow what a tool reaches but never widen it:
- A roster agent that sets `blocked_domains` keeps the coordinator's `allowed_domains` and blocks those hosts within it.
- A roster agent that sets its own `allowed_domains` can reach only the hosts that both its list and the coordinator's list cover.
A `{"type": "self"}` roster entry has no web settings of its own and follows the coordinator's current settings.
If the combined `allowed_domains` lists have no domain in common, the tool stays available to that agent but every call fails. Each call returns a `url_not_allowed` error stating that no domain is permitted. The tool description tells the model the same. To avoid this, keep each roster agent's `allowed_domains` inside the coordinator's.
The grader in [outcome-driven sessions](managed-agents-define-outcomes.md) runs without `web_search` and `web_fetch`, regardless of these settings.
## Change the lists mid-session
You can change the lists on an idle session by [updating its tools](managed-agents-session-operations.md#updating-the-agent-configuration). The new lists apply to the rest of the session.
In a multiagent session, every thread applies the new lists from its next turn. The update does not change a roster agent's own lists. Those stay as the agent's definition set them when the session was created.
## Differences from the Messages API tools
These settings use the same `allowed_domains` and `blocked_domains` fields as [domain filtering](../Agents-Tools/agents-and-tools-tool-use-server-tools.md#domain-filtering) on the Messages API server tools. Managed Agents differs in four ways:
- Each list is [capped at 64 domains](#domain-list-rules).
- Domains listed for `web_fetch` [cannot include a path](#domain-format).
- Domains [must be ASCII](#domain-format). The Messages API accepts Unicode entries, though it recommends against them.
- `max_uses`, `citations`, and `cache_control` are not available on the toolset.
## Next steps
[Tools](managed-agents-tools.md)
See the built-in tools, enable or disable them, and define custom tools.
[Permission policies](managed-agents-permission-policies.md)
Control when agent and MCP tools execute.
[Cloud environment setup](managed-agents-environments.md)
Control the sandbox's own outbound network access.
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)
Coordinate multiple agents within a single session.
---
## Tools - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/tools
Path: 04-API-Reference/Other/managed-agents-tools.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Ftools)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Define your agent
# Tools
Copy page
Configure tools available to your agent.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Claude Managed Agents provides a set of built-in tools that Claude can use autonomously within a [session](managed-agents-sessions.md). You control which tools are available by specifying them in the agent configuration.
Claude Managed Agents also supports custom, user-defined tools. Your application executes these tools separately and returns the results to Claude, which uses them to continue the task. To give the agent tools from an MCP server, use the [MCP connector](managed-agents-mcp-connector.md) instead.
## Available tools
The agent toolset includes the following tools. All are enabled by default when you include the toolset in your agent configuration. Each entry in the `configs` array is identified by its `name`, using the values in the Name column, and accepts an optional `type` field with the same value. The `web_search` and `web_fetch` entries accept additional settings; see [Restrict web search and web fetch domains](#restrict-web-search-and-web-fetch-domains).
| Tool | Name | Description |
|------------|--------------|------------------------------------------------|
| Bash | `bash` | Execute bash commands in a shell session |
| Read | `read` | Read a file from the sandbox filesystem |
| Write | `write` | Write a file to the sandbox filesystem |
| Edit | `edit` | Perform string replacement in a file |
| Glob | `glob` | Fast file pattern matching using glob patterns |
| Grep | `grep` | Text search using regex patterns |
| Web fetch | `web_fetch` | Fetch content from a URL |
| Web search | `web_search` | Search the web for information |
When a tool output exceeds 100,000 characters (about 25,000 tokens), it is automatically written to a file in the [sandbox](managed-agents-environments.md). The model receives a truncated preview with the file path and can read the full content from there.
## Configuring the toolset
Enable the full toolset with `agent_toolset_20260401` when creating an agent. Use the `configs` array to disable specific tools or override their settings. Each config entry can also set a `permission_policy` that controls whether the tool's calls run without confirmation, require confirmation, or are evaluated individually by the server. See [Permission policies](managed-agents-permission-policies.md) for the available policy types.
Config entries for `web_search` and `web_fetch` also accept domain filters and other web settings; see [Restrict web search and web fetch domains](#restrict-web-search-and-web-fetch-domains).
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Coding Assistant
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
configs:
- name: web_fetch
enabled: false
---
```
### Disabling specific tools
To disable a tool, set `enabled: false` in its config entry in the toolset object of your agent's `tools` array:
```python
{
"type": "agent_toolset_20260401",
"configs": [
{ "name": "web_fetch", "enabled": false },
{ "name": "web_search", "enabled": false }
]
}
```
### Enabling only specific tools
The `default_config` object sets the baseline for every tool in the set, and per-tool `configs` entries override it. To start with everything off and enable only what you need, set `default_config.enabled` to `false`:
```python
{
"type": "agent_toolset_20260401",
"default_config": { "enabled": false },
"configs": [
{ "name": "bash", "enabled": true },
{ "name": "read", "enabled": true },
{ "name": "write", "enabled": true }
]
}
```
### Restrict web search and web fetch domains
To control which sites the agent's web tools can reach, set `allowed_domains` (the tool can reach only these hosts) or `blocked_domains` (the tool can never reach these hosts) on the `web_search` and `web_fetch` entries of the toolset's `configs` array. Each tool carries its own list, so `web_search` and `web_fetch` can have different restrictions. A listed domain covers that host and all of its subdomains. At runtime, a `web_fetch` call for a URL that its lists do not permit returns an error result to the agent (`is_error: true` on the `agent.tool_result` event, with content that names the error code `url_not_allowed`), and `web_search` omits results that its lists do not permit.
The following toolset limits `web_search` to two sites and localizes its results, and blocks one host for `web_fetch` while capping how much fetched content enters the context:
```python
{
"type": "agent_toolset_20260401",
"configs": [
{
"type": "web_search",
"name": "web_search",
"allowed_domains": ["docs.example.com", "arxiv.org"],
"user_location": {
"type": "approximate",
"country": "US",
"timezone": "America/Los_Angeles"
}
},
{
"type": "web_fetch",
"name": "web_fetch",
"blocked_domains": ["ads.example.com"],
"max_content_tokens": 50000
}
]
}
```
In the Python, TypeScript, Go, Java, C#, Ruby, and PHP SDKs, each `configs` entry is typed per tool: a union with one member per built-in tool, discriminated by `type`. `type` is optional when you construct an entry (the server infers it from `name`) and always present on responses. This typing does not change the JSON that an entry serializes to, so a request whose entries set only `name`, `enabled`, and `permission_policy` is valid with or without `type`. In SDKs where you construct entries from typed values rather than plain dictionaries or hashes (Go, Java, C#, and PHP), the element type of `configs` is the union itself: build each entry from its per-tool member type.
The following request creates an agent with this toolset and prints the `configs` array from the response:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Research Agent
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
configs:
- type: web_search
name: web_search
allowed_domains: [docs.example.com, arxiv.org]
user_location:
type: approximate
country: US
timezone: America/Los_Angeles
- type: web_fetch
name: web_fetch
blocked_domains: [ads.example.com]
max_content_tokens: 50000
---
```
[`ant apply`](cli-sdks-libraries-cli-apply.md) creates the agent and prints its ID, not the `configs` array.
In the Claude Console, set allowed or blocked domains from the `web_search` and `web_fetch` rows of the **Built-in tools** card on the agent form; set `max_content_tokens` and `user_location` in the **Raw** view of the agent's configuration.
In addition to `enabled` and `permission_policy`, the web tool entries accept the following settings:
| Setting | Applies to | Description |
|----------------------|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `allowed_domains` | `web_search`, `web_fetch` | The only hosts the tool can reach. Cannot be combined with `blocked_domains` on the same entry. |
| `blocked_domains` | `web_search`, `web_fetch` | Hosts the tool cannot reach. |
| `max_content_tokens` | `web_fetch` | Caps the amount of fetched page content included in the context. Must be a positive integer. See [content limits](../Agents-Tools/agents-and-tools-tool-use-web-fetch-tool.md#content-limits). |
| `user_location` | `web_search` | Localizes search results. An object with the same fields as the Messages API [`user_location`](../Agents-Tools/agents-and-tools-tool-use-web-search-tool.md#localization) parameter. |
An environment's [`networking`](managed-agents-environments.md#networking) settings control the sandbox's own outbound traffic. They do not affect `web_search` or `web_fetch`, which run on Anthropic's servers whether the environment is a cloud or self-hosted sandbox. The per-tool `allowed_domains` and `blocked_domains` lists are the way to restrict what these tools can reach.
Organization-level web search and web fetch settings in the Claude Console apply to the Messages API and do not apply to Managed Agents sessions. To restrict an agent's web tools, configure `allowed_domains` or `blocked_domains` on its toolset instead.
#### Domain list rules
- Set either `allowed_domains` or `blocked_domains` on an entry, not both. An entry that sets both is rejected.
- Each list holds 1 to 64 domains, each 1 to 255 characters. An empty list is rejected: to apply no restriction, omit the field or send `null`.
- Each domain is a registrable domain name, or a subdomain of one, written as a plain hostname: ASCII letters, digits, hyphens, underscores, and dots, with no scheme, port, credentials, wildcard, or whitespace, no label that begins or ends with a hyphen, and no path other than the optional `web_search` path suffix described later in this list. Use `example.com`, not `https://example.com`, `example.com:443`, or `*.example.com`. Hostnames are compared without regard to case, and a single trailing `/` is ignored.
- A listed domain matches that host and its subdomains: `example.com` covers `docs.example.com`, but `docs.example.com` does not cover `example.com` or `api.example.com`. A leading `www.` is a subdomain like any other, so `www.example.com` does not cover `example.com`; list the bare domain to cover both.
- IP addresses are not accepted in any form, whether IPv4, IPv6, bracketed, or numeric shorthand such as `127.1`. List the site's domain name instead.
- A bare top-level domain or registry suffix such as `com`, `co.uk`, or `gov.uk` is rejected, and so is a single-label name such as `intranet`. List a full domain such as `example.co.uk`.
- `localhost` and hosts ending in `.localhost`, `.local`, `.internal`, `.localdomain`, or `.invalid` are rejected.
- Use the `xn--` (Punycode) form for internationalized domain names; a domain that contains non-ASCII characters is rejected.
- A `web_fetch` domain cannot include a path: use `example.com`, not `example.com/*`. A `web_search` domain can carry a path suffix such as `example.com/blog`, in which the path cannot contain spaces, `?`, `#`, or any of the characters `$ , | ^ !`. Prefer plain hostnames for `web_search` too, because the search provider matches path suffixes as URL patterns rather than as strict host rules.
- Duplicate domains within a list are rejected. `www.example.com` and `example.com` count as different domains; see the earlier matching rule for what each covers.
#### When settings are validated
Format and limit violations are rejected with a 400 `invalid_request_error` when you [create an agent](managed-agents-agent-setup.md#create-an-agent) or [update an agent](managed-agents-agent-setup.md#update-an-agent), and when you create or update a session that supplies `tools`. For example, the message for an entry that sets both lists includes `Only one of allowed_domains or blocked_domains may be set.`, and the message for an empty list includes `allowed_domains: Empty list of domains is ambiguous. Provide at least one domain or null.` The message for a domain that breaks a format rule names its list and zero-based position, for example `allowed_domains.0: IP addresses are not supported; provide a plain hostname like "example.com"`.
The same requests also reject three settings that depend on the search and fetch providers: a domain in `allowed_domains` that Anthropic's crawler is not permitted to access, a `user_location.country` that the search provider does not support (the message ends in `user_location.country: not a country the search provider supports`), and a `user_location.timezone` that is not a valid IANA name. The session checks the configuration again when it first initializes the tool; if a setting that was accepted earlier is no longer valid at that point, the session emits a [`session.error`](managed-agents-events-and-streaming.md) event and returns to `idle` without retrying. Fix the setting by [updating the session's tools](managed-agents-session-operations.md#updating-the-agent-configuration), update the agent as well so that new sessions start with the corrected configuration, then send a new `user.message` to continue.
#### Multiagent sessions, outcomes, and mid-session updates
In a [multiagent session](managed-agents-multiagent-orchestration.md), every domain list that applies to a thread is enforced at the same time: an agent in the roster of the coordinator is bound by its own `allowed_domains` and `blocked_domains`, by those of any agent that called it, and by the coordinator's current lists.
- Allowlists combine to the domains that all of them cover, and blocklists add together, so a roster agent can narrow what a tool reaches but never widen it. For example, a roster agent that sets `blocked_domains` keeps the coordinator's `allowed_domains` and blocks those hosts within it, and a roster agent that sets its own `allowed_domains` can reach only the hosts that both its list and the coordinator's list cover.
- If the combined allowlists have no domain in common, the tool stays available to that agent but every call fails with a `url_not_allowed` error stating that no domain is permitted, and the tool description tells the model so. Keep each roster agent's allowlist inside the coordinator's to avoid this.
- `max_content_tokens` and `user_location` are not combined: a thread uses the value from its own tool configuration if set, otherwise from the agent that called it, otherwise from the coordinator's current configuration.
- A `{"type": "self"}` roster entry has no web settings of its own and follows the coordinator's current settings.
- The grader in [outcome-driven sessions](managed-agents-define-outcomes.md) runs without `web_search` and `web_fetch`, regardless of these settings.
- You can change the lists on an idle session by [updating its tools](managed-agents-session-operations.md#updating-the-agent-configuration). The new lists apply to the rest of the session; in a multiagent session, every thread applies them from its next turn, while a roster agent's own lists stay as its agent definition set them when the session was created.
#### Differences from the Messages API tools
These settings use the same `allowed_domains` and `blocked_domains` vocabulary as [domain filtering](../Agents-Tools/agents-and-tools-tool-use-server-tools.md#domain-filtering) on the Messages API server tools, with the following differences on Managed Agents:
- Each list is capped at 64 domains.
- Domains listed for `web_fetch` cannot include a path.
- Domains must be ASCII: use the `xn--` (Punycode) form for internationalized domain names. The Messages API accepts Unicode entries, though it recommends against them.
- `max_uses`, `citations`, and `cache_control` are not available on the toolset.
## Custom tools
In addition to built-in tools, you can define custom tools. Custom tools are analogous to [user-defined client tools](../Agents-Tools/agents-and-tools-tool-use-how-tool-use-works.md#user-defined-tools-client-executed) in the Messages API.
Each custom tool defines a contract: you specify what operations are available and what they return, and Claude determines when and how to call them. The model never executes anything on its own. It emits a structured request, your code runs the operation, and the result flows back into the conversation. See [Session event stream](managed-agents-events-and-streaming.md#handling-custom-tool-calls) for how to receive custom tool calls and return results during a session.
If your sessions run in a self-hosted sandbox, the environment worker can [serve custom tools from your sandbox](managed-agents-self-hosted-sandboxes-custom-tools.md), including tools that wrap an MCP server inside your network.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
ant apply agent.md
```
agent.md
```python
---
name: Weather Agent
model: claude-opus-5-5
tools:
- type: agent_toolset_20260401
- type: custom
name: get_weather
description: Get current weather for a location
input_schema:
type: object
properties:
location:
type: string
description: City name
required:
- location
---
```
Once you've defined custom tools on the agent, the agent invokes them during a session.
### Best practices for custom tool definitions
- **Provide extremely detailed descriptions.** This is by far the most important factor in tool performance. Your descriptions should explain what the tool does and when to use it (and when not to). Explain what each parameter means and how it affects the tool's behavior. Call out any important caveats or limitations. The more context you can give Claude about your tools, the better it is at determining when and how to use them. Aim for three to four sentences for each tool description, more if the tool is complex.
- **Consolidate related operations into fewer tools.** Rather than creating a separate tool for every action (`create_pr`, `review_pr`, `merge_pr`), group them into a single tool with an `action` parameter. Fewer, more capable tools reduce selection ambiguity and make your tool surface easier for Claude to navigate.
- **Use meaningful namespacing in tool names.** When your tools span multiple services or resources, prefix names with the resource (for example, `db_query` or `storage_read`). This makes tool selection unambiguous as your library grows.
- **Design tool responses to return only high-signal information.** Return semantic, stable identifiers (for example, slugs or UUIDs) rather than opaque internal references, and include only the fields Claude needs to determine its next step. Bloated responses waste context and make it harder for Claude to extract what matters.
## Next steps
[MCP connector](managed-agents-mcp-connector.md)
Connect MCP servers to your agents for access to external tools and data sources.
[Permission policies](managed-agents-permission-policies.md)
Control when agent and MCP tools execute.
[Session event stream](managed-agents-events-and-streaming.md)
Send events, stream responses, and interrupt or redirect your session mid-execution.
---
## Authenticate with vaults - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/vaults
Path: 04-API-Reference/Other/managed-agents-vaults.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fvaults)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Authenticate with vaults
Copy page
Register per-user credentials when creating sessions.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Vaults and credentials are authentication primitives that let you register credentials for third-party services once and reference them by ID at session creation. This means you don't need to run your own secret store, transmit tokens on every call, or lose track of which end user an agent acted on behalf of.
The vault reference is a per-session parameter, so you can manage your product at the `agent` resource granularity and your users at the `session` resource granularity.
## Create a vault
Vaults and credentials are workspace-scoped, meaning any API key with workspace access can reference them when creating a session. To revoke access, delete the vault or credential.
A vault is the collection of `credentials` associated with an end user. Give it a `display_name` and optionally tag it with `metadata` so you can map it back to your own user records.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
vault = client.beta.vaults.create(
display_name="Alice",
metadata={"external_user_id": "usr_abc123"},
)
print(vault.id) # "vlt_01ABC..."
```
The response is the full vault record:
```python
{
"type": "vault",
"id": "vlt_01ABC...",
"display_name": "Alice",
"metadata": { "external_user_id": "usr_abc123" },
"created_at": "2026-03-18T10:00:00Z",
"updated_at": "2026-03-18T10:00:00Z",
"archived_at": null
}
```
## Add a credential
Two credential categories are supported:
- **MCP credentials** (`mcp_oauth`, `static_bearer`): each credential is keyed by an `mcp_server_url`. When the agent connects to a server at that URL at session runtime, the token is injected automatically.
- **Environment variables** (`environment_variable`): each credential is keyed by a `secret_name` (the environment variable name) and stored in the sandbox as an opaque placeholder. When the agent initiates an outbound request, the opaque placeholder is substituted with the real secret at egress. The agent never sees the secret value. Use this for any service that authenticates through an environment variable, such as CLIs, SDKs, or direct API calls.
The actual credential values you supply (`token`, `access_token`, `refresh_token`, `client_secret`, `secret_value`) are treated as sensitive, write-only fields and never returned in API responses.
Environment variable credentials (`environment_variable`) are not yet supported with [self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md).
MCP OAuth
MCP static bearer
Environment variable
Use `mcp_oauth` when the MCP server uses OAuth 2.0. If you supply a `refresh` block, Anthropic refreshes the access token on your behalf when it expires.
The `refresh.token_endpoint_auth.type` field indicates how to authenticate the refresh call:
- `none`: public client
- `client_secret_basic`: HTTP Basic authentication with the client secret
- `client_secret_post`: client secret in the POST body
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
credential = client.beta.vaults.credentials.create(
vault_id=vault.id,
display_name="Alice's Slack",
auth={
"type": "mcp_oauth",
"mcp_server_url": "https://mcp.slack.com/mcp",
"access_token": "xoxp-...",
"expires_at": "2099-12-31T23:59:59Z",
"refresh": {
"token_endpoint": "https://slack.com/api/oauth.v2.user.access",
"client_id": "1234567890.0987654321",
"scope": "channels:read chat:write",
"refresh_token": "xoxe-1-...",
"token_endpoint_auth": {"type": "client_secret_post", "client_secret": "abc123..."},
},
},
)
```
Set `refresh.token_endpoint` to the token endpoint of the OAuth flow that issued the refresh token, because Anthropic sends every refresh request to that URL and the field can't be changed after the credential is created.
Credentials are stored as provided and are not validated until session runtime. An invalid credential surfaces as an authentication or downstream error during the session, which is emitted but does not block the session from continuing.
Constraints:
- **Unique key per vault.** `mcp_server_url` (MCP credentials) and `secret_name` (environment variable credentials) must be unique among active credentials in a vault. Creating a duplicate returns a 409.
- **Keys are immutable.** To change `mcp_server_url` or `secret_name`, archive the credential and create a new one.
- **Maximum 20 credentials per vault.**
## Reference the vault at session creation
Pass `vault_ids` when creating a session:
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
vault_ids=[vault.id],
title="Alice's Slack digest",
)
```
Runtime behavior:
- When no MCP credential matches by `mcp_server_url`, the connection is attempted unauthenticated and will error if the server requires authentication.
- When multiple vaults contain a matching credential, the first vault with a match wins.
- In [multiagent sessions](managed-agents-multiagent-orchestration.md), vault credentials apply to every thread. An agent whose own definition declares the matching MCP server authenticates with these credentials. See [Connect agents to MCP servers](managed-agents-multiagent-orchestration.md#connect-agents-to-mcp-servers).
## Rotate a credential
Secret values, `display_name`, and (on environment variable credentials) `injection_location` can be updated. `injection_location` updates merge per field, as described in the Environment variable tab of [Add a credential](#add-a-credential). For a running session, an `injection_location` update propagates the same way as a secret rotation: the session's credentials are re-resolved without a restart, as described in [Credential lifecycle](#credential-lifecycle), and the updated locations apply to the session's subsequent outbound requests. Structural fields (`mcp_server_url`, `secret_name`, `token_endpoint`, `client_id`) are locked after creation. To change them, archive the credential and create a new one.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
client.beta.vaults.credentials.update(
credential.id,
vault_id=vault.id,
auth={
"type": "mcp_oauth",
"access_token": "xoxp-new-...",
"expires_at": "2099-12-31T23:59:59Z",
"refresh": {"refresh_token": "xoxe-1-new-..."},
},
)
```
## Credential lifecycle
Credentials are re-resolved periodically, both during a session and during the vault lifecycle. This ensures that credential rotation, archival, or deletion propagates to running sessions without a restart.
To be notified if a credential is archived, deleted, or fails to refresh, you can subscribe to the vault and credential [webhooks](managed-agents-webhooks.md) associated with those lifecycle changes.
| Event | Trigger |
|-----------------------------------|----------------------------------------------------------------------------------------------------------------------|
| `vault.archived` | Vault archived. A `vault_credential.archived` event is also emitted for each underlying credential. |
| `vault.deleted` | Vault deleted. A `vault_credential.deleted` event is also emitted for each underlying credential. |
| `vault_credential.archived` | Credential archived, either directly or as a result of vault archival. |
| `vault_credential.deleted` | Credential deleted, either directly or as a result of vault deletion. |
| `vault_credential.refresh_failed` | An `mcp_oauth` credential cannot be refreshed (invalid refresh token, or irrecoverable error from the OAuth server). |
This is a non-exhaustive list of webhooks; see [Subscribe to webhooks](managed-agents-webhooks.md) for the complete list.
For `mcp_oauth` credentials, re-resolution also refreshes the access token if it has expired. If the refresh fails, a `vault_credential.refresh_failed` event is emitted.
### Diagnose an OAuth refresh failure
To diagnose why a refresh failed, call `POST /v1/vaults/{vault_id}/credentials/{credential_id}/mcp_oauth_validate` (or `client.beta.vaults.credentials.mcp_oauth_validate(...)` in the SDK). This lets you decide how to handle the failure; the right action depends on the error type.
The top-level `status` tells you what to do next:
- `valid`: the token works; no action needed.
- `invalid`: the grant is gone or the OAuth server rejected the refresh with a 4xx. Prompt the end user to re-authorize.
- `unknown`: a transient error (5xx, 429, or network failure). Wait and retry.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
validation = client.beta.vaults.credentials.mcp_oauth_validate(
credential.id,
vault_id=vault.id,
)
print(validation.status) # "valid", "invalid", or "unknown"
```
The response is a `vault_credential_validation` object. `mcp_probe` includes the failed MCP handshake step; `refresh` includes the outcome of the attempted refresh.
```python
{
"type": "vault_credential_validation",
"credential_id": "vcrd_01ABC...",
"vault_id": "vlt_01XYZ...",
"validated_at": "2026-04-29T17:12:00Z",
"has_refresh_token": false,
"status": "invalid",
"mcp_probe": {
"method": "initialize",
"http_response": {
"status_code": 401,
"content_type": "application/json",
"body": "{\"error\":\"invalid_token\"}",
"body_truncated": false
}
},
"refresh": {
"status": "no_refresh_token",
"http_response": null
}
}
```
## Other operations
- **List vaults or credentials:** Paginated, newest first. Archived records are excluded by default (pass `include_archived=true` to include them).
- **Archive a vault:** `POST /v1/vaults/{id}/archive`. Cascades to all credentials. Secrets are purged; records are retained for auditing. Future sessions referencing this vault fail; running sessions continue.
- **Archive a credential:** `POST /v1/vaults/{id}/credentials/{cred_id}/archive`. Purges the secret payload; the credential key (`mcp_server_url` or `secret_name`) remains visible and is freed for a replacement credential.
- **Delete a vault or credential:** Hard delete. The record is not retained. Use archive if you need an audit trail.
---
## Subscribe to webhooks - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/webhooks
Path: 04-API-Reference/Other/managed-agents-webhooks.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fwebhooks)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Delegate work to your agent
# Subscribe to webhooks
Copy page
Get notified when major events happen without polling.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
Sessions are long-running interactions. While most real-time interactions happen through the [SSE event stream](managed-agents-events-and-streaming.md), webhooks notify you of major state changes.
Webhook events return the event `type` and `id`, not the full object. When you receive a webhook event, you need to fetch the object directly with a `GET` call. This avoids delivering stale data on retries and keeps every delivery small.
## Supported event types
Session events
Vault events
Agent events
Deployment events
Deployment run events
Environment events
Memory store events
Some of these events are named differently from the matching events on the session's [event stream](managed-agents-events-and-streaming.md). For example, the stream's `session.status_idle` and `session.status_running` correspond to the `session.status_idled` and `session.status_run_started` webhook events.
| Event | Trigger |
|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `session.status_run_started` | Agent execution started. This triggers at every session status transition to `running`. |
| `session.status_idled` | Agent awaiting input, for example, a tool permission approval or a new user message. |
| `session.budget_reached` | The session reached its [budget](managed-agents-budgets.md) and paused. Fires at most once for each budget value you set; changing the budget arms it again. |
| `session.status_rescheduled` | A transient error occurred and the session is retrying automatically. |
| `session.status_terminated` | The session terminated, either because of an unrecoverable error or because it was archived. |
| `session.thread_created` | New [multiagent thread](managed-agents-multiagent-orchestration.md) opened: an additional agent called by the coordinator is starting work, or the session's [advisor](managed-agents-multiagent-orchestration.md#give-the-session-an-advisor) is being consulted. |
| `session.thread_idled` | An agent in a [multiagent interaction](managed-agents-multiagent-orchestration.md) is waiting for input. |
| `session.thread_terminated` | A [multiagent thread](managed-agents-multiagent-orchestration.md) terminated, either because the thread was archived or because it exhausted its retries. A coordinator-spawned child that finishes its work goes `idle`, not `terminated` (an advisor thread terminates once its consultation completes). Fires for child threads only; the primary thread's end, including archiving the whole session, surfaces only as `session.status_terminated`. |
| `session.outcome_evaluation_ended` | [Outcome evaluation](managed-agents-define-outcomes.md) for a single iteration completed. |
| `session.updated` | Session properties changed (for example, its name or configuration was updated). |
| `session.deleted` | Session permanently deleted. There is no object left to fetch, so treat the event itself as final. |
## Register an endpoint
Visit **Manage \> Webhooks** in the [Claude Console](https://platform.claude.com/settings/workspaces/default/webhooks).
A webhook endpoint consists of:
- **URL:** Must be HTTPS on port 443 with a publicly resolvable hostname.
- **Event types:** The list of `data.type` values this endpoint receives. An endpoint only receives events it's subscribed to.
- **Signing secret:** A 32-byte `whsec_`-prefixed secret generated at creation. It's shown only once, so store it securely to verify webhook deliveries.
## Verify the signature
Every delivery carries the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers. Use the SDK's `unwrap()` helper to verify the signature and parse the event in one step. It throws if the signature is invalid or the payload is more than 5 minutes old.
Set `ANTHROPIC_WEBHOOK_SIGNING_KEY` to the `whsec_`-prefixed secret shown at endpoint creation.
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
from flask import Flask, request
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_WEBHOOK_SIGNING_KEY from env
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
try:
# unwrap() raises if the signature is invalid or the payload is stale
event = client.beta.webhooks.unwrap(
request.get_data(as_text=True),
headers=dict(request.headers),
)
except Exception:
return "invalid signature", 400
if event.data.type == "session.status_idled":
print("session idled:", event.data.id)
# handle other event types
return "", 200
```
## Handle an event
Parse the body, switch on `data.type`, and fetch the resource by ID. Return any `2xx` to acknowledge. Any other response counts against the endpoint: a `3xx` disables it immediately (redirects are never followed), while other failures are retried; see [Delivery behavior](#delivery-behavior) for the retry and auto-disable rules.
Every event payload has the same structure, including the event type, identifier, and the timestamp of when the event occurred.
```python
{
"type": "event",
"id": "whe_9d5c1f7e...",
"created_at": "2026-03-18T14:05:22Z",
"data": {
"type": "session.status_idled",
"id": "sesn_01XYZ...",
"organization_id": "8a3d2f1e-...",
"workspace_id": "c7b0e4d9-..."
}
}
```
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
if event.data.type == "session.status_idled":
session = client.beta.sessions.retrieve(event.data.id)
notify_user(session)
return "", 204
```
The top-level `event.id` is unique per event, not per delivery. If you receive the same `event.id` twice, it's a retry and you can discard it.
## Delivery behavior
- **Duplicates:** An endpoint can receive the same event more than once, and every attempt delivers the same top-level `event.id` (the same value as the `webhook-id` header). Deduplicate on it.
- **Subscription scope:** An event is delivered only to endpoints subscribed to its type at the moment it's emitted. An event emitted while no endpoint is subscribed to its type is never delivered, and subscribing later doesn't backfill it, so subscribe to an event type before you need it.
- **Ordering is not guaranteed.** Events aren't delivered in the order they occurred: `session.status_idled` might arrive before `session.outcome_evaluation_ended` even if the outcome was produced first, and a `.deleted` event can arrive before the `.archived` event for the same resource. Drive your state from the resource you fetch, not from the order events arrive in.
- **Retries:** For each endpoint and event, Anthropic makes up to three delivery attempts (a response that triggers auto-disable, described later in this section, is never retried) with jittered exponential backoff between 5 and 120 seconds. Every attempt delivers the same `event.id`. After the last attempt fails, the event is dropped: it isn't queued for later delivery and there's no signal that it was lost. Webhooks aren't a durable log, so if you need to observe every transition, reconcile by listing or fetching the resource through the API.
- **Timestamps:** The `webhook-timestamp` header is stamped when a delivery attempt is signed and is regenerated on every retry, so retries aren't rejected by the SDK's freshness check. It's the clock for the delivery attempt, not for the event: use the event payload's `created_at` for when the event occurred.
- **Auto-disable:** An endpoint is automatically set to `disabled` with a machine-readable `disabled_reason` in three cases:
- The endpoint returns a `3xx` response. Redirects are never followed; this disables the endpoint immediately, on the first attempt, with the reason `auto-disabled: endpoint URL returned a redirect (3xx)`. If your endpoint moves, update the URL in Console and re-enable the endpoint.
- The endpoint's URL resolves to a non-public IP address when Anthropic connects. This disables the endpoint immediately, with the reason `auto-disabled: endpoint URL resolved to an invalid address`.
- Deliveries to the endpoint fail continuously for a sustained period, with the reason `auto-disabled after sustained delivery failures`. The trigger is how long the endpoint has been failing without interruption, not a delivery count. A single `2xx` resets the window, so one flaky event can't disable the endpoint.
All three are reversible: re-enable the endpoint in Console after you resolve the issue. Events emitted while the endpoint was disabled aren't replayed.
---
## Workflow runs - Claude Platform Docs
Source: https://platform.claude.com/docs/en/managed-agents/workflow-runs
Path: 04-API-Reference/Other/managed-agents-workflow-runs.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fmanaged-agents%2Fworkflow-runs)
SearchCtrlK
First steps
[Overview](managed-agents-overview.md)[Quickstart](managed-agents-quickstart.md)[Build in Console](managed-agents-onboarding.md)[Migration](managed-agents-migration.md)
Define your agent
[Agent setup](managed-agents-agent-setup.md)[Tools](managed-agents-tools.md)[Web tool restrictions](managed-agents-tools-web-restrictions.md)[MCP connector](managed-agents-mcp-connector.md)[Permission policies](managed-agents-permission-policies.md)[Agent Skills](managed-agents-skills.md)
Configure agent environment
[Cloud environment setup](managed-agents-environments.md)[Cloud sandbox reference](managed-agents-cloud-sandboxes-reference.md)
[Self-hosted sandboxes](managed-agents-self-hosted-sandboxes.md)
Delegate work to your agent
[Start a session](managed-agents-sessions.md)[Session operations](managed-agents-session-operations.md)[Session event stream](managed-agents-events-and-streaming.md)[Event deltas](managed-agents-event-deltas.md)[Inspect and track usage](managed-agents-session-observability.md)[Session budgets](managed-agents-budgets.md)[Subscribe to webhooks](managed-agents-webhooks.md)[Define outcomes](managed-agents-define-outcomes.md)[Authenticate with vaults](managed-agents-vaults.md)
Manage agent context
[Access GitHub](managed-agents-github.md)[Attach and download files](managed-agents-files.md)
Build persistent memory
Advanced orchestration
[Multiagent orchestration](managed-agents-multiagent-orchestration.md)[Session threads](managed-agents-session-threads.md)[Workflow runs](managed-agents-workflow-runs.md)[Scheduled deployments](managed-agents-scheduled-deployments.md)
Reference
[Managed Agents reference](managed-agents-reference.md)
Working with files
[Files API](../Guides/build-with-claude-files.md)[PDF support](../Guides/build-with-claude-pdf-support.md)
[Images and vision](../Guides/build-with-claude-vision.md)
Skills
[Overview](../Agents-Tools/agents-and-tools-agent-skills-overview.md)[Best practices](../Agents-Tools/agents-and-tools-agent-skills-best-practices.md)[Skills for enterprise](../Agents-Tools/agents-and-tools-agent-skills-enterprise.md)
MCP
[Remote MCP servers](../Agents-Tools/agents-and-tools-remote-mcp-servers.md)
[MCP tunnels](../Agents-Tools/agents-and-tools-mcp-tunnels-overview.md)
Claude on cloud platforms
[Claude Platform on AWS](../Guides/build-with-claude-claude-platform-on-aws.md)
[Console](usage-limits.md)
[Managed Agents](managed-agents-overview.md)Advanced orchestration
# Workflow runs
Copy page
Follow an agent's workflow runs: their states and events, when the work is done, what a run blocks, budgets, and limits.
Copy page
[Managed Agents](managed-agents-overview.md)
[Beta](../Guides/build-with-claude-overview.md#feature-availability)
[Beta header](../Endpoints/beta-headers.md)
managed-agents-2026-04-01
A **workflow** is a program that an agent writes to run many agents and combine what they return. A **workflow run** is the execution of one workflow. **[Dynamic workflows](managed-agents-multiagent-orchestration.md#dynamic-workflows)** is the feature that lets an agent write workflows and start runs. You turn it on or off with the `workflows` setting in the agent's `multiagent` block.
The server runs a workflow in the background. Its agents work in [session threads](managed-agents-session-threads.md) that the server creates as the workflow needs them. You follow runs on the session's [event stream](managed-agents-events-and-streaming.md). Only the agent starts a run. No event you send ends one; archiving the session can.
## How dynamic workflows work
The agent that the session runs writes each workflow for the work that you describe. A workflow is a program: it runs other agents, collects what each one returns, and combines the results. That way, the agent can take on a task that is too large for one conversation, such as a review of hundreds of documents. During a run, the agent can keep working or end its turn, and it can check on the run.
The diagram shows one example. Each workflow that the agent writes has its own phases and agents. A run has these layers:
- **Workflow run:** The server runs the workflow in the background, as one workflow run. A session can have several runs open at the same time.
- **Phases:** A workflow can divide its work into phases. A phase is a named stage of the run, such as "Read the contracts". You follow a run's progress by its phase events.
- **Agent threads:** In a phase, the program runs agents. Each agent works in its own [session thread](managed-agents-session-threads.md), on a prompt that the program wrote. An agent in a run can be an inline agent, which the program defines itself, or a predefined agent, which you list in [`workflows.predefined_agents`](managed-agents-multiagent-orchestration.md#predefined-and-inline-agents). For what each thread shows, see [A run's threads](#a-runs-threads).
The program can do the following:
- **Run agents at the same time:** The program can run many agents at the same time, which is called fanning out. In the diagram, three agents read contracts in the first phase.
- **Pass results from one agent to another:** Each agent returns its result to the program. The program can pass that result on to another agent. In the diagram, the agent in the second phase works with what the first three returned. A run's agents also work with the same files, in the session's sandbox.
- **Take the next step by itself:** An agent's result goes to the program, not to the agent that the session runs. The program determines which agents run next, and it writes their prompts.
- **Repeat and choose:** Inside a phase, the program can repeat work and choose its next step from what an agent returned. For example, it can have a draft revised until a review passes or a set number of rounds is used up. In the diagram, the program can repeat a step inside the second phase.
- **Handle a failed agent:** When one of its agents fails, the program can handle the failure or let it end the run.
When the run ends, the agent that the session runs gets a turn to read what the run did. It can then answer you or start another run. [Run events](#run-events) lists the cases where that turn comes later or doesn't come.
You can guide how a run does the work, for example how it splits the work and what it does when an agent fails. See [Tell the agent when to use a run](managed-agents-multiagent-orchestration.md#tell-the-agent-when-to-use-a-run).
## How a run moves through its states
A run starts as running or as idle. Reaching the budget, for example, pauses a running run, which makes it idle; raising or removing the budget then makes it run again, unless an interrupt paused it too. A running run ends when its workflow finishes, the agent stops it, it fails, its lifetime passes, or the session is archived. An idle run can end too, for example when the agent stops it or the session is archived.
A run is **open** from its `workflow_run.created` event until its `workflow_run.status_ended` event, whether it's running or idle. A run is idle while it's paused, for example at the session's budget. A run's lifetime is 24 hours by default. The agent can set a shorter lifetime when it starts the run. Time that a run spends waiting on your client counts toward that lifetime. A pause doesn't stop a run's lifetime from passing, so a run that stays paused can end with `timeout_error`. The following events report a run's start, its phases, and its end. A pause at the budget sends one too. A pause after an interrupt might send none. Every `workflow_run.*` event includes `workflow_run_id`, which is `null` only on a `workflow_run.error` when no run was created.
## Run events
Run events arrive on the session's event stream, which is the primary thread's stream, and listing the session's events returns them too. Run events don't trigger [webhooks](managed-agents-webhooks.md). Status events from the run's threads arrive on the same stream. Each names its thread in `session_thread_id`, and a run's threads are the ones whose `session.thread_created` event had the run's `workflow_run_id`.
| Event | When it arrives | What to do |
|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `workflow_run.created` | The agent started a run. Includes `workflow_run_id` (`wrun_…`), the run's `name` and `description`, and `phases`, the phases the workflow declares, each with an `id`, a `name`, and a `description`. A `description` is `null` when the workflow gives none. `phases` is always present and can be empty. The run's and phases' `name` and `description` are text the model wrote, so they can repeat words from your request. A run's `name` can also be one the server assigned. | Track the run as open. Show its `name`, and progress against `phases`. |
| `workflow_run.status_running` | When the run starts to execute, which can be a while after `created`, and each time it resumes after a pause at the budget. A resume after an interrupt might not send it. A run that starts idle might get `workflow_run.status_idle` first. | Show the run as running. |
| `workflow_run.status_idle` | The run was paused, for example at the session's budget. The event doesn't say why. A pause after an interrupt might not send it. | To continue, see [Budgets and limits](#budgets-and-limits) or [Interrupt a session with runs open](#interrupt-a-session-with-runs-open). |
| `workflow_run.phase_started`, `workflow_run.phase_ended` | The workflow entered or left a phase, or the run's end closed a phase that was still open. The end event doesn't say whether the phase's work finished. Both include `workflow_run_phase_id`. The end also has `phase_started_id`, the `id` of the start event it closes. Neither has the phase's name: look it up by `workflow_run_phase_id` in the `phases` of `workflow_run.created`. | Update progress. Phases run one at a time, in the order of `phases`, each at most once, but the API doesn't guarantee it. Match a phase's end to its start by `phase_started_id`. Handle more than one open phase, a phase that isn't in `phases`, and a listed phase that never starts, even in a run that completes. Every phase that starts also ends, before the run's `workflow_run.status_ended`. |
| `workflow_run.status_ended` | The run ended. Always the last of the run's `workflow_run.*` events. Includes `result`. | Read `result` (next table). The agent then gets a turn to read how the run ended. At the budget, or while the primary thread waits on your client, that turn comes later. After an interrupt, that turn might not come: send a `user.message`, or read `result` yourself. After an archive or termination, it doesn't come. |
| `workflow_run.error` | The server reports an error of a run, or a start that it refused. A run that ends in `error` gets this event, with the same error, before its `workflow_run.status_ended`. Includes `error`: a `type` and a `message` that is safe to log. `workflow_run_id` is `null` when no run was created. | Log it, and don't take it as the run's end. If `workflow_run_id` is `null`, no run started. Otherwise keep tracking the run until its `workflow_run.status_ended`. |
| `result` | Meaning |
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `{"type": "completed"}` | The workflow finished running. The result doesn't say whether the work passed. A run can end `completed` even though work on its threads failed, or a thread couldn't be created. To find failed work, read the events of each of the [run's threads](#a-runs-threads). |
| `{"type": "stopped"}` | The agent stopped the run, or the session was archived. The event doesn't say which, and later releases might add other causes. |
| `error` with `timeout_error` | The run reached its lifetime: 24 hours by default, or the one the agent set. |
| `error` with `program_error` | The workflow failed. Its code failed, or it broke a rule for workflows, other than a limit. Or one of the run's threads failed, or couldn't be created, and the workflow let that end the run. |
| `error` with `thread_limit_error` | The run went over its [limit on the agents that a workflow starts](#budgets-and-limits). |
| `error` with `unknown_error` | The server couldn't continue the run, or the run went over one of the server's other limits on workflows. |
An error result looks like `{"type": "error", "error": {"type": "timeout_error", "message": "..."}}`, where `message` is safe to log. Treat an unrecognized `result.type` as a run that ended some other way, and an unrecognized `error.type` as an error. When something the session depends on fails, such as the model, an MCP server, credentials, or billing, the failing thread's stream gets a `session.error`. That doesn't end a run by itself. But if it makes one of the run's threads fail, and the workflow lets that end the run, the run ends with `program_error`.
For example, you ask the [contract-review agent](managed-agents-multiagent-orchestration.md#turn-on-dynamic-workflows) which of 300 contracts have a change-of-control clause, and the agent starts a run:
1. `workflow_run.created` names the run "Find change-of-control clauses" and lists the phases "Read the contracts" and "Reconcile the findings" in `phases`. Then `workflow_run.status_running` follows.
2. Phase events mark each phase, and each thread the run creates sends `session.thread_created` with the run's `workflow_run_id`.
3. `workflow_run.status_ended` arrives with `result: {"type": "completed"}`.
4. The agent answers, "41 of the 300 contracts have one," and `session.status_idle` arrives with `end_turn`.
The run's first event lists its phases:
```python
{
"type": "workflow_run.created",
"id": "sevt_01abc...",
"workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
"name": "Find change-of-control clauses",
"description": "Reads each contract and lists those that have the clause.",
"phases": [
{
"id": "wrph_01Kd3a1f3",
"name": "Read the contracts",
"description": "Reads each contract for the clause."
},
{ "id": "wrph_01Kd3b7c9", "name": "Reconcile the findings", "description": null }
],
"processed_at": "2026-10-09T14:01:45Z"
}
```
Each phase event names its phase by `workflow_run_phase_id`. That is an `id` in `phases`, but the API doesn't guarantee it:
```python
{
"type": "workflow_run.phase_started",
"id": "sevt_01def...",
"workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
"workflow_run_phase_id": "wrph_01Kd3a1f3",
"processed_at": "2026-10-09T14:01:46Z"
}
```
The run's last event reports how it ended:
```python
{
"type": "workflow_run.status_ended",
"id": "sevt_01ghi...",
"workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
"result": { "type": "completed" },
"processed_at": "2026-10-09T14:09:12Z"
}
```
## A run's threads
Each agent in a run works in its own [session thread](managed-agents-session-threads.md), which the server creates as the workflow needs it. You can list, read, and stream a run's threads like any child thread, and answer their tool calls from the primary stream. To stop them, ask the agent to stop the run (see [Interrupt a session with runs open](#interrupt-a-session-with-runs-open)). You can't stop one by its ID, or archive one while its run is open.
- **Grouping:** A run's thread carries the run's `workflow_run_id`, as does the `session.thread_created` event that announces it. Other threads, and the `session.thread_created` events that announce them, have `workflow_run_id` set to `null`.
- **Agent:** `agent` shows the agent the thread runs. For an agent you listed in [`multiagent.workflows.predefined_agents`](managed-agents-multiagent-orchestration.md#turn-on-dynamic-workflows), `agent` has that agent's `id` and `version`, as on the thread of a subagent you listed. For an agent the workflow defines (an inline agent), `agent` has `type` `inline` and no `id` or `version`. It has the system prompt the workflow wrote, not the session agent's. It also has the name and description the workflow gave it; the server assigns a name if the workflow gave none. It uses the model of the session agent, the agent the session runs. Its tools, MCP servers, and skills are a subset of the session agent's. It gets all of them, but the API doesn't guarantee that. Its tools keep their permission policies.
- **What the threads share:** A run's threads work in the session's sandbox, so every thread works with the same files. That includes the files of a memory store the session mounts. An agent the workflow defines uses its MCP servers with the credentials the session resolves for them. Each thread has its own conversation history.
- **Events:** A run thread's `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle`, and `session.thread_status_terminated` events also arrive on the primary stream (see [Run events](#run-events)). Its message events stay on its own stream. Its thread webhooks are sent as for any child thread. For what the thread's own stream records, see [Session thread events](managed-agents-session-threads.md#session-thread-events).
- **Phases:** No event or field says which phase a thread works in, and threads of one run can have the same `agent_name`. Follow a run's progress by its phase events, and tell its threads apart by `session_thread_id`.
- **Thread limit:** A run's threads are exempt from the session's [child-thread limit](managed-agents-session-threads.md#primary-thread-and-session-threads).
- **Starting runs:** Only the agent on the session's primary thread starts runs. An agent working in a run's thread can't start a run of its own, so runs don't nest.
- **Archiving:** The server archives each thread no later than the end of its run. It can archive one sooner, once the thread returns its result or the run finishes with it. If the thread is still running or waiting on your client at that point, the server stops it first. An archived thread stays in the thread list, with status `terminated`. You don't need to archive a run's threads yourself. While the run is open, a request to archive one that the server hasn't archived yet returns 400 with `error.details.error_code: "workflow_run_open"`.
- **Visibility:** You don't see the workflow's code, but you can ask the agent for the workflow, as the tip after this list describes. You also don't see the tool calls the agent makes to start and manage runs, or the result each thread returns to the workflow.
You can ask the agent for the workflow that it wrote for your request. Wait until the run has ended and the session is `idle`. Then send a `user.message` that asks the agent to print the workflow that it started the run with, word for word, and not to start another run.
``` block
Print the workflow that you started the run with, word for word, in one code block. Do not start another run.
```
## Know when the work is done
While a run is running, expect the session to stay `running`, even while none of its threads is working. It goes `idle` with `requires_action` when no thread is working and a thread waits on your client. An idle by itself doesn't mean that the work is done. The work is done when both are true:
1. Every run you've seen created has its `workflow_run.status_ended`.
2. After that, a `session.status_idle` arrives with `stop_reason` `end_turn`, and your own request, such as an interrupt, didn't cause it. After you interrupt, count only an idle that comes after your next `user.message` or `user.define_outcome`.
- **Paused runs:** A paused run doesn't keep the session `running`, so the session can go idle while the run is still open. At the budget, for example, the session goes idle with `budget_reached`. The work isn't done until the run ends.
- **Another run:** The agent can start a new run when it reads a result, so check again.
- **Outcomes:** If you [defined an outcome](managed-agents-define-outcomes.md), no evaluation starts while a run is open, whether it's running or idle. The turn in which the agent reads the run's result can start one.
- **`retries_exhausted`:** The agent's turn failed on an error: retries ran out, or the error can't be retried, such as a billing failure. A run might still be running when this idle comes. If a run ended and the agent hasn't read its result yet, the server starts a new turn with no input from you. The session goes `running` again, so wait for the next idle. If the session stays idle, read the `session.error` that came before it and fix the cause. Then send a `user.message`, or read each run's `result` yourself.
## Follow a run
This sample follows a session from your message to the agent's answer. It opens the stream and sends the message. Then it does the following:
- **Tracks each run** from its `workflow_run.created` to its `workflow_run.status_ended`, and prints each phase as it starts.
- **Answers custom tool calls** when each `agent.custom_tool_use` arrives, because a run's thread can wait on your client while the session stays `running`. If your agent's tools [ask for confirmation](managed-agents-events-and-streaming.md#tool-confirmation), add a branch that answers each `agent.tool_use` or `agent.mcp_tool_use` whose `evaluated_permission` is `ask`. The sample has none, because a branch that allows every call would turn `always_ask` into always allow.
- **Stops** when the [work is done](#know-when-the-work-is-done): no run is open, and the session goes idle with `end_turn`. It also stops if the session terminates. On an idle with any other stop reason except `requires_action`, such as `budget_reached`, `retries_exhausted`, or `refusal`, it prints the reason and stops, so handle those in your own code. It stops on `retries_exhausted` even when the server is about to start a new turn by itself. It keeps waiting on `requires_action`, and on `end_turn` while a run is open.
cURL
CLI
Python
TypeScript
C#
Go
Java
PHP
Ruby
```python
open_runs: dict[str, str] = {} # workflow_run_id -> run name
phase_names: dict[tuple[str, str], str] = {} # (run ID, phase ID) -> phase name
# Open the stream first, then send the user message
with client.beta.sessions.events.stream(session_id) as stream:
client.beta.sessions.events.send(
session_id,
events=[
{
"type": "user.message",
"content": [
{
"type": "text",
"text": "Which contracts in /contracts have a change-of-control clause?",
},
],
},
],
)
for event in stream:
match event.type:
case "workflow_run.created":
open_runs[event.workflow_run_id] = event.name
for phase in event.phases:
phase_names[event.workflow_run_id, phase.id] = phase.name
print(f"Run started: {event.name}")
case "workflow_run.phase_started":
phase_id = event.workflow_run_phase_id
key = (event.workflow_run_id, phase_id)
print(f" Phase: {phase_names.get(key, phase_id)}")
case "workflow_run.status_ended":
name = open_runs.pop(event.workflow_run_id, event.workflow_run_id)
print(f"Run ended: {name} ({event.result.type})")
case "agent.custom_tool_use":
# Answer when the event arrives. A run's thread can wait on your
# client while the session stays running.
result = call_tool(event.name, event.input)
try:
client.beta.sessions.events.send(
session_id,
events=[
{
"type": "user.custom_tool_result",
"custom_tool_use_id": event.id,
"content": [{"type": "text", "text": result}],
},
],
)
except anthropic.BadRequestError as error:
# The server refuses a result that comes too late, after it
# archived the call's thread. Keep following the run.
print(f" Answer to {event.name} refused: {error.message}")
case "session.status_idle":
# Done when every run has ended and the agent has finished its turn
if not open_runs and event.stop_reason.type == "end_turn":
break
# An idle with requires_action waits on your client, so keep reading.
# On any other stop reason, print it and stop.
if event.stop_reason.type not in ("end_turn", "requires_action"):
print(f"Session idle: {event.stop_reason.type}")
break
case "session.status_terminated":
break
```
## Interrupt a session with runs open
Send [`user.interrupt`](managed-agents-events-and-streaming.md#interrupt-the-agent) with no `session_thread_id`, or with the primary thread's ID. It stops the agent's turn. It ends no run. The session's runs might pause or keep running, and their events might not show which. A paused run's lifetime keeps passing, so the run can end with `timeout_error` while it's paused.
- **Waiting tool calls:** After the interrupt, a run thread's tool call might still wait on your client. Answer each one. To cancel a call that asks for confirmation, deny it. To cancel a custom tool call, send a result with `is_error` set to `true` and a text in `content` that says why. While the session is `idle` with `requires_action`, a `user.message` returns 400, so answer the calls first.
- **To stop the runs:** Send a `user.message` asking the agent to stop its runs. A stopped run ends with `result` `{"type": "stopped"}`. While the session is `idle` with `budget_reached`, a `user.message` returns 400 until you raise or remove the budget. Raising or removing it also resumes the runs that the budget paused, unless the interrupt also paused them.
- **To continue:** Send a `user.message` asking the agent to continue its runs. After an interrupt, the runs might wait for this message. If the session is `idle` with `budget_reached`, raise or remove the budget first.
- **Run results:** A run that ends after the interrupt still sends `workflow_run.status_ended`.
## While a run is open
| Request | While a run is open | What to do |
|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Archive or delete the session | Might return 400 while a run is open, whatever the session's status. The error's `error.details.error_code` can be `"workflow_run_open"`. Might also succeed. | Ask the agent to stop its runs, or wait until each run has ended. A paused run ends by itself only when its lifetime passes. Then send the request once the session is `idle`. An archive that succeeds ends each open run with `{"type": "stopped"}`. After an archive, a run's `workflow_run.status_ended`, and the `workflow_run.phase_ended` of a phase that was still open, don't arrive on the stream. List the session's events to read them. After a delete that succeeds, no `workflow_run` event reports the end of the session's runs. |
| Archive one of a run's threads | Returns 400 with `error.details.error_code: "workflow_run_open"` while the run is open, running or idle, unless the server has already archived the thread. | Nothing. The server archives a run's threads. |
| Update the session's `agent` | Returns 400 with `error.details.error_code: "workflow_run_open"` while any run is open, even a paused one. Updating the underlying agent is still accepted, and the session keeps its own copy. A request that also sends other fields, such as `budget`, is rejected whole. | Wait until every run has its `workflow_run.status_ended`, or ask the agent to stop its runs. |
| Answer a tool call or tool confirmation from a run's thread | Allowed. It arrives on the primary stream, and its `session_thread_id` names the thread. | Answer as soon as the event arrives, passing the event's `id` as `tool_use_id` or `custom_tool_use_id`. Don't wait for `session.status_idle`: the session can stay `running` while the run's other threads work. Once the server has archived the thread, a tool result for one of its calls has no effect, and it can return 400. When a tool result returns 400, find the call's thread in the thread list. If its status is `terminated`, the result came too late, so drop it. Send each tool result in a request of its own, because the server refuses a whole request when it refuses one of its events. A tool confirmation that comes too late returns 200, which doesn't mean that the tool ran. |
### Rebuild run state after you reconnect
Rebuild each run's state from the session's events. The stream doesn't replay what you missed: a new connection delivers only events emitted after it opened. So list the events with a `types` filter, one `types[]` entry for each event type, as in [List past events](managed-agents-events-and-streaming.md#list-past-events). Pass each response's `next_page` as `page` until `next_page` is `null` or missing. `workflow_run.created`, `workflow_run.status_running`, `workflow_run.status_idle`, and `workflow_run.status_ended` give each run's state, except that a run paused after an interrupt might still show as running. `workflow_run.phase_started` and `workflow_run.phase_ended` rebuild progress. A run with no status event yet hasn't started to execute. No endpoint lists runs.
## Budgets and limits
A run's model requests count toward the [session's budget](managed-agents-budgets.md). A run has no price of its own. The tokens its agents use are billed like the session's other tokens, at each model's rates. For all of a session's charges, see [Claude Managed Agents pricing](../../17-Billing-Plans/about-claude-pricing.md#claude-managed-agents-pricing).
- **One run's usage:** List the session's threads and add up the token counts in `usage` of the threads with the run's `workflow_run_id`. The list includes archived threads, whose status is `terminated`, so a finished run's threads are counted. Pass each response's `next_page` as `page` until `next_page` is `null` or missing, and skip a thread whose `usage` is `null`. If you add up the threads' `list_cost` instead, the total leaves out session runtime, and each figure is rounded separately.
- **At the budget:** Every open run pauses, and the session reports `idle` with `budget_reached`, or `requires_action` if a tool call is also waiting. Each thread finishes the model request it already started, so a run can pass the budget by one request for each working thread. Raising or removing the budget resumes the runs it paused, unless an interrupt also paused them. If the session's usage includes a model with no list price, only removing the budget does; see [Models without a list price](managed-agents-budgets.md#models-without-a-list-price).
| Limit | Value | At the limit |
|----------------------------------------------------|-----------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Threads working at once in one run | 64 | The run creates no more until one finishes. The API doesn't guarantee this number, so it can change. |
| Agents a workflow starts over the run's whole life | 1,000 | When the workflow asks for more, the server doesn't start another agent, and the run ends with `thread_limit_error`. The server can run a failed agent again on a new thread, so a run might have more than 1,000 threads. |
| Run lifetime | 24 hours by default, or the lifetime the agent sets | The run ends with `timeout_error`. No event says what lifetime the agent set. |
| Runs open at once in a session | 10 by default | The server refuses to start another run. The agent's tool call gets an error, and you get a `workflow_run.error` whose `error.type` is `max_workflow_runs_error`. Idle runs count toward the limit. |
The server shortens a run's or phase's `name` to 64 characters and its `description` to 256. The server has other limits on workflows, and rules for them, that aren't listed here. What you see depends on when the server finds the problem:
| What happens | What you see |
|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| The workflow is over one of the other limits when the agent starts the run | The start is refused. You get a `workflow_run.error`, and no run. |
| The run goes over one of the other limits later | You get a `workflow_run.error`, and then the run can end with `unknown_error`. |
| The server finds after the start that the workflow breaks a rule for workflows, other than a limit | You get a `workflow_run.error`, and the run can then end with `program_error`. |
A session can start any number of runs over its life.
### Rate limits
A run's work counts toward rate limits that your organization already has.
| What | Counts toward | What to do |
|---------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
| Your client's requests to retrieve or list the session, its threads, and their events | The read limit for [Managed Agents endpoints](managed-agents-reference.md#rate-limits) | Follow a run on the session's event stream instead of polling. |
| Model requests from a run's threads | Your [Messages API rate limits](../Endpoints/rate-limits.md) for the model each thread uses, along with your other traffic | Leave room for a run in those limits, or [request higher limits](../Endpoints/rate-limits.md#requesting-higher-limits). |
When a model request from one of a run's threads is rate limited, or the model is overloaded, the thread's own stream can get a `session.error` of type `model_rate_limited_error` or `model_overloaded_error`:
- If its `retry_status.type` is `retrying`, the server is retrying the request, and the thread is still working.
- If it's `exhausted`, the thread has failed. If the workflow lets that failure end the run, the run ends with `program_error`, which doesn't name the cause. Read the failed threads' events to find it.
The server also limits how much all of your organization's sessions do each minute. A thread that meets this limit stops, with a `session.error` on its own stream whose message names a rate limit. Wait a minute before you ask the agent to continue.
A run can create more than one thread for the same piece of work, so make the tools your agents call safe to call twice.
---
## Claude on AWS | Claude by Anthropic
Source: https://www.claude.com/partners/claude-on-aws
Path: 04-API-Reference/Other/partners-claude-on-aws.md
# Claude on AWS
Give your team a thinking partner, build agents, and ship AI products to customers in your AWS environment.
Choose how to get started
- [Claude Platform on AWS (API)](https://aws.amazon.com/claude-platform/)
- [Claude on Amazon Bedrock (API)](https://aws.amazon.com/bedrock/anthropic/)
- [Claude Enterprise in AWS Marketplace (Apps)](https://aws.amazon.com/marketplace/pp/prodview-nnvi6wff6ef6m)
## Deploy Claude in the
environment you trust
### Safety built in
Anthropic's frontier models focused on safety, paired with your AWS account, billing, and procurement controls.
### Capabilities for your best work
Claude reads long documents, follows complex instructions, writes and refactors code, and works through problems.
### Production infrastructure
Run with confidence on AWS scale and uptime, built for enterprise workloads.
Claude API
## Claude Platform on AWS
Access the full Claude Platform on AWS with AWS-native authentication and billing, and dual commitment retirement.
[Get started](https://aws.amazon.com/claude-platform/)[Get API docs](../Guides/build-with-claude-claude-platform-on-aws.md)
### Native Claude Experience
Native Claude experience and full suite of Claude models and capabilities on AWS.
### Native AWS integration
Seamless integration with AWS for IAM and auditing.
### One AWS invoice
Billed by AWS and counts against your AWS commitment.
-
> “Using Claude Platform on AWS gives OpenRouter and our users direct access to the latest and greatest features of the native Claude API; everything our cutting edge customers and use cases need is available and we control access through the same AWS IAM credentials we use for other AWS services. It has delivered consistent performance on uptime, latency, and throughput, and working directly with the Anthropic team has helped us move faster.”
Tomas Oliva, AI Platform Engineer
-
> “Claude Platform on AWS helped simplify how we access Claude, improved the experience for key users like our Claude Code engineers, and gave us a practical path to integrate further frontier AI capabilities into our cybersecurity and engineering workflows, while staying within our existing cloud operating model. The Anthropic team was engaged, collaborative, and gave us confidence as we expanded usage.”
Jonathan Echavarria, Principal Research Scientist
-
> “Claude Platform on AWS gives us the canonical Anthropic API with AWS as the access layer, so we get full feature parity and day-one access to new model capabilities. Support has been one of the best parts, and it felt like one team across both companies, not two separate relationships. That kind of collaboration during scale-up is rare, and it's a big reason we've been able to keep moving as fast as we have.”
Avinash Vishwakarma, Chief Architect
Claude API
## Claude on Amazon Bedrock
Use Claude models, fully managed by AWS. Build with unified governance and international data locality.
[Get started](https://aws.amazon.com/bedrock/anthropic/)[Get API docs](../Guides/build-with-claude-claude-in-amazon-bedrock.md)
### AWS security boundary
Inputs, outputs, and inference run inside the AWS security boundary you control supporting international data locality.
### Unified governance
Apply your policies, logging, and access controls.
### Familiar Bedrock SDK
Call Claude through the Bedrock SDK and integrate into your team’s AWS workflows.
> “We use Claude Code in Amazon Bedrock for so many things. It’s incredible. I’ve been coding for a very long time, and Claude Code is career-changing for me.”
Jay Gowdy, Senior Principal Architect
> “Legal use cases also require high-quality technical analysis, long context windows for processing detailed documents, and fast outputs. That’s why we’ve chosen Claude on Amazon Bedrock as an important part of our AI strategy.”
Jeff Reihl, Executive VP and CTO
> “By extracting geospatial data from our vast content libraries, Claude rapidly builds personalized travel itineraries that would otherwise take our team days to craft manually. This has reduced our production costs already by 80% in tested markets.”
Chris Whyde, Senior VP of Engineering and Data Science
Claude Apps
## Claude Enterprise in AWS Marketplace
Secure products for employees, everywhere your teams work. Billed through AWS Marketplace.
[Get started](https://aws.amazon.com/marketplace/pp/prodview-nnvi6wff6ef6m)
Claude Cowork
Claude Code
Claude Chat
### Delegate tasks to Claude Cowork
Claude does research, builds polished, on-brand deliverables, and shows its work. You review, edit, and decide what ships.
[Explore Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
### Take on more engineering work
Claude Code helps engineers move faster on the work they already do, in any IDE or terminal. It supports refactors, debugging, test coverage, and legacy code.
[Explore Claude Code](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
### Give everyone a thinking partner
Open Claude, ask a question, and work through the answer together.
[Learn more](../../15-Claude-AI-Features/product-overview.md)
## Bring Claude into your AWS environment
### Claude Platform on AWS
Access the complete Claude Platform on AWS.
[Claude Platform on AWS (API) (opens in new tab)](https://aws.amazon.com/claude-platform/)
### Claude on Amazon Bedrock
Frontier models inside the AWS security boundary.
[Amazon Bedrock (API) (opens in new tab)](https://aws.amazon.com/bedrock/anthropic/)
### Claude Enterprise in AWS Marketplace
A feature-rich way to put Claude in every employee's hands, billed through AWS.
[AWS Marketplace (Apps) (opens in new tab)](https://aws.amazon.com/marketplace/pp/prodview-nnvi6wff6ef6m)
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Google Cloud | Claude by Anthropic
Source: https://www.claude.com/partners/google-cloud
Path: 04-API-Reference/Other/partners-google-cloud.md
# Claude on Google Cloud
Build advanced AI agents with Claude on Google Cloud.
[Get started (opens in new tab)](https://cloud.google.com/products/model-garden/claude?hl=en)[Read API docs (opens in new tab)](../Guides/build-with-claude-claude-on-vertex-ai.md)
## Built for performance, safety, and scale
Claude on Google Cloud lets you run Anthropic models with the infrastructure, governance, and scaling your team needs to go from prototype to production faster.
### Optimize performance and efficiency
Control costs and maximize reliability with dedicated capacity and prompt caching so your production workloads run fast and stay predictable.
### Safety at every layer
Claude on Google Cloud combines Anthropic's safety-focused models with Google Cloud's infrastructure, unified governance, and security controls.
### Simple to deploy, made to scale
Get started with Claude on Google Cloud immediately on serverless infrastructure that scales with your workload and provides exceptional performance.
### Build intelligent agents
Deploy powerful agents in production using Claude's frontier performance, MCP support, and Google Cloud’s comprehensive platform
## Use cases
### AI agents
Claude is built for agentic workflows, combining strong reasoning with the conversational quality needed for seamless human-agent collaboration.
[Learn more](../../18-Industry-UseCases/agents.md)
### Coding
Claude handles complex engineering tasks that would take hours or days through top-tier performance on code, math, and reasoning.
[Learn more](../../18-Industry-UseCases/coding.md)
### Customer support
Handle ticket triage, complex inquiries, and multi-step support workflows with the context and conversational tone customers expect.
[Learn more](../../18-Industry-UseCases/customer-support.md)
### Financial analysis
Take teams from entry-level analysis to advanced predictive modeling, moving beyond manual processes to intelligent risk management.
[Learn more](../../18-Industry-UseCases/finance.md)
Building winning AI with Claude on Google Cloud
See how teams start, scale, and succeed with Claude on Google Cloud.
[Read guide (opens in new tab)](https://www-cdn.anthropic.com/38a1fb9db81446402a70bc45d104327aab12f3fe.pdf)
Ready to build with Claude on Google Cloud?
[Get started (opens in new tab)](https://cloud.google.com/products/model-garden/claude?hl=en)
Training and resources
### Complimentary certification course
Learn to deploy Claude on Google Cloud from setup to production. Build agents, implement tool use, and set up RAG. Earn a certificate upon completion.
[Start learning (opens in new tab)](https://anthropic.skilljar.com/claude-with-google-vertex)
### Hands-on tutorial
See how to connect Claude to BigQuery through Cloud API Registry. Build an agent that discovers datasets, reads schemas, and runs SQL queries using natural language.
[View tutorial (opens in new tab)](https://github.com/GoogleCloudPlatform/generative-ai/blob/main/agents/agent_engine/tutorial_get_started_with_cloud_api_registry.ipynb)
### Webinars and events
Explore our webinar library for on-demand sessions and upcoming events from Anthropic and Google Cloud experts.
[Explore webinars (opens in new tab)](https://claude.com/resources/webinar-series/claude-on-google-cloud)
### Proven results across industries
> “The combination of Claude and Google Cloud helps us empower millions of merchants with our AI-enabled commerce assistant, Sidekick. There’s been a lot of merchants that we’ve talked to that say Sidekick ‘feels like a lifeline for small businesses’ and helps them ‘come out on top.”
Andrew McNamara, Director of Applied AI
> “Our AI agent is made more powerful through Anthropic's Claude models running on Google Cloud. This integration allows us to easily connect with other Google Cloud services, like Cloud Run, to work together behind the scenes to help customers turn their ideas into apps.”
Amjad Masad, Founder and CEO
> “With Claude running on Google Cloud, we saw a 20% to 30% increase in code development velocity… Running Claude on Google Cloud not only accelerates development projects, it enables us to hardwire security into code before it ships.”
Gunjan Patel, Director of Engineering, Office of the CPO
> “Getting a model as powerful as Anthropic's Claude on Google Cloud is a win-win that makes life so much easier. We get a model that excels at tool calling on a comprehensive platform that integrates with our core Google Cloud workloads like GKE and Cloud Run — that's the magic.”
Justin Watts, Distinguished Engineer
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Connectors and plugins | Claude Marketplace | Claude by Anthropic
Source: https://www.claude.com/partners/mcp
Path: 04-API-Reference/Other/partners-mcp.md
# Connectors and plugins
Bring the tools you already use into every Claude conversation.
[Submit a connector (opens in new tab)](https://claude.com/docs/connectors/building/submission#submit-your-connector)[Submit a plugin (opens in new tab)](https://claude.com/docs/plugins/submit)
Filter
Commerce & shopping
Communication
Consumer health
Creative
Data & analytics
Developer tools
Education
Financial services
Health & life sciences
Legal
Media & entertainment
Nonprofit
Other
Productivity
Sales & marketing
Travel
Healthcare
Type
Web
Desktop extension
Interactive
SortRecommendedMost popularTrendingNewestA–Z
## Top connectors
Show all 934
### [Google Drive](https://www.claude.com/marketplace/connectors/google-drive)
Anthropic verified
Search, read, and upload files instantly
Add Google Drive in Claude (opens in new tab)
### [Gmail](https://www.claude.com/marketplace/connectors/gmail)
Anthropic verified
Draft replies, summarize threads, & search your inbox
Add Gmail in Claude (opens in new tab)
### [Google Calendar](https://www.claude.com/marketplace/connectors/google-calendar)
Anthropic verified
Manage your schedule and coordinate meetings effortlessly
Add Google Calendar in Claude (opens in new tab)
### [Canva](https://www.claude.com/marketplace/connectors/canva)
Anthropic verified
Search, create, autofill, and export Canva designs
Add Canva in Claude (opens in new tab)
### [Microsoft 365](https://www.claude.com/marketplace/connectors/microsoft-365)
Anthropic verified
Access your company's SharePoint, OneDrive, Outlook, and Teams directly in Claude
Add Microsoft 365 in Claude (opens in new tab)
### [Notion](https://www.claude.com/marketplace/connectors/notion)
Anthropic verified
Connect your Notion workspace to search, update, and power workflows across tools
Add Notion in Claude (opens in new tab)
### [Figma](https://www.claude.com/marketplace/connectors/figma)
Anthropic verified
Generate diagrams and better code from Figma context
Add Figma in Claude (opens in new tab)
### [Slack](https://www.claude.com/marketplace/connectors/slack)
Anthropic verified
Send messages, create canvases, and fetch Slack data
Add Slack in Claude (opens in new tab)
### [HubSpot](https://www.claude.com/marketplace/connectors/hubspot)
Anthropic verified
CRM context for every answer, insight, and action
Add HubSpot in Claude (opens in new tab)
### [Asana](https://www.claude.com/marketplace/connectors/asana)
Anthropic verified
Connect to Asana to coordinate tasks, projects, and goals
Add Asana in Claude (opens in new tab)
### [Linear](https://www.claude.com/marketplace/connectors/linear)
Anthropic verified
Manage issues, projects & team workflows in Linear
Add Linear in Claude (opens in new tab)
### [monday.com](https://www.claude.com/marketplace/connectors/monday)
Anthropic verified
monday.com project management & CRM for projects, tasks, portfolios, boards, workflows, milestones, dependencies, forms, dashboards, cross-project portfolio status, and critical paths.
Add monday.com in Claude (opens in new tab)
## Top plugins
[Show all 342](https://www.claude.com/marketplace/plugins)
### [Frontend Design](https://www.claude.com/marketplace/plugins/frontend-design)
Anthropic verified
Craft production-grade frontends with distinctive design. Generates polished code that avoids generic AI aesthetics.
### [Superpowers](https://www.claude.com/marketplace/plugins/superpowers)
Claude learns brainstorming, subagent development with code review, debugging, TDD, and skill authoring through Superpowers.
### [Code Review](https://www.claude.com/marketplace/plugins/code-review)
Anthropic verified
AI code review with specialized agents and confidence-based filtering for pull requests
### [Context7](https://www.claude.com/marketplace/plugins/context7)
Upstash Context7 MCP server for live docs lookup. Pull version-specific docs and code examples from source repos into LLM context.
### [Code Simplifier](https://www.claude.com/marketplace/plugins/code-simplifier)
Anthropic verified
Code clarity agent: simplifies and refines recently modified code while preserving functionality and consistency.
### [Playwright](https://www.claude.com/marketplace/plugins/playwright)
Browser automation and end-to-end testing MCP server by Microsoft. Enables Claude to interact with web pages, take screenshots, fill forms, and automate testing workflows.
## Trending connectors
Show all 6
### [SlickTrip](https://www.claude.com/marketplace/connectors/slicktrip)
Anthropic verifiedTrending
Search flights, hotels and seats, then get alerted the moment a price drops.
Add SlickTrip in Claude (opens in new tab)
### [GoVola](https://www.claude.com/marketplace/connectors/govola)
Anthropic verifiedTrending
Search for the best flight offers and get refunded if the ticket price drops even after you book
Add GoVola in Claude (opens in new tab)
### [Brek - Hotel Wholesale Booking](https://www.claude.com/marketplace/connectors/brek-hotel-wholesale-booking)
Anthropic verifiedTrending
Search and compare hotels with public and private wholesale rates.
Add Brek - Hotel Wholesale Booking in Claude (opens in new tab)
### [Coinversa Pulse](https://www.claude.com/marketplace/connectors/coinversa-pulse)
Anthropic verifiedTrending
Live crypto, stock, gold & oil trading data from Hyperliquid: see what the best traders and whales are doing on Bitcoin, ETH, the S&P 500 and more
Add Coinversa Pulse in Claude (opens in new tab)
### [PaperOffice](https://www.claude.com/marketplace/connectors/paperoffice)
Anthropic verifiedTrending
Built for companies. Ready for AI agents. The headless DMS for Claude: search, read, extract, approve, send for signing and archive documents in your own account, with your permissions.
Add PaperOffice in Claude (opens in new tab)
### [SuperBooks](https://www.claude.com/marketplace/connectors/superbooks)
Anthropic verifiedTrending
Financial OS for small businesses
Add SuperBooks in Claude (opens in new tab)
## New connectors
Show all 8
### [Ivo](https://www.claude.com/marketplace/connectors/ivo)
Anthropic verifiedNew
Explore your contracts, ask questions with supporting evidence, and build renewal calendars with Ivo.
Add Ivo in Claude (opens in new tab)
### [Grafana Cloud](https://www.claude.com/marketplace/connectors/grafana-cloud)
Anthropic verifiedNew
Query metrics, logs, and traces and manage dashboards and alerts
Add Grafana Cloud in Claude (opens in new tab)
### [Clerq MCP](https://www.claude.com/marketplace/connectors/clerq-mcp)
Anthropic verifiedNew
AI-powered patent retrieval and analysis capabilities
Add Clerq MCP in Claude (opens in new tab)
### [Checkout.com](https://www.claude.com/marketplace/connectors/checkout-com)
Anthropic verifiedNew
Investigate, troubleshoot, and understand payment operations.
Add Checkout.com in Claude (opens in new tab)
### [eDreams](https://www.claude.com/marketplace/connectors/edreams)
Anthropic verifiedNew
Find flights and hotels
Add eDreams in Claude (opens in new tab)
### [Robin (Workplace)](https://www.claude.com/marketplace/connectors/robin-powered)
Anthropic verifiedNew
Robin is the workplace platform teams use to manage desks, rooms, and hybrid office schedules, and optimize how their workplace is planned and used.
Add Robin (Workplace) in Claude (opens in new tab)
### [WebMCP Evaluator](https://www.claude.com/marketplace/connectors/webmcp-evaluator)
Anthropic verifiedNew
Check whether a public page is ready for agents: tools observed, tools to add, what must never be exposed.
Add WebMCP Evaluator in Claude (opens in new tab)
### [OnChainDiligence](https://www.claude.com/marketplace/connectors/onchaindiligence)
Anthropic verifiedNew
Inspect autonomous payments before execution and independently verify evidence afterward.
Add OnChainDiligence in Claude (opens in new tab)
## All connectors
934 connectors
### [Google Drive](https://www.claude.com/marketplace/connectors/google-drive)
Anthropic verified
Search, read, and upload files instantly
Add Google Drive in Claude (opens in new tab)
### [Gmail](https://www.claude.com/marketplace/connectors/gmail)
Anthropic verified
Draft replies, summarize threads, & search your inbox
Add Gmail in Claude (opens in new tab)
### [Google Calendar](https://www.claude.com/marketplace/connectors/google-calendar)
Anthropic verified
Manage your schedule and coordinate meetings effortlessly
Add Google Calendar in Claude (opens in new tab)
### [Canva](https://www.claude.com/marketplace/connectors/canva)
Anthropic verified
Search, create, autofill, and export Canva designs
Add Canva in Claude (opens in new tab)
### [Microsoft 365](https://www.claude.com/marketplace/connectors/microsoft-365)
Anthropic verified
Access your company's SharePoint, OneDrive, Outlook, and Teams directly in Claude
Add Microsoft 365 in Claude (opens in new tab)
### [Notion](https://www.claude.com/marketplace/connectors/notion)
Anthropic verified
Connect your Notion workspace to search, update, and power workflows across tools
Add Notion in Claude (opens in new tab)
### [Figma](https://www.claude.com/marketplace/connectors/figma)
Anthropic verified
Generate diagrams and better code from Figma context
Add Figma in Claude (opens in new tab)
### [Slack](https://www.claude.com/marketplace/connectors/slack)
Anthropic verified
Send messages, create canvases, and fetch Slack data
Add Slack in Claude (opens in new tab)
### [Coinversa Pulse](https://www.claude.com/marketplace/connectors/coinversa-pulse)
Anthropic verifiedTrending
Live crypto, stock, gold & oil trading data from Hyperliquid: see what the best traders and whales are doing on Bitcoin, ETH, the S&P 500 and more
Add Coinversa Pulse in Claude (opens in new tab)
### [HubSpot](https://www.claude.com/marketplace/connectors/hubspot)
Anthropic verified
CRM context for every answer, insight, and action
Add HubSpot in Claude (opens in new tab)
### [Asana](https://www.claude.com/marketplace/connectors/asana)
Anthropic verified
Connect to Asana to coordinate tasks, projects, and goals
Add Asana in Claude (opens in new tab)
### [Shopify](https://www.claude.com/marketplace/connectors/shopify)
Anthropic verified
Build, manage, and analyze your Shopify store
Add Shopify in Claude (opens in new tab)
### [Linear](https://www.claude.com/marketplace/connectors/linear)
Anthropic verified
Manage issues, projects & team workflows in Linear
Add Linear in Claude (opens in new tab)
### [Gemini](https://www.claude.com/marketplace/connectors/gemini-mcp)
Anthropic verified
Connect AI assistants to live crypto markets, prediction markets, your trading accounts, all through our secure read-only plugin
Add Gemini in Claude (opens in new tab)
### [monday.com](https://www.claude.com/marketplace/connectors/monday)
Anthropic verified
monday.com project management & CRM for projects, tasks, portfolios, boards, workflows, milestones, dependencies, forms, dashboards, cross-project portfolio status, and critical paths.
Add monday.com in Claude (opens in new tab)
### [Atlassian MCP](https://www.claude.com/marketplace/connectors/atlassian)
Anthropic verified
Search, read and update Jira, Confluence, Bitbucket, Loom and other Atlassian apps with your existing Atlassian permissions.
Add Atlassian MCP in Claude (opens in new tab)
### [Adobe](https://www.claude.com/marketplace/connectors/adobe-creativity)
Anthropic verified
Design, combine, and edit with Adobe pro tools
Add Adobe in Claude (opens in new tab)
### [Supabase](https://www.claude.com/marketplace/connectors/supabase)
Anthropic verified
Manage databases, authentication, and storage
Add Supabase in Claude (opens in new tab)
### [Box](https://www.claude.com/marketplace/connectors/box)
Anthropic verified
Search, edit and get insights on your Box content
Add Box in Claude (opens in new tab)
### [Intercom](https://www.claude.com/marketplace/connectors/intercom)
Anthropic verified
Access to Intercom data for better customer insights
Add Intercom in Claude (opens in new tab)
### [Miro](https://www.claude.com/marketplace/connectors/miro)
Anthropic verified
Access and create new content on Miro boards
Add Miro in Claude (opens in new tab)
### [Indeed](https://www.claude.com/marketplace/connectors/indeed)
Anthropic verified
Search for jobs on Indeed
Add Indeed in Claude (opens in new tab)
### [Spotify](https://www.claude.com/marketplace/connectors/spotify)
Anthropic verified
Music and podcast recommendations, just for you.
Add Spotify in Claude (opens in new tab)
### [Gamma](https://www.claude.com/marketplace/connectors/gamma)
Anthropic verified
Create presentations, docs, socials, and sites with AI
Add Gamma in Claude (opens in new tab)
View more
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Claude in Microsoft Foundry | Claude by Anthropic
Source: https://www.claude.com/partners/microsoft-foundry
Path: 04-API-Reference/Other/partners-microsoft-foundry.md
# Claude in Microsoft Foundry
Deploy frontier AI solutions safely and simply, using your existing Azure ecosystem.
[Get started (opens in new tab)](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/use-foundry-models-claude?tabs=python)[Read API docs (opens in new tab)](../../02-Claude-Code-CLI/microsoft-foundry.md)
## Simplify your path to AI production and business impact
Claude is now generally available in Microsoft Foundry. Build production applications and enterprise agents with Claude using the Azure authentication, billing, and governance you already have. Usage appears on your Azure invoice and, for customers with a Microsoft Azure commitment, counts toward your MACC. New with general availability, inference can run on Azure infrastructure in a US data zone, operated by Anthropic.
### Frontier intelligence
Build apps and enterprise agents powered by the world's leading models for coding, agents, and office tasks.
### Familiar workflows
Deploy Claude through Azure tools and workflows you already use, with Python, TypeScript, and C# SDKs and more.
### Unified procurement
Claude models in Microsoft Foundry are eligible for Microsoft Azure Consumption Commitment (MACC).
### Consolidated billing
Claude usage appears on your Azure invoice alongside other services, simplifying expense management.
## Use cases for Claude
### Coding
Anthropic’s models are at the forefront of coding, math, and reasoning skills. Claude can complete complex engineering tasks to solve problems that would typically take a full day of engineering work.
[Learn more](../../18-Industry-UseCases/coding.md)
### Customer support
Claude can handle ticket triage, on-demand complex inquiries using rich context awareness, and multi-step support workflows—all with conversational responses and a warm tone.
[Learn more](../../18-Industry-UseCases/customer-support.md)
### Productivity
Claude can extract relevant information from business emails and documents, categorize and summarize survey responses, and wrangle reams of text with high speed and accuracy.
### Financial analysis
Claude can handle everything from entry-level financial analysis to advanced predictive modeling, connecting insights across regulatory filings, market reports, and internal data.
[Learn more](../../18-Industry-UseCases/finance.md)
Claude API
## Claude in Microsoft Foundry, hosted on Azure
Inference runs in an Azure data zone, operated by Anthropic.
[Get started (opens in new tab)](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/use-foundry-models-claude)[Read API docs (opens in new tab)](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
### Runs in Azure
Inference is processed in an Azure data zone, alongside your other Azure workloads.
### One Azure invoice
Usage appears alongside your other Azure services. Eligible spend counts toward your MACC.
### Claude Code ready
Point Claude Code at your Foundry endpoint and start shipping.
> “Running Claude models on Azure has given us the sustained throughput and reliability our enterprise customers expect. The combination of frontier model quality and enterprise-grade infrastructure is what makes Bolt viable for the Fortune 500.”
Gary Ballabio, VP Partnerships
> “Between Anthropic and Azure, we get the best capabilities in the world and we get the best security in the world. And that's exactly what nuclear needs. It's how we compressed a safety analysis that would have taken 200 human days into a single day.”
Matt Huang, Founding Product Lead
> “Our customers describe their tests in plain English, and Momentic runs through the interface to verify everything works before a release ships. We found Claude Opus especially suited to this, and running it on Azure Foundry we now serve millions of tokens per minute with the reliability our customers depend on.”
Jeff An, Co-founder & CTO
> “At NVIDIA, we use autonomous AI agents every day to help our teams move faster and think bigger. Claude models bring strong reasoning, coding and enterprise capabilities that are valuable for complex technical work. With Claude now available in Microsoft Foundry running on NVIDIA GB300 GPUs, more organizations can run advanced, specialized AI agents with the performance, scale and security needed for production.”
Justin Boitano, VP and GM Enterprise Computing
> “Running Claude models on Azure has given us the sustained throughput and reliability our enterprise customers expect. The combination of frontier model quality and enterprise-grade infrastructure is what makes Bolt viable for the Fortune 500.”
Gary Ballabio, VP Partnerships
> “Between Anthropic and Azure, we get the best capabilities in the world and we get the best security in the world. And that's exactly what nuclear needs. It's how we compressed a safety analysis that would have taken 200 human days into a single day.”
Matt Huang, Founding Product Lead
> “Our customers describe their tests in plain English, and Momentic runs through the interface to verify everything works before a release ships. We found Claude Opus especially suited to this, and running it on Azure Foundry we now serve millions of tokens per minute with the reliability our customers depend on.”
Jeff An, Co-founder & CTO
> “At NVIDIA, we use autonomous AI agents every day to help our teams move faster and think bigger. Claude models bring strong reasoning, coding and enterprise capabilities that are valuable for complex technical work. With Claude now available in Microsoft Foundry running on NVIDIA GB300 GPUs, more organizations can run advanced, specialized AI agents with the performance, scale and security needed for production.”
Justin Boitano, VP and GM Enterprise Computing
> “Running Claude models on Azure has given us the sustained throughput and reliability our enterprise customers expect. The combination of frontier model quality and enterprise-grade infrastructure is what makes Bolt viable for the Fortune 500.”
Gary Ballabio, VP Partnerships
> “Between Anthropic and Azure, we get the best capabilities in the world and we get the best security in the world. And that's exactly what nuclear needs. It's how we compressed a safety analysis that would have taken 200 human days into a single day.”
Matt Huang, Founding Product Lead
1/4
Claude API
## Claude in Microsoft Foundry, hosted on Anthropic
The newest Claude models and API features, reached through Foundry.
[Get started](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/use-foundry-models-claude?tabs=python)[Read API docs](../Guides/build-with-claude-claude-in-microsoft-foundry.md)
### Every Claude model
Access the complete Claude model lineup.
### Full API feature set
Every Claude Platform capability, reached through Foundry.
### Billed through Azure
Usage appears on your Azure invoice. Eligible spend counts toward your MACC.
## Get started with Claude in Microsoft Foundry
[Get started](https://learn.microsoft.com/en-us/azure/foundry/foundry-models/how-to/use-foundry-models-claude?tabs=python)[Read API docs](../../02-Claude-Code-CLI/microsoft-foundry.md)
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Powered by Claude | Claude by Anthropic
Source: https://www.claude.com/partners/powered-by-claude
Path: 04-API-Reference/Other/partners-powered-by-claude.md
# Powered by Claude
A curated list of businesses that use Claude to build better, faster, and safer.
[Build with Claude](https://www.claude.com/platform/api)
Filter
Usecase
Business intelligence
Coding tools
Communications
Creative content
Cybersecurity
Data and analytics
Education
66 partners
Anything is the AI product engineer for the next wave of entrepreneurs. It is a conversational coding platform that turns plain English into real, production-ready apps, handling mobile, web, design, AI, backend, databases, auth, hosting, and payments.
See solution
The Arctic Wolf AI Security Assistant provides customers with self-guided access to deeper security insights within the Aurora Platform. It streamlines investigations and alert comprehension by delivering instant answers, contextual enrichment, and actionable summaries—combining the scale of the platform’s data lake with expertise from Arctic Wolf’s global SOCs.
See solution
Attention is an AI-powered sales agent platform that automates post-call activities, scores conversations and opportunities, and enables intelligent search across customer interactions. The integrated AI agents handle CRM updates, provide revenue leaders with real-time sales visibility, and generate accurate enablement content and reports in minutes rather than days.
See solution
Augment Code provides AI-powered coding assistance and development tools that help developers write better code faster with intelligent suggestions and automation.
See solution
Base44 is a platform that enables organizations to rapidly build secure, AI-powered web applications while maintaining scalability, security, and control.
See solution
Blitzy is the first autonomous software development platform built for large, complex enterprise codebases. It ingests millions of lines of code, maps every dependency, then orchestrates thousands of specialized agents to plan, build, and validate production-ready code. Claude powers the core of this process, from relationally mapping entire codebases to writing, refining, and testing code before handing it back to engineers.
See solution
Bolt.new creates apps and websites through AI chat.
See solution
Brainlabs is an independent, AI-native media agency spanning strategy and planning, paid search, paid social, SEO, influencer, creative, and data. Brainlabs uses Claude to run marketing workflows end to end: auditing brand narrative and buyer journeys, prioritizing and refreshing content from live AI-visibility data, and building client-ready decks and campaign assets.
See solution
Bubble is an AI app development platform that combines the speed of AI with the control of visual development so that anyone can launch powerful, scalable web and mobile apps without code.
See solution
Builder.io is a visual development platform that transforms design into production-ready code using AI. It integrates with Figma, any Git provider, and your backend through MCP and native integrations. Leveraging Anthropic, it helps teams generate and edit UI components, accelerating how they build, iterate, and ship digital experiences.
See solution
Ship trusted code at AI speed with the world’s first autonomous validation platform.
See solution
Clay is a GTM development environment that helps companies turn any growth idea into reality through AI-powered data enrichment and automated workflows.
See solution
CodeRabbit is an industry's leading AI code review platform that cuts review time and bugs in half. CodeRabbit delivers context-aware, automated code reviews that fix even difficult to identify bugs, and frees up developer time.
See solution
Cogent is the world’s first AI Taskforce for enterprise cybersecurity teams that fully automates the Vulnerability Management lifecycle.
See solution
Cursor is an AI-powered code editor built by Anysphere that provides intelligent code completion, generation, and editing capabilities to accelerate software development.
See solution
Databricks provides a unified analytics platform for big data and machine learning, enabling organizations to process, analyze, and derive insights from large datasets.
See solution
Day.ai is an AI-native CRM that brings the voice of the customer into every decision. Its product combines a meeting assistant, CRM and knowledge base into a single unified solution.
See solution
Devin is an autonomous AI software engineer that can write, run and test code - helping ambitious engineering teams crush their backlogs and build better software faster.
See solution
Dust is building AI assistants for work that help teams be more productive by automating workflows and providing intelligent insights across various business functions.
See solution
Elastic is the Search AI company helping organizations turn unstructured data into real-time results through a unified platform for search, observability, and security built on Elasticsearch. More than half of the Fortune 500 rely on Elastic’s distributed, open, cloud-ready solutions to build smarter, AI-powered applications. Elastic Attack Discovery leverages Claude to analyze alerts in customer's environment and identify threats.
See solution
Ellipsis is an AI software developer that reviews code, fixes bugs, tracks engineering productivity, and automates workflows. Installed in over 67,000 repositories, Ellipsis increases engineering velocity by an average of 20%.
See solution
Emergent is an AI app builder that turns ideas into monetizable software. Emergent builds autonomous coding agents that replace traditional software development by generating, testing, and deploying production applications directly from plain-language intent. The platform solves the hard part of AI-driven software creation: correctness, reliability, security, and scale in real production systems. 6M+ users across 190+ countries have built 6M+ applications on Emergent.
See solution
Factory is building AI-powered software development agents that automate coding tasks and help engineering teams ship products faster.
See solution
In the agentic era, infrastructure and spend change faster than teams can track. Finout makes FinOps work at that scale by turning complex, shared spend into reliable ownership, allocation, unit economics, and planning. With Finout's Claude integration, teams automatically ingest cost and usage data via Claude's Admin API, gaining visibility across models, tokens, service tiers, and workspaces—all alongside other cloud and SaaS spend in one unified view..
See solution
Gamma is an AI design partner for creating presentations, websites, social media, and documents. Over 50 million users create 700,000 presentations and assets daily.
See solution
GC AI is a legal platform built on Claude, tailored for in-house legal teams. We use Claude to power contract analysis, redlining, and legal research workflows with domain-specific prompting, system-level context, and enterprise-grade guardrails delivering faster and more accurate legal outcomes.
See solution
Genspark is the all-in-one AI workspace powering the future of work. AI Slides, AI Sheets, AI Docs, and more AI tools enable effortless "vibe working." Any office work can be done with a simple prompt now. The 3-day workweek is Genspark's vision.
See solution
Greptile is an AI-powered code review and repository analysis tool that helps developers understand large codebases by providing intelligent code search and analysis.
See solution
Hai is HackerOne’s coordinated team of AI agents built to eliminate vulnerabilities at scale. It analyzes findings, prioritizes validated risk, generates actionable guidance, and mobilizes fixes across workflows so enterprises shift from managing vulnerabilities to achieving risk reduction with confidence.
See solution
Harvey is domain-specific AI for legal and professional services, streamlining workflows in contract analysis, due diligence, compliance, and litigation. Global law firms and Fortune 500 enterprises use Harvey to drive efficiency, value, and faster, smarter decision-making across their operations.
See solution
Highlight AI is a context-aware desktop assistant that automatically detects meetings and tasks, transcribes calls, and helps with writing and research.
See solution
Hostinger is a world-leading web hosting provider dedicated to helping millions succeed online. They offer services including AI-powered Hostinger Horizons, designed to make online presence creation fast, affordable, and accessible for individuals and businesses worldwide.
See solution
Interop.io provides enterprise integration platform and connectivity solutions, enabling seamless data flow and interoperability between different applications and systems.
See solution
InVideo is an AI-powered video creation platform that helps users create professional videos quickly with AI-driven editing, templates, and automated workflows.
See solution
iVerify leads in mobile EDR, offering robust protection against mobile threats like fileless malware, smishing, malicious apps, ransomware, and credential theft on both iOS and Android. Its security platform spans consumer, enterprise, and government sectors, securing all workplace devices with privacy-focused BYOD and enterprise-grade solutions.
See solution
JetBrains develops professional software development tools and IDEs, including IntelliJ IDEA, PyCharm, and other productivity tools for developers.
See solution
Julius AI is an AI Data Analyst that lets anyone analyze data, get insights and create visualizations within seconds.
See solution
Lovable is an AI-powered development platform that helps teams build and deploy applications faster with intelligent code generation and automated workflows.
See solution
MagicSchool is the trusted AI platform built for K–12, used by over 6 million educators. MagicSchool helps teachers save time and reduce burnout while improving student outcomes. With curriculum-aligned tools, district-level safety, and AI literacy for students, MagicSchool empowers schools to adopt AI responsibly and effectively.
See solution
Mintlify is an AI‑native documentation platform that helps teams create, maintain, and scale beautiful documentation sites built for people and LLMs. Trusted by leading startups and enterprises, Mintlify streamlines authoring with modern workflows and AI tools.
See solution
Mirage is unlocking a new era of creative possibility. The Company offers two products: Captions, for generating and editing short-form videos, and Mirage Studio, for scaling video production. Powered by Mirage's foundation models, they allow creators, teams, and brands to ship more often, with higher-quality content.
See solution
SuperNinja, from NinjaTech AI, is a powerful AI agent for work—with its own virtual computer, it rapidly completes complex workflows from deep research to coding.
See solution
Notability provides a toolkit for note-taking with innovative study features, helping users learn their content quicker and more efficiently.
See solution
Otter.ai provides AI-powered transcription and meeting notes services, automatically converting speech to text and generating summaries for meetings and conversations.
See solution
Parcha's AI agents helps fintechs and banks carry out financial crime compliance more efficiently and accurately. Companies like Airwallex, Pipe, Flutterwave and IG user Parcha's AI agents to carry out business due diligence, reduce false positives in AML screening and verify documentary evidence in real-time.
See solution
Qodo provides a unified platform for organizations to deploy and govern custom agents across the entire SDLC, from initial coding to advanced, agentic code review.
See solution
With Replit, anyone—technical or non-technical—can build and deploy fully-functional, full-stack apps directly from their browser, without any installation, setup, or configuration.
See solution
Rork enables users to build a real mobile app using plain English, publish to the App Store in three clicks, and start a business.
See solution
Semgrep Assistant provides recommendations to review, triage, and remediate security findings. Its Autotriage reduces backlogs up to 60% by eliminating false positives, and suggests which findings can be deferred or ignored. Assistant’s remediation guidance helps security teams scale by giving developers code suggestions to fix issues, right in their workflow.
See solution
Snowflake powers the end-to-end data lifecycle - from ingesting and processing data to analyzing and modeling it, to building and sharing data and AI applications - helping engineers, analysts and leaders innovate faster and achieve more with their data.
See solution
Snyk’s AI Trust Platform helps manage and enhance enterprise security by providing an AI Bill of Materials (AI BoM) for visibility into models embedded in software. It also helps measure and mitigate novel risks like model jailbreaking through its AI model risk registry and strategic partnerships.
See solution
Socket's malicious package detection combines LLM-based code analysis with human expertise from our internal research team. This hybrid approach leverages automated AI analysis at scale alongside expert investigation, aided by LLM-powered tools, to identify malicious code and typosquatted packages, delivering continuous monitoring of open-source software packages.
See solution
Solve Intelligence is the leading AI patent solution for IP teams and legal professionals globally. Over 400 IP teams across 6 continents use Solve Intelligence’s AI patent software for patent application drafting, prosecution, claim charts, and invention harvesting. Our AI assistant puts patent attorneys in the driving seat — generating first drafts of applications, crafting office action responses, analyzing prior art, and streamlining invention capture.
See solution
Sonar’s platform, SonarQube, provides AI code verification, helping developers automatically detect and fix quality, reliability, and security issues. Claude powers Sonar’s AI features across CI, IDEs, and AI agents. Sonar's solutions guide agentic development, verify agentic output, and solve identified issues via automated remediation.
See solution
Strider’s AI-driven Strategic Intelligence Platform fuses proprietary and customer data to deliver unmatched insights on nation-state risks and competitive threats across an organizations ecosystem. By accelerating detection and response, the platform enables corporations and governments to outpace adversaries, mitigate risks, and protect critical technologies and assets.
See solution
StudyFetch is an AI-powered learning platform that helps students create interactive study materials, flashcards, and quizzes from their course content.
See solution
Sara (Synack Autonomous Red Agent) enhances Synack’s Penetration Testing as a Service (PTaaS) platform by fusing autonomous AI capabilities with human-in-the-loop analysis, to deliver proactive, risk-based security validation.
See solution
Tabnine is an enterprise software company building AI infrastructure for modern development teams. Its platform embeds trusted, context-aware AI directly into the software development lifecycle, giving organizations the control, security, and governance required to run AI at scale in production environments.
See solution
Trail of Bits uses Claude to enhance program analysis for security and vulnerability research in source code. We develop custom tools targeting specific vulnerabilities and improve traditional security tools like fuzzing, rules-based analysis, and secure software configurations. Claude is ideal for rapid prototyping and exploring security-related ideas.
See solution
Triple Whale is the agent-powered intelligence platform for faster, more confident decisions across your business.
See solution
Uptycs delivers a Claude-powered AI security assistant that transforms security operations. By unifying CNAPP and XDR through a centralized security data lake, the intelligent platform enables natural conversation-driven investigations. Security teams detect threats faster, investigate deeper, and respond confidently—transforming hours of manual analysis into minutes.
See solution
Vanta integrates with Claude via the Model Context Protocol to provide a natural language interface for compliance data. Users can query audits, controls, and evidence directly, automate reporting, and accelerate audits—making trust management faster, more transparent, and accessible to both technical and non-technical stakeholders.
See solution
Workato is the leading enterprise orchestration platform, connecting thousands of applications so businesses can automate workflows, integrate systems, and safely deploy AI agents to take real action with enterprise-grade governance, security, and control built in from the start.
See solution
Zapier uses Claude to power natural language workflow creation, autonomous AI agents, and an embedded copilot across its 9000+ connected apps: Claude's reasoning enables businesses to describe what they need in plain language, while Zapier's action layer executes it across CRMs, databases, communication tools, and more.
See solution
Zed is a next-generation code editor designed for high-performance collaboration with humans and AI.
See solution
Zencoder is an AI coding agent that enables developers to ship high-quality software faster by leveraging advanced reasoning to automate complex coding workflows, enhance accuracy, and dramatically improve productivity and reliability.
See solution
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Claude Partner Network Directory
Source: https://www.claude.com/partners/services
Path: 04-API-Reference/Other/partners-services.md
# Partner Directory
Find a Claude certified consulting partner or system integrator to help you build your AI strategy and successfully implement Claude.
[Join the Claude Partner Network](https://claude.com/form/cpn-partner-application)
Filters
Languages
All
AfrikaansArabicBahasaBengaliChineseCzechDutchDutch - FlemishEnglishFarsiFinnishFrenchGermanGreekHebrewHindiIndonesianItalianJapaneseJavaneseKoreanMarathiNdebeleNorwegianPolishPortuguesePunjabiRussianShonaSlovakSpanishSwahiliSwedishTagalogTamilTeluguThaiTurkishUkrainianUkranianUrduVietnamese
Anthropic Products
All
APIClaude CodeClaude CoworkClaude for Teams / EnterpriseOther
Budget
All
\$100K – \$500K\$25K – \$100K\$2M+\$500K – \$2M\< \$25K
Claude Use Cases
All
Business Process & OperationsCompliance & RegulatoryContent Creation & CopywritingConversation & Meeting IntelligenceCustomer Support & Service OperationsCybersecurity & Threat DetectionData Analysis & Business IntelligenceDevOps & Infrastructure OperationsDocument Processing & ExtractionEducation & LearningKnowledge Retrieval & Enterprise SearchResearch & IntelligenceSales & Revenue OperationsSoftware DevelopmentTrust & Safety / Platform Integrity
Customer Size Served
All
Enterprise (2,500–10,000 employees)Large Enterprise (10,000+ employees)Micro SMB (\<100 employees)Mid-Market (500–2,500 employees)Public SectorSMB (100–500 employees)
Deployment Surface
All
Amazon BedrockClaude API (direct)Google VertexMicrosoft Foundry
Engagement Type
All
Assessment / DiscoveryManaged / RecurringPilot / POCProject / ProgramStaff AugmentationTraining Package
Industry Expertise
All
EducationEnergy & UtilitiesFinancial Services & InsuranceHealthcare & Life SciencesHorizontal / Cross-industryManufacturing & IndustrialProfessional & Legal ServicesPublic Sector / GovernmentRetail & Consumer GoodsTechnology & SoftwareTelecom & MediaTravel & Hospitality
Office Locations (On-Site Services)
All
ANZCanadaDACH (Germany, Austria, Switzerland)France & BeneluxGreater ChinaIndiaJapanLatin AmericaMiddle East & AfricaNordicsSouth KoreaSoutheast AsiaSouthern EuropeUnited Kingdom & IrelandUnited States
Partner Tier
All
Global PremierPreferredSelect
Remote Service Coverage
All
ANZCanadaDACH (Germany, Austria, Switzerland)France & BeneluxGlobal (any region)Greater ChinaIndiaJapanLatin AmericaMiddle East & AfricaNordicsSouth KoreaSoutheast AsiaSouthern EuropeUnited Kingdom & IrelandUnited States
Service Delivery Location
All
Hybrid (on-site + remote)On-siteRemote — nearshoreRemote — same timezone
Services Offered
All
AI Readiness & Governance Assessment / DesignAI Strategy & RoadmapBuild Prototype / POCClaude Consumption / FinOps OptimisationClaude Training SessionsConnectors, plugins and API integrationsCustomer-Facing AI Product BuildIndustry Solution Accelerator DeploymentInternal Agentic Platform BuildJumpstart for SMBManaged AI OperationsOngoing Cowork / Claude Code Scaled User SupportPlatform Migration to ClaudeProcess RedesignRollout & ActivationSustained Adoption / CoE ProgramUse-Case Portfolio & Business Case
Clear all
[Accenture](https://www.claude.com/directory/partner/1784259849907x251647352390538700)
Global Premier
Accenture is a global professional services leader helping enterprises reinvent with AI, cloud, and digital technologies. With deep industry expertise and 779,000 talented people, Accenture delivers innovation at scale, creating measurable value through strategy, consulting, technology, and operations worldwide.
[Bain & Company](https://www.claude.com/directory/partner/1784259848645x689726857723364400)
Global Premier
Bain & Company is a global consultancy that helps the world’s most ambitious change makers define the future and achieve extraordinary results.
[Boston Consulting Group](https://www.claude.com/directory/partner/1784259850508x994627779836033700)
Global Premier
Boston Consulting Group bridges the gap between ambition and outcomes for the world's leading companies and organizations. We are built for this era of unprecedented change — bringing strategic clarity rooted in over 60 years of deep domain knowledge, combined with applied AI shaped by our practitioners.
[Capgemini](https://www.claude.com/directory/partner/1784259850557x525795684677261600)
Global Premier
Capgemini is an AI-powered global business and technology transformation partner, delivering tangible business value. We imagine the future of organizations and make it real with AI, technology and people. With nearly 60 years’ heritage, 420,000 team members in 50+ countries, Capgemini delivers end-to-end services across strategy, technology, design, engineering and operations.
[Cognizant](https://www.claude.com/directory/partner/1784259849813x814233558931717500)
Global Premier
Cognizant engineers modern businesses. We help our clients modernize technology, reimagine processes and transform experiences so they can stay ahead in our fast-changing world. Together, we're improving everyday life.
[Deloitte](https://www.claude.com/directory/partner/httpswwwdeloittecomusenalliancesanthropic-alliancehtml)
Global Premier
Deloitte provides industry-leading audit, consulting, tax and advisory services to many of the world’s most admired brands, including nearly 90% of the Fortune 500® and more than 8,500 U.S.-based private companies.
[DXC](https://www.claude.com/directory/partner/1784259851423x614615018606447400)
Global Premier
For 60+ years, DXC has designed, engineered and operated some of the world’s most complex technology estates for enterprises and governments. As a Claude Global Premier Partner, we embed Claude-certified engineers directly in our customers’ environments to bring agentic AI into their mission-critical systems. With our deep domain expertise in insurance, cybersecurity and application modernization,
[Fujitsu](https://www.claude.com/directory/partner/1784259851254x887974829336712900)
Global Premier
Fujitsu is a global leader in technology and business solutions that transform organizations and the world around us. For over 90 years, we have brought innovation and expertise, continuously working to contribute to the growth of society and our customers.
[Hitachi](https://www.claude.com/directory/partner/hitachi-ltd)
Global Premier
Global group in digital systems, energy and mobility that solves customer and social challenges with data and technology.
View more
No partners found
Try a different search term, or clear your filters to see every partner in the directory.
Clear search & filters
© 2026 Anthropic PBC
---
## Partners | Claude by Anthropic
Source: https://www.claude.com/partners
Path: 04-API-Reference/Other/partners.md
# Claude Partner Network
Build the enterprise AI practice your customers are asking for — with the training, technical support, and co-investment to back it up.
## Join the network
Apply to the Claude Partner Network.
[Get started (opens in new tab)](https://www.claude.com/form/cpn-partner-application)
## Partner Portal
Already working with Anthropic? Log in to access resources, templates and playbooks.
[Login (opens in new tab)](http://partnerhub.anthropic.com/)
## Get Claude certified
Check out Anthropic Academy to get Claude certified.
[Learn more (opens in new tab)](https://anthropic-partners.skilljar.com/page/partner-certifications)
## Flexible deployment for
any environment
### Cloud partners
Many of the world's leading companies access frontier Claude models on AWS, Google Cloud, and Microsoft.
[AWS](partners-claude-on-aws.md)[Google Cloud](partners-google-cloud.md)[Microsoft](partners-microsoft-foundry.md)
### Services partner directory
Looking for expert partners to help with your AI strategy, consulting, or Claude implementation? Explore our network of services partners who have successfully delivered Claude solutions for enterprise customers.
[Learn more](partners-services.md)
### Powered by Claude directory
See the list of businesses that use Claude to build better, faster, and safer product experiences for their customers.
[Learn more](partners-powered-by-claude.md)
## MCP Connectors
Claude can work with your tools, databases, and applications—and give you more relevant responses. Choose from a variety of connectors, powered by the Model Context Protocol (MCP)
[Explore connectors](partners-mcp.md)
## Partner news
Deloitte will make Claude available to 470,000 people across its global network
Announcement
Claude now available in Microsoft Foundry and Microsoft 365 Copilot
Announcement
Anthropic Lands Cognizant as Enterprise AI Customer
Announcement
[](https://www.claude.com/)
© 2026 Anthropic PBC
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
- [Security and compliance](https://trust.anthropic.com/)
- [Status](https://status.anthropic.com/)
- [Support center](https://support.claude.com/en/)
## Company
- [Anthropic](https://www.anthropic.com/)
- [Careers](https://www.anthropic.com/careers)
- [Policy](https://www.anthropic.com/policy)
- [Research](../../19-Reference/anthropic-com-research.md)
- [Anthropic news](../../19-Reference/news.md)
- [Policy on the AI Exponential](https://www.anthropic.com/policy-on-the-ai-exponential)
- [Responsible Scaling Policy](../../19-Reference/announcing-our-updated-responsible-scaling-policy.md)
- [Transparency](https://anthropic.com/transparency)
## Terms and policies
- Privacy choices
- [Privacy policy](https://www.anthropic.com/legal/privacy)
- [Responsible disclosure policy](https://www.anthropic.com/responsible-disclosure-policy)
- [Terms of service: Commercial](https://www.anthropic.com/legal/commercial-terms)
- [Terms of service: Consumer](https://www.anthropic.com/legal/consumer-terms)
- [Terms of Service: US K-12](https://anthropic.com/legal/k12-terms)
- [Data Processing Agreement: US K-12](https://anthropic.com/legal/k12-dpa)
- [Usage Policy](https://www.anthropic.com/legal/aup)
## Products
- [Claude](../../15-Claude-AI-Features/product-overview.md)
- [Claude Code](../../15-Claude-AI-Features/product-claude-code-fad1ab4b.md)
- [Claude Cowork](../../15-Claude-AI-Features/product-cowork.md)
- [@Claude](../../15-Claude-AI-Features/product-tag.md)
- [Claude Science](../../15-Claude-AI-Features/product-claude-science.md)
- [Claude Security](../../15-Claude-AI-Features/product-claude-security.md)
- [Download app](https://www.claude.com/download)
- [Pricing](../../17-Billing-Plans/pricing.md)
- [Log in](https://claude.ai/login)
## Capabilities
- [Artifacts](https://www.claude.com/features/artifacts)
- [Design](../../15-Claude-AI-Features/product-design.md)
- [Connectors](https://www.claude.com/marketplace/connectors-plugins)
- [Plugins](https://www.claude.com/marketplace/plugins)
- [Skills](https://www.claude.com/skills)
## Extensions
- [Claude in Chrome](https://www.claude.com/claude-in-chrome)
- [Claude for Microsoft 365](https://www.claude.com/claude-for-microsoft-365)
## Models
- [Mythos](../../15-Claude-AI-Features/claude-mythos.md)
- [Fable](../../15-Claude-AI-Features/claude-fable.md)
- [Opus](../../15-Claude-AI-Features/claude-opus.md)
- [Sonnet](../../15-Claude-AI-Features/claude-sonnet.md)
- [Haiku](../../15-Claude-AI-Features/claude-haiku.md)
## Enterprise
- [Overview](../../17-Billing-Plans/pricing-enterprise.md)
- [Claude Code for Enterprise](../../15-Claude-AI-Features/product-claude-code-enterprise.md)
## Departments
- [Customer support](../../18-Industry-UseCases/customer-support.md)
- [Cybersecurity](../../18-Industry-UseCases/cybersecurity.md)
- [Legal](../../18-Industry-UseCases/legal.md)
- [Sales](../../18-Industry-UseCases/sales.md)
## Industries
- [Financial advisors](../../18-Industry-UseCases/financial-advisors.md)
- [Financial services](../../18-Industry-UseCases/finance.md)
- [Government](../../18-Industry-UseCases/government.md)
- [Healthcare](../../18-Industry-UseCases/healthcare.md)
- [Higher education](../../18-Industry-UseCases/education.md)
- [K-12 teachers](../../18-Industry-UseCases/teachers.md)
- [Life sciences](../../18-Industry-UseCases/life-sciences.md)
- [Nonprofits](../../18-Industry-UseCases/nonprofits.md)
- [Small business](../../18-Industry-UseCases/small-business.md)
## Programs
- [Campus](../../15-Claude-AI-Features/programs-campus.md)
- [Frontier Academy](../../15-Claude-AI-Features/programs-frontier-academy.md)
- [Scientists](../../15-Claude-AI-Features/programs-claude-team-plan-for-research-labs.md)
- [Startups](../../15-Claude-AI-Features/programs-startups.md)
## Developers
- [Developer docs](../../02-Claude-Code-CLI/code-home.md)
- [Developer blog](https://claude.dev)
- [Community](https://www.claude.com/community)
- [Console](home.md)
- [Engineering at Anthropic](https://www.anthropic.com/engineering)
## Platform
- [Overview](https://www.claude.com/platform/api)
- [Marketplace](https://www.claude.com/marketplace)
- [Claude on AWS](partners-claude-on-aws.md)
- [Google Cloud](partners-google-cloud.md)
- [Microsoft Foundry](partners-microsoft-foundry.md)
## Resources
- [Overview](https://www.claude.com/resources)
- [Articles](https://www.claude.com/resources/articles)
- [Webinars](https://www.claude.com/resources/webinars)
- [Claude partner network](partners.md)
- [Claude Academy](https://academy.claude.com/)
- [Customer stories](../../18-Industry-UseCases/customers.md)
- [Events](https://www.anthropic.com/events)
- [Powered by Claude](partners-powered-by-claude.md)
- [Service partners](https://www.claude.com/marketplace/service-partners)
## Help and security
- [Availability](https://www.anthropic.com/supported-countries)
- [Check files](https://www.claude.com/check-files)
- [Regional compliance](https://www.claude.com/regional-compliance)
- [Report abuse](https://www.claude.com/form/anthropic-content-reporting)
---
## Playground | Claude Platform
Source: https://platform.claude.com/playground
Path: 04-API-Reference/Other/playground.md
# Build on the Claude Platform
Create agents and applications with frontier Claude models and managed agent infrastructure.
Continue with Google
Or
Continue with email
Continue with SSO
By continuing, you agree to Anthropic’s [Commercial Terms(opens in new tab)](https://www.anthropic.com/legal/archive/af81645b-040b-485c-a4a0-3205ccfb3792) and [Usage Policy(opens in new tab)](https://www.anthropic.com/legal/archive/22742366-2ef0-4c7a-a833-6523f10d3944), and acknowledge our [Privacy Policy(opens in new tab)](https://www.anthropic.com/legal/archive/baeb68ac-c81c-4931-9539-58f46b6f090c).
Companies building with the Claude API
---
## Preserved thinking: changing how the Messages API handles thinking blocks to protect against distillation | Claude Help Center
Source: https://support.claude.com/en/articles/16761192
Path: 04-API-Reference/Other/preserved-thinking-changing-how-the-messages-api-handles-thinking-blocks-to-protect.md
# Preserved thinking: changing how the Messages API handles thinking blocks to protect against distillation
We're changing how the Messages API handles thinking blocks to protect against distillation. A thinking block is a record of the reasoning Claude may produce while working on a response. On Claude Fable 5.1, Claude Opus 5.5, Claude Sonnet 5.5, and Claude Haiku 5.5, new API accounts can no longer edit the context around a thinking block, such as the messages, tools, or system prompt, during a multi-turn conversation. We’ll expand the rollout to all users with upcoming model launches.
Modifying this prior context has legitimate applications, which we continue to support using the adjustments outlined below. However, such modifications are also a common and **[publicly documented technique](https://arxiv.org/abs/2608.09867)** for industrial-scale illicit distillation, which is prohibited by our **[Usage Policy](https://www.anthropic.com/legal/aup)** and Terms of Service.
Now, the API will now verify that a thinking block is sent back with the same system prompt, tools, and messages that produced it, and will return an error if they don't match. In order for modified requests to succeed, developers may opt-in to instead have the thinking blocks *removed* from such requests; the model will respond without seeing the thinking block.
In this article, we share details on why we're doing this and the adjustments you can make to minimize disruption.
## What are thinking blocks?
Claude produces reasoning steps before providing its final answer. On the API, these are returned to the user as "thinking blocks." In a multi-turn conversation, API users send these blocks back with each exchange (along with the system prompt, tools, and earlier messages), so that Claude has full conversational context.
## What’s changing?
For affected accounts using the models listed below, the API will return an error if the system prompt, tools, or messages preceding a prior thinking block have been modified.
To avoid an error message, you may opt into "non-strict" mode. In this mode, the request will go through, but the affected thinking blocks will be dropped from what the model sees. This allows you to continue your conversation or task uninterrupted despite the prior turns’ thinking not being shown to the model. When this happens, the API response will tell you which blocks were dropped.
## Why are we making this change?
Altering the earlier turns of a conversation is a **[common technique](https://arxiv.org/abs/2608.09867)** used in illicit distillation campaigns, which aim to extract the capabilities of advanced models—especially thinking—to train another model, without authorization. Distillation is often employed on an industrial scale, using thousands of fake accounts. We encrypt Claude's thinking blocks to prevent this, but by editing the conversation before a thinking block, a user could get Claude to decrypt and print its reasoning. Systems trained this way can inherit capabilities they wouldn’t otherwise have, *without* inheriting the safeguards that we've built to prevent a broad range of misuse like cyberattacks and weapons development.
This change aims to make distillation campaigns more difficult to execute. It builds on existing anti-distillation measures like **[enhanced distillation classifiers](../../19-Reference/next-generation-constitutional-classifiers.md)** and restrictions on transferring sessions or reasoning from more advanced models to less capable models with weaker safeguards.
## What does this mean for API integrations?
Certain integrations—particularly those that involve rewriting earlier turns mid-conversation, like context compaction, injected system reminders, or changing tools mid-session—may need adjustment.
Here are the resources to help guide you through this update:
- The **[preserved thinking documentation](../Guides/build-with-claude-preserved-thinking.md)** covers where the change applies, how to check whether your integration is affected, and **[how to make common edits](../Guides/build-with-claude-preserved-thinking.md#replace-prefix-edits)** without breaking preserved thinking.
- **[Compaction](../Guides/build-with-claude-compaction.md)** (beta) can summarize older turns while keeping the most recent turns word for word, and can build the summary in the background while your agent keeps working. It replaces client-side compaction, which is likely the most common reason integrations need to change.
- **[Mid-conversation tool changes](../Guides/build-with-claude-mid-conversation-system-messages.md#mid-conversation-tool-changes)** (beta) let you add or remove tools during a conversation without editing earlier turns.
- Our **[migration guides](../../20-Models/about-claude-models-migration-guide.md)** have a full checklist for moving to each model.
Check each page for availability on Amazon Bedrock, Google Cloud Vertex AI, and Microsoft Foundry.
If the guidance above doesn't cover your use case, please **[reach out to our support team](../../15-Claude-AI-Features/how-to-get-support-claude-help-center.md)**. If you work with an account team, you can also reach out to them for support with updating more complex integrations.
There are additional benefits to keeping thinking blocks consistent: it means that the API can reuse cached prompts more often, which reduces costs and response time.
## Who will this impact?
Preserved thinking applies to these models and accounts:
[TABLE]
This covers Claude Platform organizations, Amazon Bedrock accounts, Google Cloud Vertex AI projects, and Microsoft Foundry projects.
We're taking a phased approach to enforcement, starting with new accounts, where we see the highest concentration of distillation-related abuse. This gives developers with existing accounts time to make their harnesses and integrations compatible.
If you use Claude Code, Claude Cowork, or Claude.ai, there's nothing you need to change; those products handle thinking blocks for you.
## Thinking can only be read by the account that created it
Starting with Claude Sonnet 5.5, a thinking block can only be read by the account that created it, or by an account linked to it.
Thinking blocks can contain private information. Because they are encrypted, that information isn't visible when you inspect a transcript. This check helps keep a shared or leaked transcript from exposing the reasoning inside it. It also makes it harder for distillers to move harvested transcripts to new accounts after we ban the account that produced them.
If a request includes thinking from an account that isn't linked, the API drops that thinking and the request continues. It doesn't return an error. The next response may be slower and use more tokens, similar to a compaction.
Accounts in the same Claude Platform parent organization or the same Google Cloud organization are linked automatically. If you continue conversations across other accounts, for example to fail over between the Claude Platform and Amazon Bedrock, contact your account team to link them.
In Claude Code, switching accounts in the middle of a session has the same effect: the next response is slower and uses more tokens.
---
## Model cards - Claude Platform Docs
Source: https://platform.claude.com/docs/en/resources/overview
Path: 04-API-Reference/Other/resources-overview.md
- [Managed Agents](managed-agents-overview.md)
- [Admin](manage-claude-admin-api.md)
- Resources
- [Best practices](../About/about-claude-use-case-guides-overview.md)
- [Models & pricing](../../20-Models/about-claude-models-overview.md)
- [SDKs, CLI, and libraries](cli-sdks-libraries-overview.md)
- [Claude API skill](../Agents-Tools/agents-and-tools-agent-skills-claude-api-skill.md)
- [Release notes](../../20-Models/release-notes-overview.md)
[API reference](../Endpoints/overview.md)
[Console](usage-limits.md)[Log in](https://platform.claude.com/login?returnTo=%2Fdocs%2Fen%2Fresources%2Foverview)
SearchCtrlK
Models
[Models overview](../../20-Models/about-claude-models-overview.md)
[Claude Fable 5.1](../../20-Models/models-fable-5-1-overview.md)
[Claude Opus 5.5](../../20-Models/models-opus-5-5-overview.md)
[Claude Sonnet 5.5](../../20-Models/models-sonnet-5-5-overview.md)
[Claude Haiku 4.5](../../20-Models/models-haiku-4-5-overview.md)
Specialized models
Legacy models
Guides
[Choosing a model](../../20-Models/about-claude-models-choosing-a-model.md)[Optimizing for cost and intelligence](../../20-Models/about-claude-models-optimizing-for-cost-and-intelligence.md)[Upgrade between model versions](../../20-Models/about-claude-models-migration-guide.md)
Lifecycle and reference
[Model IDs and versioning](../../20-Models/about-claude-models-model-ids-and-versions.md)[Model deprecations](../../20-Models/about-claude-model-deprecations.md)[Model cards](resources-overview.md)[Pricing](../../17-Billing-Plans/about-claude-pricing.md)
[System prompts](../../20-Models/release-notes-system-prompts-overview.md)
[Console](usage-limits.md)
[Models & pricing](../../20-Models/about-claude-models-overview.md)Lifecycle and reference
# Model cards
Copy page
Model cards with detailed documentation for Claude models.
Copy page
[Claude Opus 5.5 System Card](../../19-Reference/this-system-card-describes-claude-opus-5-5-the-latest-opus-class-large.md)
Detailed documentation of Claude Opus 5.5.
[Claude Fable 5.1 and Mythos 5.1 System Card](../../15-Claude-AI-Features/claude-fable-5-1-mythos-5-1-system-card.md)
Detailed documentation of Claude Fable 5.1 and Claude Mythos 5.1.
[Claude Opus 5 System Card](../../15-Claude-AI-Features/claude-opus-5-system-card.md)
Detailed documentation of Claude Opus 5.
[Claude Sonnet 5 System Card](../../19-Reference/added-a-footnote-in-8-9-2-browsecomp-with-a-reproduction-recipe-via-public.md)
Detailed documentation of Claude Sonnet 5.
[Claude Fable 5 and Mythos 5 System Card](../../15-Claude-AI-Features/claude-fable-5-mythos-5-system-card.md)
Detailed documentation of Claude Fable 5 and Claude Mythos 5.
[Claude Opus 4.8 System Card](../../19-Reference/correction-to-opus-4-8s-reported-performance-on-long-form-virology-task-2-in.md)
Detailed documentation of Claude Opus 4.8.
[Claude Opus 4.7 System Card](../../15-Claude-AI-Features/claude-opus-4-7-system-card.md)
Detailed documentation of Claude Opus 4.7.
[Claude Mythos Preview System Card](https://www.anthropic.com/claude-mythos-preview-system-card)
Detailed documentation of Claude Mythos Preview.
[Claude Sonnet 4.6 System Card](../../15-Claude-AI-Features/claude-sonnet-4-6-system-card.md)
Detailed documentation of Claude Sonnet 4.6.
[Claude Opus 4.6 System Card](../../15-Claude-AI-Features/claude-opus-4-6-system-card.md)
Detailed documentation of Claude Opus 4.6.
[Claude Opus 4.5 System Card](../../15-Claude-AI-Features/claude-opus-4-5-system-card.md)
Detailed documentation of Claude Opus 4.5.
[Claude Haiku 4.5 System Card](../../15-Claude-AI-Features/claude-haiku-4-5-system-card.md)
Detailed documentation of Claude Haiku 4.5.
[Claude Sonnet 4.5 System Card](../../15-Claude-AI-Features/claude-sonnet-4-5-system-card.md)
Detailed documentation of Claude Sonnet 4.5.
[Claude Opus 4.1 System Card](../../15-Claude-AI-Features/claude-opus-4-1-system-card.md)
Detailed documentation of Claude Opus 4.1.
[Claude 4 System Card](../../15-Claude-AI-Features/claude-4-system-card.md)
Detailed documentation of Claude 4 models.
[Claude Sonnet 3.7 System Card](https://www.anthropic.com/claude-3-7-sonnet-system-card)
System card for Claude Sonnet 3.7 with performance and safety details.
[Claude Haiku 3.5 and Sonnet 3.5 System Card](https://www-cdn.anthropic.com/c7822cdc35ad788ec87e14b3a9d45010f1f86c38.pdf)
Detailed documentation of Claude Haiku 3.5 and Claude Sonnet 3.5.
[Claude 3 Model Card](https://www.anthropic.com/claude-3-model-card)
Detailed documentation of Claude 3 models including latest 3.5 addendum.
[Claude 2 Model Card](https://www.anthropic.com/claude-2-model-card)
Detailed documentation of Claude 2 models.
---
## Understanding your Claude API invoices | Claude Help Center
Source: https://support.claude.com/en/articles/16608069-understanding-your-claude-api-invoices
Path: 04-API-Reference/Other/understanding-your-claude-api-invoices.md
# Understanding your Claude API invoices
August 24, 2026
This article explains the invoices and receipts you'll receive for Claude API and Console usage, where to find them, and how to read the line items on them.
## Types of invoices you may receive
**Usage invoices.** If your organization is on a paid usage contract, we aggregate your usage across API calls, playground chats, and other services on your account, and you'll receive an invoice from Stripe at the end of every calendar month.
**Credit purchase receipts.** If your organization uses prepaid billing, you'll receive a receipt for each usage credit purchase, including purchases made automatically by auto-reload.
For details on how billing works, see **[How do I pay for my Claude API usage?](https://support.claude.com/en/articles/8977456)**
## Find your invoices
After each charge, we automatically email the invoice to your registered billing email address. To find a past invoice in your inbox, search for the subject line "Your receipt from Anthropic."
Invoices are also available in the Console to users with the Admin or Billing role:
1. Log in to the Console with an Admin or Billing role.
2. Go to **[Console Settings \> Billing](playground.md)**.
3. Find the **Invoice history** section.
4. Click "Download" to save the invoice directly, or click "View" to open it in a new Stripe tab and download it from there.
Expired credit grants also appear in **Invoice history**, even though they aren't charges. For help accessing older receipts and invoices that aren't available in the Console, **[contact our Support team](../../15-Claude-AI-Features/how-to-get-support-claude-help-center.md)**.
## Read your invoice
**Applied balance.** An "Applied balance" line means an existing balance on your account was automatically applied to the invoice, reducing the amount due. A negative applied balance is credit being used, not a new charge or a discount. It comes from one of two places:
- A previous invoice totaled less than the minimum amount our billing system can charge (\$0.50), so it rolled into this invoice.
- Your account carried a credit—for example, from a credit note or an overpayment—and it was applied automatically.
**Amount due.** The invoice total minus any applied balance. This is what your payment method is charged, or what you pay if your organization pays by bank transfer.
## Billing details on your invoice
The name, address, and tax information on an invoice come from your billing details at the time the invoice was issued. If you update your billing details, the changes apply to future invoices—issued invoices can't be modified. To update your details, go to **[Console Settings \> Billing](playground.md)**.
## Frequently asked questions
### Why is my invoice still past due after I paid?
If you pay by bank transfer, the payment must match the invoice amount exactly. A payment that's short by even a few cents won't be applied, and the invoice stays past due until the remaining amount arrives. Once the full amount is received, the invoice is marked paid automatically—this can take around five business days to process.
### Why didn't I receive an invoice for a small amount of usage?
Invoices below \$0.50 aren't charged on their own. The amount rolls forward and appears as an applied balance on your next invoice.
### Can you correct the details on an invoice I already received?
No. Issued invoices can't be changed. Update your billing details in **[Console Settings \> Billing](playground.md)** and the changes will appear on future invoices.
---
## Claude Platform
Source: https://platform.claude.com/
Path: 04-API-Reference/Other/usage-limits.md
# Build on the Claude Platform
Create agents and applications with frontier Claude models and managed agent infrastructure.
Continue with Google
Or
Continue with email
Continue with SSO
By continuing, you agree to Anthropic’s [Commercial Terms (opens in a new tab)](https://www.anthropic.com/legal/archive/af81645b-040b-485c-a4a0-3205ccfb3792) and [Usage Policy (opens in a new tab)](https://www.anthropic.com/legal/archive/22742366-2ef0-4c7a-a833-6523f10d3944), and acknowledge our [Privacy Policy (opens in a new tab)](https://www.anthropic.com/legal/archive/21d66aa9-68f6-4356-ba01-2825b0f81805).
Companies building with the Claude API
---
## What is Amazon Bedrock? | Claude Help Center
Source: https://support.claude.com/en/articles/7996918-what-is-amazon-bedrock
Path: 04-API-Reference/Other/what-is-amazon-bedrock.md
# What is Amazon Bedrock?
March 16, 2026
Amazon Bedrock is a fully managed service that offers a choice of high-performing
foundation models (FMs) from leading AI companies like Anthropic through a single API,
along with a broad set of capabilities you need to build generative AI applications with
security, privacy, and responsible AI. Using Amazon Bedrock, you can easily experiment
with and evaluate Anthropic’s Claude models for your use case, privately customize
them with your data using techniques such as fine-tuning and Retrieval Augmented
Generation (RAG), and build agents that execute tasks using your enterprise systems
and data sources. Since Amazon Bedrock is serverless, you don't have to manage any infrastructure, and you can securely integrate and deploy generative AI capabilities into your applications using the AWS services you are already familiar with.
You can learn more about Anthropic’s Claude models in Amazon Bedrock [here](https://aws.amazon.com/bedrock/claude/).
---
## What should I do if I suspect my API key has been compromised? | Claude Help Center
Source: https://support.claude.com/en/articles/8384961-what-should-i-do-if-i-suspect-my-api-key-has-been-compromised
Path: 04-API-Reference/Other/what-should-i-do-if-i-suspect-my-api-key-has-been-compromised.md
# What should I do if I suspect my API key has been compromised?
March 16, 2026
If you suspect that your API key may be compromised, we recommend revoking the key immediately. You can do so by logging into your Claude Console account, going to the [API keys page](playground.md) from your profile, clicking the meatball menu (i.e. the three horizontal dots) next to the key in question, and selecting ‘Delete API Key.’
You can generate a new API key by clicking on the ‘Create Key’ button on the same page; make sure to save this key somewhere secure, such as a secrets management system and avoid keeping the key in version control. If you continue to see any suspicious API activity or have other concerns, please [contact our Support team](https://support.claude.com/en/articles/9015913-how-to-get-support).
---
## Where can I find full receipts and invoices for my Claude API and Console payments? | Anthropic Help Center
Source: https://support.claude.com/en/articles/10366473-where-can-i-find-full-receipts-and-invoices-for-my-claude-api-and-console-payments
Path: 04-API-Reference/Other/where-can-i-find-full-receipts-and-invoices-for-my-claude-api-and-console-payments.md
# Where can I find full receipts and invoices for my Claude API and Console payments?
March 16, 2026
Invoices are visible to Console Billing and Admin roles.
Follow these steps to view your Console invoices:
1. Log in to the Console with an Admin or Billing role.
2. Navigate to **[Console Settings \> Billing](playground.md)**.
3. Find the **Invoice history** section.
4. Click the "View" or "Download" button next to the invoice you want to open.
5. Clicking "Download" will automatically start the file download.
6. Clicking "View" opens a new Stripe tab where you can click "Download invoice."
Additionally, after each purchase, we automatically send them to your registered billing email address.
Expired credit grants will appear in the **Invoice history** section. For help accessing older receipts and invoices not available in the Claude Console, please **[contact our Support team](https://support.claude.com/en/21-Account-Support/how-to-get-support-57c428239c.md)**.
---
## Where can I find your API documentation? | Claude Help Center
Source: https://support.claude.com/en/articles/8114490-where-can-i-find-your-api-documentation
Path: 04-API-Reference/Other/where-can-i-find-your-api-documentation.md
# Where can I find your API documentation?
March 16, 2026
You can access our API documentation by visiting our Claude Docs: [https://docs.claude.com/en/api/overview](https://docs.claude.com/en/api/overview).
---
## Where can I go for Claude API support and assistance? | Claude Help Center
Source: https://support.claude.com/en/articles/8114535-where-can-i-go-for-claude-api-support-and-assistance
Path: 04-API-Reference/Other/where-can-i-go-for-claude-api-support-and-assistance.md
# Where can I go for Claude API support and assistance?
March 16, 2026
For our API users, we currently maintain three main resources:
- Our [Client Software Development Kits (SDKs)](https://docs.claude.com/en/api/client-sdks);
- Our [Claude Docs](https://docs.claude.com/en/home); and
- Our community [Discord](https://anthropic.com/discord) where developers can share ideas and exchange tips.
If you need to [contact our Support team](https://support.claude.com/en/articles/9015913-how-to-get-support), you can do so by clicking on the message icon in the bottom right of our Help Center, or by clicking your initials in the lower left corner of your Claude Console account, then "Get help."
---
## Where do I find Claude in Amazon Bedrock documentation? | Claude Help Center
Source: https://support.claude.com/en/articles/10280783-where-do-i-find-claude-in-amazon-bedrock-documentation
Path: 04-API-Reference/Other/where-do-i-find-claude-in-amazon-bedrock-documentation.md
# Where do I find Claude in Amazon Bedrock documentation?
March 16, 2026
Visit the Claude in Amazon Bedrock [user guide](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html).
---
## Where is the Claude API supported? | Claude Help Center
Source: https://support.claude.com/en/articles/8116515-where-is-the-claude-api-supported
Path: 04-API-Reference/Other/where-is-the-claude-api-supported.md
# Where is the Claude API supported?
March 16, 2026
We support using the Claude API in a [number of locations](https://www.anthropic.com/supported-countries). Please check our website in the future for updates.