Messages in the Claude API aren’t plain strings. Each message’s content field is an array of typed blocks. There are four types, and each flows in a specific direction.
The four types
| Block Type | Direction | Purpose |
|---|---|---|
text | Both ways | Regular text content — user sends questions, model sends answers |
image | User → Model only | Images sent for the model to analyze (vision) |
tool_use | Model → Developer | Model requests a tool call with specific parameters |
tool_result | Developer → Model | Developer returns the tool execution result |
The direction matters. You can’t send a tool_use block to the model — that’s something the model generates. You can’t receive an image block from the model — it doesn’t produce images through this mechanism.
String shorthand for simple text
For simple text-only messages, you can skip the array syntax entirely:
{"role": "user", "content": "Hello"}
This is equivalent to:
{"role": "user", "content": [{"type": "text", "text": "Hello"}]}
Both are valid. The shorthand is convenient for simple cases. The array form is necessary when you need multiple blocks in one message.
Multiple blocks in one message
A single assistant response can contain both text and tool_use blocks simultaneously. The model might explain what it’s about to do (text block) and then request a tool call (tool_use block) — all in the same response. The content array holds both.
This is important for agentic loops: don’t assume that a response with stop_reason: "tool_use" contains only tool_use blocks. There may be text blocks alongside them. Checking for text content to determine loop termination is an anti-pattern precisely because text and tool_use can coexist.
One-liner: Four content block types (text, image, tool_use, tool_result), each with a fixed direction — and a single response can contain multiple block types simultaneously.