v vanemmerik.ai / aws-ai
Tip of the Day 2026 · 05 · 27 ≈ 7 min read Bedrock AgentCore · Runtime data plane

The /invocations contract, on the wire.

Yesterday's tip framed AgentCore Runtime as the missing primitive between Lambda and a Kubernetes cluster. Today we zoom into the wire — what the data plane actually expects from your container, and what shape a single turn of conversation takes when you bypass the CLI and call InvokeAgentRuntime directly with boto3 or curl.

POST /invocations  ·  0.0.0.0:8080  ·  ARM64  — two endpoints, one container

01Two endpoints, one container

Runtime's HTTP protocol is a small, opinionated contract. Your container exposes exactly two paths on port 8080:

That's the whole HTTP surface. WebSockets (/ws) are optional and opt-in for bidirectional streaming, and live on the same port. MCP, A2A, and AG-UI agents follow their own protocol contracts at different paths — but for plain HTTP agents, /invocations plus /ping is the spec.

The shift

Runtime is doing nothing magical at the network layer. It's a managed reverse proxy in front of an ARM64 container that speaks two well-known paths. Once you internalise that, debugging a Runtime deployment stops feeling like a black box.

02Container requirements

Three immovable rules from the docs:

Run an x86 image and the container fails to start with the classic exec /bin/sh: exec format error. Bind to 127.0.0.1 and /ping fails because Runtime's health-checker can't reach you. Listen on 8081 and nothing ever connects.

03The /invocations request, exactly

The data-plane API is InvokeAgentRuntime. Internally it forwards HTTP to your container at POST /invocations, but at the AWS API boundary the URL is:

InvokeAgentRuntime — request
POST /runtimes/{agentRuntimeArn}/invocations ?accountId={accountId} &qualifier={endpointName} (defaults to DEFAULT) Content-Type: application/json Accept: application/json X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: 8f4e… (33–256 chars) X-Amzn-Bedrock-AgentCore-Runtime-User-Id: user-42 (optional) X-Amzn-Trace-Id / traceparent / tracestate / baggage { "prompt": "What's the weather today?" }

Reconstruction from the InvokeAgentRuntime reference

Headers the caller may send, all documented on the InvokeAgentRuntime reference:

HeaderRequiredNotes
Content-TypeyesMost agents use application/json.
AcceptnoMIME type the caller wants back.
X-Amzn-Bedrock-AgentCore-Runtime-Session-IdnoThe session id. 33–256 characters. Same id across calls = same conversation.
X-Amzn-Bedrock-AgentCore-Runtime-User-IdnoPer-user attribution. Adds an extra IAM check (bedrock-agentcore:InvokeAgentRuntimeForUser).
Mcp-Session-Id, Mcp-Protocol-VersionnoUsed by MCP agents.
X-Amzn-Trace-Id, traceparent, tracestate, baggagenoDistributed-tracing headers — forwarded into your container so OTEL spans link up.

Payload size cap: 100,000,000 bytes — the payload field on the API has Maximum length of 100000000. Plenty for prompts; relevant when you're sending multi-modal binary blobs.

Session id length matters. The API documents runtimeSessionId as "Minimum length of 33. Maximum length of 256." A bare UUID4 (8-4-4-4-12 = 36 chars) clears it; a 32-char hash doesn't. The docs explicitly recommend a UUID.

04The /invocations response, two shapes

Your container picks the response format per request, signalled by Content-Type:

JSON (non-streaming) — for quick, deterministic answers.

HTTP/1.1 200 OK Content-Type: application/json   {"response": "It's 18°C and clear in Cape Town.", "status": "success"}

Server-Sent Events (streaming) — for long-running reasoning, tool loops, or token-by-token chat. The format is the standard SSE shape from the WHATWG spec:

HTTP/1.1 200 OK Content-Type: text/event-stream   data: {"event": "partial response 1"} data: {"event": "partial response 2"} data: {"event": "final response"}

There is no separate "register as streaming" step. You just set Content-Type: text/event-stream on the response and start writing data: …\n\n lines.

05Calling it from boto3

The canonical client-side path, from the AWS docs:

import boto3, json, uuid   client  = boto3.client("bedrock-agentcore")  # one hyphen session = str(uuid.uuid4())  # 36 chars — clears the 33-min   response = client.invoke_agent_runtime(   agentRuntimeArn = AGENT_ARN,   runtimeSessionId = session,   payload = json.dumps({"prompt": "Hi"}).encode(), )   if "text/event-stream" in response.get("contentType", ""):   for line in response["response"].iter_lines(chunk_size=10):     if line and line.startswith(b"data: "):       print(line[6:].decode("utf-8")) else:   chunks = [c.decode("utf-8") for c in response.get("response", [])]   print(json.loads("".join(chunks)))

Things worth memorising:

06/ping, and what HealthyBusy is really for

GET /ping returns JSON with two fields:

Content-Type: application/json HTTP 200   {   "status": "Healthy",   "time_of_last_update": 1764201600 }

status has exactly two documented values:

That last sentence is the hidden feature. A HealthyBusy ping is how a background agent — one that's still grinding on a long-running job after the user disconnected — tells Runtime "don't reap me yet." For chat-style agents you'll never need anything but Healthy. For deep-research agents that finish their work hours later, HealthyBusy is what keeps the 8-hour session alive.

time_of_last_update is a Unix timestamp Runtime uses to gauge how long you've been in the current state.

07OAuth-configured agents return 401, not 403

If you've put your Runtime behind OAuth instead of SigV4, missing credentials get a different status code on purpose:

HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"

That WWW-Authenticate header is the OAuth 2.0 discovery hint — it points a confused client at GetRuntimeProtectedResourceMetadata, where it can learn which IdP to talk to. SigV4-configured agents return 403 with an ACCESS_DENIED error and no WWW-Authenticate header. Same "you can't come in," two very different protocols on the wire.

Also: if your agent uses OAuth, you can't call it with the AWS SDK. InvokeAgentRuntime over boto3 is SigV4-only. OAuth agents must be invoked via a plain HTTPS request with a bearer token.

08Limits worth knowing

09Try it in five minutes

If yesterday's agentcore deploy already gave you a Runtime ARN, you can hit it from a Python shell right now:

$ pip install --upgrade boto3 $ export AGENT_ARN="arn:aws:bedrock-agentcore:us-east-1:…:runtime/HelloAgent" $ python - <<'PY' import boto3, json, uuid, os c = boto3.client("bedrock-agentcore") r = c.invoke_agent_runtime(   agentRuntimeArn=os.environ["AGENT_ARN"],   runtimeSessionId=str(uuid.uuid4()),   payload=json.dumps({"prompt": "One-sentence joke about ARM64."}).encode(), ) print("content-type:", r.get("contentType")) for chunk in r.get("response", []):   print(chunk.decode("utf-8"), end="") PY

Tomorrow we'll switch surfaces and look at AgentCore Memory — how short-term and long-term memory slot in alongside a Runtime session, and what a namespace actually buys you.

Verified against the official AWS docs on 2026-05-27.
Sources: HTTP protocol contract, InvokeAgentRuntime API reference, Invoke an AgentCore Runtime agent.
If the docs change, this tip is a snapshot of that day — check the sources for current behaviour.
Heads up — this tip is from 2026-05-27. AWS services move fast. Cross-check the AgentCore developer guide before relying on specifics, then come back for today's tip →
C

This page — research, writing, verification, and deployment — was built by Claude Cowork. No human touched the prose, the layout, or the upload pipeline. The tip was generated this morning, cross-checked against the official AWS docs by an independent verification pass, and published to Cloudflare R2 on a schedule.

A daily experiment by Monty van Emmerik · vanemmerik.ai · what is Claude Cowork?