How to generate a json schema from an api request payload
- Step 1Capture the most complete sample payload — Use a request body that includes every optional field populated, not a minimal one. The generator only sees the keys present in the sample — a field absent from your sample is absent from the schema, so a sparse sample under-specifies the contract.
- Step 2Drop the JSON file or paste it — Drag a
.jsonfile onto the dropzone (free tier allows up to 2 MB). The tool parses one JSON value viaJSON.parseafter trimming whitespace — it is strict, so no trailing commas, comments, or NDJSON. - Step 3Set the schema title — Type a name in the Schema title field (default
MySchema). It is written to the roottitleonly — nested object schemas never get a title — so pick the request model name, e.g.CreateOrderRequest. - Step 4Decide required and additionalProperties — Leave Mark all fields required on to list every top-level key in
required; leave Allow additional properties off to emitadditionalProperties: false. For a strict create endpoint, both of these defaults are usually what you want. - Step 5Generate and download the schema — Click Generate Schema, then Download Schema (saved as
<name>.schema.json, pretty-printed at 2-space indent). The pre-view truncates output over 5,000 characters on screen, but the downloaded and copied file is complete. - Step 6Refine for AJV before compiling — Add
format/pattern/enum/bounds by hand (none are inferred), convert any"type":"null"field to a nullable union like["string","null"], and remove genuinely optional keys fromrequired. Thenajv.compile(schema)and validate.
The three real controls and what they emit
These are the only options in the generator UI. There is no indent, format, enum, or nullable control — the output is always pretty-printed at 2-space indent.
| UI control | Type | Default | Effect on the schema |
|---|---|---|---|
| Schema title | Text input | MySchema | Written to root title only. Nested objects never receive a title. Use the request-model name for self-documenting AJV errors. |
| Mark all fields required | Checkbox | On | Adds every top-level key to the root required array. Only applies when the root is an object — a root array or primitive gets no required. |
| Allow additional properties | Checkbox | Off | Off ⇒ additionalProperties: false (AJV rejects unknown keys). On ⇒ additionalProperties: true. Emitted on the ROOT only, never on nested objects. |
Sample value → inferred Draft-07 type
Inference is type-only. No value-level constraints (format, pattern, enum, min/max) are ever derived from the sample.
| Sample value | Inferred schema | AJV note |
|---|---|---|
"alice@x.com" | {"type":"string"} | No format:"email" — add it manually for AJV email validation |
30 | {"type":"integer"} | Whole numbers use integer (via Number.isInteger); AJV rejects 30.5 |
19.99 | {"type":"number"} | Decimals use number; accepts both ints and floats |
true | {"type":"boolean"} | Strict boolean — AJV rejects "true" strings unless you add coercion |
null | {"type":"null"} | NOT nullable — AJV then rejects a real string here. Change to ["string","null"] |
["a","b"] | {"type":"array","items":{"type":"string"}} | Item schema is the merge of all elements (see array rules below) |
[] | {"type":"array","items":{}} | Empty items:{} matches anything — provide a populated array to constrain items |
Cookbook
Real request bodies from common API patterns, showing the generated schema and the one refinement step AJV needs before it will reject bad input. Sample tokens and PII are placeholders.
Strict create-order request schema
ExampleA POST /orders body with required and additional-property defaults left on produces a schema AJV uses to reject both missing and unexpected keys.
Sample request body:
{ "customerId": "c_91", "amount": 4200, "currency": "USD" }
Schema title: CreateOrderRequest · required: ON · additionalProperties: OFF
Generated:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "CreateOrderRequest",
"type": "object",
"properties": {
"customerId": { "type": "string" },
"amount": { "type": "integer" },
"currency": { "type": "string" }
},
"required": ["customerId", "amount", "currency"],
"additionalProperties": false
}Compile and validate with AJV
ExampleThe Draft-07 output compiles unchanged in AJV 8. additionalProperties:false makes the typo'd curency key fail.
const Ajv = require('ajv');
const ajv = new Ajv({ allErrors: true });
const validate = ajv.compile(require('./CreateOrderRequest.schema.json'));
validate({ customerId: 'c_91', amount: 4200, curency: 'USD' });
// => false
// errors: [{ keyword: 'required', params: { missingProperty: 'currency' } },
// { keyword: 'additionalProperties', params: { additionalProperty: 'curency' } }]Fixing a null field so a real value passes
ExampleA sample with phone: null infers "type":"null", which then rejects any real phone string. Convert to a nullable union by hand.
Sample: { "name": "Sue", "phone": null }
Generated property (wrong for real traffic):
"phone": { "type": "null" }
AJV against { name:'Sue', phone:'+15551234' } => false (type must be null)
Manual fix:
"phone": { "type": ["string", "null"] }
Now both null and a string pass.Make a field optional for a PATCH endpoint
ExampleGenerate with required ON to get the full key list, then prune. PATCH typically requires nothing, so empty the required array.
Generated (required ON): "required": ["customerId", "amount", "currency"] For PATCH /orders/:id, keep additionalProperties:false but drop required: "required": [] Keeps the unknown-key rejection while allowing partial bodies.
express-validator with checkSchema
ExampleThe Draft-07 output is not express-validator's native schema format, but you can validate with AJV inside a middleware and feed errors to express-validator's error shape.
const Ajv = require('ajv');
const validate = new Ajv().compile(require('./CreateOrderRequest.schema.json'));
function validateBody(req, res, next) {
if (validate(req.body)) return next();
return res.status(400).json({ errors: validate.errors });
}
app.post('/orders', validateBody, createOrder);Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
A null sample value becomes type:null, not nullable
By design{ "middleName": null } infers { "type": "null" }. AJV then rejects a real string in that field. The generator has no nullable inference — change the property to { "type": ["string", "null"] } (Draft-07) or add nullable: true if you are targeting OpenAPI 3.0. Always check every null in your sample.
An array of objects with differing keys keeps only the first object's shape
Partial schemaItem schemas are merged by top-level type only. [{"a":1},{"b":2}] — both items are objects, so the merge returns the FIRST item's schema and the result describes only a; b is silently dropped from items.properties. Generate from an array whose first element contains every field, or from a single complete object.
format, pattern, enum, and bounds are never inferred
Not inferred"alice@x.com" infers "type":"string", not format:"email". The generator emits structural types only. Add format, pattern, enum, minLength/maxLength, minimum/maximum, and minItems by hand — these are exactly the rules that make request validation strict.
additionalProperties is only set on the root object
Root onlyThe Allow additional properties setting writes additionalProperties on the top-level object only. Nested objects (e.g. body.address) get no additionalProperties, so AJV accepts unknown keys inside them. Add additionalProperties: false to each nested object manually if you need deep strictness.
NDJSON / JSONL request logs throw a parse error
Parse error.ndjson/.jsonl are accepted by extension, but the tool runs JSON.parse on the whole file as one value. A file with one object per line throws Unexpected non-whitespace character after JSON. Wrap your log lines in a [ ... ] array first, or extract one line and generate from that single request.
Trailing comma or comment in the sample
Parse errorParsing is strict JSON.parse — { "a": 1, } or // comment throws Expected double-quoted property name in JSON. Run the sample through the JSON prettifier or JSON validator to clean it to canonical JSON before generating.
Required is empty when every top-level key is genuinely optional
Expectedrequired is only populated when Mark all fields required is on AND the root is an object. For a PATCH-style contract, turn the checkbox off (no required written) or generate with it on and then empty the array. Both yield a valid Draft-07 schema with no required fields.
Root is an array, not an object
SupportedIf the sample is a top-level array (e.g. a bulk endpoint body), the schema root is {"type":"array","items":...}. No required and no additionalProperties are written, because both apply only to object roots. The item schema follows the array merge rules.
Free-tier 2 MB limit on a large captured payload
Schema limitFree JSON processing caps at 2,000,000 bytes (2 MB). A multi-megabyte response dump is more sample than you need for a schema — trim it to one representative object. Pro raises the limit to 100 MB if you genuinely must process a large file.
Frequently asked questions
Which AJV versions does the generated schema work with?
All current ones. The output declares $schema: http://json-schema.org/draft-07/schema#, which AJV 6 supports natively and AJV 7/8 support via the default Draft-07 meta-schema. const validate = ajv.compile(schema) then validate(body) works unchanged.
Why doesn't an email field get format: email?
The generator infers structural types only — a string is "type":"string". It never reads the value to guess a format. Add format: "email" (and enable AJV's ajv-formats plugin) manually after generation.
How do I make a field optional?
Every top-level key is added to required when the checkbox is on. Delete the optional keys from the required array after generating. The property definition stays — only its presence in required makes it mandatory.
Does additionalProperties: false apply to nested objects too?
No. It is written only on the root object. Nested objects accept unknown keys unless you add additionalProperties: false to each one by hand. This is a deliberate single-level behavior in the generator.
Can I validate request bodies with a null-able field?
Generate first, then fix it. A null sample value becomes "type":"null", which only accepts null. Change it to "type": ["string", "null"] (or the appropriate base type) so AJV accepts both the value and null.
Is this the same as the OpenAPI requestBody schema?
Draft-07 is a near-superset of the OpenAPI 3.0 Schema Object. Drop the $schema line and paste the rest under requestBody.content.application/json.schema. For OpenAPI 3.0 nullable fields use nullable: true; 3.1 aligns with JSON Schema's ["string","null"].
Why did my NDJSON request log fail to load?
The tool parses the file as a single JSON value, but NDJSON has one JSON object per line. That throws a parse error. Either wrap the lines in [ ... ] and add commas, or copy one line and generate from that single object.
Can I change the indentation of the output?
Not from the UI — output is always pretty-printed at 2-space indent. If you need it minified for an env var or inline config, run the downloaded schema through the JSON minifier.
How do I get TypeScript types for the validated request?
Two paths. Generate the schema here and feed it to json-schema-to-typescript, or skip the schema and convert your sample directly with the JSON to TypeScript tool. For a Zod validator instead of AJV, use the JSON to Zod tool.
Will my sample payload be uploaded?
No. Inference runs entirely in your browser (the tool is badged 100% client-side). Request bodies, internal field names, and any tokens in the sample never reach JAD Apps servers.
What is the largest sample I can use on the free tier?
2 MB (2,000,000 bytes) per file on free, with one file at a time. Pro raises this to 100 MB and 10 files. A single representative request is normally a few kilobytes, well under the free limit.
How do I check the sample is valid JSON before generating?
Run it through the JSON validator for a precise error location, or the JSON prettifier to reformat. The generator uses strict JSON.parse, so any trailing comma or comment must be removed first.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.