# Structured outputs in Node via zodTextFormat
Same guarantee as Python, different wiring. Verified against the structured
outputs guide on 2026-09-26.
## The call
```js
import OpenAI from "openai";
import { zodTextFormat } from "openai/helpers/zod";
import { z } from "zod";
const openai = new OpenAI();
const CalendarEvent = z.object({
name: z.string(),
date: z.string(),
participants: z.array(z.string()),
});
const response = await openai.responses.parse({
model: "...",
input: "Alice and Bob are going to a science fair on Friday.",
text: { format: zodTextFormat(CalendarEvent, "event") },
});
const event = response.output_parsed;
```
## The optional-field trap
Strict schemas require every key to be present or explicitly nullable. In zod
that means: do not use `.optional()` and expect it to pass. Use
`z.string().nullable()` for fields the model may omit, or give the field a
`.default()`. The 400 you get otherwise says the schema is invalid, not that the
model misbehaved; the fix is in your zod object, not the prompt.
## The naming trap
`zodTextFormat` takes a name as the second argument. Reuse the same name for the
same shape across deploys; the name becomes part of the schema identity in logs
and traces, and renaming it per request makes debugging harder.
## Check before you ship
- In an Express route, `res.json(response.output_parsed)`; in a Next.js route
handler, `Response.json(response.output_parsed)`. No manual JSON.parse anywhere.
- Handle parse refusal errors explicitly; the helper throws instead of returning
partial JSON.