Zod z.toJSONSchema io: "input" for a Sume request body schema
Zod's z.toJSONSchema outputs the output type by default. Pass io: "input" to describe what a client may send, and note which Zod types cannot be represented.

Writing a request schema in Zod and generating its JSON Schema keeps a client and its documentation aligned. The Zod JSON Schema page says z.toJSONSchema takes a target of draft-04, draft-07, draft-2020-12 (the default) or openapi-3.0, read 2026-10-03. By default it represents the output type; io: "input" extracts the input type instead. That distinction matters for a request body, where defaults and transforms change what a caller must send.
Input versus output
Consider a field with a default. As output it is always present; as input it is optional. If you document a Sume request body from the output shape, you would mark fields required that a caller may omit. Using io: "input" gives the shape a client sends.
The default dialect, draft-2020-12, is also the default JSON Schema dialect in the MCP specification's 2025-11-25 release (SEP-1613), per the MCP changelog, so the default target is a reasonable match for MCP tool schemas.
import { z } from "zod";
const Body = z.object({
job_ids: z.array(z.string()).min(1).max(20),
wait_for: z.enum(["all", "any"]).default("all"),
});
console.log(JSON.stringify(
z.toJSONSchema(Body, { io: "input" }), null, 2
));What Zod cannot represent
The page also describes an override callback and an experimental z.fromJSONSchema, so treat the reverse direction as unstable. The Zod 4 changelog prefers z.url() over the deprecated z.string().url(), which matters if your schema validates a webhook_url.
| Zod type | Representable | Option |
|---|---|---|
| bigint, symbol, undefined, void | No | unrepresentable: "throw" (default), "any", or a function |
| date, map, set | No | Same option |
| transform | No | Same option |
| String, number, object, array, enum | Yes | None needed |
Using it with Sume
Sume's tools and gates page lists the gates that a schema can document: idempotency_key is required on paid and write tools, and max_spend_usd is enforced only when provided. Encode both in your own request schema so a caller cannot forget them. Use tools_schema to read the server's authoritative definition and compare it with yours; the server's schema wins.
- Generate with
io: "input"for request bodies. - Fail loudly on unrepresentable types rather than silently using
any. - Keep
idempotency_keyrequired in your own schema. - Compare against
tools_schemain a test.
Sources
Related posts
More in Developers
- "1080p is not supported by model": which Sume video ids take 1080p
Sume refuses 1080p on rows outside its allowlist, even kling-3 whose catalog lists it. Which ids accept 1080p, which do not, and a client fallback.
- 19-second AI video with references: Wan 3.0 limits on Sume
A 19 s reference-to-video job on Wan 3.0 allows 10 images, 5 videos and 5 audio clips, with video and audio each capped at 15 s total. Price for 19 s.
- 22-second AI video from a first and last frame on Sume
A 22 s clip that must land on a given last frame can use Wan 3.0 or Seedance 2.5 on Sume, the two end-frame models that run past 15 s.
- 23-second AI video with audio references on Wan 3.0 and Seedance
Audio references are accepted by Wan 3.0 (up to 5 clips, 15 s total) and Seedance 2.5. A 23 s clip price on Wan and the rules for audio inputs.
Written by Sume