Fields and data
A field is the part of a template that changes from one document to the next. Everything else is fixed design. Choosing the fields well is half of designing a template: a field too many makes the data hard to produce, a field too few makes the template impossible to reuse.
A field is a placeholder on an element
Section titled “A field is a placeholder on an element”A field lives on the element that shows it, as its placeholder:
"placeholder": { "id": "3b9f6c20-1000-4000-8000-000000000101", "name": "Event title", "code": "title", "placeholderType": "text", "required": true, "description": "The name of the event, at most two lines."}codeis the key the data uses: the value oftitlein the data fills this element. It must start with a letter or_and contain only letters, digits and_(at most 128 characters), and it must be unique in the template. Use short, meaningful Englishsnake_case:title,hero_photo,price,products.nameanddescriptionare for people and agents: say what the value is and its constraints.requiredsays whether a document can be rendered without it.defaultValue(always written as a string) is used when no value arrives.
Field types
Section titled “Field types”placeholderType |
Goes on | The value in the data |
|---|---|---|
text |
a text | a string |
number |
a text | a JSON number (12, 89.5), printed with the field’s format |
boolean |
a text, or a container | true or false: prints a label, or shows the element only when true |
image |
an image | an HTTPS URL, a Madoo storage path or a data:image/… URI |
color |
a text or a shape | #RRGGBB, applied to the fill or the stroke |
json |
a repeated list | a JSON array of objects, one per row |
A field on the wrong kind of element is rejected (DESIGN_PLACEHOLDER_TYPE_MISMATCH).
The data
Section titled “The data”The data of a document is one JSON object keyed by field code. For the event poster:
{ "title": "Books in the courtyard", "dates": "Every Wednesday in June, 9 pm", "photo": "https://images.example.com/courtyard.jpg", "intro": "Five summer evenings in the library courtyard…", "speakers": [ { "date": "Wed 3 June", "line": "Laura Benassi — The water houses" }, { "date": "Wed 10 June", "line": "Tommaso Rinaldi — The miller's dog" } ], "price": 12, "sold_out": false, "event_id": "courtyard-2026"}Values are checked before anything is drawn:
- an unknown code or a value of the wrong JSON type is an error;
- a required field with no value and no default stops the render with
DESIGN_PLACEHOLDER_REQUIREDand a path that names it —title, orspeakers[1].linefor a key of a list item; - an optional field with no value leaves its element empty (or hidden, for a yes/no field in visibility mode).
Keep facts and generated text apart in the data when a workflow fills the template: prices, names and dates should come from verified sources, and AI-written copy from its own object — the workflow nodes can merge several data objects so that facts always win. See Templates in workflows.
Lists: repeated rows
Section titled “Lists: repeated rows”A json field feeds a repeated list (repeat_region): the region draws its row once per item of the array.
- The region’s
sourceCodeis the list field’s code (speakers), and the region carries thatjsonfield. - Each element inside the row carries its own field with a
bindingPathitem.<key>—item.date,item.line— that reads a key of the current item. Itscodejust needs to be unique; the key is what matters. Nested keys are allowed (item.author.name). - The keys the row reads form the item contract. Keys that only a condition or a link reads can be declared in
the region’s
itemFieldsso they are part of the contract too. maxItems(1–100) caps the list;overflowPolicydecides what happens when the items do not fit:fail,fit(shrink the rows),clip(drop what does not fit) orcontinue_page(add pages). Lists have their own technique page.
A list inside an item — the tags of each dish, the features of each product — is read by a list nested in the
row with "sourceCode": "item.features" and no field of its own; inside it, item. refers to the inner item.
Lists nest two levels deep. A nested list that carries a json field with a bindingPath is rejected
(DESIGN_BINDING_INVALID: A JSON repeat source must be a root placeholder). See
Repeated lists.
Number formats
Section titled “Number formats”A number field prints its value through a format:
"format": { "locale": "en-GB", "numberStyle": "currency", "currency": "GBP", "currencyDisplay": "symbol", "minimumFractionDigits": 0, "maximumFractionDigits": 2, "prefix": "Tickets from "}Always write currencyDisplay in a currency format ("symbol" for £, €, $; "code" for GBP, EUR, USD). A
format that uses none of the options currencyDisplay, useGrouping, signDisplay, negative, prefix,
suffix or a {language} locale prints with the original rules of the first Madoo templates — GBP 49.00
instead of £49.00 — so that templates published before these options existed never change. The editor always
writes currencyDisplay; a document you write must too.
localedecides separators and currency placement (it-ITprints12,50 €,en-USprints$12.50);{language}takes the locale from the data keylanguage, so one template serves several markets.numberStyleisdecimal,currencyorpercent(percent takes a fraction:0.25prints25%).currency(ISO code) andcurrencyDisplay(symbolorcode);minimumFractionDigitsandmaximumFractionDigits(0–20);useGroupingfor thousands separators (turn it off for a year);signDisplayandnegative(minusorparentheses).prefixandsuffix(up to 24 characters) add fixed text around the number:13.5% vol,75 cl,85 m².
Always send numbers as numbers and let the template format them: the same value can then print differently on a price list and on a receipt, and a later change of format needs no change in the data.
Yes/no and color fields
Section titled “Yes/no and color fields”- A boolean field prints
trueLabelorfalseLabel(default Yes / No), or, with"booleanMode": "visibility", shows its element — a badge, a seal, a whole panel — only when the value istrue. The sold out badge of the event poster works this way. - A color field paints its text or shape with the color in the data;
colorTargetchoosesfillorstroke. One template can then carry each brand’s color.
Conditions
Section titled “Conditions”Any element can carry a condition: it prints only when the condition holds. When it does not, the element is not
drawn, and inside a flowing Layout it leaves no gap.
"condition": { "operator": "all", "conditions": [ { "operator": "greater_than", "placeholderCode": "item.stock", "literal": "0" }, { "operator": "less_than_or_equal", "placeholderCode": "item.stock", "literal": "3" }] }Operators: not_empty, equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal (with a
numeric literal), not (one condition), all and any (several). A condition reads a template field by code, or an
item key (item.stock) inside a list row. Conditions nest up to 8 levels.
Use conditions for what the data decides: a last pieces badge, a logo shown only if there is one, an optional quote, an empty-list message.
Any element can be a link in the PDF: "link": { "href": "https://shop.example.com/p/{item.sku}", "description": "Open the product" }.
- Allowed schemes:
https,http,mailto,tel. - Braces read values and are URL-encoded:
{code}reads a template field,{item.key}a key of the current list item. A link outside a list can only read fields the template has; inside a row it may read an item key that the row does not print (the key joins the item contract). - A link that resolves to nothing usable is left out, and the render reports it. Links exist only in the PDF: page images are not clickable, and print PDFs (PDF/X-4) omit them.
The contract
Section titled “The contract”When a template is published, its fields become an immutable contract: every field with its code, type,
required flag and default, and for each list the keys of its items. Workflows, Apps and agents read the contract of
a revision (get_design_template view contract) to know exactly what to send. A later revision may change the
contract; workflows keep the revision they were built with until someone moves them on.
Sample sets
Section titled “Sample sets”A sample set is a named set of values stored in the template. Sample sets are how a template is tested: the exact preview renders each of them as it will print, and publishing checks them all.
Write sample sets that cover the cases the template must survive, not only the pleasant one:
- the longest realistic values — a two-line title, the longest product name, the most list items;
- the shortest — one item, an empty optional field, a missing logo;
- each branch — a product in stock, one in its last pieces, one sold out;
- each market — a second language or locale, if the template serves more than one.
A required field that no sample set exercises is flagged when you check the template before publishing.