Skip to content

Schemas and contracts

A JSON document can be perfectly valid JSON and still be wrong. {"price": "19,90", "currency": "EURO"} parses without a complaint, yet the price is text, not a number, and no bank knows the currency EURO. JSON checks only the syntax. What a step needs — which fields, of which types, with which values — is a contract, and the standard way to write one is JSON Schema.

In a workflow the contract matters most at its borders. An AI model asked for JSON answers with something that looks right; a supplier feed changes a field without notice; a webhook sends a string where a number was agreed. A prompt that says “return a number” is a wish, not a guarantee. Validate JSON Schema (utility/json_schema_validate) turns the wish into a check: the document goes on only when it satisfies the schema, and when it does not, the errors say exactly what is wrong. It is a trust boundary — it checks structure and declared constraints, not whether the facts are true — and it costs nothing.

Madoo describes the JSON its own nodes exchange with versioned contracts, named madoo.<name>/v<N>: a dataset, a bundle of assets, a quality check, a caption track, a dubbing manifest. A JSON port that carries one declares it: get_node_type shows its schema_ref, and the schema itself is one call away — GET /api/v1/json-schemas/{reference} on REST, get_json_schema on MCP and in the AI tools, and GET /api/v1/json-schemas for the whole catalog. Read the schema before extracting a field from such a port: it is the exact list of fields, types and limits, instead of a guess from one sample.

The catalog has 44 contracts; each one has its own page in the appendix Madoo contracts, with its fields, the nodes that produce and read it, and its complete schema. By area:

Area Contracts
Data madoo.dataset-profile/v1, madoo.dataset-row/v1, madoo.dataset/v1 — Spreadsheets and datasets
Assets madoo.bundle-manifest/v1 — Asset bundles
Quality and media madoo.media-policy/v1, madoo.media-technical-info/v1, madoo.quality-check/v1, madoo.quality-policy/v1, madoo.quality-report/v1, madoo.quality-violations/v1 — Quality checks
Documents and images madoo.design-document/2.0, madoo.design-pages-manifest/v2, madoo.image-layout-report/v1, madoo.image-text-layers/v1, madoo.image-typography/v1, madoo.template-layout-report/v2
Audio madoo.audio-processing-report/v1, madoo.audio-quality-report/v1, madoo.audio-timeline/v1, madoo.stem-separation/v1, madoo.tts-generation/v1
Transcripts and captions madoo.caption-block-family/v1, madoo.caption-block/v1, madoo.caption-block/v2, madoo.caption-track/v1, madoo.caption-translation-block/v1, madoo.caption-translation-quality/v1, madoo.transcript-aggregation-quality/v1, madoo.transcript-segments/v1
Dubbing and natural speech madoo.dubbing-cue-evidence/v1, madoo.dubbing-manifest/v1, madoo.dubbing-manifest/v2, madoo.speech-timing-assessment/v1, madoo.speech-timing-candidate-rows/v1, madoo.speech-timing-candidate/v1, madoo.speech-timing-delivery/v1, madoo.speech-timing-plan/v1, madoo.speech-timing-policy/v1, madoo.speech-timing-state/v1
Video text and keyframes madoo.video-text-track/v1, madoo.video-typography/v1, madoo.keyframe-map/v1
Video highlights madoo.video-edit-manifest/v1, madoo.video-highlight-candidates/v1, madoo.video-highlight-plan/v1
Diagnostics madoo.json-schema-errors/v1, madoo.string-list/v1

Every contract carries its own name in a schema field ("schema": "madoo.caption-track/v1"), so a document says what it is. A new version gets a new name (/v2); an existing version keeps being readable.

The node receives a Document and checks it against one schema, from exactly one source:

Source The schema is… Use it for
catalog one of Madoo’s contracts, chosen in Catalog Schema a document that must be a Madoo contract — a bundle manifest or a media policy received from outside
inline written in the node, in Schema Definition your own contract: a supplier record, the JSON an AI step returns
input received at run time on the Schema input a contract that changes from run to run, supplied by the caller

The sources never fall back to one another. A catalog reference that does not exist, or an inline schema that is not a valid schema, is reported when the workflow is validated — before anything runs, not after the paid steps.

On Violation decides what an invalid document does:

Mode When the document is invalid
fail the node fails with JSON_SCHEMA_VALIDATION_FAILED, listing the first five errors — where, which rule and why, such as /colours [maxItems]: Value should have at most 6 items — in the step’s error and in the execution’s trace; the run stops
route Normalized JSON is rejected, so every step that reads it is skipped at no cost; Valid is false and Errors lists every problem, to drive a correction or review branch
warn the document goes on anyway; Valid and Errors report the problem

Wire the next steps from Normalized JSON, never from the unchecked document: that is what makes the check a barrier. With Apply Declared Defaults, missing properties that declare a default in the schema are filled in Normalized JSON, and Schema Trace says how many were.

Each error names the field (instancePointer, a JSON Pointer such as /price), the rule (keyword: type, required, enum, pattern…), what was expected and a message. The list follows the madoo.json-schema-errors/v1 contract, so it can itself be read by the next step.

Madoo reads JSON Schema Draft 2020-12. A few rules make a schema useful rather than decorative:

  • List what is required. required is the difference between “may have a price” and “has a price”.
  • Say the type of every field, and close the object with "additionalProperties": false when an unknown field means a misunderstanding (a supplier who writes colour where you expect color).
  • Constrain values: enum for a closed list (currencies, statuses), pattern for codes, minimum and exclusiveMinimum for amounts, minLength and maxLength for text that goes on a page.
  • Keep it self-contained. Shared parts go under $defs and are referenced as #/$defs/…. References to other files or URLs are refused: the check must give the same answer every time, without depending on the network.

The limits: a schema up to 256 KiB, a document up to 2 MiB (read inline or from its stored file), nesting up to 128 levels.

The schema lab checks a supplier’s product record before it becomes a listing line. The inline schema requires sku (three capitals, a dash, two digits), name, a positive price number, a currency among EUR, USD and GBP, and a whole stock, and allows nothing else. The mode is route: the listing line is made from Normalized JSON, while Valid and Errors are delivered as outputs.

The lab, left to right: the supplier record; Check the record, which reports valid and errors; Name and the listing line, skipped when the record is invalid

The figure shows the run with the wrong record. The two runs side by side:

{"sku":"LMP-04","name":"Table lamp","price":89,"currency":"EUR","stock":12} {"sku":"lmp4","name":"Table lamp","price":"19,90","currency":"EURO","colour":"brass"}
Valid true false
Listing Ready to list: Table lamp absent — Name and Listing line skipped
Errors none required: stock is missing · /sku pattern · /price type: string, should be number · /currency enum · /colour additionalProperties

Both runs complete in a few seconds at no cost. Every problem of the second record is reported at once, with the field it concerns, so whoever fixes the feed fixes it in one pass.

Terminal window
node madoo-install-example.mjs lab-schema --api <your Madoo API URL> --key <key.json> --run

The wrong record is in run-inputs-invalid.json.

An AI step in JSON mode returns an object, but not necessarily the one you described. Put Validate JSON Schema right after it, with your schema inline, and read the fields from Normalized JSON: with route, an answer that misses a field or breaks a limit never reaches the layout or the next paid step. The template gallery uses this pattern everywhere — the AI writes the copy, the schema guards the page.

When the answer arrives as text — a model without a JSON mode, an answer that wraps the object in prose — the read and the check are one node: Parse JSON (utility/json_parse) takes the same three schema sources and the same fail and route modes, reads the text (Find first takes the JSON inside the prose) and hands on json, valid and errors. A text that is not JSON at all fails with JSON_PARSE_INVALID and the line where it breaks; in route its errors carry it with the keyword json (Reading JSON).

Code Cause Fix
JSON_SCHEMA_VALIDATION_FAILED mode fail and the document breaks the schema read the errors in the message, or use route
JSON_SCHEMA_CATALOG_REF_UNKNOWN Catalog Schema names a contract that does not exist choose one from GET /api/v1/json-schemas
JSON_SCHEMA_INVALID the inline or received schema is not a valid Draft 2020-12 schema fix the schema: the message names the problem
JSON_SCHEMA_REMOTE_REF_FORBIDDEN the schema references another file or a URL move the shared part under $defs and reference #/$defs/…
JSON_SCHEMA_SOURCE_MISSING, JSON_SCHEMA_SOURCE_AMBIGUOUS the selected source is empty, or more than one source is set set exactly one source
JSON_SCHEMA_DOCUMENT_MISSING nothing JSON reached Document connect a JSON value
JSON_DOCUMENT_TOO_LARGE, JSON_SCHEMA_TOO_LARGE the document is over 2 MiB, or the schema over 256 KiB split the document, or simplify the schema