Items, search, and secret fields
Items and search#
Collections and filters#
GET /workspaces/{ws}/items
| Query | Meaning |
|---|---|
title |
Title filter; exact by default. |
title_match |
exact or contains. |
q |
Case-insensitive title, stable ID, custom ID, and body search. |
query |
Deprecated alias for q. |
custom_id |
Exact custom ID. |
parent_item_id |
Direct parent ID; reads legacy parent_task storage. |
space_id, folder_id |
Resolved ancestry filters. |
list_id |
Home or additional List. |
status, priority, tag |
Item field filters. |
include_closed |
Include Items in the effective closed status group. |
labels |
Include resolved display labels (status, priority, type, durations, custom-field options). |
limit, cursor |
Pagination; limit is at most 200. |
curl "$IT_BASE/workspaces/$IT_WS/items?list_id=LST-API&status=todo" \
-H "Authorization: Bearer $IT_KEY"
curl --get "$IT_BASE/workspaces/$IT_WS/items" \
-H "Authorization: Bearer $IT_KEY" \
--data-urlencode 'title=Write docs' \
--data-urlencode 'title_match=exact'
Resolved labels for coded values#
Many Item fields store an opaque id (a string or an int) whose human-readable
text lives in the Workspace/List configuration. Pass labels=true on any Item
read or collection to receive a labels object alongside the raw value:
curl "$IT_BASE/workspaces/$IT_WS/items/TSK-00001?labels=true" \
-H "Authorization: Bearer $IT_KEY"
{
"id": "TSK-00001",
"status": "todo",
"priority": 1,
"type": "bug",
"time_estimate": 135,
"custom_fields": { "severity": "high" },
"labels": {
"status": "To Do",
"priority": "Urgent",
"type": "Bug",
"time_estimate": "2h 15m",
"severity": "High"
}
}
Resolved fields:
| Field | Stored value | Resolved from |
|---|---|---|
status |
string id (todo) |
effective status_set[].name |
priority |
int id (1) |
effective priorities[].label |
type |
string id (bug) |
effective task_types[].name |
time_estimate, time_tracked |
minutes (int) | formatted 2h 15m |
any custom field with options |
stored value or array | custom_fields[].options[].label |
context and tags already store the human-readable name as their value, so
they are not duplicated in labels. To look up colors or descriptions for
those, read the effective field catalog.
Workspace-wide search covers indexed Spaces, Folders, Lists, Items, Docs, Pages, and Views:
GET /workspaces/{ws}/search?q=&types=&mode=&space_id=&folder_id=&list_id=&doc_id=
types accepts comma-separated singular or plural names. mode=exact compares
title, stable ID, or Item custom ID; contains also searches bodies. Results
are deterministic arrays, never an arbitrary first title match. Search also
accepts limit (maximum 200) and cursor for pagination.
Create and mutate#
| Method | Endpoint | Meaning |
|---|---|---|
POST |
/workspaces/{ws}/items |
Create in the required list_id. |
GET |
/workspaces/{ws}/items/{itemId} |
Read by stable frontmatter ID. |
PATCH |
/workspaces/{ws}/items/{itemId} |
Partial domain-validated update. |
PUT |
/workspaces/{ws}/items/{itemId} |
Complete JSON or Markdown replacement. |
DELETE |
/workspaces/{ws}/items/{itemId} |
Delete Item, attachment files, and clean implemented references. |
curl -X POST "$IT_BASE/workspaces/$IT_WS/items" \
-H "Authorization: Bearer $IT_KEY" \
-H 'Content-Type: application/json' \
-d '{
"list_id": "LST-API",
"title": "Created through API v2",
"status": "todo",
"body": "Markdown description."
}'
Move an Item by stable List ID with PATCH or the list_id field route:
curl -X PATCH "$IT_BASE/workspaces/$IT_WS/items/TSK-00001" \
-H "Authorization: Bearer $IT_KEY" \
-H 'Content-Type: application/json' \
-d '{"list_id":"LST-OTHER"}'
Whole-resource PUT cannot move an Item. Status, custom-field, parent, and
relationship values are validated against effective List configuration.
status_group, waiting_on, identity, ancestry, and creation metadata are
derived or immutable.
Effective configuration always includes the plugin's built-ins: when no level
of the chain defines status_set, priorities, or task_types, the shared
defaults (Kanban statuses, the four fixed priorities, the built-in task
types) apply — exactly what the plugin UI shows for such a workspace. A
supplied status value must therefore always exist in the effective set; a
status is required only where a set is explicitly configured. type stays
free-form until task_types is explicitly configured (the plugin tolerates
unknown stored types).
activity is server-managed and read-only. Item creation and recurring
rescheduling append activity events; clients cannot replace this field through
Item or field mutations. The API also validates the complete structures of
attachments, checklist, and comments: attachment paths and names must be
safe, checklist/comment IDs must be unique, required value types must match,
and comment/attachment timestamps must be valid. Custom fields of type
attachment use the same attachment validation.
A supplied non-null recurring value must satisfy the recurring configuration
contract. When an Item with a valid, enabled recurring rule moves from a
non-final status group into done or closed, the API runs the shared
reschedule logic: it advances due_date, updates or disables the recurring
configuration, records activity, and resets status to the configured default
or the first effective status.
The legacy storage field parent_task accepts one Item ID, an array of Item
IDs, or null. Missing/ambiguous parents, duplicates, self-links, cycles, and
unsupported depth are rejected. nested_subtasks=false permits one subitem
level; enabled permits two nested levels.
Item relationship reads expose stored blocks, derived waiting_on, and a
symmetric related view. Relationship writes may update both owner files in
one atomic operation. waiting_on cannot be written.
Deleting an Item, Doc, or containing resource also removes deleted IDs from
surviving custom fields of type relationship and doc_relationship. If that
cleanup would empty a required custom field, deletion is rejected atomically
with 409 RESOURCE_IN_USE and identifies the blocking Item and field.
Structured API deletion changes the current server state directly; it does not
move the resource into the plugin's _trash folder. Retained server history
and backups are the recovery mechanisms.
Comments, checklist, activity, and attachments#
Comments, checklist entries, activity, and attachments live inside the Item. Their routes are convenience wrappers over the Item's own field values, so a sub-route can do nothing a whole-field write could not, and it cannot bypass validation.
| Method | Endpoint | Behavior |
|---|---|---|
GET/POST |
/items/{itemId}/comments |
List, or append a comment (text required; server stamps the id and created_at). |
GET/PATCH/DELETE |
/items/{itemId}/comments/{commentId} |
Read, edit text, or remove one comment. |
GET/POST |
/items/{itemId}/checklist |
List, or add an entry (text required, done defaults to false). |
GET/PATCH/DELETE |
/items/{itemId}/checklist/{entryId} |
Read, edit text/done, or remove one entry. |
GET |
/items/{itemId}/activity |
Read the server-managed activity log. |
GET/POST |
/items/{itemId}/attachments |
List, or upload (see below). |
GET/DELETE |
/items/{itemId}/attachments/{name} |
Download or delete one attachment. |
Every comment/checklist write commits the whole comments/checklist field
through the same validator and If-Match concurrency guard as a field write.
Activity is read-only: POST .../activity returns 405 with Allow: GET, the
same refusal Item create, PUT, and field writes apply to a client-supplied
activity.
Attachment upload sends the raw file bytes as the request body and the filename (and optional MIME type) as query parameters:
curl -sX POST "$IT_BASE/workspaces/$IT_WS/items/TSK-00001/attachments?name=report.pdf&mime=application/pdf" \
-H "Authorization: Bearer $IT_KEY" -H 'Content-Type: application/octet-stream' --data-binary @report.pdf
The bytes are stored as an ordinary file below _attachments/<item-id>/main/
and one metadata record is appended to the Item's attachments array in one
atomic transaction. The same file is reachable through the generic file routes
(Files and Vaults).
Secret custom fields#
Every secret request requires Full mode, the separate secret API opt-in, and the Workspace password on that request.
| Method | Endpoint | Body |
|---|---|---|
POST |
/workspaces/{ws}/items/{itemId}/secrets/{fieldId} |
{ "password": "…" } |
PUT |
/workspaces/{ws}/items/{itemId}/secrets/{fieldId} |
{ "password": "…", "value": "…" } |
DELETE |
/workspaces/{ws}/items/{itemId}/secrets/{fieldId} |
{ "password": "…" } |
curl -X POST "$IT_BASE/workspaces/$IT_WS/items/TSK-00001/secrets/cf-token" \
-H "Authorization: Bearer $IT_KEY" \
-H 'Content-Type: application/json' \
-d '{"password":"replace-with-workspace-password"}'
The password is request-scoped, not an API session. Secret responses use
Cache-Control: no-store and Pragma: no-cache, and password attempts are
rate-limited. Normal Item/field/Markdown/file responses redact stored secret
values as null and never reveal plaintext.
A workspace whose secret_fields configuration is missing or invalid answers
409 SECRETS_NOT_CONFIGURED instead of 401 WRONG_PASSWORD, so a setup
problem is never mistaken for a typo in the password.