WEMAIDE / INSIGHTS / ENGINEERING
Structured outputs vs JSON mode: from valid JSON to valid application data
Compare JSON mode, schema-constrained output and tool calling. Run an offline invoice example that separates parsing, shape checks and business validation.
Use JSON mode when the supported API needs to return parseable JSON. Use schema-constrained structured output when the application needs a particular shape. Use tool calling when the model proposes a supported function and its arguments. Then validate the result against the current business record: a correctly typed amount can still belong to the wrong invoice.
These names do not imply identical capabilities across providers or models. Check the selected API's supported schema subset, completion states and tool execution rules. This guide compares the boundaries and supplies an offline validator. It does not benchmark models or claim that a live model produced the sample outputs.
Compare the contract you actually need
| Mechanism | Useful contract | What still needs application checks |
|---|---|---|
| JSON mode | JSON syntax under the selected provider's documented conditions | Required fields, types, values and completion outcome |
| Schema-constrained structured output | The supported shape and constraints for a successful response | Business meaning, unsupported schema rules and exceptional outcomes |
| Tool calling | A proposed function and arguments; strictness depends on the API/configuration | Allowed tool, user permissions, arguments, confirmation and execution result |
Tool calling and structured output are not mutually exclusive. An API can support schema constraints for tool arguments as well as for the final response. Selecting a tool does not by itself execute the external action. Your application controls which operations exist and whether the proposed operation is permitted now.
Use one invoice to separate three different failures
Imagine an extraction step for synthetic invoice SYN-42. The server expects USD and a draft total no higher than 50,000 minor units before additional review. Those are example rules for this dataset, not universal accounting limits. The output contract contains an invoice reference, currency, subtotal, tax and total. For this USD-only exercise, 12,000 minor units means $120.00; do not assume every currency has two decimal places.
Synthetic draft
{
"invoice_id": "SYN-42",
"currency": "USD",
"subtotal_minor": 10000,
"tax_minor": 2000,
"total_minor": 12000
}| Candidate result | Parsing / shape | Application decision |
|---|---|---|
| An unfinished JSON object | Cannot parse | parse-review; retain a diagnostic outcome |
| total_minor is the string 12000 | Valid JSON; wrong field type | schema-review |
| Identical fields with currency EUR | Valid JSON and shape | business-review: currency_mismatch |
| USD total 13000 against subtotal 10000 plus tax 2000 | Valid JSON and shape | business-review: arithmetic_mismatch |
| All sample rules pass | Valid JSON, shape and sample values | ready-for-review; no payment or export is authorised |
Run the offline example without a model account
Save the reference file and run it with Node.js 22 or later. It uses built-in modules, performs no network requests and needs no API key. The file exports the small invoice schema and an explicit validator for that schema; it is not a general-purpose JSON Schema implementation.
node structured-output-reference.mjs --demo
Expected outcomes:
valid: ready-for-review
malformed: parse-review
wrongCurrency: business-review
wrongTotal: business-review
externalWrites: 0Synthetic invoice cases, an explicit shape validator and business checks. No model calls, uploads or payments.
The local tests cover malformed JSON, unexpected fields, wrong types, an incorrect invoice identity, currency mismatch, arithmetic, negative values, the review limit and safe-integer boundaries. These are deterministic application checks. They do not measure extraction accuracy or model refusal rates, because no model is called.
Handle refusals and incomplete responses before reading the draft
When connecting a model, first inspect the response envelope and its documented completion outcome. A refusal, interrupted response or tool call is not interchangeable with a completed invoice draft. Route it explicitly instead of parsing an empty string, inventing defaults or treating a missing amount as zero. The downloadable file starts at the candidate text boundary; provider-specific envelope handling belongs in your adapter.
Choose bounded retries according to the error. Correcting an unsupported schema is different from retrying a temporary provider failure. Regenerating extraction text is also different from repeating an external write. Keep the approved draft revision and the outcome of the later write separate so a provider retry cannot silently execute a second action.
A schema does not establish source accuracy or record access
The sample can reject a wrong total, but it cannot tell whether the invoice image really said 10000. Preserve field evidence and evaluate extraction on representative labelled documents. Likewise, a model-supplied customer or invoice identifier does not grant access to that record. Load the expected record through the authenticated user's permissions and compare against server-side context.
Do not let a syntactically valid output carry its own approval. A field such as approved: true is another proposed value unless a trusted application decision establishes it. Review, destination checks and the external operation's result remain their own boundaries.
Make provider changes a small regression check
- Keep the provider/model/version, prompt, supported schema and output adapter recorded together.
- Run representative good inputs and known failures; keep extraction-quality measures separate from deterministic rule checks.
- Include refusals, empty or incomplete responses and values that fit the schema but refer to the wrong business record.
- Recheck schema support before switching providers. A keyword present in your local schema is not proof that every server enforces it.
- Release external writes only through the application's existing permission and review process.

Start a project ↗

