The notes explain the why; this is the what. It is meant for what doesn't stick because you never write it: field names, admitted values and return shapes. The exam doesn't ask you to write code, but it does ask you to recognise the right name among four that sound alike.
Messages API POST /v1/messages
| Field | Required | Notes |
model | Yes | Snapshot ID. IDs without a date are still fixed snapshots, not moving pointers |
messages | Yes | Only user and assistant roles. Consecutive turns of the same role are merged |
max_tokens | Yes | 0 is used to warm the cache without generating a response |
system | No | Top-level parameter, string or array of blocks. There is no system role |
tools, tool_choice | No | See the tool use section |
stream | No | true enables SSE |
thinking, output_config | No | See reasoning and structured output |
temperature, top_p, top_k, stop_sequences | No | Sampling |
metadata, service_tier, inference_geo | No | inference_geo: "global" by default or "us", at a surcharge |
What gets askedThat the system prompt is a separate parameter and that there is no system role inside messages. It is the most cited difference against other chat APIs.
The interaction modes
A developer reaches Claude through a REST API, normally via an SDK. The choice of mode is settled with two questions: is someone waiting? and is it real time or offline volume?
| Mode | When |
| Synchronous | Someone is waiting and the response is short. The simplest one |
| Streaming | Someone is waiting and the response is long: buys perceived latency in exchange for assembling the response yourself |
| Async / await | Nobody is waiting at that instant, but it is still real time: concurrency without blocking |
| Batch | Nobody is waiting and it is offline volume: lower cost per token, non-deterministic latency |
What gets askedThat firing synchronous calls in parallel is not batch: it gives concurrency, not a discount. And that streaming speeds nothing up — it improves perceived latency.
The context window and its two ways of breaking
It is a fixed token budget that holds the entire request at once: system, history, tools, documents and the generation. It fails in two distinct ways, and the exam may ask you to tell them apart:
| Situation | What happens |
| The input no longer fits | Error before generating. There is no partial response |
| The ceiling is hit mid-generation | Truncated output with stop_reason: model_context_window_exceeded |
What gets askedThat managing the history is the application's job, not the API's. Nobody prunes for you: compaction, pruning and subagents are your decisions.
stop_reason values
| Value | Means | What you do |
end_turn | Finished naturally | Close the turn |
tool_use | Asks to run one or more tools | Run them and return tool_result; the loop continues |
max_tokens | Truncated output | Can break strict JSON. Retry with more budget |
stop_sequence | Hit a stop sequence | Handle it per your protocol |
refusal | Declined to answer | Returns 200 and is billed. Takes precedence over the schema |
pause_turn | Pause in a long operation | Continue the turn |
model_context_window_exceeded | Went past the window | Compact or reduce context |
What gets askedtool_use versus end_turn as the control of the agentic loop, and that refusal and max_tokens are the two cases in which structured output may not satisfy the schema.
Streaming stream: true
message_start
content_block_start // per block, with its index
content_block_delta // N times
content_block_stop // HERE the block is closed
message_delta // cumulative usage
message_stop // HERE the turn is confirmed
| Delta type | Content |
text_delta | Text fragment |
input_json_delta | Partial JSON of a tool's input. Do not parse until content_block_stop |
thinking_delta | Reasoning fragment |
signature_delta | Right before closing a thinking block |
What gets askedThree things: errors arrive as event: error after an HTTP 200; tool use and thinking blocks are not recovered partially (you only resume from the last text block); and a tool use error on retry usually traces back to a half-built block, not to the schema.
Message Batches
| Concept | Value |
| Discount | 50% input and output |
| Limit per batch | 100,000 requests or 256 MB |
| Window | Most under 1 h · results on completion or at 24 h · expire at 24 h |
| Results downloadable for | 29 days |
processing_status | Only in_progress and ended |
| Result per request | succeeded · errored · canceled · expired (the last three are not billed) |
| Not supported | stream: true, max_tokens: 0 |
| Available on | Claude API and Claude Platform on AWS only |
What gets askedThat there are only two batch states, that it does not support streaming, and that firing synchronous calls in parallel is not batching because it does not lower the cost per token.
Prompt caching cache_control
{ "type": "text", "text": "...",
"cache_control": { "type": "ephemeral", "ttl": "1h" } }
// "ephemeral" is the only type. ttl optional: 5 min by default
| Parameter | Value |
| TTL | 5 min by default · 1 h extended |
| Maximum breakpoints | 4 (the automatic one consumes one) |
| Lookback | 20 blocks per breakpoint |
| Write price | 1.25× (5 min) · 2× (1 h) |
| Read price | 0.1× |
| Prefix hierarchy | tools → system → messages |
| Minimum cacheable | Between 512 and 4,096 tokens depending on the model |
What gets askedThat changing tools invalidates the whole prefix, that changing the effort level also breaks the cache because it is rendered into the prompt, and the silent failure: below the minimum it does not cache and gives no error — you detect it with both cache counters at zero in usage.
Reasoning and effort
| Parameter | Values | Notes |
thinking.type | adaptive | The model decides. On current models it is always on; disabling it gives 400 |
thinking.type | enabled + budget_tokens | Deprecated. Rejected with 400 on recent models |
display | summarized · omitted | Billed the same in both cases |
output_config.effort | max · xhigh · high · medium · low | Defaults to high. A behavioural signal, not a budget |
The underlying principleChoosing the model and choosing the reasoning mode are two separate and composable levers. The rule works from the floor up: the smallest model and the simplest reasoning and prompting that pass your eval, and add capacity only where the eval says it is needed. Not the other way round.
What gets askedThat reasoning tokens are billed as output and count against max_tokens even when the text is not returned, and that thinking blocks carry a signature and must be returned unmodified or the next request fails.
Structured output
Two mechanisms: JSON outputs (schema in output_config.format, constrains the final response) and strict tool use (strict: true on a tool, validates the arguments). Both by constrained decoding.
| Limit per request | Value |
Tools with strict | 20 |
| Optional parameters, total | 24 |
| Parameters with union types | 16 |
| Compilation timeout | 180 s |
| Grammar cache | 24 h since last use |
| Supports | Does not support (400) |
basic types, scalar enum, const, anyOf, internal $ref, default, required, string formats | recursive schemas, external $ref, minimum/maximum, minLength/maxLength, lookahead in pattern |
What gets askedThat prefill no longer works to force JSON, that the casing of enum values is not guaranteed, that required properties come first in the ordering, and that citations plus structured outputs give 400.
Errors and retry strategy
| Code | Type | Retryable? |
| 400 | invalid_request_error | No — fix the request |
| 401 / 403 | auth / permissions | No — credential |
| 413 | request_too_large | No — split it up |
| 429 | rate_limit_error | Yes, with backoff and respecting retry-after |
| 500 | api_error | Yes |
| 504 | timeout_error | Yes |
| 529 | overloaded_error | Yes — global overload, not yours |
Classifying failures
- First question on any failure: could waiting and retrying resolve it?
- If yes → exponential backoff with a cap and a retry budget. Never an immediate loop, which only makes the problem worse.
- If no → a named fallback. Without it, the unhandled exception becomes the default behaviour, and one bad response takes down the whole flow.
- Tool failure → back to the model with the error flag set, never hidden behind an empty result the model would mistake for data.
The official SDKs already retry with exponential backoff, 2 retries by default, respecting retry-after.
Evals: grading method by output shape
| Output shape | Grader |
| There is one correct form | Exact match |
| Structured output | Code-based check |
| Open-ended quality | LLM judge, calibrated against human-labelled cases before you trust it |
Official order of preference: code → LLM → human. On the human, the docs say literally "avoid it if you can". And the design principle: more cases with somewhat worse signal and automatic grading beats few hand-graded cases.
What gets askedThat the eval is written first: identifying the expected behaviour forces you to define success while the design can still change. An eval turns "it's done" from a feeling into a score over a fixed set of cases.
Test levels and what each one catches
| Level | Catches |
| Unit | Logic errors inside a component |
| Functional | That a component meets its specification |
| Integration | The seam: the handoff between two components that pass separately |
| End-to-end | The whole flow. Tells you something fails, not where |
The diagnostic patternUnits and functionals green with the e2e red means the breakage is in a handoff, not inside a component — and by definition that is what an integration test covers. The complete answer has two halves: fix at the seam and add the integration test that was missing. A trace tells you which step produced the bad result, which is what turns a day of investigation into a short fix.
The same instinct governs information retrieval: a single search for single-fact queries, search across several iterations when the question is genuinely multi-step.
Hooks
| Event | What for |
PreToolUse | Validate or block before execution |
PostToolUse | Add context, replace the output, auditing |
UserPromptSubmit | Intercept the user's input |
SubagentStart / SubagentStop | Subagent lifecycle |
PreCompact | Before compacting (trigger: manual or auto) |
SessionStart / SessionEnd / Stop / Notification | Lifecycle and notices |
PreToolUse return shape
{ "permissionDecision": "deny", // allow | deny | ask | defer
"permissionDecisionReason": "...",
"updatedInput": { ... } } // optional
- Exit 2 = blocking error, not overridable even with an
allow.
- Silence is not approval: a hook can deny, but staying quiet lets the normal permission flow continue.
- Hooks run in your process, outside the context window: they consume no tokens beyond what they return.
What gets askedThat the hook is the mechanism for deterministic enforcement as opposed to an instruction, that PreToolUse is where you put the action boundary that blocks and logs before execution, and that PostToolUse is the place for the audit log for a regulated customer.
Agent SDK
| Option | What it does |
allowedTools | Auto-approval. Must include the spawn tool to use subagents |
maxTurns | Counts only turns with tool use |
maxBudgetUsd | Cuts off by cost; includes subagent spend |
systemPrompt | Without it, a minimal prompt. With the preset, the full CLI prompt. Or your own |
settingSources | Controls whether CLAUDE.md is loaded. Empty disables it |
resume / forkSession | Pick up a specific session / branch it into a new one |
permissionMode | default · acceptEdits · plan · auto · dontAsk · bypassPermissions |
A subagent's AgentDefinition
{ "description": "...", // required
"prompt": "...", // required - the ONLY thing that crosses from the parent
"tools": [...], // if omitted, inherits all
"model": "inherit", // or a specific model
"skills": [...] } // subagents do NOT preload skills
What gets askedThat maxTurns does not count turns without tools, that the budget includes subagents, and that the subagent does not inherit history: only the prompt string.
MCP
| Primitive | Controlled by | Methods |
| Tools | The model | tools/list · tools/call |
| Resources | The application (read-only) | resources/list · resources/read |
| Prompts | The user | prompts/list · prompts/get |
| Decision | Options |
| Transport | stdio (local) · Streamable HTTP (remote or multi-developer) |
| Scope in Claude Code | local (personal) · project (.mcp.json, versioned) · user (all your projects) |
| Deferred loading | defer_loading on the toolset: defers the context, not the payload |
Naming in Claude Code: tools mcp__server__tool · prompts /mcp__server__prompt · resources @server:proto://path.
What gets askedWho controls each primitive, that stdio should not use OAuth because it takes credentials from the environment, and that a stdio server in .mcp.json looks shareable and is not.
Limits and counters
| Concept | Value |
| Request size | Messages 32 MB · Batch 256 MB · Files 500 MB |
| Non-streaming timeout | 10 minutes |
| Counting tokens | POST /v1/messages/count_tokens — free, independent rate limit |
| ITPM | input plus cache creation. Cache reads do not count |
| OTPM | Tokens actually generated. max_tokens has no effect |
| Headers | request-id on every response · anthropic-ratelimit-*-{limit,remaining,reset} |
| Tool selection degradation | Above 30-50 available tools |
| Cost of an image | ⌈width/28⌉ × ⌈height/28⌉ visual tokens |
| Image ceiling | High resolution 2576 px / 4784 tokens · standard 1568 px / 1568 |
| Cost of multi-agent | Multiplies by the number of subagents: ~15× in the published case |
What gets askedThat you cannot budget what you do not measure: you have to instrument token cost, latency and error rate on every call and then tune one chosen lever, instead of guessing by looking at the bill. And that the orchestrator-workers pattern only earns that cost on tasks that split into independent parallel parts, not on tightly coupled work a single agent solves for a fraction.