# Implementing Prompt Versioning and Rollback with Claude Managed Agents: A Complete Guide

> Learn prompt versioning and rollback with Claude Managed Agents. Pin production traffic to specific versions and instantly rollback without redeploying code. A complete guide.

- Repository: [Anthropic/claude-cookbooks](https://github.com/anthropics/claude-cookbooks)
- Tags: how-to-guide
- Published: 2026-04-14

---

**Claude Managed Agents store system prompts as immutable, server-side versioned resources, letting you pin production traffic to specific versions and rollback instantly without redeploying application code.**

Claude Managed Agents provide a lightweight configuration layer for LLM system prompts that lives entirely within Anthropic’s API. By implementing prompt versioning and rollback with Claude Managed Agents, you can iterate on prompts safely, run canary evaluations against labeled datasets, and revert to stable versions in milliseconds—all through API calls without touching your deployment pipeline or container images.

## How Prompt Versioning Works in Claude Managed Agents

Claude Managed Agents treat system prompts as **versioned server-side resources**. When you create an agent with `client.beta.agents.create()`, the API returns an `agent.id` and a `version: 1` field. Each subsequent call to `client.beta.agents.update()` increments this version counter and stores the new prompt as an immutable snapshot. Older versions remain addressable forever, allowing you to pin any session to any historical version by specifying the `version` parameter in `client.beta.sessions.create()`.

This architecture enables **zero-touch deployments**. Adding a new prompt version requires a single API call; no CI pipeline rebuilds or container restarts are necessary. If a new prompt degrades performance, you simply repin your production config to the previous version number—all new sessions immediately use the stable prompt without code changes.

## Creating Your First Agent Version

The workflow begins by creating an environment runtime and your initial agent. In the reference notebook `managed_agents/CMA_prompt_versioning_and_rollback.ipynb`, the setup looks like this:

```python

# Create the execution environment

env = client.beta.environments.create(name="ticket-triage-env")
ENV_ID = env.id

# Define the v1 system prompt

V1_SYSTEM = """You are a support-ticket triage agent for a usage‑billed API product.
Read the ticket and respond with ONLY a single line of raw JSON (no code fences, no prose):
{"team": "<billing|auth|api‑platform|dashboard>", "priority": "<P1|P2|P3>"}"""

# Create the agent - this returns version 1

agent = client.beta.agents.create(name="ticket-triage",
                                 model=MODEL,
                                 system=V1_SYSTEM)
AGENT_ID = agent.id
print(f"env={ENV_ID}  agent={AGENT_ID} v{agent.version}")

# → env=env_…  agent=agent_… v1

```

The `agent.version` field starts at `1` and auto-increments on every update. The `AGENT_ID` remains constant across all versions, acting as a stable pointer to the agent resource while versions track the prompt history.

## Pinning Sessions to Specific Versions

The core mechanism for safe rollout is **version pinning**. When creating a session, you pass a dictionary containing `type`, `id`, and `version` to guarantee that specific request uses exactly the prompt snapshot you intend:

```python
def triage(version: int, ticket: dict) -> dict:
    """Run one ticket through a pinned agent version and return its verdict."""
    session = client.beta.sessions.create(
        agent={"type": "agent", "id": AGENT_ID, "version": version},
        environment_id=ENV_ID,
    )
    try:
        prompt = "Subject: " + ticket["subject"] + "\n\n" + ticket["body"]
        client.beta.sessions.events.send(
            session.id,
            events=[{"type": "user.message",
                     "content": [{"type": "text", "text": prompt}]}],
        )
        # Poll until the session reaches idle status

        deadline = time.time() + 60
        while time.time() < deadline:
            events = client.beta.sessions.events.list(session.id).data
            if events and events[-1].type == "session.status_idle":
                break
            time.sleep(1)
        else:
            raise TimeoutError("session did not idle in 60s")
        
        # Extract the agent's JSON response

        reply = "".join(
            b.text for e in events if e.type == "agent.message"
            for b in e.content if b.type == "text"
        )
        return json.loads(reply)
    finally:
        # Clean up the ephemeral session

        try:
            client.beta.sessions.archive(session.id)
        except Exception:
            pass

```

By hardcoding the `version` parameter in `sessions.create()`, you create deterministic behavior. Production traffic can stay pinned to `version: 1` while you evaluate `version: 2` on a test dataset using the same code path.

## Evaluating Prompt Versions Against Test Sets

Before promoting a new prompt, you can score it against a labeled dataset using a helper that compares predictions across versions:

```python
def score(version: int) -> dict:
    hits = defaultdict(lambda: [0, 0])  # {team: [correct, total]}

    for t in tickets:
        pred = triage(version, t)
        hits[t["team"]][1] += 1
        if pred.get("team") == t["team"]:
            hits[t["team"]][0] += 1
    return dict(hits)

# Evaluate v1 performance

v1_scores = score(version=1)
print("v1 results:")
for team, (correct, total) in sorted(v1_scores.items()):
    print(f"  {team:14s} {correct}/{total}")

```

This pattern allows **canary testing**. Pin a small fraction of traffic to a newer version while the majority stays on the stable version, then compare accuracy metrics before promoting.

## Updating Prompts and Rolling Back

When you need to modify behavior, create a new version using `client.beta.agents.update()`. You must pass the current `agent.version` as a parameter to ensure you are updating the expected base:

```python
V2_SYSTEM = V1_SYSTEM + (
    "\n\nROUTING RULE: If the ticket text mentions API usage, rate limits, quotas, "
    "or request volume, route to api‑platform. Apply this rule before any other "
    "consideration; do not second‑guess it based on the rest of the ticket."
)

# Update creates immutable v2

agent = client.beta.agents.update(AGENT_ID,
                                 version=agent.version,
                                 system=V2_SYSTEM)
print(f"agent {AGENT_ID} now at v{agent.version}")

# → agent … now at v2

```

**Rollback requires no API call.** If `v2` shows regression for the billing team in your evaluation, simply change the pinned version in your production configuration:

```python

# Rollback configuration

pinned_version = 1  # Back to stable v1

# All new sessions now use v1 automatically

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

```

The old prompt stays on Anthropic’s servers indefinitely and can be reused instantly by referencing its version number.

## Auditing Version History

For governance and debugging, list all immutable versions with their creation timestamps and prompt snippets:

```python
for v in client.beta.agents.versions.list(AGENT_ID).data:
    print(v.version, v.created_at, v.system[:60] + "…")

```

This provides a complete **audit trail** showing who created each version, when, and the exact prompt text that was active. Archived agents retain their version history, though you cannot create new sessions from archived resources.

## Key Implementation Files

The Claude Cookbooks repository contains complete reference implementations:

- **`managed_agents/CMA_prompt_versioning_and_rollback.ipynb`** – Full end-to-end notebook demonstrating environment creation, version pinning, evaluation, and rollback for a support-ticket triage use case.
- **[`tool_use/utils/visualize.py`](https://github.com/anthropics/claude-cookbooks/blob/main/tool_use/utils/visualize.py)** – Helper utilities for streaming and monitoring session events; useful when debugging version-specific behavior differences.
- **[`patterns/agents/util.py`](https://github.com/anthropics/claude-cookbooks/blob/main/patterns/agents/util.py)** – Reusable wrapper around the Anthropic client (`llm_call`, XML extraction) for building custom version-aware tooling.
- **[`skills/skill_utils.py`](https://github.com/anthropics/claude-cookbooks/blob/main/skills/skill_utils.py)** – Contains `get_skill_version` and `list_skill_versions` functions showing the same immutable version pattern applied to Claude Skills.

## Summary

- **Claude Managed Agents** version system prompts server-side, creating immutable snapshots (`v1`, `v2`, etc.) on every update.
- **Pin sessions** to specific versions using the `version` field in `client.beta.sessions.create()` to guarantee deterministic behavior for testing or production.
- **Rollback instantly** by changing the pinned version number in your config—no code deployment or container rebuild required.
- **Audit everything** via `client.beta.agents.versions.list()` to see the full history of prompt changes with timestamps.
- ** zero-touch deployments** mean you can iterate on prompts through API calls alone, enabling rapid experimentation and safe production updates.

## Frequently Asked Questions

### How does version pinning work in Claude Managed Agents?

When calling `client.beta.sessions.create()`, you pass an `agent` dictionary that includes `type`, `id`, and a specific `version` integer. The session will use exactly that version of the system prompt, regardless of how many newer versions exist. This allows you to run `v1` in production while testing `v2` on the same agent ID.

### Can I delete old prompt versions?

No, versions are immutable and cannot be deleted. When you archive an agent using `client.beta.agents.archive()`, the version history remains stored but inactive. You cannot create new sessions from archived agents, but if you restore the agent, all historical versions are still accessible for pinning.

### How do I test a new prompt before rolling it out to production?

Create the new version with `client.beta.agents.update()`, then run evaluation scripts that pin sessions to the new version number while your production traffic remains pinned to the stable version. Compare metrics like accuracy or latency against your labeled test set before updating the production pinned version.

### What happens to existing sessions when I update an agent?

Nothing. Sessions capture the version at creation time and maintain that prompt snapshot for their entire lifetime. Updating an agent to `v2` does not affect currently running sessions using `v1`; only new sessions created after the update will use the new version (unless explicitly pinned to an older one).