Response format
The messages field
messages are the raw LLM messages (role/content) with tool call IDs, results and thinking. They — not output — are the model’s actual context.
A simple turn without tools
[
{ "role": "user", "content": [{ "type": "text", "text": "Hi!" }] },
{
"role": "assistant",
"content": [{ "type": "text", "text": "Hi! How can I help?" }],
"usage": { "input_tokens": 25, "output_tokens": 8, "cached_tokens": 0 },
"finish_reason": "stop"
}
]
A turn with a tool call
The call and its result are linked by an ID (tool_call.id ↔ tool_result.tool_call_id):
[
{ "role": "user", "content": [{ "type": "text", "text": "What's the weather in Moscow?" }] },
{
"role": "assistant",
"content": [{
"type": "tool_call",
"tool_call": { "id": "toolu_01XYZ", "name": "get_current_weather", "arguments": { … } }
}],
"finish_reason": "tool_calls"
},
{
"role": "tool",
"content": [{
"type": "tool_result",
"tool_result": { "tool_call_id": "toolu_01XYZ", "content": [{ "type": "text", "text": "{\"temp\":12}" }] }
}]
},
{
"role": "assistant",
"content": [{ "type": "text", "text": "It's +12°C and cloudy in Moscow." }],
"finish_reason": "stop"
}
]
Extended thinking
{
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "Let's work through this step by step…" },
{ "type": "text", "text": "Answer: 42" }
],
"finish_reason": "stop"
}
Why you can’t rebuild messages by hand
tool_call.idandtool_result.tool_call_idare unique IDs that tie a tool request to its response. Without them the protocol breaks.- The
signaturein a tool_call is provider-specific data. - For some models, the order of thinking blocks relative to the text matters.
For multi-turn, pass messages as is
To continue the conversation, pass the previous turn’s messages unchanged in the
turns[] of the next request. See Multi-turn conversations.
Empty or missing messages means the model doesn’t see the history.