Skip to content

Identifiers

Every resource id is a string of the form <prefix>_<value>. The prefix names the resource type, so passing an id of the wrong type is rejected with 422 validation_error (invalid_request) instead of silently resolving the wrong resource.

Prefix Resource Example
task_ task task_6594a1b2c3d4e5f6a7b8c9d0
subtask_ subtask composite — see below
time_log_ time log composite — see below
event_ scheduled event event_6594a1b2c3d4e5f6a7b8c9e0
proj_ project proj_6594a1b2c3d4e5f6a7b8c9a0
user_ workspace member user_6594a1b2c3d4e5f6a7b8c9b0
tag_ tag tag_6594a1b2c3d4e5f6a7b8c9c0
task_status_ task status task_status_6594a1b2c3d4e5f6a7b8c9d2
task_type_ task type task_type_6594a1b2c3d4e5f6a7b8c9d3
event_type_ event type event_type_6594a1b2c3d4e5f6a7b8c9d4
proj_status_ project status proj_status_6594a1b2c3d4e5f6a7b8c9d5
cf_ custom field cf_6594a1b2c3d4e5f6a7b8c9d6
cf_option_ custom field option cf_option_6594a1b2c3d4e5f6a7b8c9d7
attachment_ attachment attachment_6594a1b2c3d4e5f6a7b8c9d8
chat_message_ chat message chat_message_6594a1b2c3d4e5f6a7b8c9d9

Most resources use the simple <prefix>_<value> shape. Two things add extra parts: subtasks/time logs (which encode their parent task) and recurring occurrences (which append a date).

A subtask or time log is always addressed together with its parent task, so its id carries both — the child first, then the parent task:

subtask_<child>_<parent task>
time_log_<child>_<parent task>

For a task task_6594a1b2c3d4e5f6a7b8c9d0:

Resource Example id
subtask subtask_6594a1b2c3d4e5f6a7b8c9d1_6594a1b2c3d4e5f6a7b8c9d0
time log time_log_6594a1b2c3d4e5f6a7b8c9d2_6594a1b2c3d4e5f6a7b8c9d0

The trailing 6594a1b2c3d4e5f6a7b8c9d0 is the parent task’s value. It’s still a single opaque string — you pass the whole thing as one id, you don’t assemble it from parts.

A recurring series has a plain id — task_<value> or event_<value> — which addresses the whole series (read it, edit the rule, delete every occurrence).

A single occurrence of that series is addressed by appending the occurrence’s start time (UTC, YYYYMMDDTHHMMSSZ):

task_<series>_<date> e.g. task_6594a1b2c3d4e5f6a7b8c9d0_20260113T090000Z
event_<series>_<date> e.g. event_6594a1b2c3d4e5f6a7b8c9e0_20260113T090000Z

Anything that belongs to an occurrence carries the same date suffix after the composite part — so a subtask or time log of an occurrence looks like:

subtask_6594a1b2c3d4e5f6a7b8c9d1_6594a1b2c3d4e5f6a7b8c9d0_20260113T090000Z
time_log_6594a1b2c3d4e5f6a7b8c9d2_6594a1b2c3d4e5f6a7b8c9d0_20260113T090000Z
You want to… Use
Read/update/delete the entire series the plain series id — task_<series>
Read/update/delete a single occurrence the occurrence id — task_<series>_<date>

An update or delete always affects exactly the id you send — the whole series, or the one occurrence. Spanning edits (“this and following” / “all”) are not available yet.

Because you treat every id as opaque, the same endpoints work regardless of the shape. GET /tasks/{id} accepts a plain task id or an occurrence id — you don’t special-case the format, and you don’t need to know which one you hold. Read ids from responses, store them whole, and send them back exactly as received.