Messages API Roles and Content Block Types Reference
A dense, scannable reference for the two message roles and the four content block types you'll encounter building against the Messages API.
Search across all documentation pages
A dense, scannable reference for the two message roles and the four content block types you'll encounter building against the Messages API.
Keep this page open while wiring up requests - it's meant to be looked up, not read start to end.
| Role | Who sends it | Notes |
|---|---|---|
user | Your application | Represents input to Claude - questions, tool results, images. Must be the first entry in messages and must alternate with assistant. |
assistant | Claude (echoed back by you on the next turn) | Represents Claude's own prior replies, including any tool_use blocks. You append it after each response to preserve history. |
system (not a role in messages) | Your application | A separate top-level request parameter, not a messages entry - sets persona/constraints for the whole conversation. |
| Type | Appears In | Minimal Shape |
|---|---|---|
text | user, assistant | {"type": "text", "text": "..."} |
image | user only | {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "..."}} |
tool_use | assistant only | {"type": "tool_use", "id": "toolu_...", "name": "get_weather", "input": {"location": "Paris"}} |
tool_result | user only | {"type": "tool_result", "tool_use_id": "toolu_...", "content": "..."} |
{"role": "user", "content": [{"type": "text", "text": "What is the capital of France?"}]}| Field | Type | Notes |
|---|---|---|
type | "text" | Always the literal string "text". |
text | string | The message content. A plain string content value (no list) is shorthand for a single text block. |
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": base64_data}},
{"type": "text", "text": "What's in this image?"},
],
}| Field | Type | Notes |
|---|---|---|
type | "image" | Always "image". |
source.type | "base64" or "url" | base64 embeds the image data directly; url references a publicly reachable image. |
source.media_type | string | Required for base64 sources, e.g. "image/png", "image/jpeg". |
source.data | string | Base64-encoded image bytes, required for base64 sources. |
Only appears in user turns - Claude produces text describing images, never image blocks of its own.
# Appears inside an assistant turn's content, produced by Claude - you don't write this by hand.
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": {"location": "Paris, France"},
}| Field | Type | Notes |
|---|---|---|
type | "tool_use" | Always "tool_use". |
id | string | Unique ID for this tool call - echo it back in the matching tool_result. |
name | string | The tool name, matching one of the tools you declared on the request. |
input | object | Parsed arguments matching the tool's input_schema. Always parse with your JSON tooling rather than string-matching - never assume a fixed serialization. |
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": "toolu_01A09q90qw90lq917835lq9", "content": "72F and sunny"},
],
}| Field | Type | Notes |
|---|---|---|
type | "tool_result" | Always "tool_result". |
tool_use_id | string | Must match the id of the tool_use block it's answering. |
content | string or list of blocks | The tool's output. Can itself be a list of text/image blocks for richer results. |
is_error | boolean (optional) | Set true if the tool call failed, so Claude knows to adapt rather than treat the content as a valid result. |
| Scenario | Turn Content |
|---|---|
| Plain question | [{"type": "text", "text": "..."}] (or a bare string) |
| Question about an image | [{"type": "image", ...}, {"type": "text", "text": "..."}] |
| Claude requesting a tool call | [{"type": "text", "text": "..."}, {"type": "tool_use", ...}] |
| Returning a tool result plus a follow-up comment | [{"type": "tool_result", ...}, {"type": "text", "text": "..."}] |
Multiple blocks of the same or different types can coexist in a single turn's content list - role alternation applies to the turn as a whole, not to individual blocks.
Yes - content: "some text" is shorthand for content: [{"type": "text", "text": "some text"}]. Use the list form once you need more than one block or a non-text block type.
Claude produces text and tool_use blocks in assistant turns. image and tool_result blocks are things your application sends in user turns; Claude never generates them.
The tool_use_id field on the tool_result block must exactly match the id field Claude generated on the corresponding tool_use block.
Yes - Claude can request several tool calls in a single assistant turn. When that happens, return all the matching tool_result blocks together in a single user turn, not split across multiple turns.
It tells Claude the tool call failed rather than succeeded, so Claude reasons about the failure and can try a different approach instead of treating the error message as valid data.
No. system is a separate top-level request parameter, not a role or a content block inside messages. It has no block types of its own.
No - image blocks only appear in user turns, since they represent input you're providing to Claude, not something Claude generates.
base64 embeds the raw image bytes directly in the request; url instead points to a publicly reachable image URL that Claude fetches. Use base64 for local files and url when the image is already hosted.
Always parse it - input is delivered as a structured object matching your tool's schema, and relying on exact string formatting (like specific escaping) is fragile and can break across model versions.
Yes - content on a tool_result block can be a string, or a list of blocks (such as text and image) for richer results, like returning a generated chart image alongside a text summary.
Generally yes for readability and how Claude processes multi-block context - place explanatory text before or after the block it refers to consistently, though there's no strict alternation rule within one turn's content list the way there is across turns.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, SDK versions, and pricing move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 18, 2026