Template Definition Language (TDL)
TDL is the JSON schema that defines all Rundun checklist templates. It is the anchor for everything — the API, the mobile renderer, AI generation, and webhook payloads all derive from it.
Why TDL exists
Checklists need to work across multiple surfaces: a visual editor on web and mobile, a React Native renderer that shows the right UI control for each step type, an AI generator that creates templates from natural language, and webhook payloads that carry step answers in a predictable shape. TDL is the single source of truth that makes all of this possible.
- Humans author templates via a visual editor — they never write TDL JSON by hand
- AI authors and modifies templates by reading and writing TDL JSON directly
- IDs are always auto-generated — never typed manually
- The template is snapshotted verbatim when a run is created — future template edits do not affect in-progress runs
Structure
A template is a JSON object with metadata, settings, sections, and steps. Sections are optional — a template with no sections renders as a flat step list.
template
└── sections[]
└── steps[]
└── condition (optional)
└── photo overlay (optional)
└── config (type-specific)
Minimal complete example
A template with one section, a boolean step, and a photo step:
{
"id": "t-550e8400-e29b-41d4-a716-446655440000",
"v": 1,
"name": "Unit ready check",
"description": "Quick confirmation before guest arrival",
"tags": ["STR", "turnover"],
"settings": {
"allow_skip": false,
"signature_on_complete": false,
"voice": false,
"est_minutes": 3
},
"sections": [
{
"id": "g-unt1",
"title": "Unit condition",
"description": "Walk through each area before marking ready",
"steps": [
{
"id": "s-cl01",
"type": "boolean",
"label": "Is the unit clean and ready for the next guest?",
"hint": "Check all rooms, bathroom, and kitchen",
"required": true,
"photo": false,
"geo": false,
"voice_prompt": "Is the unit clean and ready for the next guest?",
"condition": null,
"config": {
"true_label": "Yes, ready",
"false_label": "No, needs attention"
}
},
{
"id": "s-ph01",
"type": "photo",
"label": "Photo of the living area",
"hint": "Take a wide shot from the entrance",
"required": true,
"photo": false,
"geo": false,
"voice_prompt": null,
"condition": null,
"config": {
"min": 1,
"max": 3,
"annotate": false
}
}
]
}
],
"execution": {
"scope": "any",
"identity_required": "none"
}
}
Top-level fields
| Field | Set by | Description |
|---|---|---|
id |
System | Auto-generated on template creation (t-{uuid}) |
v |
System | Integer, incremented on each save |
name |
Human / AI | Template name shown to creator and executor |
description |
Human / AI | Optional subtitle |
tags |
Human / AI | For search and categorization |
settings |
Human / AI | Template-wide behavior toggles |
sections |
Human / AI | Ordered array of section objects |
execution |
Human | Who can execute and how identity is captured |
Settings fields
| Field | Type | Description |
|---|---|---|
allow_skip |
boolean | Whether non-required steps can be explicitly skipped |
signature_on_complete |
boolean | Prompt executor for a final signature after all steps |
voice |
boolean | Enable voice guidance (post-MVP — set to false) |
est_minutes |
number | Estimated completion time shown to executor |
ID conventions
| Entity | Format | Example | Scope |
|---|---|---|---|
| Template | t-{uuid} |
t-550e8400-e29b-41d4-a716-446655440000 |
Global |
| Run | r-{uuid} |
r-550e8400-e29b-41d4-a716-446655440000 |
Global |
| Section | g-{4 alphanum} |
g-unt1 |
Template-scoped |
| Step | s-{4 alphanum} |
s-cl01 |
Template-scoped |
| Option | o-{4 alphanum} |
o-fu1 |
Step-scoped |
g= group (section),s= step,o= option- 4 alphanum = 1.6M combinations — sufficient for template-scoped IDs
- Global resources use UUID to guarantee uniqueness without coordination
Step envelope
Every step shares the same base structure regardless of type:
{
"id": "s-f1c4",
"type": "boolean",
"label": "Front bumper clean?",
"hint": "Look for dents, scratches, or cracks",
"required": true,
"photo": false,
"geo": false,
"voice_prompt": "Is the front bumper clean and undamaged?",
"condition": null,
"config": {}
}
| Field | Description |
|---|---|
id |
Auto-generated step ID (s-{4alphanum}) |
type |
Step type — determines config shape and UI control |
label |
The question or instruction shown to the executor |
hint |
Optional secondary text shown below the label |
required |
If true, step must be answered to complete the run |
photo |
false (no camera) or { min, max, annotate } (photo overlay) |
geo |
true to capture GPS when the step is answered |
voice_prompt |
Text spoken aloud in voice mode (post-MVP) |
condition |
Show/hide rule based on a prior step's answer — see Conditions |
config |
Type-specific options — see Step types |
Execution settings
execution is a top-level object on the template that controls who can execute it:
"execution": {
"scope": "any",
"identity_required": "none"
}
scope
| Value | Effect |
|---|---|
"any" |
Anyone with the link can execute (default) |
"org_only" |
Only org members (any role) can execute |
identity_required
| Value | Effect |
|---|---|
"none" |
Fully anonymous — no sign-in, no name (default) |
"name" |
Executor enters their name. No Rundun account needed. |
"email" |
Executor enters name + email. No Rundun account needed. |
"org_member" |
Must be a signed-in org member. Highest accountability. |
scope: "org_only" implies identity_required: "org_member". Any other combination is valid.
Further reading
- Step types — all 11 step types with config fields and examples
- Conditions — conditional step visibility
- Photos — photo overlay, upload flow, and webhook delivery