Template Documents
A template document is a whole Template written as one JSON object: its settings, tags, permissions, parameters, tasks, the fields on each task, the conditional logic rules that show and hide them and the custom notifications it sends. It is the only way to say what a template's tasks are through the API. You send one to create a template or publish a new version, you get one back when you export a template, and a draft holds one while it is being written.
This page is the reference for the format itself. The routes that accept and return documents are documented on Templates and Drafts, and the discovery routes that describe the format to a program — including an AI agent — are on Schema and Authoring Guide.
A field, in the document, is one of a task's controls — the Short Text box, the Dropdown, the Text block of instructions. The API calls them fields throughout. See Control Types for what each one does in the product.
Where Documents Are Used
| Method | Path | What it does with a document | MCP tool |
|---|---|---|---|
POST | /v3/templates | Creates a template from a document. | create_template |
POST | /v3/templates/{key}/versions | Publishes a document as the next version of a template. | create_template_version |
GET | /v3/templates/{key}/document | Exports a template as a document. | export_template |
POST | /v3/templates/validate | Reports what is wrong with a document without saving anything. | validate_template |
POST | /v3/drafts | Starts a draft, which holds a document that may still be invalid. | create_template_draft |
POST | /v3/drafts/{key}/commit | Checks the draft's document and publishes it. | commit_template |
All of these read and write the same shape. Everything an export returns, create and versions accept.
The Shape of a Document
{
"name": "Invoice Review",
"description": "Checking a supplier invoice, getting it approved and scheduling the payment.",
"settings": { "timeZone": "Europe/London" },
"tags": ["q3-audit"],
"permissions": [],
"parameters": [],
"tasks": [],
"conditions": [],
"notifications": []
}
| Property | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The template's name as the library lists it. At most 100 characters. |
description | string | No | What the library shows under the name. There is no per-task description — see Tasks and Headings. |
settings | object | No | Time zone, display formats and feature switches. Leave it out for the defaults. See Settings. |
tags | string[] | No | The template's own tags, by name. See Tags. |
permissions | object[] | No | Who may run and see checklists made from the template. See Permissions. |
parameters | object[] | No | Values asked for when a checklist is started. See Parameters. |
tasks | object[] | No | The tasks and headings, in order. See Tasks and Headings. |
conditions | object[] | No | The conditional logic rules. See Conditional Logic. |
notifications | object[] | No | The custom notifications. See Custom Notifications. |
Only name is required. A template with no tasks is valid, though of little use.
Rules That Apply Everywhere
- Array position is order. Tasks appear in the order they are listed, and fields in the order they are listed on their task. There is no
orderproperty. - The document carries no key and no version. The template's key is in the URL of every route that has one, and the version number is worked out when you publish.
- Everything that can be pointed at has a
ref. Tasks, fields, parameters, conditions and notifications each carry your own short name, and every cross-reference in the document is one of those refs. See Refs and Ids. - People, tags, templates and Data Sets are named, not keyed. A member is named by display name or email address, a group or tag by its name. Names are resolved against the workspace when the document is read, and a name that matches two things is refused rather than guessed at.
GET /v3/workspacereturns the names a document can use. - A document is a whole template, not an amendment. Publishing a version replaces everything, including the template's permissions and tags. See Changing an Existing Template.
- Problems are collected, not reported one at a time. A refused document comes back with every violation found, each with a path into the document. See Violations.
- Field types are spelled exactly.
DropDownis a type andDrop Downis not. Case is forgiven.
An unrecognised property on a field is refused with unknown_property, usually with a suggestion for what you meant. Elsewhere in the document — on a task, a due date or the document itself — an unrecognised property is currently ignored, so a misspelling such as hidenByDefault on a task is dropped without a word. Check those objects against the document schema.
Working With Documents
To write a new template:
- Read
GET /v3/workspacefor the member, group, tag and template names you can use. - Read
GET /v3/schema/documentfor the envelope andGET /v3/schema/content-typesfor the field types. - When you choose a field type, read
GET /v3/schema/content-types/{type}for its properties and a ready-made example. - Compose the document and send it to
POST /v3/templates/validateuntilvalidistrue, reading any warnings as well. - Send it to
POST /v3/templates, or build it in a draft if it is large.
To change a template that already exists, export it, change what you need and publish the result as a version. Do not compose a version document from scratch. See Changing an Existing Template.
A Complete Example
The document below creates an Invoice Review template. It uses a heading with instructions, template parameters, a Dropdown whose answer shows a hidden approval task and a hidden field, an approval task assigned from a parameter with a due date counted back from the payment date, a halt, a calendar due date, a date-control due date and two custom notifications.
{
"name": "Invoice Review",
"description": "Checking a supplier invoice, getting it approved and scheduling the payment.",
"settings": { "timeZone": "Europe/London", "libraryFolder": "Finance" },
"tags": ["q3-audit"],
"permissions": [
{ "type": "Group", "name": "Finance Team", "permission": "RunAndView" },
{ "type": "AllUsers", "permission": "ViewOnly" }
],
"parameters": [
{ "ref": "invoice_number", "name": "Invoice number", "type": "string" },
{ "ref": "payment_due", "name": "Payment due date", "type": "datetime" },
{ "ref": "approver", "name": "Who approves this invoice", "type": "member_or_group" }
],
"tasks": [
{
"ref": "before_you_start",
"name": "Before you start",
"isHeading": true,
"fields": [
{
"ref": "how_this_works",
"type": "Text",
"html": "<p>Have the PDF of the invoice and the purchase order number to hand.</p>"
}
]
},
{
"ref": "capture",
"name": "Capture the invoice",
"assignTo": [{ "type": "Group", "name": "Finance Team" }],
"tags": ["urgent"],
"dueDate": { "rule": "checklist-start-date", "offset": { "days": 1 } },
"fields": [
{
"ref": "invoice_ref",
"type": "ShortText",
"label": "Invoice number",
"isRequired": true,
"regex": "^INV-[0-9]{4}$",
"regexMessage": "An invoice number is INV- followed by four digits.",
"valueFrom": { "parameter": "invoice_number" }
},
{
"ref": "invoice_type",
"type": "DropDown",
"label": "What kind of spend is this",
"isRequired": true,
"items": [
{ "ref": "operating", "text": "Operating expense" },
{ "ref": "capex", "text": "Capital expenditure" },
{ "ref": "other", "text": "Something else" }
]
},
{
"ref": "other_explanation",
"type": "LongText",
"label": "What is it for",
"isRequired": true,
"hiddenByDefault": true
},
{
"ref": "invoice_pdf",
"type": "FileUpload",
"label": "Upload the invoice",
"minFiles": 1,
"maxFiles": 3
}
]
},
{
"ref": "po_check",
"name": "Check against the purchase order",
"halt": "task",
"dueDate": { "rule": "previous-task-completed", "offset": { "days": 1 } },
"fields": [
{
"ref": "po_checks",
"type": "SubTasks",
"label": "Checks",
"isRequired": true,
"items": [
{ "ref": "po_exists", "text": "The purchase order exists and is open" },
{ "ref": "amounts_match", "text": "The amounts match" },
{ "ref": "goods_received", "text": "The goods or services have been received" }
]
}
]
},
{
"ref": "approve_capex",
"name": "Approve the capital spend",
"hiddenByDefault": true,
"assignFrom": [{ "parameter": "approver" }],
"dueDate": { "rule": "task-completed", "anchorRef": "capture", "offset": { "days": 2 } },
"fields": [
{ "ref": "capex_note", "type": "LongText", "label": "Asset register reference", "isRequired": true }
]
},
{
"ref": "approve",
"name": "Approve the invoice",
"halt": "task-and-preceding",
"assignFrom": [{ "parameter": "approver" }],
"dueDate": {
"rule": "parameter",
"anchorRef": "payment_due",
"direction": "before",
"offset": { "days": 3 }
},
"fields": [
{
"ref": "decision",
"type": "DropDown",
"label": "Decision",
"isRequired": true,
"items": [
{ "ref": "approved", "text": "Approved" },
{ "ref": "rejected", "text": "Rejected" }
]
},
{
"ref": "rejection_reason",
"type": "LongText",
"label": "Why was it rejected",
"isRequired": true,
"hiddenByDefault": true
}
]
},
{
"ref": "schedule_payment",
"name": "Schedule the payment",
"dueDate": { "scenario": "next-friday", "scenarioTime": "15:00" },
"fields": [
{ "ref": "payment_date", "type": "Date", "label": "Payment date", "isRequired": true, "mode": "date" }
]
},
{
"ref": "confirm_payment",
"name": "Confirm the payment has left",
"dueDate": {
"rule": "date-control",
"anchorRef": "payment_date",
"direction": "after",
"offset": { "days": 1 }
},
"fields": [
{
"ref": "remittance_sent",
"type": "SubTasks",
"label": "Done",
"items": [{ "ref": "remittance", "text": "Remittance advice sent to the supplier" }]
}
]
}
],
"conditions": [
{
"ref": "reset_invoice_type",
"when": { "fieldRef": "invoice_type", "operator": "has-any-value" },
"then": { "effect": "hide", "tasks": ["approve_capex"], "fields": ["other_explanation"] }
},
{
"ref": "clear_invoice_type",
"when": { "fieldRef": "invoice_type", "operator": "has-no-value" },
"then": { "effect": "hide", "tasks": ["approve_capex"], "fields": ["other_explanation"] }
},
{
"ref": "capex_needs_approval",
"when": { "fieldRef": "invoice_type", "operator": "is", "value": "capex" },
"then": { "effect": "show", "tasks": ["approve_capex"] }
},
{
"ref": "explain_other",
"when": { "fieldRef": "invoice_type", "operator": "is", "value": "other" },
"then": { "effect": "show", "fields": ["other_explanation"] }
},
{
"ref": "reset_decision",
"when": { "fieldRef": "decision", "operator": "has-any-value" },
"then": { "effect": "hide", "fields": ["rejection_reason"] }
},
{
"ref": "explain_rejection",
"when": { "fieldRef": "decision", "operator": "is", "value": "rejected" },
"then": { "effect": "show", "fields": ["rejection_reason"] }
}
],
"notifications": [
{
"ref": "approval_overdue",
"event": "overdue-by",
"on": { "tasks": ["approve"] },
"offset": { "days": 1 },
"notify": [{ "type": "Group", "name": "Finance Team" }]
},
{
"ref": "invoice_done",
"event": "completed",
"on": { "checklist": true },
"notify": [{ "email": "accounts@acme.example" }]
}
]
}
What each part does:
- The heading
before_you_startgroups nothing and completes nothing; itsTextfield carries the instructions, because a task has no description. invoice_refis filled from theinvoice_numberparameter when the checklist starts, and must match the regular expression.- The rules on
invoice_typestart with two resets — one for any answer, one for a cleared answer — that hide everything the later rules show. Thencapexshows the approval task andothershows the explanation field. See Order and Resets for why the resets come first. approve_capexstarts hidden and is assigned to whoever theapproverparameter names. It falls due two days aftercaptureis completed.approveis assigned from the same parameter and falls due three days before thepayment_duedate given when the checklist starts. Itstask-and-precedinghalt stops anything after it being completed until it and every task before it are done.schedule_paymentfalls due at 15:00 on the next Friday after the checklist starts, in the template's time zone.confirm_paymentfalls due one day after the date entered inpayment_date.- The notifications tell the Finance Team group when the approval is a day overdue, and email an outside address when the checklist is completed.
Sent to POST /v3/templates/validate in a workspace that has a Finance Team group, this document comes back valid with no warnings:
{
"valid": true,
"errorCount": 0,
"warningCount": 0,
"violations": []
}
Refs and Ids
Refs
A ref is your own name for a node in the document — capture, invoice_type, approver. Every cross-reference is a ref: a condition names the field it tests and the tasks and fields it affects by ref, a due date names its anchor by ref, a binding names its source by ref.
- A ref matches
^[a-z0-9][a-z0-9_-]{0,63}$: lower-case letters, digits, underscores and hyphens, at most 64 characters, starting with a letter or a digit. A capital letter is refused, not folded. - Refs are unique across the whole document, not within a kind or a task. That is what lets every reference be one bare token — a condition says
"other_explanation", never"capture.other_explanation". The cost is that two tasks cannot both have a field with the refnotes. - Every task, field, parameter, condition and notification needs a ref. A missing ref is refused with
invalid_ref, and the hint suggests one made from the node's name. - Dropdown, Multi-Choice and Sub-Tasks items carry a ref too. An item's ref only needs to be unique among that field's items, and it is what a condition's
valuenames. An item with no ref cannot be tested by a condition. - Refs exist only inside the document. They are not stored, and an export invents fresh ones from the names. When you commit a draft, the commit status carries a
refMapfrom your refs to the keys that were created.
Ids
Every task, field, choice item, parameter, condition and notification may also carry an id — its real key, a GUID. Export writes one on every node.
| Route | What an id does |
|---|---|
POST /v3/templates | Refused with id_not_allowed. A new template gets new keys throughout. To duplicate a template, use POST /v3/templates/{key}/copy, which also copies the files a document cannot carry. |
POST /v3/templates/{key}/versions | Kept. A task or field that keeps its id stays the same task or field, so answers already given against it and running checklists that refer to it stay attached. |
POST /v3/templates/validate | Refused, unless you pass the template's key in the forTemplate query parameter to validate the document as a version of that template. |
Leave id out when you write a node yourself. An id that is not a GUID is refused with invalid_id.
What Points at What
| Property | Must name |
|---|---|
conditions[].when.fieldRef | A field of one of the eight testable types, not on a heading |
conditions[].then.tasks[] | A task or heading, other than the one the tested field is on |
conditions[].then.fields[] | A field on any task |
tasks[].dueDate.anchorRef | A task for task-completed, a Date field for date-control, a datetime parameter for parameter |
tasks[].assignFrom[].parameter | A member_or_group parameter |
tasks[].assignFrom[].field | A Members field on any task |
fields[].valueFrom.parameter | A parameter |
fields[].valueFrom.field | Another field |
notifications[].on.tasks[] | A task that is not a heading |
SendEmail attachments[] | A FileUpload field on any task |
LinkedChecklist linkedControls[].field | A field in this document |
A ref that names nothing is refused with unknown_ref, with a suggestion drawn from refs of the right kind when one is close. A ref that names the wrong kind of node is told so — "'capture' is a task, not a field."
Settings
settings is optional, and every property in it has a default matching what the template editor gives a new template.
| Property | Type | Default | Description |
|---|---|---|---|
timeZone | string | The request's time zone | The zone the template's dates are read in, as an IANA name (Europe/London) or a Windows name (GMT Standard Time). An unknown zone is refused with invalid_time_zone. |
dateFormatId | integer | 1 | How dates are displayed, from 1 to 9. |
timeFormatId | integer | 1 | How times are displayed, from 0 to 2. |
displayComments | boolean | true | Whether checklists show the comment box. |
displayTaskTags | boolean | true | Whether task tags are shown on the checklist. |
isNotApplicableEnabled | boolean | true | Whether a task can be marked not applicable. |
isShareEmbedEnabled | boolean | true | Whether a checklist can be shared or embedded. |
isAttachedTasksEnabled | boolean | true | Whether standalone tasks can be attached to a checklist. |
libraryFolder | string | The root | The library folder to file the template under, by name. At most 50 characters. |
Left out, timeZone is the time zone the request acts in: the X-CF-Timezone header if you send one, otherwise the time zone of the member the API key acts as. See Conventions.
Tags
tags is a list of tag names for the template itself. Tasks carry their own tags list in the same form.
- A name the workspace has not used before creates a new tag when the template is saved. Read the workspace's tags first rather than coining near-duplicates.
- Names are matched without regard to case, and a name repeated on the same template or task is read once.
- A tag name is at most 100 characters.
- A refused document creates no tags.
See Tags for the tag routes.
Permissions
permissions says who may run and see the checklists made from the template. It is a complete set: what you send replaces what is stored. Permissions are held once per template, not per version, so a version document that leaves them out removes them from the template altogether.
"permissions": [
{ "type": "Group", "name": "Finance Team", "permission": "RunAndView" },
{ "type": "TeamMember", "id": 1042, "permission": "RunAndViewAssigned" },
{ "type": "AllUsers", "permission": "ViewOnly" }
]
| Property | Type | Required | Description |
|---|---|---|---|
type | string | Yes, with id | AllUsers, TeamMember or Group. AllUsers is the whole workspace and takes no id and no name. |
id | integer | One of id or name | The member's or group's id. |
name | string | One of id or name | A member's display name or email address, or a group's name. Ignored when id is given. |
permission | string | Yes | What they may do — see below. |
permission | Meaning |
|---|---|
RunAndView | Can start checklists from the template and see them. |
ViewOnly | Can see checklists from the template but not start them. |
RunAndViewAssigned | Can start checklists, and see only the ones they have a task assigned in. |
ViewAssignedOnly | Can see only the checklists they have a task assigned in. |
One assigned task anywhere in a checklist opens the whole checklist to the two Assigned variants. None of the four grants editing — editing templates is a matter of the member's role and permissions, not of the template. The same member or group named twice is refused with duplicate_assignee. See Templates for the permission routes and Template Permissions for the product concept.
Parameters
Template parameters are values asked for when a checklist is started. A parameter does nothing on its own: a field's valueFrom fills the field from it, a task's assignFrom assigns the task from it, and a due date's parameter rule counts from it. See Template Parameters.
"parameters": [
{ "ref": "invoice_number", "name": "Invoice number", "type": "string" },
{ "ref": "payment_due", "name": "Payment due date", "type": "datetime" },
{ "ref": "approver", "name": "Who approves this invoice", "type": "member_or_group" }
]
| Property | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Your name for the parameter. |
id | string | No | The parameter's key. Written by export; keep it on a version, leave it out otherwise. |
name | string | Yes | What whoever starts the checklist is asked for. At most 100 characters. |
type | string | Yes | string, datetime or member_or_group. |
defaultValue | string | No | The value offered when a checklist is started. |
type | Can fill | Can assign a task | Can anchor a due date |
|---|---|---|---|
string | Any field that takes valueFrom, except a Date | No | No |
datetime | A Date field | No | Yes |
member_or_group | A Members field | Yes | No |
Tasks and Headings
A task is one piece of work. A heading is a divider in the task list that nothing ever completes.
There is no task description. A task that needs explaining carries a Text field — a block of HTML the checklist shows and nobody answers. Put the instructions there.
{
"ref": "capture",
"name": "Capture the invoice",
"halt": "none",
"hiddenByDefault": false,
"assignTo": [{ "type": "Group", "name": "Finance Team" }],
"assignFrom": [],
"assignedExclusively": false,
"tags": ["urgent"],
"dueDate": { "rule": "checklist-start-date", "offset": { "days": 1 } },
"fields": []
}
| Property | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Your name for the task. |
id | string | No | The task's key. Written by export; keep it on a version. |
name | string | Yes | The task's name. At most 100 characters. |
isHeading | boolean | No | true for a heading. Default false. |
halt | string | No | Whether the checklist stops here until the task is done. See Halts. Default none. |
hiddenByDefault | boolean | No | true to start hidden until a condition shows the task. Default false. |
assignTo | object[] | No | Members and groups assigned when a checklist is started. See Assignees. |
assignFrom | object[] | No | Where more assignees come from at run time. See Assignees. |
assignedExclusively | boolean | No | true to let only the assignees open, change or reassign the task. Has no effect while the task has no assignees. Default false. |
tags | string[] | No | The task's tags, by name. |
dueDate | object | No | When the task falls due. Leave it out for no due date. See Due Dates. |
fields | object[] | No | The task's fields, in order. See Fields. |
See Create, Edit and Delete Tasks for tasks in the template editor.
Headings
A heading sets "isHeading": true and takes only ref, id, name, tags and fields. A heading that also carries halt (other than none), hiddenByDefault, assignTo, assignFrom, assignedExclusively or dueDate is refused with one conflicting_properties violation listing them.
A heading may carry fields, and a condition may show or hide a heading. Three things may not point at one:
- A condition may not be triggered by a field on a heading.
- A
task-completeddue date may not be anchored to a heading, because nothing completes one. - A notification may not name a heading.
Halts
halt is how a template enforces task order. See Enforce Task Order.
halt | Effect |
|---|---|
none | The checklist runs past this task. The default. |
task | Nothing after this task can be completed until it is. |
task-and-preceding | Nothing after this task can be completed until it and every task before it are. |
Assignees
A task's assignees come from two lists, and a task may use both. See Template Task Assignments and Dynamic Task Assignments for the product concepts.
Static Assignees
assignTo names members and groups who are assigned when a checklist is started.
"assignTo": [
{ "type": "TeamMember", "id": 1042 },
{ "name": "priya.patel@acme.example" },
{ "type": "Group", "name": "Finance Team" }
]
| Property | Type | Description |
|---|---|---|
type | string | TeamMember or Group. Required with id. |
id | integer | The member's or group's id, from GET /v3/workspace. |
name | string | A member's display name or email address, or a group's name, matched without regard to case. Used when id is absent. |
Each entry gives type and id, or name. An email address is always unique, so it is the safest way to name a member by name. The same person or group named twice on one task is refused with duplicate_assignee.
Dynamic Assignees
assignFrom adds assignees that are resolved when the checklist runs rather than named now. Each entry names exactly one source.
"assignFrom": [
{ "parameter": "approver" },
{ "field": "reviewer" }
]
| Property | Description |
|---|---|
parameter | The ref of a member_or_group parameter. Whoever it is answered with when the checklist is started is assigned the task. |
field | The ref of a Members field on this task or any other. Whoever is chosen in it is assigned the task as the choice is made. |
An entry with both properties is refused with conflicting_properties, and an entry with neither with property_required. A parameter of another type, or a field that is not Members, is refused with property_out_of_range.
How Names Are Resolved
The same rules apply wherever a document names somebody — assignTo, permissions, a notification's notify, a Members field's exclude and a condition testing a Members field.
| Result | Violation |
|---|---|
| Nobody in the workspace matches | assignee_not_found |
| More than one member or group matches | ambiguous_assignee, with the candidates' type and id in the hint |
An id with no type, or a type that is not TeamMember or Group | property_out_of_range |
Neither id nor name | property_required |
Due Dates
A task's dueDate says when it falls due in one of two ways: a rule with an offset, or a calendar scenario. A due date carrying both rule and scenario is refused with conflicting_properties. See Dynamic Task Due Dates for the product concept.
| Property | Type | Description |
|---|---|---|
rule | string | What the offset is counted from. See the table below. |
anchorRef | string | The ref of what the rule counts from, for the three rules that need one. |
direction | string | before or after. Only date-control and parameter accept before. Default after. |
offset | object | How far from the anchor: months, days, hours and minutes. Leave it out to fall due on the anchor itself. |
scenario | string | A recurring moment in the calendar, instead of rule. |
scenarioTime | string | The time of day a scenario falls due, as HH:mm. Default 00:00. |
scenarioTimeZone | string | The zone scenarioTime is read in. Defaults to the template's timeZone. |
Rules
rule | Counts from | anchorRef | direction |
|---|---|---|---|
checklist-start-date | When the checklist is started. | Not allowed | after only |
previous-task-completed | When the task before this one is completed. | Not allowed | after only |
task-completed | When a named task is completed. | A task's ref — not a heading, not this task | after only |
date-control | The date entered in a Date field. | A Date field's ref | before or after |
parameter | The date given for a template parameter when the checklist is started. | A datetime parameter's ref | before or after |
previous-task-completed names no task, because the task it means is worked out while the checklist runs: the last task before this one that is neither a heading nor hidden.
A checklist started without an answer for a parameter anchor gets no due date from it.
Offsets
| Property | Description |
|---|---|
months | Whole months, landing on the same day of a later month. |
days | Days. |
hours | Hours. |
minutes | Minutes. |
Every part defaults to 0 and must not be negative — the direction is direction's job, not the sign's. The schema advertises upper bounds of 120 months, 3650 days, 23 hours and 59 minutes.
Scenarios
A scenario is always worked out from the day the checklist starts, so it takes no anchorRef, direction or offset — sending any of them is refused with conflicting_properties. The twenty scenarios are:
| Week | Month | Quarter | Year |
|---|---|---|---|
next-weekday | first-day-of-next-month | last-day-of-quarter | last-day-of-current-year |
next-monday | first-weekday-of-next-month | last-weekday-of-quarter | last-weekday-of-current-year |
next-tuesday | last-day-of-month | first-day-of-next-quarter | first-day-of-next-year |
next-wednesday | last-weekday-of-month | first-weekday-of-next-quarter | first-weekday-of-next-year |
next-thursday | |||
next-friday | |||
next-saturday | |||
next-sunday |
scenarioTime is a wall-clock time rather than an instant: 09:00 means 09:00 in scenarioTimeZone whatever the time of year. GET /v3/schema/due-dates returns the same list.
Due Date Examples
{ "rule": "checklist-start-date", "offset": { "days": 1 } }
One day after the checklist is started.
{ "rule": "previous-task-completed", "offset": { "days": 2, "hours": 4 } }
Two days and four hours after the previous visible task is completed.
{ "rule": "task-completed", "anchorRef": "capture", "offset": { "days": 2 } }
Two days after the task capture is completed.
{ "rule": "date-control", "anchorRef": "payment_date", "direction": "before", "offset": { "days": 5 } }
Five days before the date entered in the payment_date field.
{ "rule": "parameter", "anchorRef": "payment_due", "direction": "before", "offset": { "days": 3 } }
Three days before the date given for the payment_due parameter.
{ "rule": "date-control", "anchorRef": "payment_date" }
On the date entered in payment_date.
{ "scenario": "first-weekday-of-next-month", "scenarioTime": "09:00", "scenarioTimeZone": "Europe/London" }
09:00 London time on the first weekday of the month after the checklist starts.
What Is Refused
| Mistake | Violation |
|---|---|
Both rule and scenario | conflicting_properties |
Neither rule nor scenario | property_required on rule — leave dueDate out for no due date |
| A rule or scenario not in the lists above | property_out_of_range, with the nearest word as a hint |
anchorRef missing on a rule that needs one | property_required |
anchorRef on checklist-start-date or previous-task-completed | conflicting_properties |
anchorRef naming a heading, this task, a non-Date field or a non-datetime parameter | property_out_of_range |
direction: "before" on a rule that only counts forward | conflicting_properties |
| A negative offset part | property_out_of_range |
scenarioTime that is not a time of day | property_out_of_range |
dueDate on a heading | conflicting_properties |
Fields
A field is one control on a task. Every field has five properties in common; everything else belongs to its type and is written flat beside them, never nested under a config key.
{
"ref": "invoice_ref",
"type": "ShortText",
"hiddenByDefault": false,
"tooltip": "It is printed at the top right of the invoice.",
"label": "Invoice number",
"isRequired": true,
"regex": "^INV-[0-9]{4}$"
}
| Property | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Your name for the field. |
id | string | No | The field's key. Written by export; keep it on a version to keep the answers already given to the field. |
type | string | Yes | Which control this is. See the table below. |
hiddenByDefault | boolean | No | true to start hidden until a condition shows the field. Default false. |
tooltip | string | No | Help text behind the question mark beside the label. At most 1,200 characters. |
A property the type does not have is refused with unknown_property, usually with the property you meant as a hint — required is refused with "Did you mean 'isRequired'?".
Field Types
type | Control | Answered in the checklist | Testable by a condition |
|---|---|---|---|
Text | Text | No | No |
ShortText | Short Text | Yes | Yes |
LongText | Long Text | Yes | Yes |
EmailInput | Yes | Yes | |
Website | Website | Yes | Yes |
FileUpload | File Upload | Yes | No |
Date | Date & Time | Yes | Yes |
DropDown | Dropdown | Yes | Yes |
MultiChoice | Multi-Choice | Yes | Yes |
SubTasks | Sub-Tasks | Yes | No |
Image | Image | No | No |
Video | Video | No | No |
File | File | No | No |
SendEmail | Mail-To | No | No |
Separator | Separator | No | No |
Embed | Embed | No | No |
Members | Members | Yes | Yes |
Table | Table | Yes | No |
LinkedChecklist | Linked Checklist | No | No |
Three pairs are easy to confuse. File hands the user files and FileUpload asks them for files. Embed shows a page the author chose and Website asks the user for an address. Text tells the user something and LongText asks them something.
GET /v3/schema/content-types/{type} returns each type's full JSON Schema and an example. The tables below summarise the same properties.
Text
| Property | Type | Required | Description |
|---|---|---|---|
html | string | Yes | The prose, as HTML. Rendered as formatting, and may carry dynamic values. |
{ "ref": "read_this_first", "type": "Text", "html": "<p>Check the invoice total against the purchase order <b>before</b> you approve.</p>" }
ShortText, LongText, EmailInput and Website
These four share label, isRequired and valueFrom.
| Property | Types | Type | Description |
|---|---|---|---|
label | All four | string | The question shown above the box. Required. |
isRequired | All four | boolean | Whether the task cannot be completed until this is answered. Default false. |
regex | ShortText | string | A .NET regular expression the answer must match. At most 1,000 characters; one that does not compile is refused with invalid_regex. |
regexMessage | ShortText | string | What to show when the answer does not match. At most 400 characters. |
height | LongText | integer | The box's height in pixels, 1 to 2000. Default 100. |
valueFrom | All four | object | Where the value is filled in from. See Filling a Field In. |
EmailInput checks the answer is an email address; Website takes an address the user types.
{ "ref": "supplier_email", "type": "EmailInput", "label": "Supplier's accounts email", "isRequired": true }
Date
| Property | Type | Description |
|---|---|---|
label | string | The question shown above the picker. Required. |
isRequired | boolean | Default false. |
mode | string | date, time or date-and-time. Default date. |
valueFrom | object | See Filling a Field In. |
A Date field is the only field a date-control due date can be anchored to, and the only type with the four ordering operators.
DropDown, MultiChoice and SubTasks
A DropDown takes exactly one answer, a MultiChoice any number, and SubTasks is a list of steps ticked off as the user goes.
| Property | Type | Description |
|---|---|---|
label | string | The question shown above the list. Required. |
isRequired | boolean | Default false. |
items | object[] | The choices, in order. At least one, unless dataSet is given. |
dataSet | object | Where the choices come from instead of items. See Data Set Links. |
valueFrom | object | See Filling a Field In. |
Each item:
| Property | Type | Description |
|---|---|---|
ref | string | Your name for the choice, unique within the field. What a condition's value names. |
id | string | The choice's key. Written by export; keep it on a version. |
text | string | What the choice says. Required. At most 500 characters, or 2,000 for SubTasks. |
A choice field with neither items nor dataSet, or with an empty items, is refused with items_required. One with both is refused with conflicting_properties.
FileUpload
| Property | Type | Description |
|---|---|---|
label | string | The prompt shown above the upload box. Required. |
minFiles | integer | How many files the task cannot be completed without, 0 to 100. 0 means no minimum. |
maxFiles | integer | How many files may be uploaded, 0 to 100. 0 means no limit; otherwise at least minFiles. |
{ "ref": "receipts", "type": "FileUpload", "label": "Upload the receipts", "minFiles": 1, "maxFiles": 10 }
Image, Video and File
A document carries no files, so these three are limited in what a document can create.
| Type | Writable properties | Read-only properties | Creatable from a document |
|---|---|---|---|
Image | caption | fileName | No |
Video | url, description, valueFrom | fileName | Only with a url |
File | label | files (each with name and description) | No |
- A new
Image, a newFile, or a newVideowith nourlis refused withbinary_required. Add them in the template editor, then keep the field'sidon later versions to keep the file. - A
Videourlmust be an absolutehttporhttpsaddress, at most 2,000 characters. - Export writes the read-only properties. Sending them back is accepted with a
property_not_writablewarning and has no effect.
SendEmail
An email the checklist sends, written when the template is. Nothing in it is answered by the checklist's user. See Mail-To.
| Property | Type | Description |
|---|---|---|
label | string | A name for the field. Never shown to the user. |
to | string | Who the email goes to. May carry dynamic values. |
cc | string | Who is copied in. |
bcc | string | Who is blind copied in. |
from | string | Who the email comes from. Needs a verified sending domain on the workspace. |
replyTo | string | Where replies go, when not to the sender. |
subject | string | The subject line. May carry dynamic values. |
body | string | The message; HTML under the rich and html body modes. |
bodyMode | string | plain, rich or html. Default plain. |
sendMode | string | manual, manual-required or automatic. Default manual. |
attachments | string[] | The refs of FileUpload fields, on any task, whose uploaded files are attached. |
plainopens the reader's own mail client, so CheckFlow never sends the message andfrom,replyTo,sendModeandattachmentshave no effect.plainwith asendModeother thanmanualis refused withconflicting_properties.manualsends on a button press,manual-requiredon a button press the task cannot be completed without, andautomaticwhen the task is completed.- An email with no
toor nosubjectis accepted with anever_sentwarning, because nothing will send it.
{
"ref": "tell_the_supplier",
"type": "SendEmail",
"label": "Tell the supplier it has been paid",
"to": "billing@northwind.example",
"subject": "Payment for {{checklist.name}}",
"body": "<p>The payment has left our account today.</p>",
"bodyMode": "rich",
"sendMode": "manual-required"
}
Separator and Embed
A Separator takes no properties of its own. An Embed shows another page inside the task:
| Property | Type | Description |
|---|---|---|
url | string | The page to show. Required, absolute, http or https. |
height | integer | The frame's height in pixels, 1 to 4000. Default 500. |
valueFrom | object | See Filling a Field In. |
Members
People picked from the workspace by the checklist's user.
| Property | Type | Description |
|---|---|---|
label | string | The question shown above the picker. Required. |
isRequired | boolean | Default false. |
selectionMode | string | single or multiple. Default multiple. |
exclude | object[] | Members and groups the picker does not offer, each named by type and id or by name, as in Static Assignees. |
valueFrom | object | See Filling a Field In. |
A Members field can assign its task or any other task through assignFrom — see Dynamic Assignees.
Table
A grid of cells. Cells the author fills in and marks read-only act as headings; the rest are answered in the checklist.
| Property | Type | Description |
|---|---|---|
label | string | Words shown above the grid. Optional. |
rows | integer | How many rows the grid starts with, 1 to 50. Default 4. |
columns | integer | How many columns, 1 to 50. Default 4. |
allowAddRemoveRows | boolean | Whether the user may add and remove rows. Default true. |
cells | object[] | The cells that are not empty and editable. The rest of the grid is built empty. |
dataSet | object | Rows filled from a Data Set. See Data Set Links. |
Each cell:
| Property | Type | Description |
|---|---|---|
row | integer | The row, from 1. Required. |
column | integer | The column, from 1. Required. |
value | string | The cell's content. Export writes it only for read-only cells. |
readOnly | boolean | Whether the checklist shows the cell rather than asking for it. |
required | boolean | Whether the task cannot be completed while the cell is empty. |
background | string | white, red, orange, yellow, green, blue or grey. |
format | string | plain-text, number, percent, date, time or date-time. |
A cell outside the grid, or the same cell given twice, is refused.
{
"ref": "line_items",
"type": "Table",
"label": "Line items",
"rows": 3,
"columns": 2,
"cells": [
{ "row": 1, "column": 1, "value": "Item", "readOnly": true, "background": "grey" },
{ "row": 1, "column": 2, "value": "Amount", "readOnly": true, "background": "grey" },
{ "row": 2, "column": 2, "required": true, "format": "number" }
]
}
LinkedChecklist
Starts a second checklist from another template, as a sub-process. See Linked Checklists.
Filling a Field In
valueFrom fills a field for the user from one source. It is accepted by ShortText, LongText, EmailInput, Website, Date, DropDown, MultiChoice, SubTasks, Members, Video and Embed. Give exactly one of parameter, field or dynamicValue; dataSetField goes with field.
| Property | Description |
|---|---|
parameter | The ref of a parameter, filled when the checklist starts. See the table in Parameters for which type fills which field. |
field | The ref of another field, copied in whenever it changes. |
dataSetField | With field: a field of the record that field selected from its Data Set, by name or key. |
dynamicValue | A global dynamic value as its token, such as {{checklist.name}}, {{current_user.email}} or {{current_date}}. |
What a field source can fill:
| Target | Can be filled from |
|---|---|
Date | A Date field |
DropDown | A DropDown field |
EmailInput | An EmailInput field |
Website, Video, Embed | A Website field |
ShortText, LongText | Any answerable field |
- A field that reads its choices from a Data Set holds a record rather than a value, so a field copying from it must say which record field with
dataSetField. ADatetakes a date field of the record, anEmailInputan email field, aWebsitea URL field, andShortTextorLongTextany field. AMultiChoiceorSubTaskssource selects several records and fills onlyShortTextandLongText. - Which dynamic values a field takes depends on its type: a
Datetakes the date-valued ones, aDropDowntakes none, and text fields take any. See Dynamic Values. - A field cannot be filled from itself. A mismatched source is refused with
property_out_of_range, naming what the target accepts.
{ "ref": "supplier_region", "type": "ShortText", "label": "Region",
"valueFrom": { "field": "supplier", "dataSetField": "Region" } }
Data Set Links
A DropDown, MultiChoice or SubTasks field can take its choices from a Data Set view instead of items, and a Table can start each checklist with a row per record. Write dataSet on the field and leave items out. See Linking a Data Set to a Control.
{
"ref": "supplier",
"type": "DropDown",
"label": "Which supplier sent the invoice",
"isRequired": true,
"dataSet": { "name": "Suppliers", "view": "Active suppliers", "displayField": "Supplier name" }
}
| Property | Type | Description |
|---|---|---|
key | string | The Data Set's key, or a built-in Data Set's slug such as system-countries. Exact; wins when both key and name are given. |
name | string | The Data Set's name. Refused when two Data Sets share it. At most 200 characters. |
view | string | The view, by name or key. Left out, the default view — every record. |
displayField | string | Choice fields only. The field whose value each choice shows, by name or key. Left out, the first text field. |
prePopulateRows | boolean | Table only. Whether a checklist starts with one row per record of the view, mapped cells filled in and read-only. The view may hold at most 500 records. |
columns | object[] | Table only, required. Which Data Set field fills which column: field (name or key) and column (from 1). Between 1 and 20 entries. |
A Table keeps its own rows, columns and header cells; the link only says what fills them.
A link naming a Data Set, view or field the workspace does not have is refused with unknown_data_set, unknown_data_set_view or unknown_data_set_field, and a name matching more than one with the matching ambiguous_ code. A link the Data Set service still refuses — a pre-populating table reading a view of more than 500 records — is refused with data_set_link_invalid. GET /v3/data-sets lists the Data Sets you can name, and GET /v3/data-sets/{dataSetKey} gives one Data Set's views and fields.
Export writes each link back by name, so a linked template round-trips like any other. On a version, a field that keeps its id but drops its dataSet becomes a fixed list again, and the answer carries a data_set_link_removed warning.
Linked Checklists
A LinkedChecklist field starts a checklist from another template. See Linked Checklist.
| Property | Type | Description |
|---|---|---|
templateKey | string | The linked template's key. Exact; wins when both are given. |
templateName | string | The linked template's name. Refused when two templates share it. |
name | string | What the new checklist is called. Defaults to the template's name; may carry dynamic values. |
createAutomatically | boolean | Whether the checklist starts as soon as the task is reached rather than when somebody presses the button. Default false. |
linkedControls | object[] | Values copied into the linked checklist. See below. |
One of templateKey or templateName is required. An archived template can be linked. A link to the template it is on with createAutomatically set is refused with circular_reference, because each checklist would start another.
linkedControls copies values from fields in this document into controls of the linked template, when the checklist is started and again whenever the parent's value changes:
| Property | Type | Description |
|---|---|---|
field | string | The ref of the field here whose value is copied. Required. |
childField | string | The label, or the key, of the control in the linked template. Required. |
childTask | string | The name of the task in the linked template that the control is on. Needed only when two tasks there use the same label. Export always writes it. |
defaultValue | string | What the child control gets when the parent has no value. |
Values can be copied from Date, DropDown, EmailInput, FileUpload, LongText, Members, MultiChoice, ShortText, SubTasks and Website fields, into Date, DropDown, EmailInput, Embed, LongText, Members, MultiChoice, ShortText, SubTasks, Text, Video and Website controls. A label that matches no control of the linked template is refused with unknown_linked_control, and one matching several with ambiguous_linked_control. GET /v3/templates/{key} on the linked template lists its tasks and fields.
{
"ref": "onboard_supplier",
"type": "LinkedChecklist",
"templateName": "Supplier Onboarding",
"name": "Onboarding for {{checklist.name}}",
"createAutomatically": true,
"linkedControls": [
{ "field": "supplier_email", "childTask": "Collect their details", "childField": "Accounts email" },
{ "field": "payment_date", "childField": "First payment date", "defaultValue": "2026-10-01" }
]
}
Conditional Logic
A condition shows or hides tasks and fields when a field's answer passes a test. Conditions live in the document's own conditions array, not on a task: the rule is attached to the task holding the field it tests. See Conditional Logic for the product concept.
{
"ref": "capex_needs_approval",
"when": { "fieldRef": "invoice_type", "operator": "is", "value": "capex" },
"then": { "effect": "show", "tasks": ["approve_capex"], "fields": [] }
}
| Property | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Your name for the rule. |
id | string | No | The rule's key. Written by export. |
when.fieldRef | string | Yes | The ref of the field tested — the conditional logic trigger. |
when.operator | string | Yes | The test. See Operators. |
when.value | varies | Depends on the operator | What the answer is compared with. See Values. |
then.effect | string | Yes | show or hide. |
then.tasks | string[] | One of tasks or fields | The refs of tasks or headings to show or hide. |
then.fields | string[] | One of tasks or fields | The refs of fields to show or hide, on any task. |
- Only eight types can be tested:
Date,DropDown,MultiChoice,EmailInput,LongText,ShortText,WebsiteandMembers. Design the fields a template's logic depends on around this before writing the tasks. - A rule must name at least one task or field in
then, or it is refused withproperty_required. - A rule may not show or hide the task its own tested field is on — hiding it would hide the question. It may show or hide other tasks, and fields on any task including its own. The mistake is refused with
conflicting_properties. - A field on a heading cannot be tested.
- A task or field named twice in one rule is affected once.
Operators
operator | Takes a value | Available on |
|---|---|---|
is | Yes | All eight testable types |
is-not | Yes | All eight testable types |
has-no-value | No | All eight testable types |
has-any-value | No | All eight testable types |
is-greater-than | Yes | Date only |
is-greater-than-or-equal-to | Yes | Date only |
is-less-than | Yes | Date only |
is-less-than-or-equal-to | Yes | Date only |
A value sent with has-no-value or has-any-value is refused with conflicting_properties rather than ignored. An ordering operator on any type but Date is refused with property_out_of_range. GET /v3/schema/condition-operators returns the same table.
Values
The shape of value follows the type of the field tested.
| Field tested | value | Example |
|---|---|---|
DropDown, MultiChoice with items | The ref of one of that field's items | "capex" |
DropDown, MultiChoice with dataSet | A record of the view: { "recordKey": "..." }, or { "record": "..." } with the words the choice shows | { "record": "Northwind Traders" } |
Date | An ISO date, or a date and time, with no offset | "2026-10-01", "2026-10-01T09:00" |
Members | A member or group, named as in Static Assignees | { "type": "Group", "name": "Finance Team" } |
ShortText, LongText, EmailInput, Website | Text compared exactly as written, at most 500 characters | "N/A" |
- A
Datevalue is accepted asyyyy-MM-dd,yyyy-MM-ddTHH:mmoryyyy-MM-ddTHH:mm:ss, and nothing else. - A number or boolean given for a text field is read as its text.
- A record named by its words must match exactly one record of the view, compared without regard to case; otherwise
unknown_data_set_recordorambiguous_data_set_record. Export writesrecordKey. - An item ref the field does not have is refused with
unknown_ref, with the nearest item ref as a hint.
Order and Resets
When an answer changes, the checklist takes the rules on that field in the order they appear in conditions and applies each one that fires. A rule that does not fire changes nothing, nothing goes back to hiddenByDefault on its own, and the last rule to fire wins. So a lone show rule is a one-way door:
{ "ref": "explain_other", "when": { "fieldRef": "invoice_type", "operator": "is", "value": "other" },
"then": { "effect": "show", "fields": ["other_explanation"] } }
Choose Something else and the field appears. Change the answer to Operating expense and it stays, because no rule fired to hide it.
The fix is a reset, first among the rules on the same field, that puts back everything the later rules move:
{ "ref": "reset_invoice_type", "when": { "fieldRef": "invoice_type", "operator": "has-any-value" },
"then": { "effect": "hide", "tasks": ["approve_capex"], "fields": ["other_explanation"] } },
{ "ref": "capex_needs_approval", "when": { "fieldRef": "invoice_type", "operator": "is", "value": "capex" },
"then": { "effect": "show", "tasks": ["approve_capex"] } },
{ "ref": "explain_other", "when": { "fieldRef": "invoice_type", "operator": "is", "value": "other" },
"then": { "effect": "show", "fields": ["other_explanation"] } }
- The reset goes first. Placed after the show rules, it fires every time they do and undoes them.
- It is on the same field. A field's rules run only when that field is answered.
- It names every target of the rules after it, with the opposite effect: a
hidereset for rules that show things hidden by default, ashowreset for rules that hide things visible by default. - Add a
has-no-valuereset when the answer can be cleared.has-any-valuedoes not fire when a Dropdown, Multi-Choice or Members answer is emptied. - A matching
is-notrule works for a single value only. With two or more show values, eachis-notrule fires for the other value and undoes it.
Validation warns with never_restored when a rule has no way back, or when a reset comes after the rule it undoes. It warns with never_shown when a task or field starts hidden and no rule shows it — a legitimate way to park something, and an easy mistake.
Custom Notifications
A custom notification tells people when something happens to the checklist or some of its tasks. Notifications live in the document's own notifications array, because one may be about the checklist and several tasks at once. See Custom Notifications for the product concept.
{
"ref": "approval_overdue",
"event": "overdue-by",
"on": { "tasks": ["approve"] },
"offset": { "days": 1 },
"notify": [
{ "type": "Group", "name": "Finance Team" },
{ "email": "controller@acme.example" }
]
}
| Property | Type | Required | Description |
|---|---|---|---|
ref | string | Yes | Your name for the notification. |
id | string | No | The notification's key. Written by export; keep it on a version. |
event | string | Yes | The notification trigger. See the table below. |
on.checklist | boolean | One of checklist or tasks | true when the notification is about the checklist itself. |
on.tasks | string[] | One of checklist or tasks | The refs of the tasks it is about. Not headings. |
offset | object | For due-in and overdue-by only | How far from the task's due date: months, days, hours, minutes, at least one above zero. |
notify | object[] | Yes, at least one | Who is told. |
event | Sent when | offset | About the checklist |
|---|---|---|---|
status-changed | The checklist or task is completed, uncompleted or marked not applicable. | Refused | Allowed |
completed | It is completed. | Refused | Allowed |
uncompleted | It is uncompleted. | Refused | Allowed |
not-applicable | It is marked not applicable. | Refused | Allowed |
due-in | The offset before a task's due date. | Required | Refused |
overdue-by | The offset after a task's due date. | Required | Refused |
Each notify entry is a member or group of the workspace, named by type and id or by name as in Static Assignees, or { "email": "..." } for anybody outside it. An entry with both an email and a name is refused with conflicting_properties, and the same recipient twice with duplicate_assignee.
Limits
Each of these is the size the storage keeps. A longer value is refused with property_out_of_range rather than stored cut short.
| What | Longest |
|---|---|
| A ref | 64 characters |
Template name | 100 characters |
Task name | 100 characters |
Parameter name | 100 characters |
| A tag name | 100 characters |
settings.libraryFolder | 50 characters |
A field's tooltip | 1,200 characters |
ShortText regex | 1,000 characters |
ShortText regexMessage | 400 characters |
A DropDown or MultiChoice item's text | 500 characters |
A SubTasks item's text | 2,000 characters |
A Video url | 2,000 characters |
A condition's text value | 500 characters |
A Data Set link's name | 200 characters |
Numeric ranges — LongText height, Embed height, Table rows and columns, FileUpload minFiles and maxFiles, dateFormatId and timeFormatId — are given with each property above and in GET /v3/schema/content-types/{type}. Labels, descriptions, html, email bodies and Embed addresses have no length limit.
Violations
Every problem found in a document is a violation. They are collected rather than raised one at a time, so a document with four problems is refused once with all four.
The Violation Object
{
"path": "$.conditions[2].when.fieldRef",
"code": "unknown_ref",
"message": "No field with ref 'invoice_typ' exists in this document.",
"hint": "Did you mean 'invoice_type'?",
"severity": "error"
}
| Field | Type | Description |
|---|---|---|
path | string | A JSONPath into the document as you sent it, such as $.tasks[2].fields[1].regex. A problem with a whole node points at the node, such as $.tasks[0].fields[0]. |
code | string | What kind of problem it is. Stable — branch on this, not on the message. See Violation Codes. |
message | string | What is wrong, in a sentence. The wording may change. |
hint | string | What to do about it, often the value you probably meant. Absent when there is nothing useful to suggest. |
severity | string | error refuses the document; warning does not. |
Where Violations Appear
POST /v3/templates/validate(andPOST /v3/drafts/{key}/validate) answers200 OKwhatever it finds, withvalid,errorCount,warningCountandviolations.validisfalseonly when there are errors. This is the only way to see warnings on a document that would be accepted.POST /v3/templates,POST /v3/templates/{key}/versionsand a draft commit refuse a document with errors with422 Unprocessable Entityand the codeTEMPLATE_INVALID. The error'sviolationscarries every violation found, warnings included, and the message counts only the errors. Nothing is written — not even new tags. A document with only warnings is accepted and the warnings are not reported.
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json; charset=utf-8
X-Request-Id: req_7d2e41c9a0b84f5f9e6c3a1b2d7f8e90
{
"error": {
"code": "TEMPLATE_INVALID",
"message": "The template document has 2 problems.",
"requestId": "req_7d2e41c9a0b84f5f9e6c3a1b2d7f8e90",
"violations": [
{
"path": "$.tasks[1].fields[0].required",
"code": "unknown_property",
"message": "'required' is not a property of a ShortText field.",
"hint": "Did you mean 'isRequired'?",
"severity": "error"
},
{
"path": "$.conditions[0].when.fieldRef",
"code": "unknown_ref",
"message": "No field with ref 'invoice_typ' exists in this document.",
"hint": "Did you mean 'invoice_type'?",
"severity": "error"
}
]
}
}
See Errors for the error body.
Violation Codes
Errors:
| Code | Meaning |
|---|---|
unknown_content_type | A field's type is not one of the nineteen. The hint names the nearest. |
unknown_property | A field carries a property its type does not have — usually a misspelling. |
property_type_mismatch | A property is the wrong kind of JSON value, such as a string where an object belongs. |
property_required | A property that cannot be left out was left out. |
property_out_of_range | A value the storage cannot hold or the product does not recognise: an unknown word, a number outside its range, a string too long, an anchor of the wrong kind. |
invalid_ref | A ref is missing or does not match the ref grammar. The hint suggests one made from the name. |
invalid_id | An id (or a recordKey or templateKey) is not a GUID. |
id_not_allowed | An id on a route that creates a template. |
duplicate_ref | Two nodes claim one ref. The second is reported, with a numbered alternative as the hint. |
unknown_ref | A ref names nothing, or names the wrong kind of node, or a condition names an item the field does not have. |
invalid_regex | A regex does not compile. |
invalid_url | A URL is not an absolute http or https address. |
invalid_time_zone | A time zone the server does not know. |
items_required | A choice field has no items and no Data Set link. |
binary_required | An Image, File, or Video with no url, that has no existing file to keep. Create it in the template editor. |
assignee_not_found | A named member or group is not in the workspace. |
ambiguous_assignee | A name matches more than one member or group. |
duplicate_assignee | The same member, group or email address named twice where once is the only sense. |
conflicting_properties | Two properties that cannot both be meant, such as rule and scenario, or a property a heading cannot have. |
unknown_template | A linked template does not exist in the workspace. |
ambiguous_template | A linked template's name matches more than one template. |
unknown_linked_control | A linked control names a task or control the linked template does not have. |
ambiguous_linked_control | A linked control's label matches more than one control. |
unknown_data_set | A dataSet names a Data Set the workspace does not have. |
ambiguous_data_set | A Data Set name matches more than one Data Set. |
unknown_data_set_view | A dataSet.view names no view of the Data Set. |
ambiguous_data_set_view | A view name matches more than one view. |
unknown_data_set_field | A display field, mapped column or dataSetField names no field of the Data Set. |
unknown_data_set_record | A condition names a record the view does not have. |
ambiguous_data_set_record | A record's words match more than one record. |
data_set_link_invalid | The Data Set service refused a link, such as a pre-populating table reading a view of more than 500 records. |
circular_reference | A LinkedChecklist links to the template it is on and is set to create automatically. |
Warnings:
| Code | Meaning |
|---|---|
never_shown | A task or field starts hidden and no rule shows it. |
never_restored | A rule moves something off how it starts and no rule on the same field moves it back, or the reset comes after the rule it undoes. |
never_sent | A SendEmail field has no to or no subject, so nothing will send it. |
property_not_writable | A read-only property, such as an Image's fileName, was sent. It is ignored. |
data_set_link_removed | On a version, a field that read from a Data Set no longer does. |
Changing an Existing Template
Publish changes by round trip, not by writing a version document from scratch:
- Export the template with
GET /v3/templates/{key}/document. - Change the part that is wrong.
- Send the whole result to
POST /v3/templates/{key}/versions, or start a draft from the template withfromTemplateKey— see Drafts.
The round trip matters for three reasons:
idon every task and field. A version document that drops one describes a new task or field, and the answers already given against the old one do not follow it.- Permissions and tags are stored once per template. A version document that leaves them out removes them from every version, including ones somebody else published.
- A
dataSetis part of the field. A version that keeps a field'sidand drops its link turns it back into a fixed list.
An export also writes name beside id for permissions, invents refs from the names (numbered _2, _3 when two names slug alike), writes Data Set links and linked templates by name, and writes the read-only properties of Image, Video and File fields. Publishing a version never changes an older one; see Template Versioning and Templates for moving running checklists onto a new version.
Posting an exported document to POST /v3/templates is refused with id_not_allowed. To duplicate a template, use POST /v3/templates/{key}/copy, which copies the files behind Image, Video and File fields that a document cannot carry and repoints every internal reference at the copy.
Related Pages
- Templates — the routes that create, export, validate and version templates from documents.
- Drafts — building a large document step by step and committing it.
- Schema and Authoring Guide — the discovery routes that describe this format to a program.
- Errors — the error body that carries
violations. - Conditional Logic — how show and hide rules behave in the product.