7 routes
/headings
7 routes.
Named groups of to-dos INSIDE a project or subproject (“before the move”, “moving day”, “after”). A heading only orders and groups its project’s list: it has no page, no dates, no notes and no trash, and its to-dos still belong to the project (parentId), with headingId naming the group. Areas have none. If a group needs its own deadline or notes, make it a subproject instead (/headings/{id}/convert).
- GET /v1/headings
- POST /v1/headings
- GET /v1/headings/{id}
- PATCH /v1/headings/{id}
- DELETE /v1/headings/{id}
- POST /v1/headings/{id}/convert
- POST /v1/headings/{id}/complete
GET /v1/headings
List headings
A project’s headings in their order with ?project_id= (projectId also works); without it, every heading of the account, grouped by project. A heading of a project in the trash is left out (it is in the trash with its project). openCount is what the apps show after the name.
Scopes: containers:read
Query
| parameter | type | notes | |
|---|---|---|---|
limit |
integer | optional | min 1, max 200 |
cursor |
string | optional | max 4096 chars |
updated_since |
string (RFC 3339) | optional | max 24 chars |
project_id |
string | optional | max 64 chars |
projectId |
string | optional | max 64 chars |
Response
- 200 HeadingList. One page of headings.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * account_too_large * validation_failed * rate_limited * internal
POST /v1/headings
Create a heading
projectId must be a live project or subproject (an area is refused). after: omitted puts it LAST, null puts it FIRST, a heading id puts it right after that heading.
Scopes: containers:write Idempotent. Send Idempotency-Key and a retry replays the first answer instead of writing twice.
Request body
| field | type | notes | |
|---|---|---|---|
projectId |
string | required | max 64 chars, min 1 |
name |
string | required | max 512 chars |
after |
string | optional | or null, max 64 chars |
id |
string (UPPERCASE UUID) | optional | max 64 chars |
Response
- 201 Heading. The heading as stored.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * conflict * duplicate_id * payload_too_large * unsupported_media_type * account_too_large * validation_failed * rate_limited * internal
GET /v1/headings/{id}
One heading
Readable by id even while its project is in the trash (the list leaves those out). Writes to such a heading (PATCH, DELETE, /convert, /complete) are validation_failed until the project is restored.
Scopes: containers:read
Response
- 200 Heading. The heading.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * not_found * account_too_large * rate_limited * internal
PATCH /v1/headings/{id}
Rename or reorder a heading
name renames it. after reorders it among its project’s headings (null = first, a heading id = right after it). A heading never changes project. A heading of a project in the trash takes no writes (validation_failed): restore the project first.
Scopes: containers:write
Request body
| field | type | notes | |
|---|---|---|---|
name |
string | optional | max 512 chars |
after |
string | optional | or null, which clears it, max 64 chars |
Response
- 200 Heading. The heading after the change.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * not_found * payload_too_large * unsupported_media_type * account_too_large * validation_failed * rate_limited * internal
DELETE /v1/headings/{id}
Delete a heading, keeping its tasks
There is one delete and the tasks always stay: every to-do under the heading gets headingId: null and stays in the project, in its order; then the heading row is deleted for good (a heading has no trash). Returns the cleared to-do ids. Fires heading.deleted, and todo.updated for each cleared to-do. A heading of a project in the trash is validation_failed.
Scopes: containers:write, todos:write
Response
- 200 HeadingDeleted. The heading is gone; these to-dos lost it.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * not_found * account_too_large * rate_limited * internal
POST /v1/headings/{id}/convert
Make a heading a subproject
One batch: creates a subproject of the project with the heading’s name, moves every to-do under the heading into it (keeping their order, headingId cleared), then deletes the heading. ⚠ Only for a heading in a PROJECT: a subproject holds to-dos only, so a heading inside a subproject is validation_failed. Safe to retry when you pass no id: the subproject’s id is derived from the heading, so a retry after a failure part-way finds the subproject this heading’s convert made and finishes the move. It never files into any other subproject: an id that already exists is duplicate_id, and a derived subproject that was since trashed (or moved) is conflict (restore it, then convert again). With your own id, a retry after a part-way failure is duplicate_id, so omit id if you may retry. A heading of a project in the trash is validation_failed.
Scopes: containers:write, todos:write Idempotent. Send Idempotency-Key and a retry replays the first answer instead of writing twice.
Request body
| field | type | notes | |
|---|---|---|---|
id |
string (UPPERCASE UUID) | optional | max 64 chars |
Response
- 201 HeadingConverted. The new subproject and the to-dos moved into it.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * not_found * conflict * duplicate_id * payload_too_large * unsupported_media_type * account_too_large * validation_failed * rate_limited * internal
POST /v1/headings/{id}/complete
Complete every open to-do under a heading
Completes each open, live to-do under the heading exactly as /todos/{id}/complete does (a recurring one rolls to its next occurrence and stays open). The heading stays. Send an Idempotency-Key if you may retry: a second run completes whatever is open by then, including a recurring to-do’s next occurrence. Pass ?today= as for /complete. A heading of a project in the trash is validation_failed.
Scopes: todos:write Idempotent. Send Idempotency-Key and a retry replays the first answer instead of writing twice.
Response
- 200 HeadingCompleted. The heading and the to-dos completed.
Errors: grant_expired * grant_revoked * unauthorized * forbidden_scope * not_found * conflict * payload_too_large * unsupported_media_type * account_too_large * rate_limited * internal