← udle.app
Udle developer documentation

Todo API

Read and manage todos from agents, scripts, and integrations while Udle safely merges concurrent changes.

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_...
A token can read and modify todos in every profile in your account. Treat it like a password and revoke it immediately if it is exposed.

Base URL

https://udle.app/api/v1

Endpoints

GET/profiles
GET/profiles/:profileId/todos
POST/profiles/:profileId/todos
PATCH/profiles/:profileId/todos/:todoId
DELETE/profiles/:profileId/todos/:todoId
GET/profiles/:profileId/changes?afterRevision=N

Start 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

FieldTypeNotes
titlestringRequired when creating a todo.
notesstring or nullPlain-text notes.
tagsstring[]Up to 100 tags.
dueAtISO 8601 or nullDue date and time.
deferUntilISO 8601 or nullHide until this date and time.
effectiveDueAtread-only, ISO 8601 or nullDue date after inheriting from the nearest ancestor that has one; the todo's own dueAt always wins. dueAt stays the raw own value.
effectiveDeferUntilread-only, ISO 8601 or nullDefer date after ancestor inheritance; the todo's own deferUntil always wins. deferUntil stays the raw own value.
completedbooleanComplete or reopen the todo.
flaggedbooleanSet the flagged state.
parentIdstring or nullMove below another todo or back to the root.
positionnonnegative integerPosition 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.