Structured output with JSON schemas: CCAR-F task statement 4.3
CCAR-F · Prompt Engineering & Structured Output (20% of the exam)
Task statement 4.3 sits in Prompt Engineering & Structured Output, 20% of the CCAR-F exam. It tests how you get data back from Claude in a fixed JSON shape on every call: an extraction tool with a JSON schema, the right tool_choice setting, and a schema that gives Claude an honest way to say a value is missing.
What the official guide covers
The Claude Certified Architect Foundations exam guide (version 1.0, effective July 2026) lists this under task statement 4.3, "Enforce structured output using tool use and JSON schemas":
| Knowledge of | Skills in |
|---|---|
| Tool use with a JSON schema is the most reliable way to get schema-compliant output, with no JSON syntax errors | Defining extraction tools with a JSON schema as the input and reading the data from the tool_use block |
tool_choice "auto" (Claude may reply in text), "any" (Claude must call a tool but chooses which) and a forced tool (Claude must call the named tool) | Setting "any" when there are several extraction schemas and the document type is unknown; forcing a named tool so one extraction runs before enrichment steps |
| Strict schemas remove syntax errors but not semantic errors, such as line items that do not add up to the total or values in the wrong field | Making fields optional (nullable) when the source may not contain the information, so Claude does not invent values |
| Schema design: required or optional fields, and enums with an "other" value plus a detail string for categories that grow | Adding enum values such as "unclear" and "other" plus a detail field; putting format rules in the prompt alongside a strict schema |
How tool use produces structured output
You define a tool whose input_schema is the shape of the data you want, such as record_invoice, and never implement it. When Claude calls it, the input of the tool_use block is your extracted data, already parsed.
Add "strict": true to the tool definition and the API uses constrained sampling, so the tool input always follows your schema: correct types, required fields present, and no extra fields when additionalProperties is false. Without strict mode Claude usually follows the schema, but nothing guarantees it.
The API also offers JSON outputs: output_config.format with a json_schema constrains Claude's text reply to a schema, with no tool involved. The exam guide frames the topic as tool use.
Choosing tool_choice
| Setting | What Claude must do | Use it when |
|---|---|---|
{"type": "auto"} (default with tools) | Decide whether to call a tool or reply in text | Conversational agents |
{"type": "any"} | Call one of the tools; Claude picks which | Several extraction tools, document type unknown |
{"type": "tool", "name": "extract_metadata"} | Call that tool | One extraction must run, for example before enrichment |
{"type": "none"} | Not call any tool | Turns where tools must not be used |
With any or a named tool, the API prefills the reply so Claude goes straight to the tool_use block, with no text before it.
Not every model or setting supports forced tool use. With manual extended thinking (thinking: {"type": "enabled"}), any and a named tool return an error, and some newer models reject forced tool use in every case. For those, Anthropic's tool docs recommend auto with strict tool use, or JSON outputs when you need a reply in a fixed JSON shape. Check the "Define tools" page for the model you use.
An extraction tool with strict mode (Python)
import anthropic
client = anthropic.Anthropic()
MODEL = "your-model-id" # a model that supports forced tool use
RECORD_INVOICE = {
"name": "record_invoice",
"description": "Record the fields of one supplier invoice. "
"Use null for any field the invoice does not show.",
"strict": True,
"input_schema": {
"type": "object",
"properties": {
"invoice_number": {"type": "string"},
"invoice_date": {"type": ["string", "null"],
"description": "YYYY-MM-DD, or null if not shown"},
"category": {"type": "string",
"enum": ["goods", "services", "subscription", "unclear", "other"]},
"category_detail": {"type": ["string", "null"],
"description": "What the invoice says, when category is other"},
"stated_total": {"type": ["number", "null"]},
},
"required": ["invoice_number", "invoice_date", "category",
"category_detail", "stated_total"],
"additionalProperties": False,
},
}
response = client.messages.create(
model=MODEL,
max_tokens=1024,
system="Write dates as YYYY-MM-DD. Write amounts as plain numbers "
"with no currency symbols or thousands separators.",
tools=[RECORD_INVOICE],
tool_choice={"type": "tool", "name": "record_invoice"},
messages=[{"role": "user", "content": f"<invoice>\n{invoice_text}\n</invoice>"}],
)
invoice = next(b.input for b in response.content if b.type == "tool_use")
Three design choices in this schema match the guide:
- Nullable fields.
invoice_dateandstated_totalacceptnull, so Claude can say "not on this invoice" instead of inventing a value. - "unclear" and "other" with a detail field. Hard cases can be marked unclear, and a new category arrives as "other" with its text in
category_detail. - Format rules in the prompt. The schema says
invoice_dateis a string; the system prompt says which format.
Check stop_reason as well. Anthropic's docs note that a reply cut off at max_tokens, or a refusal, may not match the schema.
What constrained output costs, and what it replaces
Strict tool use and JSON outputs are not free switches. Anthropic's structured outputs page lists the trade-offs:
- First-call latency. The first request with a new schema is slower while the API compiles it into a grammar. A compiled grammar stays cached for 24 hours after its last use, so a stable schema pays once. A pipeline that builds a new schema for every request pays every time.
- A few more input tokens. The API adds a short system prompt describing the expected format. Changing
output_config.formatalso invalidates the prompt cache for that conversation. - Feature conflicts. JSON outputs cannot be combined with prefilling the assistant message, and combining them with citations returns an error. JSON outputs and strict tool use can be used together in one request.
Older material shows a prompt-level trick for clean JSON: start Claude's reply with an opening code fence and set a stop sequence on the closing one. That only shaped where the reply began and ended; it never checked the shape inside. Anthropic's prompting guide now says recent models reject a prefilled final assistant turn with a 400 error. If an exam option offers prefilling or "reply only in JSON" against a schema option, the schema is right.
What the schema cannot catch
A schema checks shape, not meaning. Strict mode guarantees that stated_total is a number, not that it matches the line items or that tax did not land in the discount field. Those semantic errors need validation code after extraction (task statement 4.4).
| Situation | Choose | Why |
|---|---|---|
| Output must parse and match a schema every time | Strict tool use, or JSON outputs | Constrained output, no syntax errors |
| Several document types, each with its own extraction tool, type unknown | tool_choice any, where the model supports it | A tool is always called and Claude picks the match |
| One extraction must run before the next step | Force the named tool | Guarantees that step runs |
The model rejects forced tool_choice | auto with strict tool use, or JSON outputs | The fallback the tool docs recommend |
| A field may be missing from the source | Nullable type | Removes the pressure to invent a value |
| Categories will grow over time | Enum with "other" plus a detail string | New cases fit without a schema change |
Rules that decide exam answers
- A schema beats "reply in JSON" and prefilling. When an option asks for JSON in the prompt, or starts Claude's reply for it, and another defines a tool or output schema, the schema is right.
autolets Claude answer in text. If every document must produce structured data, chooseanyor a forced tool, notautowith a prompt instruction.- Valid shape is not correct data. Strict mode removes syntax errors only. Sums, field placement and cross-field rules need validation code.
- Nullable beats required for data that may be absent. A required non-null field pushes Claude to make a value up.
- Use "other" plus detail for open categories. Use "unclear" when the source is ambiguous, rather than forcing a guess.
Where it appears in the exam
Prompt Engineering & Structured Output is a primary domain in two of the six exam scenarios: Claude Code for Continuous Integration and Structured Data Extraction. This task statement belongs mainly to the extraction scenario, which describes a system that validates extracted data with JSON schemas and feeds downstream systems.
Two sample questions
These are original Timo practice questions. They are not official exam questions.
Build exercise
- Use the
record_invoicetool above to extract five invoices, one with no date. Confirm the date comes back asnull. - Make
invoice_datea required non-null string and run the same invoice again. Note what Claude puts in the field. - Add a second tool for purchase orders, set
tool_choiceto "any" (on a model that supports it) and send a mix of documents. Check which tool each one triggers. - Edit one invoice so its line items do not match the total. Confirm the output still passes the schema, then write a check that catches it.
Practise this topic
- Claude Certified Architect practice exam: free, 20 questions, no sign-up
- Claude Certified Architect hub
- CCAR-F study guide: all topics
- Same topic in another exam: Output Handling (CCDV-F)
- Previous topic: 4.2 Few-shot prompting
- Next topic: 4.4 Validation and retry loops
Sources
- Claude Certified Architect Foundations Exam Guide, version 1.0, effective July 2026 (Anthropic), task statement 4.3
- Anthropic documentation: Structured outputs
- Anthropic documentation: Strict tool use
- Anthropic documentation: Define tools
- Anthropic documentation: Prompting best practices
By Amotion AI