Firstpass v0.2.1
DocsGuaranteeBenchmarksCLIGitHub

Docs / How Agents Talk to Models / One request, one response

Part A · The core pathModule 1 of 13

One request, one response

One model call is a single HTTP POST carrying a model name and a messages array. The reply is one JSON object whose usage field is the meter: input_tokens read, output_tokens written. The model keeps no memory of the call — that one fact drives every cost in this page.

The wire, in full

A language model is a stateless function: text in, text out, no memory of the last call. You talk to it over HTTP. The two dialects you'll meet are Anthropic's POST /v1/messages and OpenAI's POST /v1/chat/completions — same idea, different field names. Here is the whole thing, provider-direct, nothing clever:

bash
# a single call, straight to the provider
curl https://api.anthropic.com/v1/messages \
  -H 'content-type: application/json' \
  -H 'x-api-key: $KEY' -H 'anthropic-version: 2023-06-01' \
  -d '{
    "model": "claude-haiku-4-5",
    "max_tokens": 256,
    "system": "You are a terse senior engineer. Answer in one line.",
    "messages": [{"role": "user", "content": "Does POST /orders reject a request with no total field?"}]
  }'

That is the developer's first move on our running task — just asking, before any agent touches the code. Two fields beyond messages are worth naming now. max_tokens is the hard ceiling on the output — the model will be cut off there (more on that below). The optional system field is the system prompt: standing instructions, separate from the conversation, that ride along as input on every call. It's the first thing loaded and the last thing that changes — which (jump to caching) makes it prime cache material.

Same wire, different front doors

The curl above hits Anthropic directly, but the body — model, messages, tools — is identical wherever Claude runs. Only the front door changes:

AccessEndpoint & authWhere the model id goes
Anthropic APIapi.anthropic.com · x-api-keyin the body ("model")
Amazon Bedrockbedrock-runtime.<region>… · AWS SigV4 (IAM)in the URL path; body carries anthropic_version: "bedrock-2023-05-31"
Google VertexVertex AI endpoint · Google OAuthin the URL path; body carries anthropic_version: "vertex-2023-10-16"
Under the hood Everything in this course — tokens, multi-turn resend, tool loops, caching, streaming, retries — is identical across all three; only the endpoint, auth, and where the model id sits differ. Firstpass treats Anthropic, Bedrock, and Vertex as one Anthropic-dialect family: it forwards your body verbatim and swaps only the model, so routing Bedrock- or Vertex-hosted Claude works the same as calling Anthropic direct.

The response is one JSON object. The part that matters for cost is usage — the model reports how many tokens it read (input) and how many it wrote (output):

json
{
  "id": "msg_01F3k…",
  "role": "assistant",
  "model": "claude-haiku-4-5",
  "content": [{ "type": "text", "text": "No — it reads req.body.total without a presence check." }],
  "stop_reason": "end_turn",
  "usage": { "input_tokens": 32, "output_tokens": 14 }   // ← the meter
}

Reading the response

Four parts matter. id and model echo the call. content is an array of blocks — a text block here, but it can be a tool_use block (tools), or several blocks at once. stop_reason says why it ended (below). And usage is the meter — input_tokens and output_tokens, plus cache_creation_input_tokens and cache_read_input_tokens once caching is on. If you log one field per call, log usage.

When the model stops: stop_reason

Every response says why it ended — and a truncated reply still looks like a reply, so always check it before trusting the body:

stop_reasonMeansYou do
end_turnfinished naturallyuse the answer
tool_usewants a tool run firstrun it, call again with the result
max_tokenshit your output ceiling — cut off mid-sentencedon't trust the body; raise max_tokens and retry
stop_sequencehit a stop string you setuse the answer up to it

The max_tokens row is the quiet failure mode — you were billed for every token written before the cut, and it bites hardest with structured output, where half a JSON object doesn't parse (structured outputs).

Common mistake Treating max_tokens as “how long an answer I want” and setting it low to save money. It's a guillotine, not a hint — the model doesn't aim to finish under it, it just stops when it hits it. Set it comfortably above the longest valid answer and always check stop_reason == "max_tokens" before trusting the body.
A single HTTP POST carrying a model name and a messages array reaches the model, which is stateless and keeps no memory; it returns one JSON response whose usage field reports 32 input tokens read and 14 output tokens written. POST → model → JSON · stateless, no memory kept POST /v1/messages "model": "claude-haiku-4-5" "messages": [ {role:"user", content:"Does /orders…"}] 32 input tokens reads the model stateless · no memory writes 200 OK · response JSON content: [{ text: "No — no check." }] usage: { input_tokens: 32, output_tokens: 14 } 14 output tokens the model forgets everything the instant it answers — no state persists between calls
One stateless POST in, one JSON response out — the usage field reports every token the model read and wrote, and nothing is retained.
Key idea The usage object is the only cost signal you get, and the provider hands it to you on every response. input_tokens is everything the model read — your system prompt, tools, the whole message history, this turn's text — and output_tokens is only what it just wrote. If you log one thing per call, log usage.
Under the hood Tokens aren't words. A tokenizer splits text into sub-word pieces — "orders.py" might be three tokens, a curly brace one, a run of whitespace one. Rough rule of thumb for English prose and code: ~4 characters per token, or ~0.75 tokens per word. Punctuation-dense code trends higher. You never count them by hand — usage is ground truth — but the 4-chars mental model tells you why a 300-line file lands around 3–4k input tokens.
A model call is a stateless POST; the reply's usage field reports input vs output tokens, and the model forgets everything the instant it answers.