This reference documents all available field types for defining Schemas in Ligantic. Schemas define the structure of your data and are the foundation for both Experiences and Flows.
Field Type | Description | Supported Validations |
|---|---|---|
| Text values | required, minLength, maxLength, pattern, minWords, maxWords |
| Numeric values | required, min, max |
| True/false values | required, mustBeChecked, mustNotBeChecked |
| Date and datetime values | required |
| Time duration values | required |
| Recurring event/period rules (RRULE / RFC 5545) | required |
| File attachments | required, minLength, maxLength |
| Rich text content | required, minLength, maxLength, minBlocks, maxBlocks, minWords, maxWords |
| Geographic data | required |
| Nested object structure | required |
| List of values | required, minLength, maxLength |
| Link to other Records | required |
All field types share these base properties:
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| Yes | — | Display name (min 1 character) |
|
| No |
| Sort order in UI |
|
| No |
| Help text for the field |
|
| No |
| Whether field is required |
|
| No |
| Show in table views |
stringText values with optional display modes and validation.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Input display mode |
|
| No | — | Placeholder text |
|
| No | — | Options for select display |
|
| No |
| Default value |
|
| No | — | Validation rules |
Mode | Description | Use Case |
|---|---|---|
| Single-line text input | Names, emails, short text |
| Multi-line text input | Descriptions, notes |
| Dropdown selection | Predefined choices |
| Radio button group | Single choice selection |
select options support two formats:
"Option" (label and value are both "Option"){ "label": "Option Label", "value": "option-value" }required — Field must have a valueminLength — Minimum character countmaxLength — Maximum character countpattern — Regex pattern matchminWords — Minimum word countmaxWords — Maximum word count{
"type": "string",
"name": "Email Address",
"display": "text",
"placeholder": "Enter your email",
"validations": [
{
"rules": [
{ "type": "required", "message": "Email is required" },
{
"type": "pattern",
"value": "^[^@]+@[^@]+\\.[^@]+$",
"message": "Invalid email format"
}
]
}
]
}numberNumeric values (integers or decimals), with a configurable display format.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Default value |
|
| No | — | Default display format for this field (see below) |
|
| No | — | Validation rules |
The defaultFormat property controls how number values are displayed. It changes only the display; the stored value stays a plain number. It uses a discriminated union:
Type | Properties | Description |
|---|---|---|
|
| One of the built-in format presets (see below) |
|
| Custom options (see below) |
Formatting follows the viewer's locale, so separators differ by locale (for example, 1,234.50 in en-AU and 1.234,50 in de-DE).
Preset | Example Input | Example Output | Notes |
|---|---|---|---|
|
|
| Rounded to a whole number |
|
|
| Exactly two decimal places |
|
|
| Value is a fraction: |
|
|
| Short scale notation |
|
|
| Scientific notation |
Property | Type | Required | Description |
|---|---|---|---|
|
| Yes | Plain number, percent (value is a fraction), or currency |
|
| When | Three-letter ISO 4217 code in upper case, such as |
|
| No | Fewest decimal places to show, padding with zeros |
|
| No | Most decimal places to show, rounding the rest |
|
| No | Show thousands separators. Defaults to |
Example: { "type": "custom", "style": "currency", "currency": "AUD" } → $1,234.50
If the options cannot be applied (for example, "currency" style without a currency, or minimumFractionDigits greater than maximumFractionDigits), the number is shown unformatted.
When displaying a number value, formats are resolved in this order:
defaultFormat — the Schema field's configured default format1234.5required — Field must have a valuemin — Minimum value (inclusive)max — Maximum value (inclusive){
"type": "number",
"name": "Age",
"default": 18,
"defaultFormat": {
"type": "preset",
"preset": "integer"
},
"validations": [
{
"rules": [
{ "type": "required" },
{ "type": "min", "value": 0, "message": "Age must be positive" },
{ "type": "max", "value": 150, "message": "Invalid age" }
]
}
]
}In the editor, a number field also accepts arithmetic expressions. Prefix the value with = to evaluate it on blur or Enter, for example, =2 + 3 * 4 commits 14.
+, -, *, /, and x/X for multiplication; parentheses for grouping; unary +/- for signs.=1.5e-2 commits 0.015.% divides the number by 100. In additive contexts the percent is relative to the other operand: =100 + 10% commits 110 and =100 - 10% commits 90. In multiplicative contexts it is an absolute fraction: =100 * 10% commits 10.booleanTrue/false toggle values.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Default value |
|
| No | — | Validation rules |
required — Field must have a value (true or false, not null)mustBeChecked — Field must be true; intended for consent or terms-and-conditions style checkboxesmustNotBeChecked — Field must be false; intended for flows where a checkbox must remain uncheckedrequired only enforces that the stored value is not null or undefined. Both true and false satisfy this rule.mustBeChecked enforces an explicit true value. false, null, and undefined fail validation.mustNotBeChecked enforces an explicit false value. true, null, and undefined fail validation.mustBeChecked for opt-in consent flows. Leave the field without validations when the checkbox is optional.mustNotBeChecked when the unchecked state is the valid submission requirement.{
"type": "boolean",
"name": "Accept Terms",
"default": false,
"validations": [
{
"rules": [{ "type": "mustBeChecked", "message": "You must accept the terms" }]
}
]
}dateDate and datetime values. Supports per-value timezone storage via IANA timezone identifiers (for example, 2024-05-15T23:20:16+10:00[Australia/Melbourne]), configurable display formats, and timezone display preferences.
When a datetime includes both an offset and an IANA timezone annotation, the annotated timezone is treated as authoritative for wall-clock scheduling. If they conflict, the value is normalised by preserving the provided wall time in the annotated timezone (the offset is adjusted to match the zone).
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Include time component |
|
| No |
| Default value or special value |
|
| No | — | Default display format for this field (see below) |
|
| No | — | Show dates in the stored (source) or viewer's timezone |
|
| No | — | Validation rules |
The defaultFormat property controls how date values are displayed. It uses a discriminated union:
Type | Properties | Description |
|---|---|---|
|
| One of the built-in format presets (see below) |
|
| A custom CLDR/Unicode date format pattern |
Preset | Example Output | Notes |
|---|---|---|
|
| Date only |
|
| Date only |
|
| Date only |
|
| Date only |
|
| Requires |
|
| Requires |
|
| With timezone name |
|
| Full date + time + timezone |
|
| Time only, requires |
Uses CLDR/Unicode date format tokens. When timezoneDisplay resolves a timezone ("source" or "viewer"), custom patterns are rendered in that timezone. If no display timezone can be resolved, formatting falls back to the runtime local timezone. Common tokens:
yyyy — 4-digit year, MM — 2-digit month, dd — 2-digit dayHH — 24-hour, hh — 12-hour, mm — minutes, ss — secondsa — AM/PM, EEEE — full weekday name, MMMM — full month nameExample: "dd/MM/yyyy HH:mm" → 15/01/2024 14:30
When includeTime is true, the timezoneDisplay property controls which timezone is used for rendering:
"source" (default): Display in the timezone stored with the value"viewer": Convert to the viewer's timezone (from user preferences or browser)When displaying a date value, formats are resolved in this order:
defaultFormat — the Schema field's configured default formatValue | Description |
|---|---|
| Current date and time |
| Current date at midnight |
ISO string | Specific date/time value |
"today" uses the calendar date in the viewer's timezone when one is available (for example, Flows that create Records use the triggering user's timezone). Otherwise it uses the runtime's timezone, which is the browser timezone in forms. Date-only fields get a plain date such as 2026-04-08. Fields with includeTime: true get midnight in that timezone with its IANA identifier, such as 2026-04-08T00:00:00+10:00[Australia/Brisbane].
In an Experience, "now" and "today" read the time the page was loaded until the page has finished loading its data. Blocks that appear after that read the time they appear.
required — Field must have a value{
"type": "date",
"name": "Event Date",
"includeTime": true,
"default": "now",
"defaultFormat": {
"type": "preset",
"preset": "longDateTime"
},
"timezoneDisplay": "viewer",
"validations": [
{
"rules": [{ "type": "required" }]
}
]
}durationTime duration values (ISO 8601 duration format).
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No | — | Validation rules |
required — Field must have a value. A zero duration (PT0S) counts as a value and satisfies required.Duration supports no other constraints: there is no minimum, maximum, allowed-unit or precision option.
Durations use ISO 8601 designator format: P[n]Y[n]M[n]W[n]DT[n]H[n]M[n]S
Examples:
PT1H — 1 hourPT30M — 30 minutesP1D — 1 dayP1W — 1 weekP1Y2M3D — 1 year, 2 months, 3 days-PT1H30M — negative 1 hour 30 minutesPT0.5S — half a secondWhat is accepted:
Zero | Yes — |
Negatives | Yes, with a leading |
Weeks | Yes |
Fractional values | Yes, on the smallest unit present, down to nanosecond precision |
Mixed signs | No — |
Only ISO 8601 is accepted. Values such as "90 minutes", "1:30" and a bare number are rejected. The API, CSV import, the editor, and AI-driven Flows accept exactly the same set of values.
Durations are entered through a segmented control with a field per unit — years, months, weeks, days, hours, minutes, seconds — plus a sign toggle that appears when the value is negative or when you engage the control. Negative durations are legal.
Values are stored exactly as authored and are never normalised. A duration entered as PT90M stays PT90M; it is shown as 90 minutes in the control and displayed as 90m, not 1h 30m.
Tab moves directly between unit inputs, skipping the stepper buttons. Up and Down arrow keys adjust the focused unit; the stepper buttons remain clickable.
Stepping a field with the arrow keys carries into the next unit — 60s to 1m, 60m to 1h, 24h to 1d, 30d to 1mo, 12mo to 1y. Typing a value directly is never rewritten: type 31 into days and the value stays 31 days.
Display uses compact unit notation, largest unit first, with zero components omitted: 1y 2mo 3d 4h 5m 6s. Negatives take a leading minus (-1h 30m). A zero duration displays as 0s; an unset field displays as empty.
Durations sort and compare by elapsed time, not by the text of the stored value. Calendar units have no fixed length, so comparison uses a fixed conversion: one month counts as 30 days, and one day counts as 24 hours.
Consequences worth knowing:
P1M, P30D and PT720H all compare as equal.P1Y counts as 360 days, so P1Y is less than P365D.eq matches on elapsed time, so filtering a field for P1M also returns Records storing P30D.Equality follows the same rule as ordering, so filtering for eq P1M returns the same Records as gte P1M AND lte P1M.
Records that tie on elapsed time fall back to the stored text for a stable, repeatable order.
The comparison operators eq, ne, gt, gte, lt, lte, in and nin all use this rule. isNull and isNotNull test only for the presence of a value.
CSV export writes the bare ISO 8601 string as stored (P1M, not 1mo), and CSV import accepts the same, so a round trip is lossless. An empty cell imports as unset; an unparseable one is reported as an invalid file rather than being silently guessed at.
In generated JSON Schema — including the tool schemas handed to AI models — a duration field is emitted as a string with format: "duration" and a matching pattern.
{
"type": "duration",
"name": "Meeting Length",
"validations": [
{
"rules": [{ "type": "required" }]
}
]
}recurrenceA recurring event or period, grounded in RRULE / RFC 5545. A recurrence is a rule + a DTSTART anchor + an optional extent (duration). With no extent it is a point event ("deliver every Monday"); with an extent it is a period ("a delivery window each Monday, 09:00–12:00"). This is the RFC 5545 model: a period is an event with a DURATION.
Every occurrence is a zoned date-time, and wall-clock time is preserved across DST — a rule anchored at 09:00[America/Chicago] fires at 09:00 local wall time on every occurrence, including across a spring-forward. The full RFC 5545 grammar is supported, including BYSETPOS ("the 2nd / last Friday").
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Anchor carries a time component. When |
|
| No | — | Validation rules |
required — Field must have a valueThe stored value is a structured, JSON-safe object holding the RFC 5545 recurrence parts, a ZonedDateTime ISO anchor, and an optional ISO-8601 duration extent (reusing the duration representation):
Field | Type | Description |
|---|---|---|
| ZonedDateTime ISO string | The anchor. Carries an IANA zone annotation. Omitted for a rule with no start date; see Rules without a start date |
| IANA time zone name | The time zone of a rule with no start date, written exactly as named in the IANA database, e.g. |
|
| Recurrence frequency (required) |
|
| Every |
|
| Stop after |
| UTC ZonedDateTime ISO string | Stop after this instant. Must be UTC when the anchor carries a zone (RFC 5545) |
|
| Weekday tokens: |
|
| Restrict to these months |
|
| Restrict to these days of month (negative = from end) |
|
| Restrict to these days of year |
|
| Restrict to these week numbers |
|
| Select the n-th / last item after the other |
|
| Restrict to these hours |
|
| Restrict to these minutes |
|
| Restrict to these seconds (60 = leap second) |
|
| Week start day (default |
| ZonedDateTime ISO string[] | Explicitly excluded occurrences |
| ZonedDateTime ISO string[] | Additional (one-off) occurrences |
| ISO 8601 duration | The extent — makes the recurrence a period rather than a point |
The RFC 5545 RRULE string is a derived interop form of this value, not the canonical storage. In a Flow, the recurrenceToRRule expression function produces the DTSTART;TZID=… + RRULE lines. The lines do not include the extent: for a period, add duration as a DURATION property where you need it. For a rule without a start date, DTSTART is its first occurrence on or after 1 January 1970 in its time zone; a rule with no occurrences fails with "The recurrence rule has no occurrences to export". RSCALE/SKIP parts (RFC 7529) are not supported.
A recurrence can omit dtstart and still carry a time of day, for example "weekly on Tuesday at 8:54 PM". The editor stores such a rule with the author's time zone in tzid and the time in byHour/byMinute:
{
"freq": "WEEKLY",
"byDay": ["TU"],
"byHour": [20],
"byMinute": [54],
"tzid": "Australia/Melbourne"
}byHour/byMinute/bySecond time in tzid (midnight when there is none), wherever and whenever the rule is expanded.tzid (saved before time zones were stored) is expanded in UTC. Open and save it in the editor to record your time zone.interval counts from a fixed point in the past, so "every 2 weeks" falls on the same weeks every time it is expanded. Set a start date to choose which weeks.To list its occurrences, a rule without a start date must say which days it falls on, and cannot have a count:
| Needs |
|---|---|
|
|
|
|
|
|
The editor shows a message such as "A yearly recurrence needs a start date" when a choice needs one. A stored value that does not meet these requirements still loads and shows its summary, and the editor shows the same message when it is opened; Flows that list its occurrences fail with that message until a start date or the missing days are added.
{
"type": "recurrence",
"name": "Delivery Schedule",
"includeTime": true,
"validations": [{ "rules": [{ "type": "required" }] }]
}A value for that field — "every Monday at 09:00 America/Chicago, for a 3-hour window, up to 10 times":
{
"dtstart": "2024-01-01T09:00:00-06:00[America/Chicago]",
"freq": "WEEKLY",
"byDay": ["MO"],
"count": 10,
"duration": "PT3H"
}Recurrence fields are created in the designer like any other field type. The value editor is a curated picker over the full grammar, so the common cases are a few clicks while the stored value remains the full RFC 5545 structure. It sets:
freq and interval, daily to yearly.On: which days the rule falls on:
byMonthDay: [-1]), or the Nth / last weekday ("the 2nd Friday"). Months too short for a chosen day skip it, so use Last day for the end of every month.exdate. Each skipped date removes every occurrence that day, and follows the rule if you later change its time or time zone. Both the summary and the date list show skipped dates in the recurrence's time zone. Up to three dates are shown in chronological order: the most recent past skip, the next upcoming skip (including today), and the closest remaining date to today in the rule's time zone. Equal distances favour the upcoming date; if all skips are on one side of today, the nearest three are shown. Additional dates are in an expandable disclosure. Skip adds a date without closing the editor and returns focus to the date input.count), or on a date (until).When the field is disabled, its value cannot be changed or cleared, even if the picker is already open.
Parts of a stored value the editor has no control for (rdate, bySecond, wkst, several times a day, minute-only constraints without an hour, and byHour/byMinute alongside a start date) are kept unchanged when the value is edited. Entering an explicit time replaces minute-only constraints with that time. The same goes for a day selection the editor cannot show, such as byYearDay or byWeekNo: the editor says so and keeps it until you choose Replace day pattern or change the frequency.
Use the Data - Recurrence node to list the occurrences in a date range, find the next or previous occurrence, or test whether a date/time falls on one. The same operations are available as expression functions. A Flow trigger can also take a recurrence as an input.
fileFile attachments with type restrictions.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Allow multiple files |
|
| No |
| Show media and document previews |
|
| Yes | — | Allowed file type categories |
|
| No |
| Max file size in bytes (5MB default) |
|
| No | — | Validation rules |
Category | Extensions |
|---|---|
| jpg, jpeg, png, gif, webp, svg |
| mp4, webm, mov, avi |
| mp3, wav, ogg, m4a |
| pdf, doc, docx, txt, rtf |
| xls, xlsx, csv |
| ppt, pptx |
| zip, rar, 7z, tar, gz |
| js, ts, py, java, html, css, json |
required — At least one file must be uploadedminLength — Minimum number of uploaded files when multiple: truemaxLength — Maximum number of uploaded files when multiple: truerequired ensures the field is not empty.minLength and maxLength count the number of uploaded files, not the length of file names.multiple: true.maxLength validation group has an activation rule, the editor only disables additional uploads while that activation rule matches the current form values.maxSize continues to apply to each file individually.maxLength is reached, the UI normally blocks further uploads; in rare cases, more files can still be uploaded.When displayMediaPreview is true, supported images, video, audio, DOCX, PDF, and PPTX files are rendered inline. DOCX and PDF files are displayed as paginated document previews; PPTX files are displayed as a scrollable slide preview. In Record tables, document files remain filename links instead of rendering inline; a preview button opens the full document in a modal. The same button appears in the top-left of an inline document preview. PDFs remain file links when the browser does not support canvas rendering. Other file types remain file links with a download action.
{
"type": "file",
"name": "Profile Photo",
"multiple": true,
"displayMediaPreview": true,
"allowedTypes": ["image"],
"maxSize": 10485760,
"validations": [
{
"rules": [
{ "type": "required", "message": "Please upload supporting files" },
{ "type": "minLength", "value": 2, "message": "Upload at least two files" },
{ "type": "maxLength", "value": 5, "message": "Upload no more than five files" }
]
}
]
}contentRich text content, stored as an array of blocks.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No | See Block Types | Allowed block types |
|
| No | See Mark Types | Allowed mark types |
|
| No | — | Placeholder text |
|
| No |
| Show floating toolbar on selection |
|
| No | — | Validation rules |
When allowedBlocks is omitted, the field allows the blocks in this table. Other block types can only be used when listed in allowedBlocks.
Block | Description |
|---|---|
| Standard paragraph |
| Heading levels |
| Block quote |
| Numbered list |
| Bulleted list |
| List item |
| Code block |
| Hyperlink |
| Embedded image |
| Embedded video |
| Embedded audio |
| Table |
When allowedMarks is omitted, the field allows the marks in this table. Other marks can only be used when listed in allowedMarks.
Mark | Description |
|---|---|
| Bold text |
| Italic text |
| Underlined text |
| Strikethrough text |
| Inline code |
When creating, patching, JSON-patching, or replacing a Record through the API, a content field's value can be either the block array or a plain markdown string. A markdown string is converted to blocks, so the stored value is always a block array. This applies at any depth inside object and array fields.
required — Content must not be emptyminLength — Minimum character countmaxLength — Maximum character countminBlocks — Minimum block countmaxBlocks — Maximum block countminWords — Minimum word countmaxWords — Maximum word count{
"type": "content",
"name": "Article Body",
"allowedBlocks": [
"paragraph",
"heading1",
"heading2",
"block-quote",
"numbered-list",
"bulleted-list",
"image"
],
"allowedMarks": ["bold", "italic", "code"],
"placeholder": "Write your article here...",
"hoveringToolbar": true,
"validations": [
{
"rules": [
{ "type": "required" },
{
"type": "minWords",
"value": 100,
"message": "Article must be at least 100 words"
}
]
}
]
}geojsonGeographic data stored either as raw GeoJSON or, in map editors, as standard geography references.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No | All types | Allowed geometry types |
|
| No | — | Schema for feature properties |
|
| No |
| Display configuration |
|
| No | — | Validation rules |
Type | Description |
|---|---|
| Single coordinate |
| Line of coordinates |
| Closed polygon |
| Multiple points |
| Multiple lines |
| Multiple polygons |
Inputs Display:
{
"display": "inputs"
}Map Display:
{
"display": "map",
"defaultZoom": 13,
"defaultCenter": {
"latitude": 40.7128,
"longitude": -74.006
}
}required — GeoJSON data must be provided{
"type": "geojson",
"name": "Store Location",
"geometryTypes": ["Point"],
"display": {
"display": "map",
"defaultZoom": 15,
"defaultCenter": {
"latitude": 51.5074,
"longitude": -0.1278
}
},
"validations": [
{
"rules": [{ "type": "required" }]
}
]
}When the map editor is used in geography-reference mode, values are stored as:
{
"mode": "geography-reference",
"rows": [
{
"id": "AU",
"label": "Australia",
"sources": [
{
"type": "reference",
"geographySet": "world-country-ne-v1",
"geographyCode": "AU"
}
],
"properties": {
"displayCount": 10,
"detailLevel": "Continental"
}
}
]
}Row-level property data can be supplied either as additional top-level row keys or inside rows[].properties. Both are merged into the rendered feature's GeoJSON properties object. When using the map editor in geography-reference mode, clicking a selected geography keeps the current viewport and exposes inline label editors for the selected rows.
When shaping geography-reference values, you must choose the correct rows[].sources[].geographySet from the identifier level of your data (for example, country codes vs. subdivision codes). For detailed instructions on data shaping and a complete list of available geography sets, see Visualisation — Geography-Reference Data Shaping.
For one row to one shipped geography unit, the minimal reference-source shape is:
{
"id": "US-CA",
"sources": [
{
"type": "reference",
"geographySet": "world-admin1-north-america-ne-v1",
"geographyCode": "US-CA"
}
]
}objectNested object with its own schema structure.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| Yes | — | Nested field definitions |
|
| No | — | Validation rules |
required — Object must have valuesWhen an object field contains conditional required rules, Ligantic evaluates property defaults in dependency order. If a property's required condition references another property, the referenced property's default is applied first, including when that dependency is introduced by nested object fields.
{
"type": "object",
"name": "Address",
"showInTable": false,
"properties": {
"street": {
"type": "string",
"name": "Street",
"display": "text"
},
"city": {
"type": "string",
"name": "City",
"display": "text"
},
"country": {
"type": "string",
"name": "Country",
"display": "select",
"options": ["United States", "United Kingdom", "Canada", "Australia"]
},
"postalCode": {
"type": "string",
"name": "Postal Code",
"display": "text"
}
}
}arrayList of values with a defined item schema.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| No |
| Array input display mode |
|
| Yes | — | Schema for array items |
|
| No | — | Validation rules |
Note: In Studio, selecting array
display = "checkboxes"auto-configuresitemsto a stringselectschema and shows the options editor at the array level (the nested item schema editor is hidden in this mode). Ifitemsis not a string schema, Studio shows a warning confirmation before applying this conversion.
Note: In Studio, array validation rule selection supports
required,minLength, andmaxLength, including whendisplay = "checkboxes". UseminLengthto require multiple checkbox selections.
required — Array value must be present (can be empty)minLength — Minimum item countmaxLength — Maximum item count{
"type": "array",
"name": "Tags",
"items": {
"type": "string",
"name": "Tag",
"display": "text"
},
"validations": [
{
"rules": [
{
"type": "minLength",
"value": 1,
"message": "At least one tag required"
},
{
"type": "maxLength",
"value": 10,
"message": "Maximum 10 tags allowed"
}
]
}
]
}{
"type": "array",
"name": "Team Members",
"items": {
"type": "object",
"name": "Member",
"properties": {
"name": {
"type": "string",
"name": "Name",
"display": "text"
},
"role": {
"type": "string",
"name": "Role",
"display": "select",
"options": [
{ "label": "Developer", "value": "developer" },
{ "label": "Designer", "value": "designer" },
{ "label": "Manager", "value": "manager" }
]
},
"email": {
"type": "string",
"name": "Email",
"display": "text"
}
}
}
}Validations are organised into groups. Each group has:
condition (optional): when to apply these rules.rules: the rules to apply.{
"type": "string",
"name": "Company Name",
"validations": [
{
"condition": {
"type": "property",
"path": ["isBusinessAccount"],
"operator": "eq",
"value": { "type": "literal", "value": true }
},
"rules": [
{
"type": "required",
"message": "Company name required for business accounts"
}
]
}
]
}A condition path normally starts at the Record's top-level fields. A path that starts with @ starts at the field the rule belongs to instead:
@ is the field's own value.^ after it moves one level up. Every level counts, including an array: from a field at lines[2].hours, @^ is the line lines[2], @^^ is the lines array and @^^^ is the Record.@^.kind is lines[2].kind.In JSON, write each part as its own segment, ["@", "^", "kind"]; the string forms "@.^.kind" and "@^.kind" are the same path.
For example, a hours field inside invoice lines can be required only on lines whose kind is service:
{
"type": "number",
"name": "Hours",
"validations": [
{
"condition": {
"type": "property",
"path": ["@", "^", "kind"],
"operator": "eq",
"value": { "type": "literal", "value": "service" }
},
"rules": [{ "type": "required" }]
}
]
}kind: the rule applies to the second line of [{ "kind": "goods" }, { "kind": "service" }] only.^ keeps counting levels: from a field inside weeks[1].days[3], @^^^.billable is weeks[1].billable.^ after a field name, is invalid for that field: the condition never applies, including when it is negated with not.relationshipLinks a Record to one or more Records in another Schema. The field's value is the linked Record ID or IDs.
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes | — | Field type identifier |
|
| Yes | — | The Schema ID of the related Records |
|
| Yes | — | Whether the field holds one or many references |
|
| No | Source Schema name | Label shown on the target Record's relationship side |
|
| No | — | Template for rendering related Record labels |
"one": The field value is a single Record ID string (or null if empty)."many": The field value is an array of Record ID strings.targetLabel is trimmed; omitted, empty, whitespace-only, or default-equivalent values are omitted so the cardinality-aware default applies.targetLabel for a field that sends the property is an explicit revert — the relationship's label is reset to the cardinality-aware default, discarding any custom label previously set from the field or the relationship editor. A field that omits targetLabel entirely leaves the existing label untouched.eq, ne, in, isNull, and isNotNull.A relationship field can be declared on either side of the relationship. The direction of the relationship does not depend on which Schema declares the field.
Saving the Schema updates only the target-side label, from targetLabel. It does not change the relationship's direction or the label and settings on the other side.
The Schema editor shows the target-side label currently in use, including changes made in the relationship editor.
targetSchemaId replaces the relationship with a new one. Links to the old target are not carried over.Validation | Type | Description |
|---|---|---|
|
| Record must have at least one link |