Tasks
GET /lists/{listId}/tasks
List tasks under a list with optional filters.
Query parameters:
limit(default50, max200),offsetstatus— status_id filterq— title search
{
"tasks": [
{
"id": "...", "title": "Fix login bug",
"status_id": "...", "priority": 2,
"due_date": 1730764800,
"custom_data": {...},
"assignees": [{ "id": "...", "name": "Alice" }]
}
],
"limit": 50, "offset": 0
}
POST /lists/{listId}/tasks
Create a task. Two body formats are accepted:
JSON (most common)
curl -H "Authorization: Bearer kdt_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Fix the login bug",
"priority": 3,
"due_date": 1730764800,
"assignees": ["user_id_1", "user_id_2"],
"custom_data": { "field_id_xxx": "high severity" }
}' \
https://api.konduit.work/v1/lists/<id>/tasks
status_id defaults to the list's first active status if omitted.
Multipart (task + attachments in one request)
When the task is meaningless without its files (bug report with screenshot, customer issue with PDF, etc.), submit as multipart/form-data with a JSON data field plus one or more file fields:
curl -H "Authorization: Bearer kdt_..." \
-F 'data={"title":"Bug: login broken","priority":"high","custom_data":{"field_severity":"opt_p1"}}' \
-F "file=@./screenshot.png" \
-F "file=@./console-log.txt" \
https://api.konduit.work/v1/lists/<id>/tasks
Response includes the created task plus an attachments array:
{
"id": "task_abc...",
"attachments": [
{
"id": "att_xyz...", "filename": "screenshot.png",
"mime_type": "image/png", "size_bytes": 245678,
"url": "/api/files/attachments/{ws}/task_abc/att_xyz/screenshot.png"
},
{ "id": "att_qrs...", "filename": "console-log.txt", "..." }
]
}
Multipart limits:
| Max files per request | 10 |
| Max size per file | 25 MB (same as POST /tasks/{id}/attachments) |
| Blocked extensions | exe, bat, cmd, com, msi, scr, ps1, vbs, js, sh |
All-or-nothing semantics: if any attachment upload fails (storage cap exceeded, a transient storage error, etc.), the task itself is rolled back so you never observe a partial state. The whole request returns a 500 with the failure reason. On success, every file in the request is attached.
For uploading attachments to an existing task, use POST /tasks/{taskId}/attachments — that's the right endpoint when you don't need atomicity with task creation.
Single-task operations
GET /tasks/{taskId}
Returns the full task including custom_data, assignees, and computed fields like assignee names.
PATCH /tasks/{taskId}
Partial update. Accepts:
title, status_id, priority, due_date, date_start, description,
assignees, custom_data,
time_estimate, repeat_config, project_id, parent_task_id, type_id
repeat_config is a JSON object that turns a task into a recurring one. Pass null to clear recurrence. Shape:
{
"trigger": "on_schedule" | "on_complete",
"frequency": "daily" | "weekdays" | "weekly" | "monthly",
"day_of_week": 0,
"day_of_month": 1,
"time": "09:00",
"end_date": 1735689600
}
trigger: "on_schedule"— the task clones forward on the given cadence.frequencyrequired.trigger: "on_complete"— the task clones only when the current occurrence is marked done (a status withtypedoneorclosed). All other fields ignored.day_of_weekis0(Sun) through6(Sat). Required whenfrequency: "weekly".day_of_monthis1through31. Required whenfrequency: "monthly"(clamped to actual month length at run time).timeis"HH:MM"in the workspace's timezone. Defaults to"09:00"if omitted.end_dateis a Unix timestamp; recurrence stops after that date. Optional.
When repeat_config is set, the server reschedules the task automatically so the next clone fires on the given cadence.
DELETE /tasks/{taskId}
Soft-delete — sets deleted_at, hides the task from all GET endpoints. Fires the task_deleted automation trigger with the pre-delete snapshot. Recoverable via the in-app trash.
Validation rules
PATCH and POST validate every writable field. Violations return 400 with the offending field in the error message.
| Field | Rule |
|---|---|
priority |
One of low, normal, high, urgent, or null |
status_id |
Must exist in the task's space |
assignees |
Each must be a workspace member |
project_id, parent_task_id, type_id |
Must reference an existing record |
parent_task_id |
Cannot equal the task's own ID (no self-parenting) |
time_estimate |
Integer, in minutes |
custom_data |
See custom_data validation below |
custom_data validation
Custom field values are validated against the field type:
| Type | Accepted |
|---|---|
text, longtext, email, phone, link |
string |
number, currency, slider |
number (slider also range-checked) |
checkbox |
boolean (or 0/1) |
date |
unix timestamp (seconds) |
dropdown |
an option ID from the field's config |
tags |
array of option IDs |
people |
array of workspace member user IDs |
datatable (select-mode) |
array of row IDs from the field's table_id (single-select rejects arrays of length > 1) |
datatable (items-mode) |
rejected — manage via the DataTables API |
To get the field IDs and option IDs for a list, use GET /lists/{listId}/schema.
Task subresources
| Resource | Page |
|---|---|
| Comments | /api/comments/ |
| Time entries | /api/time-entries/ |
| Attachments | /api/attachments/ |