Skip to content
Siteon
Contents Writing schema.json

Docs

Writing schema.json

The file that declares the editable fields (groups / collections) and forms. Lists every type and constraint.

Siteon is infrastructure for AI-built websites: it adds hosting, a CMS your client can edit, contact forms, analytics and control from AI agents (CLI and MCP) around static HTML built with AI or by hand, so it is ready to ship.

Overall shape

schema.json declares "what can be edited on this site" and "which forms it has". Both the dashboard's input fields and the shape of the public JSON are built from it.

{
  "locales": ["ja", "en"],
  "groups": [
    { "key": "hours", "label": "Opening hours", "fields": [
      { "key": "days", "label": "Open days", "type": "text" },
      { "key": "time", "label": "Hours", "type": "text", "translate": false },
      { "key": "notice", "label": "Closure notice", "type": "textarea",
        "hint": "Any temporary closures. Leave empty to hide the whole box" }
    ] }
  ],
  "collections": [
    { "key": "news", "label": "News", "sort": "date desc", "fields": [
      { "key": "title", "label": "Title", "type": "text", "required": true },
      { "key": "date", "label": "Date", "type": "date", "translate": false },
      { "key": "tags", "label": "Tags", "type": "tags" },
      { "key": "body", "label": "Body", "type": "blocks" }
    ] }
  ],
  "forms": [
    { "key": "contact", "label": "Contact", "fields": [
      { "key": "name", "label": "Name", "type": "text", "required": true },
      { "key": "email", "label": "Email", "type": "email", "required": true },
      { "key": "message", "label": "Message", "type": "textarea" }
    ] }
  ],
  "tracking": { "gtm_id": "GTM-XXXXXXX" }
}
KeyRequiredContents
localesRequiredList of languages. The first is the primary language
groupsSets of single-value fields (opening hours, store details, etc.)
collectionsLists that grow over time (news, menu items, etc.)
formsForm definitions
trackinggtm_id only. A GTM ID set under "Settings → Advanced" in the dashboard takes precedence

Unknown keys are not saved.

Rules for key

The key of groups / collections / forms, and of the fields inside them, all follow the same rules.

  • Starts with a lowercase letter and uses only lowercase letters, digits, and _ (/^[a-z][a-z0-9_]*$/)
  • Up to 40 characters
  • Unique within the same list

locales

  • In the form ja or zh-tw (/^[a-z]{2}(-[a-z]{2})?$/). No duplicates. At least one is required
  • The first is the language editors type in, and it is also shown when a translation is missing

groups

Sets of fields that each hold a single value. label (a non-empty string) and fields are required.

In HTML, refer to them as <group>.<field>.

<dd data-cms="hours.days">Mon–Sat</dd>

collections

Lists that grow over time. label (a non-empty string) and fields are required.

  • sort — "<field>" or "<field> asc|desc". You can't name a field the collection doesn't have. If omitted, items follow the order arranged in the dashboard
  • Sorting by a number field compares numerically
  • Order is determined by the primary-language values, so it is the same in every language

In HTML, repeat them with <template data-cms-list="<collection key>"> (Editable areas).

forms

Form definitions. fields is required; label is optional.

KeyContents
keyThe value you write in the form's data-sitekit-form="…"
labelName shown in the dashboard and notifications
fieldsFields. Each key matches an input's name
success_urlWhere to go after a submission without JavaScript (application/x-www-form-urlencoded). Must be an http(s) URL within the site's allowed origins
  • Notification destinations (email, Slack) and the auto-reply text are not set in schema.json. The owner can change them under "Settings → Notifications → each form" in the dashboard. forms[].notify / forms[].mail_template are ignored with a warning if present
  • For regular forms sent with JavaScript, set the destination with data-success-url in the HTML (Forms)

fields

KeyTypeContents
keystringRequired. Follows the key rules above
labelstringField name in the dashboard
typestringOne of the types below. Defaults to text
requiredbooleanMakes the field required
translatebooleanfalse disables translation. If omitted, text types (text / textarea / blocks) are translated
hintstringInput help shown in the dashboard
optionsarrayOptions for select (required for select)

Types

TypeUseValues accepted from forms
textSingle line of textUp to 200 characters
textareaMultiple lines of textUp to 4,000 characters
blocksArticle body (blocks such as headings, photos, video)—
dateDateA real date as YYYY-MM-DD
imagePhoto (absolute URL in the public JSON)—
selectChoiceOne of options
numberNumberA finite number
emailEmail address (used as the recipient of the form's auto-reply)Up to 254 characters. Saved in lowercase
telPhone numberDigits, +, -, spaces, parentheses; up to 32 characters
checkboxCheckboxtrue / "on" / "1" / "true"
tagsTags (for collections)—

email / tel / checkbox are meant for forms. In groups or collections they are treated as a plain single line of text.

Options for select

Write options as an array of strings or as objects with value / label / color (you can mix both).

{ "key": "category", "label": "Category", "type": "select", "translate": false,
  "options": [
    { "value": "Announcement", "color": "gray" },
    { "value": "New product", "label": "New arrival", "color": "red" },
    "Other"
  ] }
  • value is a non-empty string; no duplicates
  • If you write label, it must be a non-empty string. If omitted, value is displayed as is
  • color is one of red / gold / gray / black / green / blue
  • The public JSON gains category_label and category_color (only for options with a color)
  • Categories are classification labels, so set "translate": false

tags

  • Up to 20 per item, each up to 40 characters
  • / # ? are not allowed (they appear in tag page URLs)
  • Leading and trailing whitespace is trimmed, and duplicates are merged
  • translate is always false even if you set it (tags are the same in every language)

Validation errors

If schema.json has mistakes, you get a list of what's wrong and nothing is saved. For example:

groups[0].fields[1].key must match /^[a-z][a-z0-9_]*$/ (max 40)
collections[0].sort names a field this collection does not have
forms[0].fields[2] is a select and needs a non-empty options array
forms[0].success_url must be an http(s) URL
tracking.gtm_id must match /^GTM-[A-Z0-9]+$/

Matching schema.json and HTML

schema.jsonHTML
groups[].key and fields[].keydata-cms="<group>.<field>"
collections[].key<template data-cms-list="…"> / data-cms-entry / data-cms-empty
A collection's fields[].keydata-cms="…" inside the template
forms[].key<form data-sitekit-form="…">
A form's fields[].keyThe input's name="…"

Form inputs whose name isn't in schema.json are ignored without an error (and not saved). When you add a field, add it to schema.json too.

This page is also available as Markdown. For AI agents, pass llms.txt (contents) or llms-full.txt (full text).