Form tokens
The dashboard renders forms from JSON Schema. A role’s form_schema (and a workflow’s inputSchema) declares structure: properties, types, required, title, description, and x-lt-* tokens. The renderer supplies layout, styling, validation and accessibility. This page covers how a schema becomes controls; each token family has its own page.
Controls from JSON Schema
Section titled “Controls from JSON Schema”The control follows the field’s declared type and its tokens; a stored value only fills it. A field with no value renders its control’s empty state: an empty number input, an unchecked box, a select opened on Choose…, a multi-select with nothing picked, an empty JSON editor. Every property starts at its default, or {} for an object, or empty text.
| Declared type | Renders as |
|---|---|
boolean |
Checkbox |
boolean + enum or x-lt-options |
Select emitting true or false (a yes or no decision) |
number / integer |
Number input |
string |
Text input; format: "textarea" for multi-line |
string / number + enum |
Select |
Any scalar + x-lt-options |
Select with labeled options: inline, from the context, or from a lookup (see Validation and options) |
array + x-lt-options |
Multi-select chips emitting the picked values in list order |
array / object + x-lt-widget: "json" |
Validated JSON editor (see Widgets) |
array without options |
Tag display (read-only) |
object |
Nested fields from properties, or a widget (see Widgets) |
null |
Read-only “null” display |
String formats
Section titled “String formats”format on a string field selects a specialized input:
| Format | Renders as |
|---|---|
"password" |
Masked input. An ephemeral token replaces the value in the resolver payload |
"date" |
Date picker |
"date-time" |
Date and time picker |
"email" |
Email input; a pre-submission check enforces a valid address |
"uri" |
URL input; a pre-submission check enforces https?://... |
"textarea" |
Multi-line text area |
{ "properties": { "due_date": { "type": "string", "format": "date" }, "contact_email": { "type": "string", "format": "email" }, "report_url": { "type": "string", "format": "uri" }, "notes": { "type": "string", "format": "textarea" } }}Labels
Section titled “Labels”A field’s label resolves in order:
- The
titlekeyword, the author’s explicit label. It always wins. - Snake and kebab keys, title-cased:
item_countbecomesItem Count. All-caps tokens inside a key keep their casing:po_numberbecomesPo Number,PO_numberbecomesPO Number. - Single-token keys pass through unchanged:
PO,SKU,ITEMCOUNT.
Declare title on every field whose key is not already a readable label. The label appears everywhere the field faces a person: the form, the dictionary display and the errors panel.
{ "properties": { "ITEMCOUNT": { "type": "number", "title": "Item count" } }}Schema title and description
Section titled “Schema title and description”title and description at the schema root render in the form header: title as the section header, description as a short subtitle beneath it. Keep the description to a phrase; longer guidance belongs in x-lt-help. A field’s own title and description render as its label and its inline instruction.
{ "title": "Expense approval", "description": "Review the expense report and approve or reject with notes.", "properties": { "approved": { "type": "boolean", "title": "Approve", "description": "Check to approve. Leave unchecked to reject." } }}Required and read-only fields
Section titled “Required and read-only fields”Every required input (text, select, number, upload or checklist group) shows a red asterisk at its label and blocks submission when empty. A checklist with x-lt-require-all is required by definition and carries the asterisk too.
{ "required": ["decision"], "properties": { "decision": { "type": "string", "enum": ["approve", "reject"] }, "notes": { "type": "string", "description": "Optional comments" } }}A required object field (a checklist widget, for example) is satisfied when at least one of its boolean values is true. Fields hidden by x-lt-showIf at submit time skip required validation (see Conditional visibility).
readOnly: true displays the value as static text:
{ "properties": { "request_amount": { "type": "number", "readOnly": true }, "approved_amount": { "type": "number", "description": "Enter the approved amount" } }}Pair readOnly with x-lt-hide-if-empty: true to suppress empty facts, with x-lt-display: "dictionary" to render facts as a definition list, or with x-lt-widget: "markdown" to embed a rendered content block such as review instructions.
Credential fields
Section titled “Credential fields”"format": "password" fields are masked. On submit, the platform replaces the plain-text value with a short-lived ephemeral token (eph:v1:*, 15-minute TTL) before storing it or signaling the workflow, so the secret stays out of the escalation record. The workflow receives the token and passes it to the integration layer for redemption.
{ "required": ["api_key", "api_secret"], "properties": { "api_key": { "type": "string", "description": "API key" }, "api_secret": { "type": "string", "format": "password", "description": "API secret, stored as an ephemeral token" } }}How fields render
Section titled “How fields render”The renderer applies one set of rules to every generated form:
- Width follows content. Forms hold a readable measure, selects size to their options, numbers stay hand-sized, and prose fields fill their cell.
- Decisions are enums. A decision field renders as a select that opens on a disabled Choose… placeholder, so the resolver makes an explicit pick. Model decisions as
enum. - Checkboxes confirm work and live in checklists. Sentence-length require-all items render one per row; short pick-any tags flow as chips. The renderer chooses from item length and
x-lt-require-all;x-lt-variantoverrides. - Geometry follows the container. Dictionaries reflow from paired columns to stacked labels as space narrows, tables fold into cards, and the form grid drops to one column when the side panel narrows it.
A form that resolves quickly follows one shape: facts first as a dictionary, one explicit decision that gates the rest, each outcome revealing its own section with x-lt-showIf, pairs side by side with x-lt-column-group, and a short sign-off note last.
Errors
Section titled “Errors”At the field. The first failing check on a visible field renders inline: the border takes the error color, the field shakes once, and the message appears beneath the control. Format checks fire on blur; everything else fires on submit.
The errors panel. A blocked submit opens the side panel with every issue: the field’s label and the message. Clicking a row scrolls to and focuses the field. The count (“3 issues to resolve”) updates as fixes land.
Server enforcement. Roles with enforce_schema validate the submission on the server and reject an invalid payload with a canonical 422 carrying per-field errors. The form maps those onto the same inline and panel surfaces, so the resolver sees one error language wherever the check ran. See Schema enforcement.
Write custom messages as the instruction the resolver should follow, in one line: “Enter a quantity between 1 and 4.”
Drafts
Section titled “Drafts”Form edits save to browser storage, per escalation, a moment after each change, and restore when the resolver returns to the item. A lapsed claim, an accidental navigation or a browser restart keeps typed input. The draft holds field values only; the form always renders from the current schema. Untouched defaults are not stored. The draft clears when the escalation is resolved, acknowledged or cancelled from that browser.
Accessibility
Section titled “Accessibility”- Every input is associated with its label (
label[for]toinput[id]), and checkbox labels wrap their controls. - Required fields set
aria-required; failing fields setaria-invalidand link their message througharia-describedby. - Inline messages and the errors panel render as
role="alert"live regions, so corrections are announced as they happen. - The checklist widget is a
role="group"labeled by its field description. - A locked form (unclaimed, claimed by another user, or a lapsed claim) is
inert: its fields leave the tab order. - Modals are
role="dialog"witharia-modaland a labeled title; Escape closes them. - Entries in the errors panel are buttons: activating one focuses the failing field, by keyboard too.
Token reference
Section titled “Token reference”| Token | Placement | Purpose |
|---|---|---|
x-lt-layout |
Root | "two-column" grid; on a list schema, the page layout |
x-lt-span |
Field | 2 spans the full width of a two-column grid |
x-lt-order |
Root | Explicit field order |
x-lt-section |
Field | Groups fields under a labeled section |
x-lt-column-group |
Field | Pairs consecutive fields inside one grid cell |
x-lt-bind |
Field | The path the value occupies in the payload |
x-lt-help |
Root | Markdown guidance in the side panel, with {{domain.path}} tokens |
x-lt-context |
Root | Plain-text guidance when x-lt-help is absent |
x-lt-showIf |
Field | Show the field when a condition holds |
x-lt-hide-if-empty |
Field | Suppress the field when its value is empty |
x-lt-hidden |
Field | Never render; the value passes through |
x-lt-widget |
Field | A rich input or display block in place of the default control |
x-lt-language |
Field | Syntax hint for the code-editor widget |
x-lt-source |
Field | The path holding checklist items, or the embedded escalation id |
x-lt-default-checked |
Field | An unanswered checklist starts fully checked |
x-lt-variant |
Field | Checklist shape: rows or chips |
x-lt-display |
Field or root | "dictionary" renders read-only facts as a definition list |
x-lt-section-options |
Root | Per-section display and column settings |
x-lt-scan |
Field | The scan schemes a field accepts and the values it expects |
x-lt-scan-submit |
Root | Submit once every required scan field holds a value |
x-lt-min-length / x-lt-max-length |
Field | String length bounds read from a path at submit |
x-lt-minimum / x-lt-maximum |
Field | Numeric bounds read from a path at submit |
x-lt-pattern-error |
Field | Custom message when pattern fails |
x-lt-options |
Field | Select options: literal, from a path, or from a lookup; cascades |
x-lt-nullable |
Field | A select can be cleared back to null |
x-lt-require-all |
Field | Every checklist item must be checked |
x-lt-require-any |
Root | At least one field of a group holds a value |
x-lt-require-sum |
Root | A group of numeric fields reaches a minimum total |
x-lt-labels |
Root | Rename the footer actions; cancel: false removes cancel |
x-lt-submit-on-claim |
Root | Claiming also submits the form |
x-lt-transition |
Root | Wait screen until the next step is assigned to the submitter |
x-lt-transition-message |
Root | Markdown on the wait screen |
x-lt-transition-max-wait-seconds |
Root | How long the wait screen waits (5 to 300, default 30) |
x-lt-transition-done |
Root | Where the page goes when the step ends |
x-lt-href |
Field | URL template for the link widget |
x-lt-fields |
Field | Facts on an embedded escalation card |
x-lt-query |
Field | Facet query for the escalation-list widget |
x-lt-columns |
Field or list schema | Columns for an escalation list, roster or facet table |
x-lt-actions |
Field | Inline row actions on an escalation list or roster |
x-lt-submit-guard |
Root | Hold submit while a query or roster has outstanding rows |
x-lt-invoke |
Field | Start a workflow from the form, directly or through its form |
x-lt-roster |
Field | Expected members of a container, each waiting, in or flagged |
x-lt-active / x-lt-history |
List schema | The live item card and the history column |
x-lt-group-by |
List schema | Board entity key, or table row grouping |
x-lt-related |
List schema | Named related sets beside each row |
x-lt-row-detail |
List schema | Sections in a row expansion |
x-lt-card |
List schema | The card on a facet board |
x-lt-row-action |
List schema | The row click: claim or view |
x-lt-viewport |
Root | Replace the form with your own app in an iframe |
Invoke forms covers the same vocabulary on a workflow’s inputSchema.
