Template file format (thendoo-template/v1)
Every template on the Thendoo website, and every file the routine builder makes, is a single JSON document in the thendoo-template/v1 format. It’s plain JSON, so you can write one by hand, keep it in Git or generate it from a script.
- JSON Schema:
/schemas/thendoo-template-v1.json - Media type:
application/json, with the file extension.json
The shape
Section titled “The shape”{ "format": "thendoo-template/v1", "id": "weekly-reset", "name": "Weekly reset", "locale": "en", "templates": [], "habits": [], "categories": []}| Field | Required | Meaning |
|---|---|---|
format |
yes | Always thendoo-template/v1. A future breaking change gets a new version. |
id |
yes | Stable id in lowercase with hyphens. It matches the website slug and the import link thendoo://import/{id}. |
name |
yes | Shown when importing. Up to 120 characters. |
locale |
no | The language the text is written in, e.g. en or he. Defaults to en. |
templates |
one of the three | Lists of tasks (up to 20). |
habits |
one of the three | Recurring habits (up to 50). |
categories |
one of the three | Money categories (up to 200). |
At least one of templates, habits or categories must have an entry.
Lists, tasks and subtasks: templates[]
Section titled “Lists, tasks and subtasks: templates[]”Each entry is a list. Its shape is exactly the body of POST /v1/templates in the API, so an entry can be sent as it is.
{ "type": "project", "name": "Trip prep", "body": { "items": [ { "title": "Pack the bag", "offset_days": -1, "checklist": ["Chargers and adapters", "Medication for the whole trip"], "notes": null } ], "body_md": null }}typeis one oftask,project,checklist,routineornote.- Task, project and routine: applying the list creates one task per item.
- Checklist: creates one task, with the items as its checklist.
- Note: creates a note from
body_mdand has no items.
offset_dayscounts days from the date the user picks when applying the list.-3means three days before.nullmeans no due date. The limit is ±3650.- Subtasks are written as
checklistlines in v1, up to 100 per task. notescan be up to 20,000 characters.
Recurring habits: habits[]
Section titled “Recurring habits: habits[]”Habits are written the way Thendoo thinks about them: a cue and an action. When I pour my coffee, then I take my vitamins.
{ "key": "vitamins", "name": "Vitamins", "cue": "After I pour my morning coffee", "action_text": "Take the vitamins from the jar next to the kettle", "trigger": { "type": "after_habit", "habit": "coffee" }, "schedule": { "kind": "daily" }, "streak_rule": { "misses_per_week": 1 }, "color": null, "icon": null}keyis a short id that is unique within the file. Other habits refer to it.triggersets when the nudge fires:{ "type": "none" }: no automatic nudge.{ "type": "time", "at_min": 480 }: a time, in minutes after local midnight (480 = 08:00).{ "type": "after_habit", "habit": "<key>" }: right after another habit in the same file.{ "type": "event_end", "title_contains": null }: when the day’s last meeting ends, or the last one whose title contains the text.{ "type": "place", "event": "arrive", "label": "Home" }: when you arrive at or leave a place. The person chooses the actual place on import. Coordinates are never part of a template.
schedulesets how often:{ "kind": "daily" }{ "kind": "weekdays", "days": ["MO", "WE", "FR"] }{ "kind": "x_per_week", "times": 3 }{ "kind": "flexible" }
streak_rule.misses_per_week(0–3) sets how many misses a week a streak forgives. Thendoo’s default is 1.
Money categories: categories[]
Section titled “Money categories: categories[]”[ { "key": "home", "name": "Home", "kind": "expense", "parent": null, "color": null }, { "key": "rent", "name": "Rent", "kind": "expense", "parent": "home", "color": "#4F46E5" }]kindisexpense,incomeortransfer.parentis thekeyof another category in the same file, which makes the category a subcategory.coloris a hex color such as#4F46E5, ornull.
Rules a valid file follows
Section titled “Rules a valid file follows”- Every
keyis unique within its block. after_habitandparentrefer to keys that exist, and never to the item itself.- No database ids or personal data. The importer creates fresh records and maps keys to the new ids.
- Strings are trimmed, and unknown fields are rejected, so a typo fails loudly instead of being ignored.
Importing
Section titled “Importing”The app doesn’t have an importer for the whole file yet. Until then, send each templates[] entry on its own to POST /v1/templates. Habits and categories need the importer before they can be loaded.