Prefer natural-language task management? Install the Udle CLI and agent skill.
Authentication
Create a personal access token in Settings → Account & billing → API access. The token is shown once. Send it with every request:
Authorization: Bearer udle_pat_...
Base URL
https://udle.app/api/v1
Endpoints
/profiles/profiles/:profileId/todos/profiles/:profileId/todos/profiles/:profileId/todos/:todoId/profiles/:profileId/todos/:todoId/profiles/:profileId/changes?afterRevision=NStart by listing profiles
curl -sS \
-H "Authorization: Bearer $UDLE_TOKEN" \
https://udle.app/api/v1/profiles
Use a returned profile id to fetch its todos. Include response headers so you retain the current ETag:
curl -i \
-H "Authorization: Bearer $UDLE_TOKEN" \
https://udle.app/api/v1/profiles/PROFILE_ID/todos
List a perspective
Every todo-list response includes the built-in and saved custom perspectives. Filter by a stable perspective ID or an unambiguous name. For calendar-day views such as Today, send the caller's JavaScript-style UTC offset in minutes.
curl -H "Authorization: Bearer $UDLE_TOKEN" \
'https://udle.app/api/v1/profiles/PROFILE_ID/todos?perspective=Today&timezoneOffset=-120'
Built-in IDs are all, inbox, today, available, projects, flagged, and review. Saved perspectives can be selected by ID or name; duplicate names must use an ID.
Create a todo
Every mutation requires the latest known todo-list ETag in If-Match.
curl -X POST \
-H "Authorization: Bearer $UDLE_TOKEN" \
-H "Content-Type: application/json" \
-H 'If-Match: W/"42"' \
-d '{"title":"Book dentist","tags":["health"],"dueAt":"2026-08-12T09:00:00Z"}' \
https://udle.app/api/v1/profiles/PROFILE_ID/todos
Update or complete a todo
curl -X PATCH \
-H "Authorization: Bearer $UDLE_TOKEN" \
-H "Content-Type: application/json" \
-H 'If-Match: W/"43"' \
-d '{"completed":true}' \
https://udle.app/api/v1/profiles/PROFILE_ID/todos/TODO_ID
Writable fields
| Field | Type | Notes |
|---|---|---|
title | string | Required when creating a todo. |
notes | string or null | Plain-text notes. |
tags | string[] | Up to 100 tags. |
dueAt | ISO 8601 or null | Due date and time. |
deferUntil | ISO 8601 or null | Hide until this date and time. |
effectiveDueAt | read-only, ISO 8601 or null | Due date after inheriting from the nearest ancestor that has one; the todo's own dueAt always wins. dueAt stays the raw own value. |
effectiveDeferUntil | read-only, ISO 8601 or null | Defer date after ancestor inheritance; the todo's own deferUntil always wins. deferUntil stays the raw own value. |
completed | boolean | Complete or reopen the todo. |
flagged | boolean | Set the flagged state. |
parentId | string or null | Move below another todo or back to the root. |
position | nonnegative integer | Position among siblings. Creation inserts at that index (clamped to the end); an update re-inserts after removing the todo from its old slot. |
containerMode | "sequential" | "parallel" | "singleActionList" | Project type; projects only, otherwise 422 not_project. Sequential projects make each child wait for the previous one. |
Project context
Each todo includes isProject, projectId, projectName, projectPath, and read-only containerMode (the project's type, or null for plain todos). Use the stable project ID for mutations and show the full path when project names are duplicated.
Concurrent changes
Udle uses sparse synchronization. A stale ETag can still succeed when another client changed a different todo or a different field. If both clients changed the same field or structural relationship, the API returns 409 sync_conflict with the conflicting paths. Reload the todo list and decide which value to keep.
Incremental synchronization
Poll /changes?afterRevision=N to retrieve sparse operations after a known revision. Change cursors are retained for 1,000 sparse revisions. If requiresFullSnapshot is true, reload the complete todo list before continuing.
Errors
Errors use a consistent JSON shape:
{
"error": {
"code": "sync_conflict",
"message": "The same todo fields changed elsewhere.",
"fields": ["tasks/{todo-id}/completedAt"]
}
}
Common statuses are 401 for an invalid token, 404 for a missing profile or todo, 422 for invalid input or a missing ETag, and 409 for a synchronization conflict or expired revision.