Skip to main content

Schema and Authoring Guide

The discovery routes describe the template document format to a program. They answer questions about the format rather than about your workspace's data: which field types exist and what each one takes, which operators a condition can use, which due date rules and scenarios there are, and — in prose — how to put a document together. A client, or an AI agent working through the MCP server, reads them before composing a document so that it gets the document right first time instead of learning the format from refusals.

Every answer is generated from the same declarations the document reader validates against, so a property or value these routes offer is one a document may use, and one they do not offer is one a document is refused for. None of them depends on the workspace: every API key gets the same answers. The one discovery read that does depend on the workspace — the member, group, tag and template names a document can use — is GET /v3/workspace.

All six routes are reads that change nothing, and all six are charged to the cheap rate limit budget rather than the standard one, so reading them freely costs almost nothing against your workspace's allowance. Responses carry X-RateLimit-Bucket: cheap.

Endpoints​

MethodPathDescriptionMCP tool
GET/v3/schema/guideGet the authoring guideget_authoring_guide
GET/v3/schema/documentGet the document schemaget_template_schema
GET/v3/schema/content-typesList content typeslist_content_types
GET/v3/schema/content-types/{type}Describe a content typedescribe_content_type
GET/v3/schema/condition-operatorsList condition operatorslist_condition_operators
GET/v3/schema/due-datesList due date rules and scenarioslist_due_date_scenarios

Each MCP tool returns the same object as its route, as the tool's structured content. The MCP tools take the same inputs: describe_content_type takes type, and list_condition_operators takes an optional contentType.

Composing a Document With These Routes​

The routes are designed to be read in an order that keeps what you hold small: the envelope and a one-line index first, and one field type unfolded at a time, at the moment you choose it.

  1. Read the authoring guide once — GET /v3/schema/guide. It covers what the schemas cannot say: that a task has no description and its instructions go in a Text field, that array position is order, how to choose between the nineteen field types and how to write conditional logic that resets correctly.
  2. Read the workspace — GET /v3/workspace. Members, groups, tags and templates a document can name. Most refused documents are refused for a name that does not resolve.
  3. Read the envelope — GET /v3/schema/document. Settings, tags, permissions, parameters, tasks, conditions and notifications, with fields reduced to the properties every field has.
  4. Choose field types — GET /v3/schema/content-types lists all nineteen with a summary and whether conditional logic can test them. If the template's logic depends on a field, choose one of the eight testable types.
  5. Unfold each type as you use it — GET /v3/schema/content-types/{type} returns that type's full JSON Schema and an example field to adapt.
  6. Check the vocabularies you need — GET /v3/schema/condition-operators for rules and GET /v3/schema/due-dates for due dates.
  7. Validate, then publish. Send the document to POST /v3/templates/validate until valid is true, then to POST /v3/templates or a draft. Each violation carries a path, a code and usually a hint. See Violations.
tip

Because the answers are the same for every workspace and every key, a client can read them once per session and keep them. The workspace read is different: re-read it before publishing if time has passed, because people join, tags are created and templates are published.

For an AI agent, the MCP server's instructions already tell the model to read the guide before composing its first document. The guide is also offered as an MCP resource so that a person can attach it to a conversation instead — see Get the Authoring Guide.

Get the Authoring Guide​

Returns the template authoring guide: prose, in Markdown, explaining how to compose a template document — the order to work in, what tasks and fields are, how to choose a field type, how conditions and due dates are written and how to publish. It ends with a complete worked example that validates in any workspace. Read it before writing a template for the first time.

GET /v3/schema/guide

Parameters​

This endpoint takes no parameters.

Example​

GET https://api.checkflow.io/v3/schema/guide
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap

The content string is shortened here; the real guide is around fifteen kilobytes.

{
"format": "markdown",
"content": "# Writing a CheckFlow template\n\nA template is a process written down once: a list of tasks, the fields on them, the rules that\ndecide which of them a particular run shows, and when each one falls due. ..."
}
FieldTypeDescription
formatstringWhat content is written in. Always markdown.
contentstringThe whole guide.

Responses​

StatusCodeWhen
200—Always, for a valid key.

Notes​

  • The guide is JSON on the wire, like every other v3 response, rather than text/markdown. Render content as Markdown.
  • Over MCP the same text is available three ways: the get_authoring_guide tool, which returns this object; the template_authoring_guide resource at checkflow://guides/template-authoring, with MIME type text/markdown, which a client can offer a person to attach; and a pointer in the server's instructions. The checkflow:// URI names the resource within the MCP server and is not a web address.
  • The worked example names no members, tags or templates, so it validates in any workspace. As soon as you add an assignee or a tag, the names have to come from GET /v3/workspace.

Get the Document Schema​

Returns the whole template document as a JSON Schema (draft 2020-12): the top-level properties, settings, tags, permissions, parameters, tasks, due dates, conditions and notifications. Use it to validate a document's structure client-side, to generate types, or to give an agent the envelope before it chooses any field types.

GET /v3/schema/document

Parameters​

This endpoint takes no parameters.

Example​

GET https://api.checkflow.io/v3/schema/document
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap

Abridged — the full response describes every property of the document:

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Template document",
"description": "A whole CheckFlow template, written as one object. POST /v3/templates takes one, GET /v3/templates/{key}/document emits one, and a draft holds one while it is being written. ...",
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 100,
"description": "The template's name, as the library lists it."
},
"tasks": {
"type": "array",
"description": "The tasks, in the order they appear. Array position is the order -- there is no order property, and sending one is an unknown property.",
"items": {
"type": "object",
"properties": {
"halt": {
"type": "string",
"enum": ["none", "task", "task-and-preceding"],
"description": "Whether the checklist stops here until this task is done. Defaults to none."
},
"fields": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "string",
"enum": ["Text", "ShortText", "LongText", "EmailInput", "Website", "FileUpload", "Date", "DropDown", "MultiChoice", "SubTasks", "Image", "Video", "File", "SendEmail", "Separator", "Embed", "Members", "Table", "LinkedChecklist"]
}
},
"additionalProperties": true,
"required": ["type"]
}
}
},
"additionalProperties": false,
"required": ["name"]
}
}
},
"required": ["name"],
"additionalProperties": false
}

Responses​

StatusCodeWhen
200—Always, for a valid key.

Notes​

  • Fields are folded. Each field in tasks[].fields[] is described only by the properties every field has — ref, id, type, hiddenByDefault and tooltip — with type as an enum of the nineteen content types and additionalProperties left true. A field's own properties are written flat beside those five, so a closed object would refuse every real field. Use Describe a Content Type for the type you are writing; that schema is closed.
  • Every other object is closed (additionalProperties: false). Note that the server itself refuses unknown properties only on fields; see Rules That Apply Everywhere.
  • Enums are the accepted vocabularies. halt, parameter type, permission type and permission, due date rule, direction and scenario, condition operator and effect, and notification event are all given as enums.
  • The schema cannot tell you what the workspace contains. A tag is a string here; in a document it has to be a real or intended tag name. Names come from GET /v3/workspace.
  • The schema is rebuilt on every request, and its descriptions carry the same explanations as Template Documents.

List Content Types​

Returns the nineteen field types a document can use, each with a one-line summary and whether a condition can test it. Read it before choosing a field's type: the type decides every other property the field may carry.

GET /v3/schema/content-types

Parameters​

This endpoint takes no parameters.

Example​

GET https://api.checkflow.io/v3/schema/content-types
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap
{
"types": [
{ "type": "Text", "summary": "A block of formatted prose the template shows and nobody answers -- an instruction, a warning, a paragraph of context before the questions start.", "isTestable": false },
{ "type": "ShortText", "summary": "One line of text the checklist's user types -- a name, a reference, a serial number.", "isTestable": true },
{ "type": "LongText", "summary": "Several lines of text the checklist's user types -- an explanation, a note, an account of what happened.", "isTestable": true },
{ "type": "EmailInput", "summary": "An email address the checklist's user types, checked for being one.", "isTestable": true },
{ "type": "Website", "summary": "A web address the checklist's user types. To show a page the author chose, rather than ask for one, use an Embed field instead.", "isTestable": true },
{ "type": "FileUpload", "summary": "Files the checklist's user uploads -- receipts, photographs, a signed form. To hand them files instead, use a File field.", "isTestable": false },
{ "type": "Date", "summary": "A date, a time, or both, picked from a calendar by the checklist's user.", "isTestable": true },
{ "type": "DropDown", "summary": "A list the checklist's user picks exactly one thing from, shown collapsed.", "isTestable": true },
{ "type": "MultiChoice", "summary": "A list of boxes the checklist's user may tick any number of, all shown at once.", "isTestable": true },
{ "type": "SubTasks", "summary": "A list of steps the checklist's user ticks off as they go -- a checklist inside the task, rather than a question with answers.", "isTestable": false },
{ "type": "Image", "summary": "A picture the template shows. The picture itself is uploaded in the editor and a document cannot carry one, so a new Image field has to be made there.", "isTestable": false },
{ "type": "Video", "summary": "A video the template shows, either from an address or uploaded in the editor. An address is the half a document can write.", "isTestable": false },
{ "type": "File", "summary": "Files the template hands the checklist's user -- a handbook, a form, a drawing. To ask them for files instead, use a FileUpload field.", "isTestable": false },
{ "type": "SendEmail", "summary": "An email the checklist sends, written when the template is. Every property is the author's -- there is nothing here for the checklist's user to answer.", "isTestable": false },
{ "type": "Separator", "summary": "A rule drawn across the task, to group what is above it apart from what is below. Nothing to configure and nothing to answer.", "isTestable": false },
{ "type": "Embed", "summary": "A page from somewhere else, shown inside the task -- a dashboard, a form, a video. To ask the checklist's user for an address instead, use a Website field.", "isTestable": false },
{ "type": "Members", "summary": "People picked from the workspace by the checklist's user -- who signs it off, who was present, who to hand it to next.", "isTestable": true },
{ "type": "Table", "summary": "A grid of cells -- readings, quantities, a row per item. Cells the author fills in and marks read-only are headings; the rest are answered in the checklist.", "isTestable": false },
{ "type": "LinkedChecklist", "summary": "A second checklist started from another template -- a sub-process run from inside this one.", "isTestable": false }
]
}
FieldTypeDescription
typesobject[]Every content type, in a fixed order. Not paged.
types[].typestringThe word a field's type is written with, spelled exactly.
types[].summarystringWhat the type is for, written to tell it apart from its neighbours.
types[].isTestablebooleanWhether a condition can be triggered by a field of this type.

Responses​

StatusCodeWhen
200—Always, for a valid key.

Notes​

  • The type names are the API's own and differ from the control names in the product in a few places: EmailInput is the E-Mail control, Date is Date & Time, DropDown is Dropdown and SendEmail is Mail-To. Field Types maps each one to its control page.
  • Eleven types cannot be tested by a condition. That is a fact about which controls the product supports conditions on, so a field that other parts of the template branch on has to be one of the eight with isTestable: true.

Describe a Content Type​

Returns one field type in full: its JSON Schema, the condition operators it supports and an example field. Call it when you choose a type — it is the only place a type's own properties are listed.

GET /v3/schema/content-types/{type}

Parameters​

NameInTypeRequiredDescription
typepathstringYesThe content type, spelled as List Content Types spells it. Matched without regard to case, so table finds Table.

Example​

GET https://api.checkflow.io/v3/schema/content-types/Date
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap
{
"type": "Date",
"summary": "A date, a time, or both, picked from a calendar by the checklist's user.",
"isTestable": true,
"operators": [
"is", "is-not", "has-no-value", "has-any-value",
"is-greater-than", "is-greater-than-or-equal-to", "is-less-than", "is-less-than-or-equal-to"
],
"schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Date",
"description": "A date, a time, or both, picked from a calendar by the checklist's user.",
"type": "object",
"properties": {
"ref": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$", "maxLength": 64, "description": "The author's own name for this field, which everything referring to it uses. Invented from the field's label when left out." },
"id": { "type": "string", "format": "uuid", "description": "The field's key. Leave it out to create a field; keep the one export gave you to carry an existing field, and the answers already given to it, into a new version." },
"type": { "const": "Date", "description": "A date, a time, or both, picked from a calendar by the checklist's user." },
"hiddenByDefault": { "type": "boolean", "description": "Whether the field starts hidden, to be shown by a condition." },
"label": { "type": "string", "description": "The question or prompt shown above the picker." },
"isRequired": { "type": "boolean", "description": "Whether the task cannot be completed until this field has been answered.", "default": false },
"mode": { "type": "string", "description": "What the picker asks for: a day, a time of day, or both.", "enum": ["date", "time", "date-and-time"], "default": "date" },
"valueFrom": {
"type": "object",
"description": "Where the field's value comes from when it is filled in for the user: a parameter, another field, a field of the record another field selected from a Data Set, or a global dynamic value. One of the three, and at most one binding per field.",
"properties": {
"parameter": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$", "maxLength": 64, "description": "The ref of a parameter whose value fills this field when a checklist is started. A string parameter fills any field but a Date, a datetime parameter fills a Date, and a member_or_group parameter fills a Members field." },
"field": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$", "maxLength": 64, "description": "The ref of a field whose answer is copied into this one whenever it changes -- a field of the same kind, or any answerable field for a ShortText or LongText. With 'dataSetField', the field whose selected record is read instead." },
"dataSetField": { "type": "string", "description": "A field of the record 'field' selected from its Data Set, by name or by key. Only with a 'field' that carries a dataSet." },
"dynamicValue": { "type": "string", "description": "A global dynamic value, as its token: {{checklist.name}}, {{current_user.email}}, {{current_date}}. Which ones a field takes depends on its type; a Date takes the date-valued ones and a DropDown takes none." }
},
"additionalProperties": false,
"anyOf": [
{ "required": ["parameter"] },
{ "required": ["field"] },
{ "required": ["dynamicValue"] }
]
},
"tooltip": { "type": "string", "description": "Help text shown against the field, behind the question mark beside its label.", "maxLength": 1200 }
},
"required": ["type", "label"],
"additionalProperties": false
},
"example": {
"ref": "installed_on",
"type": "Date",
"hiddenByDefault": false,
"label": "When was it installed",
"isRequired": true,
"mode": "date-and-time"
}
}
FieldTypeDescription
typestringThe content type's name.
summarystringThe same summary List Content Types gives.
isTestablebooleanWhether a condition can be triggered by a field of this type.
operatorsstring[]The condition operators a field of this type supports. Empty for an untestable type; four for most testable types; eight for Date.
schemaobjectThe type's JSON Schema, complete on its own: every property with its type, description, range, enum, default and whether it is required. additionalProperties is false. A property marked readOnly is written by export and ignored on the way in.
exampleobjectA realistic field of this type, ready to put in a task's fields array once you change its ref and label.

Responses​

StatusCodeWhen
200—The type exists.
400VALIDATION_ERRORtype is not a content type. field is type. The message names the nearest real type when one is close, such as "'ShortTxt' is not a content type. Did you mean 'ShortText'?".

Notes​

  • The keys of schema.properties are the complete list of properties a field of this type may carry. There is no separate property list.
  • The example is the small, realistic case rather than a field with every property set.
  • The Image and File examples cannot be used in a new template as they stand: a new Image or File field needs a file, which a document cannot carry, and is refused with binary_required. See Image, Video and File.

List Condition Operators​

Returns the tests a condition can make, which of them need a value, which apply only to dates, the two effects and the eight field types a condition can test. Pass contentType to ask about one type.

GET /v3/schema/condition-operators

Parameters​

NameInTypeRequiredDescription
contentTypequerystringNoNarrow the answer to one field type, matched without regard to case. Omit for every operator.

Example​

GET https://api.checkflow.io/v3/schema/condition-operators
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap
{
"operators": [
{ "operator": "is", "needsValue": true, "datesOnly": false },
{ "operator": "is-not", "needsValue": true, "datesOnly": false },
{ "operator": "has-no-value", "needsValue": false, "datesOnly": false },
{ "operator": "has-any-value", "needsValue": false, "datesOnly": false },
{ "operator": "is-greater-than", "needsValue": true, "datesOnly": true },
{ "operator": "is-greater-than-or-equal-to", "needsValue": true, "datesOnly": true },
{ "operator": "is-less-than", "needsValue": true, "datesOnly": true },
{ "operator": "is-less-than-or-equal-to", "needsValue": true, "datesOnly": true }
],
"testableTypes": ["Date", "DropDown", "EmailInput", "LongText", "MultiChoice", "ShortText", "Website", "Members"],
"effects": ["show", "hide"]
}

Asked about a type that cannot be tested, the answer is not an error:

GET https://api.checkflow.io/v3/schema/condition-operators?contentType=FileUpload
X-API-KEY: your-api-key-here
{
"type": "FileUpload",
"isTestable": false,
"operators": [],
"testableTypes": ["Date", "DropDown", "EmailInput", "LongText", "MultiChoice", "ShortText", "Website", "Members"],
"effects": ["show", "hide"]
}
FieldTypeDescription
typestringThe type asked about. Absent when contentType was not sent.
isTestablebooleanWhether that type can be tested. Absent when contentType was not sent.
operatorsobject[]Every operator, or those the type supports, or none.
operators[].operatorstringThe word a condition's when.operator is written with.
operators[].needsValuebooleanWhether the operator compares against a when.value. A value sent with an operator that takes none is refused.
operators[].datesOnlybooleanWhether only a Date field supports the operator.
testableTypesstring[]The eight types a condition can test, always included so that an untestable answer says what to use instead.
effectsstring[]What a rule can do: show or hide.

Responses​

StatusCodeWhen
200—No contentType, or a contentType that names a type — testable or not.
400VALIDATION_ERRORcontentType names no type at all, such as Drop Down. field is contentType, and the message names the nearest type when one is close.

Notes​

  • A condition's rules on one field run in order each time its answer changes, and nothing returns to hiddenByDefault on its own. A show or hide rule needs a reset rule ahead of it on the same field. See Order and Resets.
  • What when.value looks like for each type is on Template Documents.

List Due Date Rules and Scenarios​

Returns everything a task's dueDate can be written with: the rules an offset is counted from, what each rule's anchorRef names, which rules accept before, the two directions and the twenty calendar scenarios.

GET /v3/schema/due-dates

Parameters​

This endpoint takes no parameters.

Example​

GET https://api.checkflow.io/v3/schema/due-dates
X-API-KEY: your-api-key-here
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-RateLimit-Bucket: cheap
{
"rules": [
{ "rule": "checklist-start-date", "takesDirection": false },
{ "rule": "previous-task-completed", "takesDirection": false },
{ "rule": "date-control", "anchor": "field", "takesDirection": true },
{ "rule": "task-completed", "anchor": "task", "takesDirection": false },
{ "rule": "parameter", "anchor": "parameter", "takesDirection": true }
],
"directions": ["before", "after"],
"scenarios": [
"next-weekday", "next-monday", "next-tuesday", "next-wednesday", "next-thursday",
"next-friday", "next-saturday", "next-sunday",
"first-day-of-next-month", "first-weekday-of-next-month", "last-day-of-month", "last-weekday-of-month",
"last-day-of-quarter", "last-weekday-of-quarter", "first-day-of-next-quarter", "first-weekday-of-next-quarter",
"last-day-of-current-year", "last-weekday-of-current-year", "first-day-of-next-year", "first-weekday-of-next-year"
]
}
FieldTypeDescription
rulesobject[]The five rules a due date's rule can take.
rules[].rulestringThe rule's word.
rules[].anchorstringWhat anchorRef must name under this rule: task, field (a Date field) or parameter (a datetime parameter). Absent when the rule counts from something the checklist already knows and takes no anchorRef.
rules[].takesDirectionbooleanWhether direction: "before" is accepted. true for date-control and parameter, whose anchors are dates; the other rules count forward from something that has just happened.
directionsstring[]before and after.
scenariosstring[]The twenty recurring calendar moments a due date's scenario can take, in the order the template editor lists them.

Responses​

StatusCodeWhen
200—Always, for a valid key.

Notes​

  • A due date is a rule with an optional offset, or a scenario with an optional scenarioTime — never both. Every combination, with examples, is on Due Dates.
  • A scenario outside this list is refused rather than stored, because it would never compute a due date.
  • Template Documents — the full reference for the format these routes describe.
  • Workspace — the member, group, tag and template names a document can use.
  • Templates — validating, creating and versioning templates from documents.
  • Rate Limits — the cheap and standard budgets.
  • MCP Template Tools — the same discovery reads as MCP tools.