Help
Help
List Schema DSL
Every list has a schema: a small domain-specific language (DSL) that describes the list's columns (their keys, types, labels, validation, and conditional visibility). This page is the complete reference for that schema object.
The schema is a JSON object (not a comma-separated string). You send it as the schema field when you:
- create a list:
POST /api/lists(see Lists), or - rebuild a list's schema:
PUT /api/lists/:id/schema(destructive; wipes and recreates all columns).
For small, non-destructive column edits (rename a label, add/remove one column while preserving row data), send a
propertiesarray toPUT /api/lists/:id/schemainstead (see Lists → Updating the schema). This page covers the DSL object form.
The schema object
{
"name": "Books to Read",
"description": "My reading backlog.",
"fields": [
{ "key": "title", "type": "text", "label": "Title", "required": true },
{ "key": "author", "type": "text", "label": "Author" },
{ "key": "year", "type": "number", "label": "Year" },
{ "key": "read", "type": "boolean", "label": "Read", "defaultValue": false }
]
}
| Key | Required | Description |
|---|---|---|
name | Yes | Schema name (non-empty string). Required by the validator even when creating a list, where the list's displayed title comes from the top-level title; set both to the same value. On PUT …/schema (rebuild), name becomes the list's new title. |
description | No | Optional description of the schema. |
fields | Yes | Array of column definitions. Must contain at least one column (see below). |
Every list has at least one column
The fields array can never be empty: every list must define at least one column. An empty schema is rejected:
{ "error": "Invalid schema: DSL must have at least one field", "code": "bad_request" }
This is separate from a column's own required flag: fields must contain ≥ 1 column, while each individual column may independently be optional or required: true for row entry. Column keys must be unique within the schema (duplicates return 400 Invalid schema: Duplicate field keys found: …).
Column (field) definitions
Each entry in fields describes one column. key, type, and label are always required; everything else is optional.
| Property | Required | Description |
|---|---|---|
key | Yes | Machine key stored in each row's rowData (e.g. author). Unique within the schema. |
type | Yes | The column's data type: one of the Field types listed below. |
label | Yes | Human-readable column header shown in forms and tables. |
required | No | true makes the column mandatory when a row is added or edited (default false). |
defaultValue | No | Value pre-filled for new rows. |
options | select / multiselect | Array of allowed values. Required for select and multiselect. |
placeholder | No | Placeholder text for the input. |
helpText | No | Help/tooltip text shown beneath the field. |
validation | No | Extra rules (see Validation rules below): min, max, minLength, maxLength, pattern, step. |
visible | No | false hides the column by default (default true). |
visibility | No | Conditional-visibility rule (see Conditional visibility below): show this column only when another column matches a condition. |
displayOrder | No | Integer ordering; defaults to the field's position in the array. |
Each fields[] entry is stored as a ListProperty row (key → propertyKey, label → propertyName, type → propertyType). Row data (POST /api/lists/:id/data) is keyed by each column's key, not its label.
Field types
Twelve column types are supported:
| Type | Stores | Options | Notes |
|---|---|---|---|
text | Single-line text | n/a | Honors minLength / maxLength / pattern. |
textarea | Multi-line text | n/a | Honors minLength / maxLength / pattern. |
number | Number | n/a | Honors min / max. |
boolean | true / false / null | n/a | Rendered as a True/False dropdown; optional fields also offer Neither (null). |
date | Calendar date (YYYY-MM-DD) | n/a | Honors min / max (ISO date strings). |
datetime | Date + time (ISO) | n/a | Honors min / max (ISO datetime strings). |
email | Email address | n/a | Built-in email-format validation; pattern optional. |
url | URL | n/a | Built-in URL-format validation. |
tel | Phone number | n/a | Free text; add a pattern to constrain format. |
select | One value from options | Required | Value must be one of options. |
multiselect | Array of values from options | Required | Each value must be in options. |
priority | One of low, medium, high, urgent | Optional | Defaults to those four options when none are supplied. |
{
"name": "Support Tickets",
"fields": [
{ "key": "subject", "type": "text", "label": "Subject", "required": true },
{ "key": "priority", "type": "priority", "label": "Priority" },
{ "key": "status", "type": "select", "label": "Status", "options": ["open", "pending", "closed"], "defaultValue": "open" },
{ "key": "labels", "type": "multiselect", "label": "Labels", "options": ["bug", "feature", "question"] },
{ "key": "due", "type": "date", "label": "Due date" },
{ "key": "contact", "type": "email", "label": "Contact email" }
]
}
Validation rules
Rules go in a field's validation object and are applied when a row is added or edited (POST/PUT /api/lists/:id/data), not when the schema itself is created. A failing row returns the offending column's message.
| Rule | Applies to | Effect · error message |
|---|---|---|
min | number, date, datetime | Minimum value/date. <label> must be at least <min> (numbers) · … must be after <min> (dates). |
max | number, date, datetime | Maximum value/date. <label> must be at most <max> · … must be before <max>. |
minLength | text, textarea | Minimum length. <label> must be at least <n> characters. |
maxLength | text, textarea | Maximum length. <label> must be at most <n> characters. |
pattern | text, textarea, email | Regex the value must match. <label> format is invalid. |
step | number | Increment hint for the number input (UI only; not enforced on the stored value). |
Type-level checks always run regardless of validation: email must be a valid address, url a valid URL, boolean a real boolean, select/multiselect values must be within options (<label> must be one of: …), and a required empty value yields <label> is required.
{
"key": "sku",
"type": "text",
"label": "SKU",
"required": true,
"validation": { "pattern": "^[A-Z0-9-]+$", "minLength": 4, "maxLength": 20 }
}
Conditional visibility
A column can be shown or hidden based on another column's value via a visibility.condition of { field, operator, value }. field is the key of the column to watch.
{
"key": "studentId",
"type": "text",
"label": "Student ID",
"visibility": {
"condition": { "field": "ticketType", "operator": "equals", "value": "student" }
}
}
Supported operators:
| Operator | Works with | Shows the column when… |
|---|---|---|
equals | any | watched value = value |
notEquals | any | watched value ≠ value |
contains | string, array | watched value contains value |
notContains | string, array | watched value does not contain value |
greaterThan | number, date | watched value > value |
lessThan | number, date | watched value < value |
greaterThanOrEqual | number, date | watched value ≥ value |
lessThanOrEqual | number, date | watched value ≤ value |
isEmpty | any | watched value is empty (value is ignored) |
isNotEmpty | any | watched value is non-empty (value is ignored) |
Notes:
- Visibility controls form display. A hidden column is skipped during row validation while empty, so a
requiredcolumn that is currently hidden by its condition is not enforced until the condition makes it visible. - Reference columns that appear earlier in
fields: visibility is evaluated indisplayOrder. - A single condition per column is supported (there is no
and/orgrouping).
A complete example
POST /api/lists
Content-Type: application/json
{
"title": "Event Registrations",
"isPublic": false,
"schema": {
"name": "Event Registrations",
"description": "Attendees and their options.",
"fields": [
{ "key": "attendee", "type": "text", "label": "Attendee", "required": true },
{ "key": "email", "type": "email", "label": "Email", "required": true },
{ "key": "ticketType", "type": "select", "label": "Ticket type", "required": true,
"options": ["general", "student", "vip"], "defaultValue": "general" },
{ "key": "studentId", "type": "text", "label": "Student ID",
"visibility": { "condition": { "field": "ticketType", "operator": "equals", "value": "student" } } },
{ "key": "guests", "type": "number", "label": "Guests", "defaultValue": 0,
"validation": { "min": 0, "max": 5 } },
{ "key": "notes", "type": "textarea", "label": "Notes", "helpText": "Dietary needs, accessibility, etc." }
]
}
}
Errors
Schema problems are returned as 400 with a message naming the offending column:
{ "error": "Invalid schema: Field 'year' has invalid type 'integer'. Valid types: text, number, date, datetime, boolean, select, multiselect, textarea, email, url, tel, priority", "code": "bad_request" }
Common causes: missing name, an empty fields array, a field missing key/type/label, an unknown type, a select/multiselect without options, or duplicate keys.
Related
- Lists (Help Center): the user-facing guide to building lists and their columns.
- Lists: the list CRUD, rows, watchers, and connections endpoints.
- API overview: base URL, authentication at a glance, and response conventions.
- API explorer: try list endpoints live with the interactive Swagger console.