Tutorials Logic, IN info@tutorialslogic.com

Claude Structured Outputs: Hand a Safe Object to Your Application

Stop parsing prose when the next step is software

Free-form prose is ideal for a user-facing explanation but fragile for software decisions. Structured output lets an application request a response that conforms to a JSON Schema, while strict tool use constrains the parameters Claude sends to a tool. These solve different parts of the contract and can be combined.

Schemas reduce parsing ambiguity, but they do not make the underlying facts true. Validate semantic rules such as permissions, account ownership, date ranges, and business invariants after schema validation.

The key distinction: A schema makes the response easier to parse. Your own validation still decides whether its values are true, permitted, and safe to use. Use a schema to control shape, then use application validation to control meaning and authorization.

From a ticket to an application decision

Each handoff narrows what can go wrong and leaves an audit trail for the next person.
  1. Customer text Unstructured input arrives at your service.
  2. Schema-shaped proposal Claude returns only the fields the workflow needs.
  3. Business validation Code checks allowed values, ownership, and invariants.
  4. Route or review The workflow accepts a safe result or sends it to a person.

Pick the smallest object that unlocks the next step

A schema can guarantee that a response has fields such as `category`, `priority`, and `needs_review`. It cannot guarantee that the category is correct for your business. Use schemas to remove parsing work, not to remove evaluation.

Prefer a small discriminated result over a giant optional object. The smaller contract is easier to version, test, and display.

  • Schema controls data shape.
  • Evaluation checks correctness.
  • Authorization checks permission.

Model uncertainty in the data shape itself

Design fields around decisions. Include an allowed enum, a human-readable rationale for auditing, and a `needs_review` boolean or status. Avoid accepting a free-form field where an enum or typed identifier is required.

Do not place secrets or protected health information in a JSON schema description. Structured-output schema processing and caching have their own operational considerations in official documentation.

  • Use enums for bounded categories.
  • Model uncertainty explicitly.
  • Keep fields minimal and versioned.

Validate meaning after you validate JSON

Run ordinary validators after decoding JSON. Check that a customer ID belongs to the session, a refund amount does not exceed the order total, and a date is valid for the product state. The model should not control defaults for privileged values.

If validation fails, preserve the raw redacted response for debugging, return a safe retry or review state, and measure the failure rate.

  • Validate ownership and ranges.
  • Reject unknown identifiers.
  • Measure validation failures.

Use strict tool inputs without granting authority

Tool schemas constrain how Claude asks your code to act. Your tool handler must still validate the request and authorize the action. A schema that accepts `recipient_id` does not mean the current user may message that recipient.

Use strict tool use when you need parameters to follow the schema, and keep destructive tools narrow, confirmed, and auditable.

  • Strict tools control call shape.
  • Handlers enforce policy.
  • Writes need confirmation and audit logs.

Use the object in a normal application workflow

Once you have decoded a structured response, treat it like input from any outside service. Convert it into your own type, validate the allowed values, and decide whether the next step is automatic or requires review.

This is where a small schema helps most: controller code can branch on a status instead of guessing from prose.

  • Decode JSON once at the boundary.
  • Convert it to an application type.
  • Keep a safe needs-review state.

Build a triage object that a route handler can trust

Ask Claude for a Triage Object

This is a real Messages API request. The JSON schema gives the route handler a predictable object instead of asking it to parse a sentence.

Ask Claude for a Triage Object
import json
import os
from anthropic import Anthropic

triage_schema = {
    "type": "object",
    "properties": {
        "category": {"type": "string"},
        "priority": {"type": "string"},
        "needs_review": {"type": "boolean"},
    },
    "required": ["category", "priority", "needs_review"],
    "additionalProperties": False,
}

client = Anthropic()
response = client.messages.create(
    model=os.environ["CLAUDE_MODEL"],
    max_tokens=200,
    system="Classify the support ticket. Use needs_review when unsure.",
    messages=[{"role": "user", "content": "I was charged twice this month."}],
    output_config={"format": {"type": "json_schema", "schema": triage_schema}},
)

text = next(block.text for block in response.content if block.type == "text")
triage = json.loads(text)
print(triage)
  • Keep the model name in configuration because structured-output availability depends on the model enabled for your account.
  • The next example still validates business rules after the JSON is decoded.

Validate a Model Classification Locally

Schema compliance is only the first check. This second layer rejects categories and priorities that the product does not support.

Validate a Model Classification Locally
from dataclasses import dataclass

@dataclass(frozen=True)
class Triage:
    category: str
    priority: str
    needs_review: bool

def validate_triage(value: Triage) -> bool:
    return value.category in {"billing", "technical", "account"} and value.priority in {"low", "normal", "high"}

print(validate_triage(Triage("billing", "high", False)))
Output
True
  • A valid shape is not enough; the allowed values are verified in code.

A Narrow Tool Input Contract

A narrow schema is easier to authorize than a generic “run action” tool. The tool handler must still compare order_id with the authenticated user.

A Narrow Tool Input Contract
{
  "name": "get_order_status",
  "description": "Read the status for one order owned by the authenticated customer.",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {"type": "string"}
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}
  • Do not expose arbitrary database query tools to a model.

Normalize a Category Before Routing

Even when a schema returns a string, normalize and compare it against the categories supported by your product.

Normalize a Category Before Routing
def normalized_category(value: str) -> str:
    category = value.strip().casefold()
    return category if category in {"billing", "technical", "account"} else "other"

print(normalized_category(" Billing "))
Output
billing
  • Do not rely on a response matching your preferred capitalization.
Shape is only the first guarantee

Structured-output design review

4 checks
  • I can distinguish schema validity from factual correctness.
  • I use enums and explicit review states.
  • I validate all privileged identifiers in backend code.
  • I keep tool schemas narrow.

Schema mistakes that still leave a risky workflow

  • Parsing prose with regex when a schema is needed.
  • Using an unbounded free-text command field.
  • Skipping backend validation because a response matched a schema.

A practical refactor

Replace one brittle parser with a small contract

0 of 2 completed

Structured-output questions developers ask in code review

Yes. Structured output controls the final response shape while strict tools constrain tool input shape.

It prevents uncertainty from being silently converted into a confident automated decision.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.