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.id and tool_result.tool_call_id are unique IDs that tie a tool request to its response. Without them the protocol breaks.
  • The signature in 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.