Examples
Task Operations
Create a task
# Quick add (AI enrichment)
curl -X POST https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "buy groceries tomorrow afternoon"}'
# Structured (skip AI)
curl -X POST https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Buy groceries", "target_at": "2026-03-02T17:00:00Z", "priority": 1}'
# With a real deadline and a pace, no target (it gets an automatic target 3 or 4 days before the deadline)
curl -X POST https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Renew passport", "deadline_at": "2026-04-30T22:00:00Z", "horizon": "weeks"}'List tasks
# All open items (tasks, reminders, and quotas)
curl https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN"
# Filter by project
curl "https://tasks.example.com/api/tasks?project=1" \
-H "Authorization: Bearer $TOKEN"
# Only one kind: task, reminder, or quota
curl "https://tasks.example.com/api/tasks?kind=reminder" \
-H "Authorization: Bearer $TOKEN"
# Search
curl "https://tasks.example.com/api/tasks?search=dentist" \
-H "Authorization: Bearer $TOKEN"
# Tasks over target right now (the same set as the app's badge)
curl "https://tasks.example.com/api/tasks?overdue=true" \
-H "Authorization: Bearer $TOKEN"The response is { "data": { "tasks": [...], "count": N } }. The project filter parameter is project, not project_id.
overdue=true returns the tasks that are over target right now. Reminders and quotas are never over target, so they never appear in this list.
Update a task
curl -X PATCH https://tasks.example.com/api/tasks/42 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"priority": 3, "labels": ["errands", "work"], "create_label": true}'create_label: true creates any label that doesn't exist yet. Without it, an unknown label returns 400.
Move or remove a target
A plain target_at change moves the target. It's the same as a snooze: the task keeps its first target (and its age), and the move stops at a deadline that's still ahead (capped_at_deadline: true in the response when it did).
curl -X PATCH https://tasks.example.com/api/tasks/42 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"target_at": "2026-03-05T15:00:00Z"}'To move a reminder to a new time, add "retarget": true: a plain target_at change on a reminder is refused because reminders can't be snoozed. On a task, retarget is accepted and changes nothing.
To remove a target and send the task back to its task segment, send "target_at": null. On a one-off task with a deadline this is Back to auto: the deadline's automatic target is set again (target_auto: true), computed from now.
Snooze a task
curl -X POST https://tasks.example.com/api/tasks/42/snooze \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"until": "2026-03-02T14:00:00Z"}'Snoozing a reminder returns 400. A snooze past a deadline that's still ahead stops at the deadline; the response's capped_at_deadline is true when it did. Once the deadline has passed, any time goes.
Mark done
curl -X POST https://tasks.example.com/api/tasks/42/done \
-H "Authorization: Bearer $TOKEN"For a recurring task, this advances it to the next occurrence.
Reminders
Create a reminder
curl -X POST https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Stretch", "is_reminder": true, "rrule": "FREQ=DAILY;BYHOUR=9;BYMINUTE=0"}'A reminder lands in the reminder period that contains its time of day. With the default periods, 9:00 is Morning.
Leave out rrule and the reminder goes in your default period, daily at its start; with AI enabled, a time stated in the title can move it. Send "rrule": null for a one-time reminder with no time of day.
List reminder periods
curl https://tasks.example.com/api/time-slots \
-H "Authorization: Bearer $TOKEN"Returns { "data": { "time_slots": [...], "count": N } }, earliest first. Each period has an id, a label (e.g. "Morning"), and a start_time in 24-hour local time (e.g. "09:00").
Quotas
Create a quota
curl -X POST https://tasks.example.com/api/tasks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Exercise", "progress_target": 3, "rrule": "FREQ=WEEKLY"}'A quota counts toward progress_target within each period (FREQ=DAILY, WEEKLY, MONTHLY, or YEARLY). Don't send a target_at (or due_at): quotas have no target and the request is rejected if you include one.
Log progress
# +1 (an empty body is fine)
curl -X POST https://tasks.example.com/api/tasks/42/progress \
-H "Authorization: Bearer $TOKEN"
# Correct a mistake
curl -X POST https://tasks.example.com/api/tasks/42/progress \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"delta": -1}'delta is an integer from -100 to 100 (default 1). The response is the updated task plus met (true when this change reached the target). Reaching the target doesn't close the quota. It stays open, and further progress counts past the target, until the period ends and the count resets.
Close a quota's period early
POST /api/tasks/{id}/done on a quota returns 400, because it would end the period and reset the count to 0. If that is what you want, say so explicitly:
curl -X POST https://tasks.example.com/api/tasks/42/done \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"close_period": true}'POST /api/tasks/bulk/done skips quotas unless the body also includes "close_period": true.
Bulk Operations
Bulk snooze all over target
# Snooze all over-target tasks (uses the user's default snooze duration)
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze-overdue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
# Snooze to a specific time
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze-overdue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"until": "2026-03-02T17:00:00Z"}'
# Snooze to the start of the next reminder period
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze-overdue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"slot": "next"}'
# Snooze to a specific period, by its start_time (today if it's still ahead, otherwise tomorrow)
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze-overdue \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"slot": "16:00"}'The body takes at most one of until, delta_minutes (counted from now; 60 or more is rounded to the hour), slot, and "tomorrow": true (tomorrow at your morning time). Which tasks move depends on priority:
- None, Low, and Medium tasks always move.
- High tasks, and tasks whose deadline has passed, move only when nothing lower is left over target. A second call moves them. Those held back by a passed deadline are counted in
skipped_deadline. - Urgent tasks never move, unless you list them in
include_task_ids(up to 10). - Reminders and quotas are never moved.
- A task the snooze would carry past a deadline that's still ahead stops at the deadline (
capped_deadline).
The response reports tasks_affected, skipped_urgent (High and Urgent skipped on priority), skipped_high (the High part of that), snoozed_high, and skipped_reminders, plus until (the resolved target) and message, a ready-to-display summary such as "Snoozed 3 tasks (2 high, 1 urgent skipped)". An Apple Shortcut can show message as is. When High or Urgent tasks were left, the server also sends a Bulk snooze results notification (feedback_notification: true in the response); send "notify": false if your Shortcut shows message instead.
Bulk snooze specific tasks
# Everything to one time
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": [1, 2, 3], "until": "2026-03-02T17:00:00Z"}'
# One hour later than each task's own due time, including a High or Urgent task you picked
curl -X POST https://tasks.example.com/api/tasks/bulk/snooze \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": [1, 2, 3], "delta_minutes": 60, "include_task_ids": [3]}'Send exactly one of until or delta_minutes. Here delta_minutes is added to each task's current target, and tasks without a target are skipped. The same priority rules apply as above, and include_task_ids exempts the listed tasks from them.
Bulk mark done
curl -X POST https://tasks.example.com/api/tasks/bulk/done \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ids": [1, 2, 3]}'Undo
# Undo last action
curl -X POST https://tasks.example.com/api/undo \
-H "Authorization: Bearer $TOKEN"
# Redo
curl -X POST https://tasks.example.com/api/redo \
-H "Authorization: Bearer $TOKEN"Data Export
# Full JSON export
curl https://tasks.example.com/api/export?format=json \
-H "Authorization: Bearer $TOKEN" > backup.json
# CSV export (tasks)
curl "https://tasks.example.com/api/export?format=csv&type=tasks" \
-H "Authorization: Bearer $TOKEN" > tasks.csv