Stop Parsing AI Text: Build Reliable Features with Structured Outputs A developer explains how to build reliable AI features by using structured outputs with JSON Schema instead of parsing free-form text. The approach defines a data contract that models must follow, validated at runtime with tools like Zod, improving reliability for applications that consume AI responses. A model returns this response: Priority: high Team: billing Reason: The customer was charged twice. Your application needs this: { "priority": "high", "team": "billing", "reason": "The customer was charged twice." } They look equivalent to a person. To software, they are completely different interfaces. The first response must be interpreted. The second can be validated. That distinction matters whenever an AI response is used by code rather than displayed directly to a user. If you are extracting data, routing support requests, generating UI components, calling tools, or building an agentic workflow, free-form text is often the wrong boundary. Developers have been asking models to “return JSON only” for years. It is better than parsing prose, but it does not create a reliable contract. All of these values are valid JSON: {"priority":"urgent"} {"priority":"high","team":null} {"priority":"high","team":"billing","confidence":"probably"} A parser can read them, but your application may still reject them. The enum is unexpected, a required field is missing, or a number has arrived as descriptive text. JSON answers a syntax question: Can this text be parsed as JSON? A schema answers a contract question: Does this value have the structure and constraints our application expects? JSON Schema provides a standard vocabulary for describing types, required properties, allowed values, nested objects, arrays, and other constraints. Its official documentation positions schemas as a way to improve data consistency, validation, documentation, and interoperability. This approach is increasingly relevant to AI development. OpenAI and Google both document structured-output features based on JSON Schema, with SDK support for familiar schema tools such as Zod and Pydantic. The exact API differs, but the architectural idea is portable: define the data contract, ask the model to produce it, and validate the result before using it. Imagine a support form that accepts an unstructured customer message. We want an AI model to suggest: The result will be consumed by application code, so prose is not a suitable interface. A TypeScript type might look like this: type TicketTriage = { team: "billing" | "account" | "technical" | "other"; priority: "low" | "normal" | "high"; summary: string; needsHumanReview: boolean; }; This type is useful inside the codebase, but TypeScript types disappear at runtime. The model response is external data, just like an HTTP request or a message from a queue. It still needs runtime validation. A common workflow begins with the prompt and asks what data the model can produce. Reverse it. Start with the code that will consume the result: async function routeTicket ticket: TicketTriage { if ticket.needsHumanReview { return sendToReviewQueue ticket ; } return sendToTeam ticket.team, ticket.priority, ticket.summary ; } This function reveals the actual contract: team and priority must use known values, summary must always exist, needsHumanReview must be a real boolean,The model should fit that contract. The rest of the application should not be redesigned around whatever shape the model happened to return during an early experiment. A good schema is strict enough to protect the application and small enough for people to understand. Here is a Zod schema for the example: js import { z } from "zod"; export const TicketTriageSchema = z.object { team: z.enum \ "billing", "account", "technical", "other" , priority: z.enum \ "low", "normal", "high" , summary: z.string .min 1 .max 240 , needsHumanReview: z.boolean , } .strict ; export type TicketTriage = z.infer