Tasks
Tasks and task lists, and the counts behind the Tasks sidebar.
On this page
- List task lists
- Add a task list
- Get a task list
- Change or move a task list
- Delete a task list
- Restore a task list
- List tasks
- Add a task
- Get a task
- Change a task
- Delete a task
- Restore a task
- Get a task's date history
- Work on a task now
- Stop working on a task
- Get the task that's Now
- Get the task settings
- Change the task settings
- Task counts per list
- Task counts per project
List task lists
GET/task_lists
Task lists, in the owner's order.
curl "$NIFTY_URL/api/v1/task_lists" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Query parameters
| Name | Description |
|---|---|
orderposition | id | name | updated_at · optional | The field to sort by; ties are broken by id, so the order is stable. |
directionasc | desc · optional | asc (ascending) or desc (descending), for order. |
updated_sincedate-time · optional | Only records updated at or after this time (ISO 8601 with Z or a UTC offset), ordered by updated_at, id. |
cursorstring · optional | meta.next_cursor from the previous page. |
limitinteger · optional | Items per page, up to 100; a larger number is taken as 100. |
counttrue | false · optional | true adds meta.total_count: the size of the filtered set (with updated_since too), ignoring cursor and limit. Anything but true or false is a 400. |
Response
200 OK Errors: 400 401 429
{
"data": [
{
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Garden",
"color": "emerald",
"icon": "leaf",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
],
"meta": {
"next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
"limit": 1,
"total_count": 1
}
}
Add a task list
POST/task_lists
Needs a name. Goes last unless position is sent. A blank color gets the first one no other list uses
(colours and icons are those of GET /category_choices).
curl -X POST "$NIFTY_URL/api/v1/task_lists" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task_list":{"name":"Garden","color":"emerald","icon":"leaf"}}'
Headers
| Name | Description |
|---|---|
Idempotency-Keystring · optional | Makes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth). |
Request body
| Name | Description |
|---|---|
task_listobject | |
task_list.namestring · optional | Spaces are squished. A name another list has, ignoring case, is a 422. |
task_list.colorstring or null · optional | A key from GET /category_choices. Blank picks the first one no other list uses. |
task_list.iconstring or null · optional | An icon from GET /category_choices, or null for the default list icon. |
task_list.positioninteger · optional | 1-based; out-of-range values are clamped. Moves the list there. |
Response
201 Created Errors: 400 401 403 409 415 422 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Garden",
"color": "emerald",
"icon": "leaf",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Get a task list
GET/task_lists/{id}
curl "$NIFTY_URL/api/v1/task_lists/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
200 OK Errors: 401 404 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Garden",
"color": "emerald",
"icon": "leaf",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Change or move a task list
PATCH/task_lists/{id}
Only the fields sent change. Sending position moves the list there and renumbers the live lists 1..n; the
others whose position changed get a new updated_at, so re-sync with updated_since after a move. Tasks
hold only task_list_id, so renaming or recolouring a list changes no task.
curl -X PATCH "$NIFTY_URL/api/v1/task_lists/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task_list":{"name":"Garden","color":"emerald","icon":"leaf"}}'
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Request body
| Name | Description |
|---|---|
task_listobject | |
task_list.namestring · optional | Spaces are squished. A name another list has, ignoring case, is a 422. |
task_list.colorstring or null · optional | A key from GET /category_choices. Blank picks the first one no other list uses. |
task_list.iconstring or null · optional | An icon from GET /category_choices, or null for the default list icon. |
task_list.positioninteger · optional | 1-based; out-of-range values are clamped. Moves the list there. |
Response
200 OK Errors: 400 401 403 404 415 422 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Garden",
"color": "emerald",
"icon": "leaf",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Delete a task list
DELETE/task_lists/{id}
Delete a task list and its tasks.
Restorable for 10 seconds (POST /task_lists/{id}/restore), then permanent. The list and its tasks are
gone from every endpoint at once, with deletion records for the list and each of its tasks. Its name is
free for a new list at once. Deleting again is a 404.
curl -X DELETE "$NIFTY_URL/api/v1/task_lists/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
204 Deleted Errors: 401 403 404 429
Restore a task list
POST/task_lists/{id}/restore
Undo deleting a task list.
Within 10 seconds of DELETE /task_lists/{id}: brings the list back with its tasks. Their deletion records are
removed, and they get a new updated_at. A task deleted on its own before the list stays deleted. If another
list took its name meanwhile, it comes back renamed "Name (2)", so check name. No body. Restoring a list
that isn't deleted changes nothing and returns it, so a retry is safe. Once the 10 seconds have passed, or for an
unknown id, it's a 404. If other lists take the name it picks twice in a row, it's a 422 on name; try again.
curl -X POST "$NIFTY_URL/api/v1/task_lists/01j9zq3k8m5x2v7c4n6b0t1r9e/restore" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Headers
| Name | Description |
|---|---|
Idempotency-Keystring · optional | Makes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth). |
Response
200 Restored. The list, as GET /task_lists/{id} returns it. Errors: 400 401 403 404 409 415 422 429
{
"data": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"name": "Garden",
"color": "emerald",
"icon": "leaf",
"position": 1,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
List tasks
GET/tasks
Live tasks: not deleted, and not in a deleted list or project. Filters combine (AND); they're for
browsing, so sync unfiltered. Without view or status, every status is listed. Before answering, open tasks
whose expires_on has passed are archived (as every task endpoint does), so none is listed as open.
curl "$NIFTY_URL/api/v1/tasks" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Query parameters
| Name | Description |
|---|---|
statusstring · optional | Comma-separated statuses, e.g. open,completed. Anything else, or with view, is 400. |
viewinbox | today | upcoming | due_soon | due_not_today | today_queue | delegated | logbook · optional | A smart list, with today in the owner's time zone. Each lists only open tasks, except logbook. Combines
with one home filter (inbox, task_list_id or project_id). Every view but delegated and logbook
leaves delegated tasks (assignee_id set) out; inbox=true leaves them out of every view, so
view=delegated&inbox=true is always empty.
- inbox: in the Inbox with no start date.
- today: started today or earlier (start_on ≤ today), from every home, the Inbox too.
- upcoming: starting after today, from every home, the Inbox too. Ordered by start_on by default.
- due_soon: overdue, or due within 14 days, from every home. Ordered by deadline_on by default.
- due_not_today: due_soon without today's (no start date, or starting after today). Ordered by deadline_on by default.
- today_queue: Today's queue: started (start_on ≤ today), or with no start and a deadline, however far off; one that starts after today isn't in it, even overdue. Ordered by deadline (none last) by default; Today groups them itself.
- delegated: delegated to someone, from every home, the Inbox too.
- logbook: completed and archived tasks, by when they closed (completed_at or archived_at, whichever is set; ordered as closed_at), newest first by default.
|
inboxtrue · optional | true: only the Inbox (tasks with no home), without its delegated tasks. Anything else is 400. |
assignee_idULID · optional | Only the tasks delegated to this person. Not a home filter, so it combines with one. An unknown or deleted person is an empty page. |
task_list_idULID · optional | Only this list's tasks. An unknown or deleted list is an empty page. At most one home filter (inbox, task_list_id, project_id); two is 400. A home filter combines with view (e.g. view=logbook&task_list_id=…, one list's Logbook). |
person_idULID · optional | People aren't homes any more, so this is always an empty page. Counts as a home filter. |
project_idULID · optional | Only the tasks whose home is this project. |
note_idULID · optional | Only the tasks attached to this note (its inspector's Tasks). Not a home filter, so it combines with one. An unknown or deleted note is an empty page. With document_id is 400. |
document_idULID · optional | Only the tasks this document is linked to (its inspector's Tasks). Not a home filter. An unknown or deleted document is an empty page. With note_id is 400. |
orderposition | created_at | updated_at | id | start_on | deadline_on | closed_at | completed_at · optional | position (the default: the home's manual order; ties, e.g. across homes, by id), created_at,
updated_at or id. view=upcoming adds start_on (its default), view=due_soon, view=due_not_today and
view=today_queue deadline_on (their default; today_queue puts tasks with none last) and view=logbook closed_at (its default: completed_at or archived_at, whichever is set; not
a field of its own). status=completed (alone) adds completed_at,
e.g. the latest completed with direction=desc. Anything else is 400.
|
directionasc | desc · optional | asc by default; desc for view=logbook. |
updated_sincedate-time · optional | Only records updated at or after this time (ISO 8601 with Z or a UTC offset), ordered by updated_at, id. |
cursorstring · optional | meta.next_cursor from the previous page. |
limitinteger · optional | Items per page, up to 100; a larger number is taken as 100. |
counttrue | false · optional | true adds meta.total_count: the size of the filtered set (with updated_since too), ignoring cursor and limit. Anything but true or false is a 400. |
Response
200 OK Errors: 400 401 429
{
"data": [
{
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
],
"meta": {
"next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
"limit": 1,
"total_count": 1
}
}
Add a task
POST/tasks
Needs a title. A new task is open unless status is sent, in the Inbox unless a home id is sent, and goes to
the top of its home's open tasks unless position is sent (a closed one, to the bottom). Going to the top
changes no other task: its position is one less than the first's, so it may be zero or negative.
curl -X POST "$NIFTY_URL/api/v1/tasks" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task":{"title":"Book the plumber","deadline_on":"2026-10-16","project_id":"01j9zq3p4q6r8s0t2v5w7x9y1z"}}'
Headers
| Name | Description |
|---|---|
Idempotency-Keystring · optional | Makes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth). |
Request body
| Name | Description |
|---|---|
taskTaskWrite | A home id that isn't an id, an unknown or deleted home, or two home ids at once (a 422 on base), are 422s.
People aren't homes: person_id is ignored.
|
task.titlestring · optional | Spaces are squished. |
task.description_htmlstring or null · optional | See "Note bodies": mentions, tags and project references by id. An image (signed-id) is a 422 on description. Null or "" clears it. |
task.start_ondate or null · optional | YYYY-MM-DD. Today (owner's zone) or earlier puts it in view=today. One that isn't a date is a 422. A later start, or clearing it, counts in delays. |
task.deadline_ondate or null · optional | YYYY-MM-DD. It may be before start_on: the task will be late. A later deadline, or clearing it, counts in deadline_moves. |
task.expires_ondate or null · optional | Not before start_on or deadline_on, nor, on an open task, before today (each a 422). The task stays
open through that day and is archived from the next, with archived_at that midnight in the owner's zone.
|
task.statusTaskStatus · optional | Any status can change to any other. completed sets completed_at, archived sets archived_at, open
clears both, and reopening a task whose expires_on has passed clears it. Sending the current status
changes nothing.
|
task.task_list_idULID or null · optional | The home. Sending one home id clears the other; both null is the Inbox. |
task.project_idULID or null · optional | A record's ID. |
task.assignee_idULID or null · optional | Delegates it to this person, or null to take it back. Its home stays as it is. Me, or an unknown or deleted
person, is a 422 on assignee_id; one that isn't an id is a 422 too.
|
task.positioninteger · optional | 1-based among the (new) home's open tasks, its custom order; out-of-range values are clamped. Without one, a new open task goes to the top of its home, and a task moved to another home to the bottom. |
task.before_task_idULID or null · optional | Instead of position (both is a 422): just before this open task in its (new) home, or null for the
bottom, among every open task the home orders. One that isn't an open task there (or the task itself) is
a 422.
|
task.document_idsULID[] or null · optional | The task's documents after the save: the full set, linked and unlinked in the save's transaction. Omitted
leaves them as they are; null or [] unlinks them all. An unknown or deleted one is a 422 on documents,
and nothing saves. Not an array of ids is a 400.
|
task.document_uploadsstring[] or null · optional | signed_ids of PDFs or images from POST /uploads, each becoming a document (named from its filename)
linked to the task, in the save. One that isn't an unused PDF upload or PNG, JPEG or WebP image upload, or
an image under POST /documents' minimum size, is a 422 on documents, and nothing saves.
|
task.note_idsULID[] or null · optional | The notes it's attached to after the save: the full set, attached and detached in the save's transaction.
Omitted leaves them as they are; null or [] detaches them all (a deleted note's link stays, for its
undo). An unknown or deleted one is a 422 on notes, and nothing saves. Not an array of ids is a 400.
|
Response
201 Created Errors: 400 401 403 409 415 422 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Get a task
GET/tasks/{id}
A deleted task, or one in a deleted list or project, is a 404.
curl "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
200 OK Errors: 401 404 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Change a task
PATCH/tasks/{id}
Change, move, complete or reopen a task.
Only the fields sent change. Moving it to another home puts it at the bottom there unless position is
sent too. Sending position moves it among its home's open tasks and renumbers them 1..n; the others
whose position changed get a new updated_at, so re-sync with updated_since after a move.
curl -X PATCH "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task":{"title":"Book the plumber","deadline_on":"2026-10-16","project_id":"01j9zq3p4q6r8s0t2v5w7x9y1z"}}'
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Request body
| Name | Description |
|---|---|
taskTaskWrite | A home id that isn't an id, an unknown or deleted home, or two home ids at once (a 422 on base), are 422s.
People aren't homes: person_id is ignored.
|
task.titlestring · optional | Spaces are squished. |
task.description_htmlstring or null · optional | See "Note bodies": mentions, tags and project references by id. An image (signed-id) is a 422 on description. Null or "" clears it. |
task.start_ondate or null · optional | YYYY-MM-DD. Today (owner's zone) or earlier puts it in view=today. One that isn't a date is a 422. A later start, or clearing it, counts in delays. |
task.deadline_ondate or null · optional | YYYY-MM-DD. It may be before start_on: the task will be late. A later deadline, or clearing it, counts in deadline_moves. |
task.expires_ondate or null · optional | Not before start_on or deadline_on, nor, on an open task, before today (each a 422). The task stays
open through that day and is archived from the next, with archived_at that midnight in the owner's zone.
|
task.statusTaskStatus · optional | Any status can change to any other. completed sets completed_at, archived sets archived_at, open
clears both, and reopening a task whose expires_on has passed clears it. Sending the current status
changes nothing.
|
task.task_list_idULID or null · optional | The home. Sending one home id clears the other; both null is the Inbox. |
task.project_idULID or null · optional | A record's ID. |
task.assignee_idULID or null · optional | Delegates it to this person, or null to take it back. Its home stays as it is. Me, or an unknown or deleted
person, is a 422 on assignee_id; one that isn't an id is a 422 too.
|
task.positioninteger · optional | 1-based among the (new) home's open tasks, its custom order; out-of-range values are clamped. Without one, a new open task goes to the top of its home, and a task moved to another home to the bottom. |
task.before_task_idULID or null · optional | Instead of position (both is a 422): just before this open task in its (new) home, or null for the
bottom, among every open task the home orders. One that isn't an open task there (or the task itself) is
a 422.
|
task.document_idsULID[] or null · optional | The task's documents after the save: the full set, linked and unlinked in the save's transaction. Omitted
leaves them as they are; null or [] unlinks them all. An unknown or deleted one is a 422 on documents,
and nothing saves. Not an array of ids is a 400.
|
task.document_uploadsstring[] or null · optional | signed_ids of PDFs or images from POST /uploads, each becoming a document (named from its filename)
linked to the task, in the save. One that isn't an unused PDF upload or PNG, JPEG or WebP image upload, or
an image under POST /documents' minimum size, is a 422 on documents, and nothing saves.
|
task.note_idsULID[] or null · optional | The notes it's attached to after the save: the full set, attached and detached in the save's transaction.
Omitted leaves them as they are; null or [] detaches them all (a deleted note's link stays, for its
undo). An unknown or deleted one is a 422 on notes, and nothing saves. Not an array of ids is a 400.
|
Response
200 OK Errors: 400 401 403 404 415 422 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Delete a task
DELETE/tasks/{id}
Restorable for 10 seconds (POST /tasks/{id}/restore), then permanent. Writes a task deletion record at once. Deleting again is a 404.
curl -X DELETE "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
204 Deleted Errors: 401 403 404 429
Restore a task
POST/tasks/{id}/restore
Undo deleting a task.
Within 10 seconds of DELETE /tasks/{id}: brings the task back, removes its deletion record and gives it a new
updated_at. No body. Restoring a task that isn't deleted changes nothing and returns it, so a retry is safe.
Once the 10 seconds have passed, for an unknown id, or while its list, person or project is deleted (restore that
instead), it's a 404. If its expiry passed meanwhile, it comes back archived.
curl -X POST "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e/restore" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Headers
| Name | Description |
|---|---|
Idempotency-Keystring · optional | Makes a POST safe to retry for 24 hours. Needs a bearer token (400 under session auth). |
Response
200 Restored. The task, as GET /tasks/{id} returns it. Errors: 400 401 403 404 409 415 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Get a task's date history
GET/tasks/{id}/history
The changes to a task's start and deadline, newest first, and how often it's been worked on as Now.
A change within ten minutes of the task's creation isn't kept. One within ten minutes of an entry's first time
merges into that entry, which keeps its from; one that undoes it removes it. Not synced: read it when it's
shown. A deleted task is a 404.
curl "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e/history" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Query parameters
| Name | Description |
|---|---|
cursorstring · optional | meta.next_cursor from the previous page. |
limitinteger · optional | Items per page, up to 100; a larger number is taken as 100. |
Response
200 OK Errors: 400 401 404 429
{
"data": [
{
"id": 1,
"at": "2026-10-10T09:30:00Z",
"start_changed": true,
"start_from": "2026-10-10",
"start_to": "2026-10-10",
"deadline_changed": true,
"deadline_from": "2026-10-10",
"deadline_to": "2026-10-10",
"delay": true,
"deadline_moved": true
}
],
"meta": {
"next_cursor": "eyJrIjpbIjIwMjYtMTAtMDlUMDg6MzA6MDBaIl19",
"limit": 1,
"total_count": 1,
"sessions": 0,
"first_session_at": "2026-10-10T09:30:00Z"
}
}
Work on a task now
PUT/tasks/{id}/now
Makes the task Now, ending any other task's session; both get a new updated_at. A task that starts after
today has its start_on moved to today (an earlier start, so not a delay; task.start_changed fires). No
body. Starting the task that's already Now changes nothing and returns it. A closed or delegated task is a 422
on base; a deleted one is a 404.
curl -X PUT "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e/now" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
200 Started. The task, as GET /tasks/{id} returns it. Errors: 401 403 404 415 422 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Stop working on a task
DELETE/tasks/{id}/now
Ends the task's session, so nothing is Now; it gets a new updated_at. Stopping a task that isn't Now
changes nothing and returns it. Completing, closing, deleting, delegating or delaying the task, deleting its
home, its expiry and the stop times (GET /task_settings) end it too. A deleted task is a 404.
curl -X DELETE "$NIFTY_URL/api/v1/tasks/01j9zq3k8m5x2v7c4n6b0t1r9e/now" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Path parameters
| Name | Description |
|---|---|
idULID | A record's ID. |
Response
200 Stopped. The task, as GET /tasks/{id} returns it. Errors: 401 403 404 429
{
"data": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
}
}
Get the task that's Now
GET/now
The task the owner is working on (PUT /tasks/{id}/now), or null. A session still running at a stop time
(GET /task_settings) ends at that time, when this or any task read next runs. stops_at is when it will
stop by itself: the next stop time or the end of its expiry day, whichever comes first; read it again then.
curl "$NIFTY_URL/api/v1/now" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Response
200 OK Errors: 401 429
{
"data": {
"task": {
"planned_start_on": "2026-10-10",
"delays": 0,
"deadline_moves": 0,
"delegated_at": "2026-10-10T09:30:00Z",
"now_since": "2026-10-10T09:30:00Z",
"received_via_api": {
"at": "2026-10-10T09:30:00Z",
"source": "Zapier"
},
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "Book the plumber",
"status": "open",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10",
"expires_on": "2026-10-10",
"completed_at": "2026-10-10T09:30:00Z",
"archived_at": "2026-10-10T09:30:00Z",
"task_list_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"assignee_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"position": 1,
"description_html": "<p>Ask about the boiler too.</p>",
"description_text": "Ask about the boiler too.",
"has_documents": true,
"document_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"note_ids": [
"01j9zq3k8m5x2v7c4n6b0t1r9e"
],
"expired": true,
"created_at": "2026-10-10T09:30:00Z",
"updated_at": "2026-10-10T09:30:00Z"
},
"since": "2026-10-10T09:30:00Z",
"stops_at": "2026-10-10T09:30:00Z"
}
}
Get the task settings
GET/task_settings
The times of day at which Now stops by itself. One resource; there's no collection.
curl "$NIFTY_URL/api/v1/task_settings" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Response
200 OK Errors: 401 429
{
"data": {
"now_stop_times": [
"18:00"
]
}
}
Change the task settings
PATCH/task_settings
Only the fields sent change. now_stop_times replaces the list: a time that's added applies from now, so
it never ends a session at a moment before it was added.
curl -X PATCH "$NIFTY_URL/api/v1/task_settings" \
-H "Authorization: Bearer $NIFTY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"task_settings":{"now_stop_times":["08:00","18:00"]}}'
Request body
| Name | Description |
|---|---|
task_settingsobject | |
task_settings.now_stop_timesNowStopTimes · optional | Times of day (HH:MM, the owner's time zone) at which Now stops by itself, earliest first. No duplicates (a 422). |
Response
200 OK Errors: 400 401 403 415 422 429
{
"data": {
"now_stop_times": [
"18:00"
]
}
}
Task counts per list
GET/task_counts
How many open tasks are in the Inbox, Today, due soon, overdue and each list; and one page's counts.
Live, open tasks, with today in the owner's time zone; each count except overdue matches its GET /tasks
listing. Expired tasks are archived first. Not a record: there's nothing to sync, so ask again when tasks
change.
Name a page with view, or one home (inbox, task_list_id or project_id), to get its
counts in page. view=logbook may also name one home. An unknown or hidden home counts zeros.
curl "$NIFTY_URL/api/v1/task_counts" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Query parameters
| Name | Description |
|---|---|
viewinbox | today | upcoming | logbook · optional | A Tasks page. Anything else, or any but logbook with a home, is 400. |
inboxtrue · optional | true: the Inbox's page. Anything else is 400. At most one home; two is 400. |
task_list_idULID · optional | A list's page. Not an id is 400. |
person_idULID · optional | People aren't homes any more, so this counts zeros. |
project_idULID · optional | A project's Tasks. |
Response
200 OK Errors: 400 401 429
{
"data": {
"inbox": 1,
"today": 1,
"today_queue": 1,
"due_soon": 1,
"overdue": 1,
"delegated": 1,
"task_lists": [
{
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"open": 1
}
],
"page": {
"open": 1,
"completed": 1,
"completed_today": 1,
"overdue": 1,
"wont_do": 1,
"expired": 1
}
}
}
Task counts per project
GET/project_task_counts
Each project's open and completed tasks, and its next task.
Live tasks of live projects, by project_id; a project with none is left out (archived tasks aren't
counted). next_task is the open task to do next: started ones (no start_on, or today or earlier in the
owner's time zone) first, then by deadline_on (none last), position and id; null when none is open.
Expired tasks are archived first. Not paginated, and not a record: ask again when tasks change.
curl "$NIFTY_URL/api/v1/project_task_counts" \
-H "Authorization: Bearer $NIFTY_TOKEN"
Response
200 OK Errors: 401 429
{
"data": [
{
"project_id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"open": 1,
"completed": 1,
"next_task": {
"id": "01j9zq3k8m5x2v7c4n6b0t1r9e",
"title": "string",
"start_on": "2026-10-10",
"deadline_on": "2026-10-10"
}
}
]
}