Short answer: Pass an initial_events array on POST /v1/sessions and the session is created directly in running status — the agent loop starts in the same call, so you skip the second request to the events endpoint. It takes up to 50 user.message and user.define_outcome events, validation is all-or-nothing, and the seeded events aren't echoed back, so read them from the session's event list.

A Claude Managed Agents session has always followed a two-step lifecycle: create the session, then send a user event to start work. That's a clean state machine, but for the common case — you already know the first thing you want the agent to do — it costs you a round-trip you didn't need. A July 22, 2026 change closes that gap with an initial_events parameter.

Before: create, then send#

The classic two-call pattern. Create the session, capture its id, then post the first event:

# 1) create an idle session
session=$(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 "{\"agent\":\"$AGENT_ID\",\"environment_id\":\"$ENVIRONMENT_ID\"}")
SESSION_ID=$(jq -r '.id' <<< "$session")

# 2) hand it work — this is what actually starts the agent loop
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" \
  -H "content-type: application/json" \
  -d '{"events":[{"type":"user.message",
        "content":[{"type":"text","text":"List the files in the working directory."}]}]}'

After: seed at creation#

Move the first event into initial_events on the create call. A non-empty list creates the session directly in running status and starts the agent loop inside that same request, so there is no follow-up call to the events endpoint at all. The body is the same as a plain create, plus one initial_events array carrying your opening user.message:

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",
  "initial_events": [
    {
      "type": "user.message",
      "content": [{"type": "text", "text": "List the files in the working directory."}]
    }
  ]
}
EOF

Same thing in the SDKs — pass initial_events to sessions.create:

seeded = 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."}]},
    ],
)
const seeded = await 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." }] }
  ]
});

Reading the seeded events back#

The one surprise: initial_events are not echoed on the create response. They're persisted with server-assigned ids exactly as if you'd posted them to the events endpoint — you just have to list them:

for event in client.beta.sessions.events.list(seeded.id):
    if event.type == "user.message":
        for block in event.content:
            if block.type == "text":
                print(f"Seeded event: {block.text}")

Seed the goal too, not just the prompt#

initial_events isn't limited to a plain message. You can seed a user.define_outcome event in the same call, so the agent starts with both its task and the rubric it will be graded against. Two rules keep you out of a 400:

That's the highest-leverage use of the feature: one request that spins up the session, states the task, and pins the success criteria, all before the sandbox is even provisioned.

The gotchas, in one place#

When to reach for it#

Use initial_events whenever you already know the opening move: a one-shot task, a cron-style kickoff, a fan-out where each session gets a fixed brief — and if each of those sessions needs a different model or tool set, pair it with per-session config overrides in the same create call. Stick with create-then-send when you genuinely need the session id before you can compose the first message — for instance, when the task text references the session itself. For everything else, it's a free round-trip back in your latency budget, and a cleaner call site: the session and its first instruction arrive together.