For AI agents: a documentation index is available at /llms.txt. A markdown version of this page is available at /guide/api/conventions.md.
Conventions
The response envelope, identifiers, filters, dates and other patterns shared by every endpoint.
Every endpoint in the Developer API follows the same handful of rules. Learn them once and the other 58 endpoints behave the way you expect.
#Everything is a POST
Including the reads. projects/list and tasks/search are POST calls just
like tasks/create.
That is a consequence of the encrypted payload: filters and identifiers travel
inside the encrypted body rather than the query string, so there is no
meaningful GET form. Two practical effects:
- Never cache responses on the assumption that
POSTmeans "changes data". - URLs are stable and carry no parameters, which keeps them out of proxy logs.
#The response envelope
Every response — success or failure — has the same three top-level fields:
{
"success": true,
"message": "Projects retrieved successfully",
"data": []
}
successbooleanrequiredWhether the operation completed. Check this rather than inferring from the
presence of data.
messagestringrequiredA human-readable summary. Useful in logs; do not branch on its wording.
dataobject | arrayThe payload. Collection endpoints return an array, single-record endpoints return an object. Absent on some error responses.
Tip
Branch on the HTTP status code first, then on success. A 200 with
"success": false is possible for soft failures such as an empty filter match
on some endpoints.
#Identifiers
Records are addressed by UUID, not by the numeric primary key:
{ "project_id": "550e8400-e29b-41d4-a716-446655440000" }
| Field | Refers to |
|---|---|
project_id | A project's uniq_id |
task_id | A task's UUID |
assigned_to | A numeric user id, not a UUID |
Users are the exception
Assignment fields such as assigned_to take the integer user id returned by
users/list. Passing a UUID there fails validation.
#Filters
List endpoints take an optional filters object. Unknown keys are ignored, so a
typo silently returns unfiltered results — check your filter names when a
response looks too large.
{
"filters": {
"status": "Started",
"start_date": "2024-01-01",
"end_date": "2024-12-31"
}
}
Date filters are inclusive: start_date matches records on or after the date,
end_date matches records on or before it.
#Dates and times
| Kind | Format | Example |
|---|---|---|
| Date | YYYY-MM-DD | 2024-01-15 |
| Timestamp | YYYY-MM-DD HH:MM:SS | 2024-01-10 10:30:00 |
| Signing timestamp | Unix seconds | 1704806400 |
Timestamps in responses are in the workspace's configured timezone, not UTC. The
X-TIMESTAMP header used for signing is always Unix epoch seconds.
#Statuses and priorities
These are validated against fixed sets. Sending anything else returns 422.
| Field | Accepted values |
|---|---|
Task priority | low, medium, high, urgent |
Task status | Open, Closed |
Project status | Started, Hold, Stack, Completed |
Note
Workspaces that use custom task statuses still accept Open and Closed
over the API; the custom status is reported in the task detail response.
#Creating versus updating
create endpoints return the new record's UUID in data. update endpoints
take that UUID plus only the fields you want to change — omitted fields are left
alone, they are not blanked.
To clear a field, send an explicit empty value rather than omitting it.
#Deletes are usually archives
Endpoints named delete in the test-management area archive the record rather
than destroying it, so links from defects and runs stay intact. Check each
endpoint's description for the exact behaviour.
#Pagination
Collection endpoints return the full filtered set. Where an endpoint supports
paging it is documented on that endpoint's own page — do not assume page and
limit are accepted everywhere.
Status codes, validation failures and what to retry.