★ JSON Schema · from JSON
JSON to JSON Schema Converter
A draft 2020-12 schema inferred from your sample, with required lists, nullable types, string formats and nested objects factored into $defs. Useful for API docs, OpenAPI components and validating payloads in any language.
- Runs in your browser
- No signup
Convert to
Strictness
Your JSON
JSON Schema
3 types · 16 fields{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Root",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"email": {
"type": "string",
"format": "email"
},
"display-name": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"plan": {
"type": "null"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"team": {
"$ref": "#/$defs/Team"
},
"invoices": {
"type": "array",
"items": {
"$ref": "#/$defs/Invoice"
}
}
},
"required": [
"id",
"email",
"display-name",
"createdAt",
"plan",
"tags",
"team",
"invoices"
],
"$defs": {
"Team": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
},
"website": {
"type": "string",
"format": "uri"
}
},
"required": [
"id",
"name",
"website"
]
},
"Invoice": {
"type": "object",
"properties": {
"number": {
"type": "string"
},
"amount": {
"type": "number"
},
"paid": {
"type": "boolean"
},
"paidAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"note": {
"type": "string"
}
},
"required": [
"number",
"amount",
"paid",
"paidAt"
]
}
}
}
Typing APIs by hand?
I set up end-to-end type safety (DB → API → UI) so this never happens again.
How the JSON Schema mapping works
Each JSON value is read, merged with every other sample of the same key, and then mapped to the closest JSON Schema type:
| In your JSON | JSON Schema |
|---|---|
| string | { "type": "string" } |
| timestamp / email / uuid / URL | "format": date-time / email / uuid / uri |
| integer / decimal | "integer" / "number" |
| null alongside a value | "type": ["string", "null"] |
| key present in every sample | listed in "required" |
| nested object | "$ref": "#/$defs/Name" |
| mixed values | anyOf |
| empty array | "items": {} |
Worked example
A user record with a nested team, a list of invoices (one missing a note, one with a null payment date) and an awkward display-name key:
{
"id": 1042,
"email": "ada@example.com",
"display-name": "Ada",
"createdAt": "2026-11-07T09:30:00Z",
"plan": null,
"tags": ["beta", "founder"],
"team": {
"id": "7f3c9a2e-1b4d-4c8e-9f2a-6d5e4c3b2a19",
"name": "Analytical Engines",
"website": "https://example.com"
},
"invoices": [
{ "number": "INV-001", "amount": 49.5, "paid": true, "paidAt": "2026-10-01T00:00:00Z" },
{ "number": "INV-002", "amount": 49, "paid": false, "paidAt": null, "note": "Card declined" }
]
}Becomes this JSON Schema, generated by the same engine that runs the converter above (3 types, 16 fields):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Root",
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"email": {
"type": "string",
"format": "email"
},
"display-name": {
"type": "string"
},
"createdAt": {
"type": "string",
"format": "date-time"
},
"plan": {
"type": "null"
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"team": {
"$ref": "#/$defs/Team"
},
"invoices": {
"type": "array",
"items": {
"$ref": "#/$defs/Invoice"
}
}
},
"required": [
"id",
"email",
"display-name",
"createdAt",
"plan",
"tags",
"team",
"invoices"
],
"$defs": {
"Team": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid"
},
"name": {
"type": "string"
},
"website": {
"type": "string",
"format": "uri"
}
},
"required": [
"id",
"name",
"website"
]
},
"Invoice": {
"type": "object",
"properties": {
"number": {
"type": "string"
},
"amount": {
"type": "number"
},
"paid": {
"type": "boolean"
},
"paidAt": {
"type": [
"string",
"null"
],
"format": "date-time"
},
"note": {
"type": "string"
}
},
"required": [
"number",
"amount",
"paid",
"paidAt"
]
}
}
}
JSON Schema gotchas
format is advisory
Many validators only check format when you turn it on (Ajv needs ajv-formats). Without it, a bad email still passes.
Extra keys are allowed
The schema doesn't set additionalProperties: false, so unknown keys pass. Add it if you want a strict contract.
Draft versions differ
2020-12 uses $defs; older tools may expect definitions (draft-07). Rename the key if your validator is old.
Questions developers actually ask
Can I use this in OpenAPI?
How do I validate data against it in JavaScript?
Why is a field required?
Is my JSON sent to a server?
How does it know a field is optional?
Other languages
More from the workbench
Fine print
Tools: on the house.
Engineer: by the hour.
A codebase that doesn't need this tool is the upgrade. I set up end-to-end type safety so your API, database and UI agree by construction.
I'm Himanshu, an AI and full-stack engineer. I build MVPs, AI agents and the growth plumbing around them for early-stage startups.
