Claude Managed Agents API Guide: Agent-Session and SSE Streaming in 5 Steps

Table of Contents

agent = client.beta.agents.create(
    name="Coding Assistant",
    model="claude-opus-4-8",
    tools=[{"type": "agent_toolset_20260401", "default_config": {"enabled": True}}],
)
session = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": agent.version},
    environment_id=environment.id,
)

These two code blocks are essentially the entire Claude Managed Agents API in a nutshell. Create an Agent, spin up a Session, and the agent starts running. In practice, however, connecting this structure to a production system requires handling SSE streaming, Beta header configuration, and disconnect recovery — a non-trivial amount of additional work. From a data pipeline perspective, Agent maps to a pipeline definition (DAG definition), while Session corresponds to a pipeline execution (DAG run).

This article walks through the full flow from Agent creation to event streaming, based on Anthropic’s Managed Agents Python SDK guide.

Agent and Session — The Core Two-Stage Structure of Claude Managed Agents API

The first concept to grasp in this Claude Managed Agents API guide is the Agent-Session separation pattern. Managed Agents provisions a container per session and runs the agent loop within Anthropic’s orchestration layer.

The core flow consists of two stages:

  • Agent: A config object created once. It’s an immutable configuration containing the model, tools, and system prompt. Version control is applied, so a created Agent config stays unchanged by design.
  • Session: An execution instance created per run. Each Session is pinned to the Agent version at the time of creation.
Benefits of Agent-Session Separation
Managing Agents as immutable configs guarantees configuration consistency even when hundreds of Sessions are spawned from the same Agent. This follows the same design philosophy as separating DAG definitions from DAG runs in data pipelines.

One important constraint exists in this pattern: Sessions cannot directly include model, system, or tools fields. They must reference an Agent object. When creating a Session, only the Agent’s id and version are passed — the Agent handles the rest.

Agent Version Pinning and Flexible References

The Session’s agent field accepts two forms. The first is passing just a string ID, which automatically uses the latest version. The second is passing a {"type": "agent", "id": "...", "version": "..."} object to pin a specific version.

For production environments, explicitly pinning the version is recommended. This follows the same principle as pinning a specific DAG version for execution in a data pipeline. It prevents existing Sessions from unexpectedly running on a new version right after an Agent config update.

Reference Type Format Version Behavior Recommended for Production
String ID "agent_id_123" Always uses latest version
Object Reference {"type": "agent", "id": "...", "version": "..."} Uses pinned version

This is analogous to the difference between fetching the latest DAG definition by dag_id in Airflow versus pinning to a specific execution_date.

Creating an Agent with the Python SDK

Using the Claude Managed Agents API starts with installing the Anthropic Python SDK. All API calls go through the client.beta.* namespace.

pip install anthropic

After installation, the code to create an Agent looks like this:

agent = client.beta.agents.create(
    name="Coding Assistant",
    model="claude-opus-4-8",
    tools=[{"type": "agent_toolset_20260401", "default_config": {"enabled": True}}],
)

The client.beta.agents.create call requires three fields:

  • name: An identifier for the agent, used for differentiation in the management console.
  • model: The Claude model to use. The example above specifies claude-opus-4-8.
  • tools: The list of tools to grant the agent. The agent_toolset_20260401 type enables the default toolset.

A system field can also be specified. The system prompt controls the agent’s behavioral scope and response style. Since Agents are versioned immutable configs, changing the system prompt creates a new version.

The beta namespace is required
All Managed Agents API calls must go through `client.beta.*`. Calling `client.agents.create` without beta will fail to find the endpoint. This namespace automatically sets the `anthropic-beta: managed-agents-2026-04-01` header internally.

Design Principles for Agent Creation

From a data pipeline design perspective, deciding how to split Agents requires clear criteria. One option is putting all tools into a single general-purpose Agent; another is separating Agents by purpose, each with optimized system prompts and tool combinations.

A general-purpose Agent is easier to manage but tends toward longer system prompts and reduced tool selection accuracy. Purpose-specific Agent separation improves response quality per Agent but increases version management overhead as the number of Agents grows. This mirrors the tradeoff between monolithic DAGs and micro DAGs in pipeline architecture.

Creating a Session and Connecting an Environment

Once the Agent is ready, create a Session to start actual execution. Session creation only requires two fields: agent and environment_id.

session = client.beta.sessions.create(
    agent={"type": "agent", "id": agent.id, "version": agent.version},
    environment_id=environment.id,
)

The environment_id specifies the container environment where the Session runs. Managed Agents provisions an isolated container per session, and the Environment config controls the runtime.

Setting model, system, or tools directly on a Session isn’t possible. These three can only be configured at the Agent level — Sessions inherit them indirectly through the Agent reference. This constraint is the crux of the Agent-Session separation pattern.

MCP Server Authentication — Vault Integration

When integrating with external MCP servers, create Credentials in a Vault and link them to the Session via vault_ids. This pattern injects authentication data like API keys and tokens safely through the Vault, rather than hardcoding them in Session code.

Verify the billing structure
Session-hour-based billing has been reported, but a detailed pricing page hasn’t been confirmed on the official domain. Check the Anthropic official pricing page before deploying to production.

From a data pipeline perspective, the Vault pattern is similar to Airflow’s Connections/Variables. Just as database passwords aren’t embedded directly in pipeline code but managed through Airflow Connections, Managed Agents separates Credentials into a Vault and injects them into Sessions.

Implementing SSE Event Streaming

The section requiring the most attention in this Claude Managed Agents API guide is SSE (Server-Sent Events) streaming. Event streaming is SSE-based, and the stream-first pattern is recommended — open the stream first, then send the message to avoid missing initial events.

The reason this ordering matters is clear: sending a message before opening the stream risks missing initial events that fire while the Agent begins processing.

with client.beta.sessions.events.stream(
    session_id=session.id,
) as stream:
    client.beta.sessions.events.send(
        session_id=session.id,
        events=[{"type": "user.message", "content": [{"type": "text", "text": "..."}]}],
    )
    for event in stream:
        if event.type == "agent.message":
            for block in event.content:
                if block.type == "text":
                    print(block.text, end="", flush=True)

The code structure opens the stream first with a with block, sends the message via events.send inside the block, then iterates through events in the for event in stream loop.

Key Event Types

The event types available on the SSE stream are as follows:

Event Type Description How to Handle
agent.message Agent’s text response Iterate content blocks and output text
agent.custom_tool_use Agent invokes a custom tool Execute the tool and return the result via events.send
session.status_idle Session enters idle state Send additional messages or decide to terminate
session.status_terminated Session terminated Clean up resources and close connections

When an agent.custom_tool_use event is received, the client side must execute the corresponding tool and send the result back via events.send. This process is the core of the agent loop — an asynchronous handshake pattern similar to how an External Task Sensor in data pipelines waits for external task completion.

Stream-first pattern as a data pipeline analogy
This is identical to subscribing a Kafka Consumer first, then having the Producer send messages. Just as a late-connecting Consumer misses initial messages, the SSE stream must be connected before sending messages to capture all initial events.

API Endpoints and Beta Header Configuration

The Python SDK auto-configures the necessary headers on client.beta.* calls, but when making direct REST API calls or using HTTP clients in other languages, headers must be set manually.

The required header set for all Managed Agents API requests:

x-api-key: <your-api-key>
anthropic-version: 2023-06-01
anthropic-beta: managed-agents-2026-04-01

The anthropic-beta header value is managed-agents-2026-04-01. If this value is missing or incorrect, the API returns a 400 error. The full endpoint specification is available in the Managed Agents API reference.

Four Core Endpoints

The Claude Managed Agents API has four core endpoints:

  • POST /v1/agents: Creates an agent. Include name, model, tools, and system fields in the body.
  • POST /v1/sessions: Creates a session. Pass agent and environment_id.
  • GET /v1/sessions/{id}/events/stream: Opens an SSE streaming connection for real-time event reception.
  • POST /v1/sessions/{id}/events: Sends messages and tool results. Used to deliver user messages or custom tool execution results to the Agent.

When integrating this API in NestJS, use HttpModule or @nestjs/axios to build a service module that calls these endpoints. For SSE streaming, wrapping the connection in an EventSource pattern or an rxjs Observable fits naturally with NestJS’s reactive patterns.

Handling SSE Disconnects and Event Polling

In production, SSE connections can drop due to network instability, load balancer timeouts, or client restarts. The Claude Managed Agents API’s SSE stream does not support replay. This is the most critical reliability concern from a data pipeline perspective.

The recovery procedure after a disconnect:

  1. Poll existing events via GET /v1/sessions/{id}/events
  2. Deduplicate against previously received events using event IDs
  3. Open a new SSE stream connection to receive subsequent events

This pattern resembles Kafka’s offset-based reprocessing, but unlike Kafka where the broker manages offsets, the client must track event IDs directly. Recording received event IDs in local storage (memory, Redis, DB, etc.) is therefore an essential implementation requirement.

Deduplication Strategy

Event reception flow:
┌─────────────────────────────────────────┐
│  SSE Stream (1st connection)             │
│  event_001 → event_002 → event_003      │
│                          ↓ disconnected  │
├─────────────────────────────────────────┤
│  Polling (GET /events)                   │
│  event_001, event_002, event_003,        │
│  event_004  ← missed event              │
├─────────────────────────────────────────┤
│  SSE Stream (2nd connection)             │
│  event_005 → event_006 → ...            │
└─────────────────────────────────────────┘

From the polling results, event_001 through event_003 have already been processed and should be skipped. Only event_004 gets processed before switching to the second stream. Missing this implementation can lead to duplicate message processing or event loss during the disconnection window.

Preparing for the lack of SSE replay
Since the SSE stream doesn’t support replay, production deployments must implement event-ID-based deduplication logic. Omitting this risks duplicate agent responses or double-executed tool calls.

Claude Managed Agents API Starter Checklist

Based on the preceding sections, here’s a checklist of items to verify when applying this in practice. Areas not yet clearly covered in the official documentation are also noted.

Required Implementation Items

  • SDK installed via pip install anthropic
  • model, tools, and name fields specified during Agent creation
  • Agent version pinning applied during Session creation ({"type": "agent", "id": ..., "version": ...})
  • Stream-first pattern applied — SSE stream opened before sending messages
  • anthropic-beta: managed-agents-2026-04-01 header included when making direct REST API calls
  • Event-ID-based deduplication logic implemented

Gaps in Official Documentation

Even after reviewing the Managed Agents architecture overview, the following items remain unaddressed in official documentation:

  • No official guide is available in Korean. docs.anthropic.com redirects to platform.claude.com, making direct access difficult in some cases.
  • The detailed pricing page for the reported $0.08/session-hour billing structure hasn’t been confirmed on the official domain.
  • Multi-agent coordination and self-evaluation are in research preview status and require separate access requests.
  • Practical examples for self-hosted environment configuration (EnvironmentWorker) are limited.

Given these gaps, verifying the latest information through official Anthropic channels before production deployment is the safe approach.

Next Steps — MCP Server Integration and NestJS Modularization

With the basics of the Claude Managed Agents API flow understood, the next step is MCP server integration. Storing Credentials in a Vault and linking them to Sessions via vault_ids enables secure authentication with external services. In NestJS, separating Vault Credential management into a dedicated VaultModule and injecting it into AgentService via DI is a fitting pattern.

When integrating the Anthropic Python SDK agent with a NestJS backend, the choice comes down to two approaches: running the Python process as a sidecar, or calling the REST API endpoints directly. The former leverages the SDK’s convenience features but adds process management complexity; the latter eliminates language dependencies but requires implementing Beta headers and SSE handling manually.

Connecting the Claude API agent session’s event streaming to NestJS’s @Sse() decorator enables building a pipeline that delivers real-time responses to clients. When the managed-agents-2026-04-01 beta phase transitions to GA, migration from client.beta.* to client.agents.* namespace may be required — adding an abstraction layer during module design is advisable. Mapping the Claude Managed Agents API’s Agent-Session pattern to NestJS module structure extends naturally from NestJS custom providers and the factory pattern.

Scroll to Top