Skip to main content

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.

note

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 like calculatedWidth/calculatedHeight.
  • Omits fields that equal their default — e.g. a row whose padding is 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) and fontSize ("14px" becomes the number 14).

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:

Two flags shape the output of full-to-simple:

  • includeConversion: true attaches a _conversion block to the Simple result. It records the fields that were stripped or reshaped so that simple-to-full can rebuild the original Full design exactly — a lossless round-trip. Without it, stripped editor metadata and unknown fields can't be recovered. See _conversion on the Design object.
  • includeDefaultValues: true keeps 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.

  • Design object — the root JSON object and the _conversion round-trip block.
  • Block object — the rows, columns, and content envelope shared by both forms.
  • Content types — the per-type values shapes.
  • AI Assistant — generates and edits designs in the Simple form.
  • Cloud API — the REST surface that converts and validates these structures.