Skip to content
CCAF Preparation

Task Statement 4.3·Domain 420% of exam

Structured Output with Tool Use

Enforce structured output using tool use and JSON schemas

Jump to practice →

Official Exam Guide Objectives

Task 4.3: Enforce structured output using tool use and JSON schemas

Knowledge of

  • Tool use (tool_use) with JSON schemas as the most reliable approach for guaranteed schema-compliant structured output, eliminating JSON syntax errors
  • The distinction between tool_choice: "auto" (model may return text instead of calling a tool), "any" (model must call a tool but can choose which), and forced tool selection (model must call a specific named tool)
  • That strict JSON schemas via tool use eliminate syntax errors but do not prevent semantic errors (e.g., line items that don't sum to total, values in wrong fields)
  • Schema design considerations: required vs optional fields, enum fields with "other" + detail string patterns for extensible categories

Skills in

  • Defining extraction tools with JSON schemas as input parameters and extracting structured data from the tool_use response
  • Setting tool_choice: "any" to guarantee structured output when multiple extraction schemas exist and the document type is unknown
  • Forcing a specific tool with tool_choice: {"type": "tool", "name": "extract_metadata"} to ensure a particular extraction runs before enrichment steps
  • Designing schema fields as optional (nullable) when source documents may not contain the information, preventing the model from fabricating values to satisfy required fields
  • Adding enum values like "unclear" for ambiguous cases and "other" + detail fields for extensible categorization
  • Including format normalization rules in prompts alongside strict output schemas to handle inconsistent source formatting

What You Need to Know

Where the requirement is output that reliably conforms to a schema, two approaches are available and they are not close:

  1. tool_use with JSON schemas — eliminates JSON syntax errors entirely
  2. Prompt-based JSON — model can produce malformed JSON

That ordering is worth committing to memory, because several exam items rest on it. A tool's JSON schema constrains the shape of what comes back, so the failure modes of hand-produced JSON — an unclosed bracket, a trailing comma, an unquoted key — stop being possible rather than becoming rarer. Note that the schema constrains the shape; it is the separate tool_choice parameter that determines whether the tool is called at all. Asking for JSON in a text response offers no structural guarantee whatever, and at production volumes will periodically return something that does not parse.

tool_choice: The Three Modes

Whether and how the model reaches for a tool is governed by tool_choice, and all three settings are examinable:

"auto" (default): The choice is the model's — it may call a tool or it may answer in text. Appropriate where responding conversationally is a legitimate outcome, and unsuitable wherever structured output is mandatory, because nothing obliges it to call the extraction tool.

"any": A tool call is compulsory; which tool remains open. This is the setting for a bank of schemas — extract_invoice, extract_receipt, extract_contract — against a document whose type is not known in advance. Structured output is guaranteed, and the selection stays flexible.

{"type": "tool", "name": "extract_metadata"}: One named tool, compulsory. Use it to make a particular step unavoidable — metadata extraction running before any enrichment, for instance. No selection, maximum control.

// Force guaranteed structured output with unknown document type
const response = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 4096,
  tool_choice: { type: "any" },
  tools: [extractInvoiceTool, extractReceiptTool, extractContractTool],
  messages: [{ role: "user", content: documentText }]
});

// Force a specific extraction step
const response = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 4096,
  tool_choice: { type: "tool", name: "extract_metadata" },
  tools: [extractMetadataTool],
  messages: [{ role: "user", content: documentText }]
});

What tool_use Does NOT Prevent

Here is where items get sharp. Schema enforcement removes syntax failures and leaves semantic ones entirely untouched:

  • Sum discrepancies: the individual amounts are all present and correct, and they do not add up to the total the document states
  • Field placement errors: the right value in the wrong property — a date sitting in an amount field passes validation whenever both are typed as strings
  • Fabrication: a required field with nothing in the source to fill it gets filled anyway, with something that looks like the real thing

Every one of those produces JSON that validates perfectly. The schema is a statement about shape, and shape is silent on whether the values are right — which is what Task Statement 4.4 exists to address.

Schema Design for Production

A well-shaped schema removes whole categories of error before any validation logic runs:

Optional/nullable fields — Where a source document may simply not carry a piece of information, the corresponding field should be optional or nullable, and this is the main structural defence against fabrication. A required field presents the model with a demand it must satisfy, and something plausible is easier to produce than nothing. Make the field nullable and null becomes an available, honest answer.

{
  "type": "object",
  "properties": {
    "invoice_number": { "type": "string" },
    "vendor_name":    { "type": "string" },
    "payment_terms":  { "type": ["string", "null"] },
    "purchase_order": { "type": ["string", "null"] },
    "tax_reference":  { "type": ["string", "null"] }
  },
  "required": ["invoice_number", "vendor_name"]
}

"unclear" enum value — Classification fields need somewhere to put a genuinely ambiguous case. Without an explicit unclear option the model must pick one of the definite categories, which converts uncertainty into a confident-looking answer.

"other" + detail string — For categories that will not stay closed, pair an other enum member with a freeform detail field. Cases outside your taxonomy are then recorded rather than forced into the nearest approximation.

{
  "category": {
    "type": "string",
    "enum": ["invoice", "receipt", "contract", "unclear", "other"]
  },
  "category_detail": {
    "type": ["string", "null"],
    "description": "Freeform detail when category is 'other'"
  }
}

Format normalisation rules — Formatting consistency belongs in the prompt rather than the schema, because the schema constrains types and not conventions. State the rules alongside it: dates in ISO 8601, currency amounts as decimal numbers with the symbol stripped.

Deep Dive

tool_choice: four values, not three, in the API reference

The lesson's three-mode framing (auto / any / forced) is the exam's practical grouping, but the API reference actually documents four tool_choice types: "The model can use a specific tool, any available tool, decide by itself, or not use tools at all" — auto, any, tool, and none. none prevents Claude from using any tools and is the default when no tools are provided; if you supply no tools at all, tool_choice: "none" costs zero additional system-prompt tokens. auto is the default the moment any tools array is present.

Sources: https://platform.claude.com/docs/en/api/messages (the four types) · https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools (none as the default without tools) · https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview (zero additional tokens) (fetched 2026-07-30)

Combine tool_choice "any" with strict:true for a double guarantee

Guaranteeing "a tool was called" and guaranteeing "the tool's input matches the schema" are two separate guarantees, and the docs spell out how to get both at once: "Combine tool_choice: {\"type\": \"any\"} with strict tool use to guarantee both that one of your tools will be called AND that the tool inputs strictly follow your schema. Set strict: true on your tool definitions to enable schema validation." tool_choice: "any" alone only forces a tool call — without strict: true the input JSON can still technically satisfy an unvalidated schema. This is the mechanism the reliability hierarchy in this lesson is built on.

Sourceplatform.claude.com › implement-tool-usefetched 2026-07-30

A newer, separate mechanism: the Structured Outputs feature

Beyond tool_use, the API now offers a distinct structured outputs feature that constrains a plain-text JSON response (not a tool call) via constrained decoding: "Structured outputs constrain Claude's responses to follow a specific schema, ensuring valid, parseable output for downstream processing... through constrained decoding." It exposes two related controls that solve different problems and can be combined: output_config.format shapes what Claude says (JSON outputs), while strict: true on a tool definition validates how Claude calls your functions (tool parameters).

Sourceplatform.claude.com › structured-outputsfetched 2026-07-30

What structured-output schemas can and cannot express

The constrained-decoding grammar supports a defined subset of JSON Schema: "All basic types: object, array, string, integer, number, boolean, null; enum (strings, numbers, bools, or nulls only — no complex types); ... required and additionalProperties (must be set to false for objects)." Several common constraints are explicitly not supported: "Recursive schemas; ... External $ref; Numerical constraints (such as minimum, maximum, multipleOf); String constraints (minLength, maxLength)." A schema with additionalProperties set to anything other than false is also rejected. Note that null appears in the supported list both as a basic type and as a permitted enum member, so nullability has first-class support in the grammar.

Sourceplatform.claude.com › structured-outputsfetched 2026-07-30

First-request latency and a 24-hour grammar cache

Constrained decoding is not free on the first call: "The first time you use a specific schema, there is additional latency while the grammar compiles... Compiled grammars are cached for 24 hours from last use." Changing output_config.format also invalidates the prompt cache for that conversation thread, and using structured outputs adds a small hidden system prompt ("Claude automatically receives an additional system prompt explaining the expected output format"), which slightly raises input token counts on every call, not just the first.

Sourceplatform.claude.com › structured-outputsfetched 2026-07-30

Prefill — a retired workaround, now a 400 error on the newest models

Before tool_use and structured outputs existed as reliable mechanisms, teams forced JSON/YAML output by prefilling part of the assistant turn. The docs now describe this as a historical technique with a clear successor: "Prefills have been used to force specific output formats like JSON/YAML, classification, and similar patterns... The Structured Outputs feature is designed specifically to constrain Claude's responses to follow a given schema." Crucially, prefill is no longer just discouraged — it is rejected outright on the newest models: "Starting with Claude 4.6 models and Claude Mythos Preview, prefilled responses... on the last assistant turn are no longer supported. Requests with prefilled assistant messages to these models return a 400 error." (Note separately that when tool_choice is any or tool, the API itself prefills the assistant message to force the tool call — that API-side mechanism is unaffected and is how forced tool use suppresses preamble text.)

Sources: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices (prefill history and the 400 error) · https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools (the API-side prefill under forced tool choice) (fetched 2026-07-30)

required vs optional in a tool's input_schema

The lesson's nullable-field defence against fabrication rests on ordinary JSON Schema mechanics inside input_schema: fields listed in the schema's required array are mandatory; everything else is optional, and enum restricts a field to a fixed set of values (the documented example limits a unit parameter to "celsius" or "fahrenheit"). There is nothing tool_use–specific about optionality — it is the same JSON Schema required array the constrained-decoding structured-outputs feature also reads.

Sourceplatform.claude.com › implement-tool-usefetched 2026-07-30

Quick Reference

ItemValue / rule
tool_choice values (full set)auto (default with tools), any, tool (forced), none (default with no tools)
Guarantee "any" alone givesA tool is called — but not that its input matches the schema
Guarantee "any" + strict: true givesA tool is called AND its input strictly follows the schema
Structured Outputs featureoutput_config.format — constrains a plain JSON response via constrained decoding, no tool call needed
Structured Outputs model availabilityGA on Claude 4.5 and later models
additionalPropertiesMust be false for objects — any other value is rejected
Not supported in structured-output schemasRecursive schemas, external $ref, minimum/maximum/multipleOf, minLength/maxLength
enum values allowedStrings, numbers, booleans, nulls only — no complex types
First use of a new schemaExtra latency while the grammar compiles
Grammar cache duration24 hours from last use; invalidated by schema or tool-set changes
Structured outputs token costAdds a hidden system prompt — input tokens slightly higher on every call
Prefill on Claude 4.6+ / Mythos PreviewReturns a 400 error on the last assistant turn — no longer supported
Prefill's documented successorThe Structured Outputs feature
tool_choice any/tool API-side prefillStill active — suppresses preamble text before tool_use blocks
What tool_use eliminatesJSON syntax errors (missing brackets, trailing commas, unquoted keys)
What tool_use does NOT eliminateSemantic errors: sum discrepancies, field placement, fabrication

Exam Traps

Practice Scenario

Your extraction system uses tool_use with a strict JSON schema where all fields are required. Testers report the model invents plausible-looking dates and monetary amounts when processing documents that lack this information. What is the best fix?

Build Exercise

Build a Structured Extraction Tool with JSON Schema

Difficulty: Intermediate (2/4)

45 minutes

  1. Define an extraction tool with a JSON schema: 3 required fields, 3 optional/nullable fields, an enum with unclear and other options, and a detail string field for the other category

Why: Schema design directly prevents fabrication. Required fields pressure the model to invent values when information is absent. Optional/nullable fields allow honest null responses. This is the root cause fix for hallucinated extraction data.

You should see: A valid JSON schema with required array containing only the 3 always-present fields, nullable type definitions for optional fields, and an enum array including unclear and other alongside the standard categories.

  1. Test with tool_choice auto and observe cases where the model returns text instead of calling the tool

Why: The exam tests the distinction between auto, any, and forced tool_choice. Auto allows the model to respond conversationally instead of calling a tool, which means no guaranteed structured output. You need to see this failure mode firsthand.

You should see: At least one response where the model returns a text message describing the document contents instead of calling the extraction tool. This demonstrates why auto is unsuitable when you need guaranteed structured output.

  1. Switch to tool_choice any and verify the model always returns structured output via a tool call

Why: tool_choice any guarantees a tool call while letting the model choose which tool. This is the correct setting for guaranteed structured output when the document type is unknown, a key exam distinction from auto.

You should see: Every response has stop_reason of tool_use and contains a valid tool call with structured output conforming to your schema. No text-only responses.

  1. Force a specific tool with tool_choice {type: tool, name: extract_metadata} and verify the mandatory extraction step runs

Why: Forced tool selection ensures a mandatory first step executes regardless of the model decision. The exam tests this for scenarios like metadata extraction that must run before enrichment steps.

You should see: The response always calls the exact tool you specified, even when the document content might suggest a different tool would be more appropriate. The model has no flexibility in tool selection.

  1. Process 5 documents — 3 with complete data and 2 with missing fields — and verify nullable fields return null rather than fabricated values

Why: This validates the most important schema design principle: optional/nullable fields prevent fabrication. The exam specifically tests the scenario where required fields pressure the model to invent plausible-looking data for absent information.

You should see: For the 3 complete documents, all fields populated with correct values. For the 2 documents missing information, the nullable fields return null instead of fabricated values. No invented dates, amounts, or identifiers.

Sources


Appendix A — Build Exercise Step Hints

Progressive hints revealed by the "Stuck? Get a nudge" control on each step.

Step 1. Define an extraction tool with a JSON schema: 3 required fields, 3 optional/nullable fields, an enum with unclear and other options, and a detail string field for the other category

Why: Schema design directly prevents fabrication. Required fields pressure the model to invent values when information is absent. Optional/nullable fields allow honest null responses. This is the root cause fix for hallucinated extraction data.

You should see: A valid JSON schema with required array containing only the 3 always-present fields, nullable type definitions for optional fields, and an enum array including unclear and other alongside the standard categories.

Stuck? Get a nudge

Step 2. Test with tool_choice auto and observe cases where the model returns text instead of calling the tool

Why: The exam tests the distinction between auto, any, and forced tool_choice. Auto allows the model to respond conversationally instead of calling a tool, which means no guaranteed structured output. You need to see this failure mode firsthand.

You should see: At least one response where the model returns a text message describing the document contents instead of calling the extraction tool. This demonstrates why auto is unsuitable when you need guaranteed structured output.

Stuck? Get a nudge

Step 3. Switch to tool_choice any and verify the model always returns structured output via a tool call

Why: tool_choice any guarantees a tool call while letting the model choose which tool. This is the correct setting for guaranteed structured output when the document type is unknown, a key exam distinction from auto.

You should see: Every response has stop_reason of tool_use and contains a valid tool call with structured output conforming to your schema. No text-only responses.

Stuck? Get a nudge

Step 4. Force a specific tool with tool_choice {type: tool, name: extract_metadata} and verify the mandatory extraction step runs

Why: Forced tool selection ensures a mandatory first step executes regardless of the model decision. The exam tests this for scenarios like metadata extraction that must run before enrichment steps.

You should see: The response always calls the exact tool you specified, even when the document content might suggest a different tool would be more appropriate. The model has no flexibility in tool selection.

Stuck? Get a nudge

Step 5. Process 5 documents — 3 with complete data and 2 with missing fields — and verify nullable fields return null rather than fabricated values

Why: This validates the most important schema design principle: optional/nullable fields prevent fabrication. The exam specifically tests the scenario where required fields pressure the model to invent plausible-looking data for absent information.

You should see: For the 3 complete documents, all fields populated with correct values. For the 2 documents missing information, the nullable fields return null instead of fabricated values. No invented dates, amounts, or identifiers.

Stuck? Get a nudge

Appendix B — Interactive Study Prompts

Two prompts to paste into Claude. B1 drills the judgement the exam actually measures; B3 reviews the code you wrote for the Build Exercise above. The exam simulator between them is the interactive quiz on this page.

B1. Concept Check — Discrimination Drill

Prompt — paste into Claude

You are examining me for the Claude Certified Architect – Foundations (CCAR-F) exam, Domain 4: Prompt Engineering & Structured Output (20% of the exam), Task Statement 4.3: Structured Output with Tool Use. Use British English throughout.

What this exam actually measures. Not one item on the official exam asks what something is. Every item drops you into a production system that is already misbehaving, offers four defensible engineering responses, and asks which is best. The skill being tested is proportionality: fix the root cause with the cheapest instrument that gives the guarantee the situation demands. So do not quiz me on definitions. Make me choose between options that are both defensible, then attack whatever I chose.

How to run this session.

  • One question at a time. Stop and wait. Never answer your own question, and never move on until I have committed.
  • Never reveal which option is right before I commit to one.
  • Do not praise me. A correct answer earns "Yes" and the next question. If I am right for the wrong reason, say so — that is the failure that costs marks on exam day.
  • When I am wrong, quote the exact phrase in my answer that gave it away, correct it in one sentence, and move on. One correction at a time.
  • If I write something fluent but empty, name it: "That is a restatement, not a reason."
  • Set every scenario inside one of the exam's production contexts: Structured Data Extraction (pulling fields out of unstructured documents, validating them against a JSON schema, handling edge cases, feeding a downstream system) or Claude Code for Continuous Integration (automated review on pull requests, generated tests and PR feedback, with false positives to keep down).

Session plan — about twelve questions.

Round 1 — Anchor (1 question). One concrete question to check I have actually read the material. If I cannot answer it, stop the session and tell me to read the lesson before continuing.

Round 2 — Discrimination (5 questions). Each one: describe a symptom in one of the contexts above, with a number or a log observation in it. Offer exactly two responses, both defensible. Ask me to pick one and justify it in a single sentence. Then argue the case for the option I rejected as strongly as you can, and ask whether I am holding or changing my answer. Only after I answer that, tell me which is right and why the other one is the more tempting trap.

Round 3 — Proportionality (2 questions). Take one symptom and run it twice with different stakes: once where the cost of an error is a wasted re-extraction or a noisy comment on a pull request, once where it is a payment issued against a mis-extracted total or a security defect merged to main. The right answer must change between the two. If I answer the same way both times without noticing the stakes moved, that is the finding — tell me.

Round 4 — Code review (3 questions). Present a colleague's confident proposal containing one of the trap errors listed below, written the way a teammate would write it in a pull request. Ask me what is wrong with it. Do not signal that anything is wrong.

Round 5 — Verdict. Rate me green, amber or red on each concept below. Name the single weakness most likely to cost me marks, and give me one specific next action: a section of this lesson to re-read, or a step of the Build Exercise to redo. If I am not ready for this task statement, say so plainly.

Concepts in scope

  1. The reliability hierarchy — a tool's JSON schema constrains the shape of what Claude returns and removes syntax failures such as missing brackets, trailing commas and unquoted keys. Asking for JSON inside a text response carries no structural guarantee and will periodically return something unparseable.
  2. The three tool_choice modesauto lets the model answer with text instead of calling the tool; any forces a tool call but leaves the choice of tool to the model, which is the setting for guaranteed structure when the document type is unknown; {"type": "tool", "name": ...} forces one named tool, which is how an extraction step is made mandatory before enrichment.
  3. Structure is not correctness — a schema cannot stop line items that fail to sum to the stated total, a value dropped into the wrong field, or a value invented for a field the source never carried. Those are semantic errors and need validation outside the schema.
  4. Nullable fields as the defence against fabrication — a required field pressures the model into producing something; a nullable one lets it return null honestly, which always beats plausible invented data.
  5. Enums that admit the awkward cases — an "unclear" member stops the model forcing a classification on ambiguous evidence, and an "other" member paired with a freeform detail string absorbs categories the predefined list does not cover.
  6. Format normalisation belongs in the prompt — the schema enforces structure; instructions such as ISO 8601 dates and decimal amounts without currency symbols enforce consistent formatting.

Trap errors to plant in Round 4

  • Treating a tool schema as a guarantee of correct extraction, so nothing downstream ever checks totals, field placement or invented values.
  • Leaving tool_choice at auto in a pipeline that needs structured output on every document, when auto permits a text answer instead.
  • Marking every schema field required in the name of data completeness, which pressures the model to fabricate whatever the source omitted.
  • Reaching for any when the requirement is that one specific extraction runs first — any guarantees a tool call, not which tool.

Stay inside the material above. If I raise something outside it, tell me it is out of scope for this task statement and return to the drill. Begin with Round 1.

B2. Exam Simulator

Exam simulator

Question 1 of 11

Scenario · Structured Data Extraction

Your extraction tool's schema lists every field as required. Reviewers spot-checking output keep finding invoice dates and payment terms that look plausible but appear nowhere in the source — always on documents where that field is genuinely absent. What change would most effectively address this?

B3. Build Coach — Code Review

The Build Exercise and its hint ladder are already on this page. This prompt is for the one thing the page cannot do: review the schema and code you actually wrote.

Prompt — paste into Claude

You are a staff engineer reviewing my implementation of a build exercise for the Claude Certified Architect – Foundations exam, Domain 4, Task Statement 4.3: Structured Output with Tool Use. Use British English throughout.

I am building a structured extraction tool with a JSON schema: a tool definition mixing always-present required fields with nullable ones, an enum carrying an "unclear" and an "other" member plus a companion detail string, exercised under each of the three tool_choice modes, and then run over documents where some of the information is genuinely absent.

It has to satisfy all of the following:

  • The required array lists only fields that appear in every document; everything that may be absent is typed as nullable.
  • The enum admits both the ambiguous case and the unpredicted case, and the detail string is populated only for the latter.
  • Under auto I can show at least one response that came back as text rather than a tool call.
  • Under any every response carried a tool call, and under the forced mode the named tool ran even on a document another tool would have suited better.
  • On documents with genuinely missing information the nullable fields come back null, with no invented dates, amounts or identifiers.

How to review.

  • Ask me to paste the tool definition, the request configuration for each tool_choice mode, and a sample of real model output including the raw tool_use input. If I have not pasted them, ask for them and nothing else. Do not write the schema for me, do not offer a reference solution, and do not fill in a step I have skipped.
  • Work through the criteria above in order. For each one, quote the line of my schema, my code or my output that satisfies it, or say plainly that nothing does.
  • Then hunt for the failure modes below. Each is a real production bug, not a style preference.
  • Rank everything you find: (1) would fail in production, (2) would lose marks on the exam, (3) style. Give me the first item under (1) and then stop — wait for my fix before giving me the next one.
  • If my code satisfies everything, do not congratulate me. Change the requirements — a document arrives whose line items do not sum to its stated total, and the schema is not allowed to change — and make me handle it.
  • If I ask you to just write it for me, refuse once and give me the smallest nudge that would unblock me instead.

Failure modes to probe

  • A required array containing fields only some documents carry, which writes the fabrication bug into the schema itself.
  • The forced mode expressed as a bare string rather than an object naming the tool, so the run is not testing the mode it claims to test.
  • The nullable claim demonstrated on a document where the information is merely hard to find rather than genuinely absent, so the test cannot fail.
  • Extraction read out of the response's text rather than the tool_use block's input, which reintroduces the parsing failure the schema was chosen to remove.
  • No semantic check anywhere — sums, field placement, date ordering — on the assumption that the schema already covered it.

Start by asking me for my code.