Skip to content

The document model

A template is one JSON document. The visual editor saves it, the element tools edit it, and you can write it whole. Knowing its shape is what lets an agent build a template in one piece instead of a hundred calls.

The format is madoo.design-document/2.0. Its JSON Schema is served at GET /api/v1/json-schemas/madoo.design-document/2.0 (MCP: get_json_schema). Read a real document with get_design_template view content (REST: GET /api/v1/design-templates/{id}/draft/content).

This document is accepted as it is: a coloured page, a band, a title field and a photo field. Properties you do not write take their defaults.

{
"schemaVersion": "2.0",
"pages": [
{
"id": "7f1c2a10-0000-4000-8000-000000000001",
"name": "Poster",
"width": 595, "height": 842, "pageFormat": "A4",
"backgroundColor": "#f6f1e7",
"elements": [
{ "$type": "rectangle", "id": "7f1c2a10-0000-4000-8000-000000000010", "name": "Header band",
"left": 0, "top": 0, "width": 595, "height": 300, "fill": "#23493a" },
{ "$type": "text", "id": "7f1c2a10-0000-4000-8000-000000000011", "name": "Title",
"left": 40, "top": 60, "width": 330, "height": 110,
"text": "Books in the courtyard",
"fontFamily": "Playfair Display", "fontSize": 40, "fontWeight": "bold", "fill": "#ffffff",
"placeholder": { "id": "7f1c2a10-0000-4000-8000-000000000101", "name": "Event title",
"code": "title", "placeholderType": "text", "required": true } },
{ "$type": "image", "id": "7f1c2a10-0000-4000-8000-000000000012", "name": "Photo",
"left": 390, "top": 40, "width": 165, "height": 220, "src": "", "fitMode": "fill",
"placeholder": { "id": "7f1c2a10-0000-4000-8000-000000000102", "name": "Photo",
"code": "photo", "placeholderType": "image", "required": true, "fitMode": "fill" } }
]
}
]
}

A complete example with a flowing layout, a repeated list, a formatted price, a conditional badge, a link and a sample set is the event poster.

The root object has schemaVersion (always "2.0"), pages, and optionally masterPages, metadata (title, author, subject, keywords of the PDF), sampleSets, styles and components. Unknown properties are errors, anywhere in the document.

A page has an id, a name, a size and its elements.

  • Size. width and height are in PDF points (1 pt = 1/72 inch) and they decide the size: A4 is 595 × 842, US Letter 612 × 792, a landscape A4 certificate 842 × 595, a 4:5 social post 540 × 675. pageFormat is only a label (A4, Letter, Custom). The page commands of REST and MCP accept the named formats a3, a4, a5, letter, legal, tabloid, square with an orientation, or custom with a width and a height.
  • Background. backgroundColor is the base; backgroundPaint can add a linear or radial gradient; a backgroundImage (with fitMode cover, contain or stretch, a focal point and a scale) sits above both.
  • Height that follows the content. fitHeight with a bottomMargin makes a page end a fixed distance below its lowest printed element — never taller than the drawn height. It suits a one-page summary whose length varies.
  • A document has 1 to 100 pages. The order of pages is the order of the PDF.

Every element has left, top, width and height in points, measured from the top-left corner of the page. Children of a container — a group, a flowing Layout, a row of a repeated list — are positioned relative to their container. An element can also be rotated (angle), mirrored (flipX, flipY), made translucent (opacity) or hidden (visible: false).

Elements are painted in array order: the first element of elements is at the back, the last is in front.

Every element has an $type, a unique id and a name. The types are:

$type What it is
text A text box: font, size, weight, color, alignment, line height, letter spacing, optionally rich text with mixed styles and bullet or numbered lists
image A picture: from a URL, from Madoo storage or from a field; fitMode fit, fill or stretch; an optional frame shape and mask
rectangle, circle, ellipse, line, path Vector shapes with fill and stroke (solid or gradient), rounded corners, dashes, arrow heads; path takes standard SVG path data
svg_artwork Imported SVG artwork kept as vector, with its colors exposed as a palette you can recolor
group Elements that move together
layout_region A flowing Layout: its children are arranged vertically, horizontally or in a grid, with padding and gaps; a text that grows pushes what follows it
repeat_region A repeated list: one row template drawn once per item of a JSON list field, vertically, horizontally or as a grid
component_instance A copy of a reusable component defined in components, with per-copy overrides
folder A layer folder: it only organises the layers panel and does not change what prints

Each of them is covered in depth by the technique pages. Any element can carry a field (placeholder), a condition that decides whether it prints, and a link that makes it clickable in the PDF — see Fields and data.

A master page holds what several pages share: a letterhead, a footer, a page number. Masters live in masterPages (up to 32, not counted among the 100 pages) and have the same shape as pages.

  • A master applies only to pages of the same size.
  • A page links to a master with masterPageId, draws the master below or above its own elements (masterLayer: underlay or overlay), and can use the master’s background (useMasterBackground).
  • Masters can be assigned automatically: a master’s automaticRule is all, odd or even, and a page whose masterAssignment is automatic receives the master that matches its final page number. manual and none opt out.
  • A master holds static content only — no fields, no conditions, no repeated lists.

Page numbers are tokens inside a text: {{page}} (the page number), {{pages}} (the pages of the document) and {{sequencePages}} (the pages of the current numbering sequence). They are resolved after lists have expanded, so they are right even when a list adds pages. A page can start a new sequence (numberingStart) in arabic, roman_upper or roman_lower (numberingStyle), or hide its number (hidePageNumber).

Every page, master, element, field, style and sample set has an id — a GUID unique in the document. Write them yourself when you create a document; keep them when you rewrite one, so that edits and history stay connected. Field codes (title, price) are what the data refers to; element names are for people.

Limit
Pages 1–100
Master pages 32
Elements 1,000 per page or master, 10,000 in the document
Nesting groups 16 levels, Layouts 8, repeated lists 2
Repeated list 1–100 items per list
Components 200, each up to 500 elements
Rich text 1,000 paragraphs, 10,000 runs, 10,000 characters per text

A single render has its own ceilings (200 output pages, 25,000 resolved elements, 20 MiB per image, 100 MiB of images in total).

A document is validated as a whole every time it is saved, by the same rules as an editor save. A rejected document returns every problem with its JSON path and a code, for example:

{ "code": "content_invalid",
"errors": [ { "field": "element:3b9f…024.link",
"message": "DESIGN_LINK_INVALID: The link reads {event_id}, but the document has no field with that code." } ] }

Fix all of them and save again. Common causes: an unknown property, a duplicated id, a field code that is not a valid identifier, a list key used outside its list, a link or condition that reads a field the document does not have, a value outside its limits.

The document uses camelCase (fitMode, placeholderType); REST, MCP and agent commands use snake_case (fit_mode, placeholder_type). A few names differ beyond the case — write the document form in documents:

In the document In REST / MCP commands
number format numberStyle style
paintOrder: fill_then_stroke, stroke_then_fill paint_order: fill_stroke, stroke_fill