Mid · Core path · 8 chapters
Chat API shape
Roles, message arrays, multi-turn
Small schema mistakes and unbounded history become expensive, opaque production failures when the SDK hides the wire format.
Read these first
What you will know
Construct a multi-turn request by hand, without an SDK, and get a response
Construct the JSON by hand with curl once. Knowing what the SDK sends removes a whole layer of mystery.
Run equivalent requests with an instruction placed in each role and compare the results
Role precedence and provider-specific handling can change compliance, especially when assistant messages are used to steer generation.
Trace a multi-turn client and verify that each request contains the prior turns rather than relying on server-side memory
A retry on another worker or a process restart loses context unless the application persists and resends the conversation.
Calculate cumulative input tokens and estimated cost for full-history resend, then compare it with a truncation policy
With similarly sized turns, full-history input grows O(n²) across the conversation, while a bounded window keeps it roughly linear.
Identify a malformed message array from the error alone
Wrong role, missing content, or an unpaired tool result — the error text usually names the index.
Chapters
- 1
The messages array
A chat API request is one JSON document: a model, a ceiling, and an ordered array of role-tagged messages. Build one by hand with curl, read the response envelope to find where the generated text sits and where the token counts and stop reason sit, then extend it into a multi-turn conversation. Once you have seen that body, every SDK convenience becomes a transformation you can predict.
- 2
The system role
The instruction channel is either a top-level field beside the messages array or an entry at the head of it, and on providers of the first kind a mid-array system message is a schema violation that fails with an error naming the index. Underneath the schema question is a behavioural one: post-training weights the instruction channel above user content, which is measurable on your own workload in about twenty minutes.
- 3
Assistant prefill
A partial assistant message at the end of the messages array makes some providers continue from it rather than reply to it: send an opening brace and JSON comes back, minus the brace you sent. Other providers reject a trailing assistant turn outright. This chapter covers how to probe which behaviour a model has, how to re-prepend the prefill before parsing, and what constrained decoding replaces it with.
- 4
Tool calls and tool results
Beyond system, user and assistant there is a fourth shape in the messages array: an assistant entry carrying a structured tool call, and a separate entry answering it, joined by an id the provider issued. Send one by hand to see both entries and the id linking them, then see the provider reject an array where the pairing is broken — a constraint that makes some cut positions in a conversation unsafe later.
- 5
Stateless requests and conversation history
Each chat API request is independent: the model sees only the array you send, and nothing on the provider's side accumulates a transcript. This chapter gives the two-request experiment that demonstrates it, the storage schema that survives three workers and a deploy, and the distinction between owning conversation history and the prompt caching people confuse it with.
- 6
Quadratic growth of input tokens
Turn n of a conversation carries every prior turn with it, so with roughly equal turns the cumulative billed input follows n(n+1)/2 rather than n. Work the arithmetic on a realistic support-bot shape — forty turns, six hundred tokens per pair, fifty thousand conversations a month — set it against a bounded window and against prompt caching, and see which ceiling each one moves.
- 7
Trimming conversation history
A window of the last eight messages can cut between the assistant entry that issued a tool call and the entry answering it, orphaning a pair the provider validates by id. This chapter covers reading a malformed messages array from the error text alone, trimming on a token budget cut at legal boundaries, validating the array before it goes out, and replaying real conversation shapes offline to find the cut positions production will hit.
- 8
Retries and duplicate generations
When a client times out after the provider has produced a full response, the queued retry sends a byte-identical messages array and bills a second full generation of a conversation that already grows quadratically. An unguarded write then leaves two assistant messages answering one user turn, which the next request feeds back as real conversation. Compare optimistic and post-call persistence, then build the claim record and idempotency key that make a redelivered job harmless.
External resources
- Messages API Referencedocs.anthropic.com
Exact request/response shape: role alternation, content blocks, system param.
- Messages APIplatform.claude.com
Shows roles, message arrays, and multi-turn conversation structure for Claude’s Messages API.
- Using the Messages APIplatform.claude.com
Shows how roles, message arrays, and multi-turn conversations work in Claude’s Messages API.
- Using the Messages API (examples)docs.anthropic.com
Copyable multi-turn, prefill and vision request bodies to run immediately.
- Work with chat completion modelslearn.microsoft.com
Clear breakdown of system/user/assistant roles and how the transcript is replayed.
- Claude API: How to get a key and use the APIzapier.com
Walks through Claude API requests using roles, message arrays, and multi-turn conversations.
- Why LLMs Need System, User & Assistant Messages (Spring AI Implementation)Programming Tutorials
Shows how roles collapse into one prompt string the model actually sees.