Claude Certified Architect - FoundationsCCAR-F Learning Guide
Domain 4 · Lesson 4.3 · 5 min read

Structured Output with Tool Use and JSON Schemas

Exam taskEnforce structured output using tool use and JSON schemas

Define the output as a tool whose input schema is your JSON schema and read the tool_use block. Use tool_choice to require it, design optional and enum fields so the model never has to invent values, and remember schemas guarantee structure, not truth.

What you need to know

Tool use as the output channel

"Respond with JSON" in a prompt usually works, and occasionally returns markdown fences, a trailing comma or a sentence of prose. The reliable approach: define an extraction tool whose input_schema is the JSON schema you want, have Claude call it, and read the structured data from the tool_use block. With strict tool use, the arguments match the schema, which removes JSON syntax errors.

extract_invoice = {
  "name": "extract_invoice",
  "description": "Record the fields of one invoice.",
  "strict": True,                       # strict tool use: arguments follow the schema
  "input_schema": {
    "type": "object",
    "properties": {
      "vendor_name":   {"type": "string"},
      "vendor_tax_id": {"type": ["string", "null"]},
      "invoice_date":  {"type": "string", "description": "YYYY-MM-DD"},
      "category": {"type": "string",
                   "enum": ["goods", "services", "utilities", "other"]},
      "category_detail": {"type": ["string", "null"]},
      "total": {"type": "number"}},
    "required": ["vendor_name", "vendor_tax_id", "invoice_date", "category", "total"]}}

resp = client.messages.create(model=MODEL, max_tokens=1024, tools=[extract_invoice],
          tool_choice={"type": "tool", "name": "extract_invoice"}, messages=msgs)
data = next(b.input for b in resp.content if b.type == "tool_use")

tool_choice decides whether a tool is called

SettingBehaviour
autoClaude may answer in text instead of calling a tool
anyClaude must call a tool, and picks which: right when there are several extraction schemas and the document type is unknown
{"type": "tool", "name": ...}Claude must call that tool: right when one extraction (such as metadata) must run before enrichment steps

Design the schema so the model never has to invent

  • Nullable or optional fields for information a document may not contain. A required vendor_tax_id with no tax ID in the document pushes the model to fabricate one; "type": ["string", "null"] lets it say "not present".
  • Enums with an escape hatch: "other" plus a category_detail string for documents that fit no category, and "unclear" for genuinely ambiguous cases.
  • Normalisation rules in the prompt ("dates as YYYY-MM-DD; amounts as numbers without currency symbols") alongside the schema, because sources are inconsistent.

Structure is not correctness

A schema guarantees the output parses and has the right types. It does not guarantee the values are right: line items that do not sum to the total, a vendor name in the address field, a date from the wrong part of the page. Those are semantic errors, and they need validation (lesson 4.4).

In the exam

Scenario 6 (extraction) carries most of these: parse failures from "respond with JSON", fabricated values in required fields, unknown document types, extensible categories, and valid JSON with wrong numbers.

Check your understanding

Question 1

A pipeline asks for JSON in the prompt, and 4% of responses fail to parse. What is the most reliable fix?

Show answer

Answer: C. Tool use with a JSON schema returns structured arguments that match the schema, removing syntax errors at the source rather than patching them afterwards.

Question 2

A required vendor_tax_id is fabricated on invoices that have no tax ID. What should change?

Show answer

Answer: A. A required field forces a value. Allowing null removes the pressure to invent one while keeping the field for invoices that have it.

Question 3 · select 2

Your extractions always match the schema, but line items sometimes do not add up to the total. Which TWO statements are true? Select TWO.

Show answer

Answer: B, C. Schema compliance and semantic correctness are different things. Catching arithmetic errors needs a validation step that compares values.

Apply it: Bayview's extraction tools

Exercise · about 20 minCase: Bayview Payables

Design Bayview's structured output for invoices and receipts.

  1. Write the invoice tool schema with at least eight fields, marking which are nullable and why.
  2. Design the category field so the 5% of unexpected documents still extract cleanly.
  3. Choose tool_choice when the document type is unknown and there is one tool per type.
  4. List two semantic errors the schema cannot catch, for lesson 4.4.
Check your work
Show model answer

1. Fields

FieldTypeNullable?Why
vendor_namestringnoEvery invoice has a sender
vendor_tax_idstringyesAbsent on about 15% of invoices
invoice_numberstringyesSome receipts-as-invoices lack one
invoice_datestring, YYYY-MM-DDnoNeeded for posting; normalised by prompt rules
due_datestringyesOften missing
currencyenum of ISO codes plus "unclear"no
line_itemsarray of {description, quantity, unit_price, amount}no (may be empty)
stated_totalnumberno
calculated_totalnumbernoSum of line items, for checking

2. Category. enum: ["invoice", "receipt", "contract", "credit_note", "statement", "other"] plus category_detail (nullable string) that must be filled when other is chosen, for example "cover letter".

3. tool_choice. any: a tool must be called, and Claude picks the schema that fits the document.

4. Semantic errors. calculated_total differs from stated_total; the vendor's address extracted into vendor_name.

Sources

Go hands-on optional, needs a Claude API key

Runnable notebooks from Anthropic's Claude Cookbook that build the patterns in this lesson.