API
Version 1
Todoly's API is the API the app itself uses, so everything you can do in the app you can do from a script or an agent. The full description is the OpenAPI document at app.todoly.io/api/v1/openapi.json; this page is the part that is easier read than generated.
Authentication
Create a personal token under Settings → Profile → API tokens. It is shown once. Send it on every request:
Authorization: Bearer tdl_…
A token acts as you in every workspace you belong to, exactly like your sign-in. There are no scopes: revoke a token from the same page when it should stop. You can hold ten at a time.
Base URL and shape
Every path below is under https://app.todoly.io/api. Requests and responses are JSON; dates are ISO 8601, due dates are YYYY-MM-DD, ids are UUIDs, and every record carries created_at and updated_at. Field names are snake_case.
Errors
Every error has one shape and an HTTP status that matches:
{ "error": { "code": "task_limit", "message": "100 of 100 tasks on Free. Deleting tasks frees room." } }
The message is written for a person and can be shown as is. Codes worth handling: unauthenticated (401), not_found (404, also for workspaces you are not in), validation (400), read_only (403, the workspace is over its plan), task_limit, project_limit, seat_limit, trial_required and storage_limit (400, a Free cap), email_unverified (403, inviting needs a confirmed address), bad_cursor (400).
Rate limits
600 requests a minute per token, counted separately from your browser. Every response carries x-ratelimit-limit, x-ratelimit-remaining and x-ratelimit-reset; past the limit you get 429 with retry-after.
Listing tasks
For a whole workspace, use GET /v1/workspaces/{id}/tasks rather than one call per project. Filters: status (a comma list of backlog, todo, in_progress, in_review, done, cancelled), assignee_id (a member id), label_id, project_id, updated_since. Pages: limit up to 500 and the next_cursor from the previous page passed back as cursor; null means the last page. Results are ordered by updated_at, so polling is updated_since set to the time of your last call.
Live updates
GET /v1/workspaces/{id}/events is a server-sent event stream of every change in the workspace: task.updated, task.deleted, project.updated, comment.created and so on, each with the record. Apply them newest updated_at wins. Keep at most a few streams open per token.
Endpoints
The OpenAPI document lists every public route with its request and response schemas. In short:
- You:
GET /v1/me(profile, settings and your workspaces with your role),GET/POST/v1/me/tokens,DELETE /v1/me/tokens/{id}. - Workspaces:
GET/POST/v1/workspaces,GET/PATCH/DELETE/v1/workspaces/{id},…/members,…/invites,…/labels,…/projects,…/tasks,…/my-tasks,…/search?q=,…/notifications,…/export,…/events. - Projects:
GET/PATCH/DELETE/v1/projects/{id},GET /v1/projects/{id}/tasks,POST /v1/projects/{id}/attachments. - Tasks:
POST /v1/tasks,GET/PATCH/DELETE/v1/tasks/{id},…/comments,…/attachments. - Attachments:
GET /v1/attachments/{id}/download,DELETE /v1/attachments/{id}.
Not part of the API and free to change: sign-in, configuration, billing, email settings and the email-change flow.
Examples
curl https://app.todoly.io/api/v1/me \
-H "Authorization: Bearer $TODOLY_TOKEN"
curl "https://app.todoly.io/api/v1/workspaces/$WS/tasks?status=todo,in_progress&limit=200" \
-H "Authorization: Bearer $TODOLY_TOKEN"
curl https://app.todoly.io/api/v1/tasks \
-H "Authorization: Bearer $TODOLY_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "project_id": "'$PROJECT'", "title": "Renew the domain", "due_date": "2026-11-01", "priority": "high" }'
Versions
Additive changes (new fields, new endpoints) land in v1 without notice; code that ignores unknown fields keeps working. A breaking change would be a /v2, with v1 kept for six months after. Changes are noted on this page.