template.json
template.json describes a template's display name, its editable
fields, and the capabilities it supports. Every template has one, and
you can write it in two places:
| Where | How |
|---|---|
| In Allegro | Open the template, go to the Options tab, and click Edit template.json. Apply updates the template's name, Fields, and Options tabs; save the template to keep the changes. See Editing template.json. |
| In a GitHub repository | Put template.json in the template's folder alongside index.html. On sync, name becomes the template's title and each field becomes an editable input. The file is read-only in Allegro: View template.json shows it as last synced. |
The Local Template Preview reads the same file. The REST
API's get template endpoint and the MCP get-template tool return it as
template_json.
A machine-readable
JSON Schema is
published for editor autocompletion and validation. Reference it from your file
with $schema:
{
"$schema": "https://docs.allegroaudience.com/template.schema.json",
"name": "Newsletter Signup",
"fields": [
{
"slug": "headline",
"type": "text",
"label": "Headline",
"default_value": "Stay in the know"
}
]
}
Top-level properties
| Property | Type | Required | Description |
|---|---|---|---|
$schema | string | No | The schema URL, for editor autocompletion. Allegro adds it to every file it writes. |
name | string | Yes | Display name (title) of the template, trimmed. A synced template without one is named after its folder (plan-picker → "Plan Picker"). |
fields | array | Yes | The editable fields exposed by the template, in the order editors see them. May be empty. |
supports | array | No | Capabilities the template declares. See Supports. |
plans | array | No | Preview only. Sample plans for the local preview. Ignored on sync and when applied in Allegro. |
Any other top-level property is allowed but not stored. When you apply the file in Allegro, the editor lists the properties it will leave out.
Field properties
| Property | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | Placeholder identifier. Lowercase letters, numbers, -, and _ only (^[a-z0-9_-]+$). |
type | string | Yes | One of text, textarea, richtext, number, checkbox, enum, group. |
label | string | No | Name shown to editors. Defaults to the slug in words (faq_sections → "Faq sections"). |
description | string | No | Help text shown under the label. |
tab | string | No | Top-level fields only. The tab the field appears in when editors fill it in, up to 60 characters. See Tabs. |
formats | array | No | richtext only. Any of bold, italic, link, bulleted_list, numbered_list; all five when omitted. |
repeatable | boolean | No | Lets editors enter a list of values instead of one. A group is always repeatable. |
autocomplete | string | No | text fields that aren't repeatable only. An autocomplete source whose values editors pick from. See Autocomplete. |
options | array | For enum | At least one choice: a string (used as both value and label) or { "value", "label" }, where label defaults to value. |
fields | array | For group | The subfields of a group. Subfields take every property except tab and default_value, and can be groups themselves up to three levels deep. |
default_value | any | No | Value an interaction uses until it saves its own, and the preview editor's initial value. Use an array for a repeatable field. |
An interaction action that uses the template renders default_value for any
field it has no saved value for, including fields added to the template later. A
saved value wins, even an empty one. A checkbox default of true or false is
stored as the text "true" or "false", and a default that doesn't match the
field's shape (an array on a single field, or text on a repeatable one) is
dropped on sync.
Field types
| Type | Editors see | Value |
|---|---|---|
text | Single-line text input | The text |
textarea | Multi-line text input | The text |
richtext | Editor with the field's formats | Cleaned HTML |
number | Number input | The number, as text |
checkbox | Toggle | "true" or "false" |
enum | Select of the field's options | The chosen option's value |
group | A list of items, each with one input per subfield | A list of objects keyed by subfield slug |
text, number, checkbox, and enum values are limited to 200 characters;
textarea and richtext values to 10,000. A repeatable field holds up to 50
items. Every value except richtext is inserted into the page as escaped text.
A richtext value keeps only paragraphs, line breaks, and its allowed formats,
and links must use http, https, or mailto.
Repeatable fields and groups
Mark a field repeatable to let editors add as many values as they need, such
as a list of links. Use a group when each item needs several values, such as
newsletters that each have a title, image, and ID. A group's subfields can be
repeatable, and can be groups themselves, up to three levels deep (a top-level
field, a group inside it, and a group inside that).
{
"fields": [
{
"slug": "links",
"type": "text",
"label": "Links",
"repeatable": true
},
{
"slug": "newsletters",
"type": "group",
"label": "Newsletters",
"fields": [
{ "slug": "id", "type": "text", "label": "List ID" },
{ "slug": "title", "type": "text", "label": "Title" },
{ "slug": "image", "type": "text", "label": "Image URL" },
{
"slug": "frequency",
"type": "enum",
"options": ["daily", "weekly"]
}
]
}
]
}
Loop over a repeatable field in the template HTML with
{% for item in slug %}…{% endfor %}. See
Repeatable fields for the full syntax.
Tabs
Set tab on top-level fields to split a long list of fields into tabs where
editors fill them in: the interaction editor's template fields drawer and the
template editor's Fields tab. Fields that share a tab value appear
together, so a template with a header, an FAQ, and a contact section can give
each its own tab.
{
"name": "Help Center",
"fields": [
{ "slug": "intro", "type": "textarea", "label": "Intro" },
{ "slug": "title", "type": "text", "label": "Title", "tab": "Header" },
{
"slug": "faq",
"type": "group",
"label": "Questions",
"tab": "FAQ",
"fields": [
{ "slug": "question", "type": "text" },
{ "slug": "answer", "type": "richtext" }
]
},
{
"slug": "subtitle",
"type": "text",
"label": "Subtitle",
"tab": "Header"
}
]
}
Editors see three tabs: General (intro), Header (title, then
subtitle), and FAQ (faq).
- Fields without a
tabgo in a General tab, which always comes first. - The other tabs follow in the order their first field appears in
fields. Within a tab, fields keep their order fromfields. - Tab names are matched exactly after trimming spaces, so
"FAQ"and"faq"make two tabs. - A tab name can be up to 60 characters.
- Only top-level fields take a
tab. A group's subfields always appear inside the group, and the sync ignorestabon a subfield.
Tabs only arrange the editor. They don't change how fields render, and you reference a field by its slug whichever tab it's in.
Autocomplete
Set autocomplete on a text field to let editors pick its value from a list
that one of your packages provides, such as campaign IDs from a fundraising
integration, while still letting them type their own. The value is the source's
name, package/key:
{
"slug": "campaign",
"type": "text",
"label": "Campaign",
"autocomplete": "my-package/campaign-ids"
}
To find a source's name, pick it under Autocomplete on the field in the template editor's Fields tab, then open Edit template.json.
- It works on a
textfield at any depth, including group subfields, as long as the field isn'trepeatable. - If the source's package isn't enabled, editors get a plain text input. The source stays on the field and comes back when the package is enabled.
- The field's value is whatever editors enter, so treat it like any other text value in your template.
Supports
supports lists the capabilities a template declares. plans is the only one
today: it marks the template as showing plans from an offer.
{
"name": "Plan Picker",
"supports": ["plans"],
"fields": []
}
When a template supports plans:
- Every interaction that uses the template must choose an offer. The interaction editor shows an Offer picker for it, and saving without one fails.
- No field may use the slug
plans. The sync skips a field with that slug and records a warning in the sync log.
Values other than plans are ignored on sync and recorded as warnings, so a
typo doesn't stop the rest of the template from syncing.
See Plans for how the template renders the offer's plans
in HTML, Alpine.js, and index.js.
Validation
Allegro checks the file differently depending on where it comes from.
In Allegro, Apply checks the file against the JSON syntax and the schema, lists every error with its line number, and changes nothing until they are all fixed. Saving the template then runs the same checks as the Fields tab, such as the 60-character tab limit.
From GitHub, the sync keeps what it can, so one bad field doesn't stop the
template from syncing. Skipped fields, invalid autocomplete sources, and unknown
supports values are listed as warnings on the sync status in
GitHub Sync settings.
| Problem | What the sync does |
|---|---|
| File missing or not valid JSON | Syncs the template with no fields, named after its folder |
A field's slug doesn't match ^[a-z0-9_-]+$ | Skips the field |
A field's type is unknown, or a group is nested too deep | Skips the field |
An enum field with no valid options | Skips the field |
A group with no valid subfields | Skips the group; invalid subfields are skipped alone |
A field with the slug plans on a template that supports plans | Skips the field |
An unknown supports value | Ignores the value |
An autocomplete that isn't a package/key name | Drops the source and keeps the field |
An autocomplete on a field that isn't a single text field | Drops the source |
A name another template in the repository already uses | Syncs both, with a warning |
A default_value that doesn't fit the field | Drops the default |
Files Allegro writes
When Allegro generates template.json for a template built in Allegro, it adds
$schema, leaves out properties that hold their default (an empty
description, repeatable on a group), and writes an enum option whose label
matches its value as a plain string. Copying that file into a GitHub repository
syncs back to the same template.
In local preview
Local Template Preview reads template.json and renders
an editor input per field in its sidebar, seeded with default_value.
Repeatable fields and groups get Add item, reorder, and Remove buttons.
Seed them with an array:
{
"slug": "newsletters",
"type": "group",
"fields": [{ "slug": "title", "type": "text" }],
"default_value": [
{ "title": "Morning Briefing" },
{ "title": "Weekend Reads" }
]
}
The local preview renders a plans template with two sample plans. To preview
your own, add a top-level plans list in the shape a template receives. The
sync ignores it. See Previewing Plans.
{
"name": "Plan Picker",
"supports": ["plans"],
"fields": [],
"plans": [
{
"id": "annual",
"name": "Annual",
"formatted_price": "$99.00",
"duration_label": "1 year",
"fields": { "badge": "Best value" }
}
]
}
Complete example
{
"$schema": "https://docs.allegroaudience.com/template.schema.json",
"name": "Newsletter Signup",
"fields": [
{
"slug": "headline",
"type": "text",
"label": "Headline",
"default_value": "Stay in the know"
},
{ "slug": "intro", "type": "textarea", "label": "Intro" },
{
"slug": "theme",
"type": "enum",
"label": "Theme",
"options": [
{ "value": "light", "label": "Light" },
{ "value": "dark", "label": "Dark" }
],
"default_value": "light"
},
{
"slug": "newsletters",
"type": "group",
"label": "Newsletters",
"fields": [
{ "slug": "id", "type": "text", "label": "List ID" },
{ "slug": "title", "type": "text", "label": "Title" }
],
"default_value": [{ "id": "morning", "title": "Morning Briefing" }]
},
{
"slug": "show_privacy_note",
"type": "checkbox",
"label": "Show privacy note"
}
]
}