Items, search, and secret fields

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.