Schema validation
The SDK uses schemas both to describe structured data to the model and to validate data inside your application. Choose the schema form based on whether you need local validation and type inference.
| Schema form | Local validation and transforms | TypeScript inference | Supported surfaces |
|---|---|---|---|
| Zod object | Yes | Yes | Function-tool and agent-tool parameters, function-tool outputSchema, agent outputType, and handoff inputType |
| Standard Schema with Standard JSON Schema conversion | Yes, synchronously | Yes, from the validation output type | Function-tool and agent-tool parameters, agent outputType, and handoff inputType |
| Raw JSON Schema | No; the SDK only parses the JSON | No; values are typed as unknown | Function-tool and agent-tool parameters, function-tool outputSchema, agent outputType, and handoff inputType |
Use Zod when it already fits your application. Use Standard Schema when you want the same SDK validation and inferred output types with another compatible validation library. Use raw JSON Schema when you already own the wire schema and will validate values elsewhere.
Use a Standard Schema library
Section titled “Use a Standard Schema library”A compatible value must implement Standard Schema V1 validation and Standard JSON Schema conversion. Some libraries expose both capabilities directly. Others provide an adapter. This example uses Valibot with toStandardJsonSchema() from @valibot/to-json-schema.
Install the example dependencies with npm install valibot @valibot/to-json-schema in an application that already uses @openai/agents.
import { Agent, handoff, tool } from '@openai/agents';import { toStandardJsonSchema } from '@valibot/to-json-schema';import * as v from 'valibot';
const LookupOrderParameters = toStandardJsonSchema( v.object({ orderId: v.pipe(v.string(), v.minLength(1)), includeHistory: v.optional(v.boolean(), false), }),);
const lookupOrder = tool({ name: 'lookup_order', description: 'Look up an order by ID.', parameters: LookupOrderParameters, // The argument type is inferred after Valibot validation and defaults run. execute: async ({ orderId, includeHistory }) => ({ orderId, status: 'shipped', history: includeHistory ? ['placed', 'shipped'] : undefined, }),});
const Resolution = toStandardJsonSchema( v.object({ orderId: v.string(), message: v.string(), }),);
const supportAgent = new Agent({ name: 'Order support', instructions: 'Resolve order questions and return a structured summary.', tools: [lookupOrder], // The final output is converted to JSON Schema, then validated by Valibot. outputType: Resolution,});
const supportAgentTool = supportAgent.asTool({ toolName: 'resolve_order', toolDescription: 'Resolve an order question with the support specialist.', parameters: LookupOrderParameters, // inputBuilder receives the same validated and inferred parameter type. inputBuilder: ({ params }) => `Resolve order ${params.orderId}. Include history: ${params.includeHistory}.`,});
const EscalationDetails = toStandardJsonSchema( v.object({ reason: v.pipe(v.string(), v.minLength(1)), priority: v.optional(v.picklist(['normal', 'urgent']), 'normal'), }),);
const billingAgent = new Agent({ name: 'Billing specialist', instructions: 'Resolve billing questions.',});
const billingHandoff = handoff(billingAgent, { inputType: EscalationDetails, // The callback receives the validated value, including Valibot defaults. onHandoff: async (_context, details) => { if (details) { await recordEscalation(details.reason, details.priority); } },});
async function recordEscalation(_reason: string, _priority: string) {}
export { billingHandoff, supportAgent, supportAgentTool };The same schema contract has a different role on each surface:
tool({ parameters })converts the schema for the model, validates each tool call locally, applies library transforms or defaults, and passes the inferred validation output toexecute.agent.asTool({ parameters })applies the same validation and passes the inferred validation output toinputBuilderbefore the nested agent runs.new Agent({ outputType })requests structured model output, validates the parsed value locally, and exposes the inferred validation output throughresult.finalOutput.handoff(..., { inputType })validates the handoff tool-call arguments and passes the inferred validation output toonHandoff.
Requirements and limits
Section titled “Requirements and limits”Standard Schema support has a deliberately narrow contract:
- The value must provide synchronous
~standard.validatebehavior and both~standard.jsonSchema.input()and~standard.jsonSchema.output(). Validation-only Standard Schema values are not supported. - The input JSON Schema used by these SDK surfaces must have
type: "object"at its root. Wrap scalar or array values in an object schema. - Standard Schema function-tool parameters require strict mode. Do not set
strict: falsewhen using them. - Asynchronous Standard Schema validation is not supported. A validator that returns a Promise fails the operation instead of running a tool fallback or invalid-final-output handler.
- The generated JSON Schema must use constructs supported by the SDK’s strict-schema normalization. Unsupported constructs fail when the tool, handoff, or agent is created, before a model request.
- Function-tool
outputSchemadoes not currently accept Standard Schema. Use a Zod schema, a raw JSON Schema, or validate the tool result in application code.
If you are writing your own adapter, import the StandardSchemaWithJSON<Input, Output> type from @openai/agents to check that it exposes the required validation and conversion methods. Prefer a library-maintained adapter when one is available.