Full and Simple schemas
Every Unlayer design exists in two interchangeable forms of the same JSON. They describe the same design — same rows, columns, and content — but at different levels of verbosity.
The one you reach for most when working with AI is the Simple schema.
The Simple schema is the AI schema
When the AI Assistant generates or edits a design — and when you use the template importer — the model reads and writes the Simple schema, not the Full one.
That's deliberate. The Simple form drops everything a language model doesn't need to reason about a design: editor bookkeeping, computed values, and any field already sitting at its default. The result is a much smaller payload that costs fewer tokens, is easier for a model to produce correctly, and contains only the fields that actually describe the design.
The editor always holds the Full design in memory. On an AI request it converts that to Simple before handing it to the model, validates the Simple JSON the model returns, then converts back to Full to apply it to the canvas. You never have to do this dance by hand inside the builder — but if you call the AI or convert APIs directly, knowing which form you're holding is the whole game.
Simple isn't only for AI. It's also the compact form Unlayer stores and ships over the wire, and the shape the Cloud API convert endpoints produce. "AI schema" and "wire format" are the same Simple schema.
Full vs Simple at a glance
The Full schema is what the embedded editor manipulates in memory — every option, every computed default, every internal property. Verbose, but lossless.
The Simple schema is the compact projection of it. Going from Full to Simple:
- Drops
counters— the editor's ID bookkeeping at the design root. - Strips editor- and admin-only fields —
_meta,_styleGuide,selectable,draggable,duplicatable,deletable,hideable,locked,displayCondition,anchor, and computed values likecalculatedWidth/calculatedHeight. - Omits fields that equal their default — e.g. a row whose
paddingis already"0px"simply won't carry it. - Reshapes some fields into terser forms — most notably
border(a per-side object becomes a CSS-shorthand string) andfontSize("14px"becomes the number14).
Everything that's stripped or reshaped can be restored exactly — see Converting between forms.
Structure, side by side
Here is the same design — one row, one column, one button — in each form. Watch counters, the _meta/selectable/displayCondition bookkeeping, border, and fontSize.
Full
84 lines · ~2.7 KB
{
"counters": { "u_row": 1, "u_column": 1, "u_content_button": 1 },
"body": {
"id": "body",
"rows": [
{
"id": "row_1",
"cells": [1],
"columns": [
{
"id": "column_1",
"contents": [
{
"id": "button_1",
"type": "button",
"values": {
"text": "<span style=\"font-size: 14px;\">Get started</span>",
"href": {
"name": "web",
"values": {
"href": "https://example.com",
"target": "_blank"
}
},
"buttonColors": {
"color": "#FFFFFF",
"backgroundColor": "#0879A1",
"hoverColor": "#FFFFFF",
"hoverBackgroundColor": "#0879A1"
},
"size": { "autoWidth": true, "width": "100%" },
"fontSize": "14px",
"textAlign": "center",
"padding": "10px 20px",
"border": {
"borderTopWidth": "2px",
"borderTopStyle": "solid",
"borderTopColor": "#2dc26b"
},
"borderRadius": "4px",
"containerPadding": "10px",
"displayCondition": null,
"_meta": {
"htmlID": "u_content_button_1",
"htmlClassNames": "u_content_button"
},
"selectable": true,
"draggable": true,
"duplicatable": true,
"deletable": true,
"hideable": true,
"locked": false
}
}
],
"values": {
"backgroundColor": "",
"padding": "0px",
"border": {},
"_meta": { "htmlID": "u_column_1", "htmlClassNames": "u_column" }
}
}
],
"values": {
"backgroundColor": "",
"columnsBackgroundColor": "",
"padding": "0px",
"hideDesktop": false,
"_meta": { "htmlID": "u_row_1", "htmlClassNames": "u_row" },
"selectable": true,
"draggable": true,
"deletable": true
}
}
],
"values": {
"contentWidth": "500px",
"contentAlign": "center",
"textColor": "#000000",
"backgroundColor": "#F7F8F9",
"fontFamily": { "label": "Arial", "value": "arial,helvetica,sans-serif" },
"preheaderText": "",
"_meta": { "htmlID": "u_body", "htmlClassNames": "u_body" }
}
},
"schemaVersion": 24
}
Simple
59 lines · ~1.7 KB
{
"body": {
"id": "body",
"rows": [
{
"id": "row_1",
"cells": [1],
"columns": [
{
"id": "column_1",
"contents": [
{
"id": "button_1",
"type": "button",
"values": {
"text": "<span style=\"font-size: 14px;\">Get started</span>",
"href": {
"name": "web",
"values": {
"href": "https://example.com",
"target": "_blank"
}
},
"buttonColors": {
"color": "#FFFFFF",
"backgroundColor": "#0879A1",
"hoverColor": "#FFFFFF",
"hoverBackgroundColor": "#0879A1"
},
"size": { "autoWidth": true, "width": "100%" },
"fontSize": 14,
"textAlign": "center",
"padding": "10px 20px",
"border": "2px solid #2dc26b, 0px solid #000, 0px solid #000, 0px solid #000",
"borderRadius": "4px",
"containerPadding": "10px"
}
}
],
"values": {
"backgroundColor": "",
"padding": "0px"
}
}
],
"values": {
"backgroundColor": "",
"columnsBackgroundColor": "",
"padding": "0px"
}
}
],
"values": {
"contentWidth": "500px",
"contentAlign": "center",
"textColor": "#000000",
"backgroundColor": "#F7F8F9",
"preheaderText": ""
}
},
"schemaVersion": 24
}
Both describe an identical button, yet the Simple form is 25 lines (~30%) shorter and ~38% smaller — and it contains only the fields that carry meaning. That's exactly what makes it the right input and output for a language model, and the gap widens fast on a real, multi-row template where the stripped bookkeeping, defaults, and per-side border objects add up across every block.
Converting between forms
Convert in either direction with the Cloud API:
POST /v3/convert/full-to-simple— Full → Simple.POST /v3/convert/simple-to-full— Simple → Full.
Two flags shape the output of full-to-simple:
includeConversion: trueattaches a_conversionblock to the Simple result. It records the fields that were stripped or reshaped so thatsimple-to-fullcan rebuild the original Full design exactly — a lossless round-trip. Without it, stripped editor metadata and unknown fields can't be recovered. See_conversionon the Design object.includeDefaultValues: truekeeps default-valued fields in the Simple output instead of omitting them.
The same conversion is available to AI agents through the MCP convert_design tool.
Validation
Before storing a design or handing it to the editor, check it against the canonical schema with POST /v3/templates/validate. Pass displayMode (email, web, document, or popup) so per-mode field rules apply — much better DX than letting a malformed design crash a render.
Related
- Design object — the root JSON object and the
_conversionround-trip block. - Block object — the rows, columns, and content envelope shared by both forms.
- Content types — the per-type
valuesshapes. - AI Assistant — generates and edits designs in the Simple form.
- Cloud API — the REST surface that converts and validates these structures.