Skip to content

Local API Server

Mindwtr includes an optional local REST API server for scripting and integrations. On desktop it runs inside the app binary and uses the same local storage paths as the app. The repository also includes a Bun helper for development and advanced scripting.


Desktop Toggle

Desktop builds can start the local REST API without running source code:

  • Open Settings -> Advanced.
  • Enable Local API server.
  • Keep the default port 3456 or choose another localhost port.
  • Copy the generated bearer token from the same settings card.

The app binds to 127.0.0.1 only and requires Authorization: Bearer <token> on every request. Mobile binaries do not expose a local REST API surface.

Development Helper

The repo helper is still available when you want to run the API outside the desktop app or point it at explicit files.


Quick Start

From the desktop app:

text
Settings -> Advanced -> Enable local API server

Default URL:

text
http://127.0.0.1:3456

From the repo root with Bun:

bash
bun install
MINDWTR_API_TOKEN=replace-with-a-strong-token bun run mindwtr:api -- --port 4317 --host 127.0.0.1

Options

OptionDefaultDescription
--port <n>4317Server port
--host <host>127.0.0.1Bind address
--data <path>Platform defaultOverride data.json location
--db <path>Platform defaultOverride mindwtr.db location
--dangerously-disable-authoffRun with no token at all — unsafe, and not browser-safe even on loopback

Environment Variables

VariableDescription
MINDWTR_DATAOverride data.json location (if --data is omitted)
MINDWTR_DB_PATHOverride mindwtr.db location (if --db is omitted)
MINDWTR_API_TOKENRequired. Bearer token the helper demands on every request; it refuses to start without one unless --dangerously-disable-auth is passed
MINDWTR_API_CORS_ORIGINOptional. One exact http(s) origin allowed to call the API; no CORS headers are sent when unset

By default, the API resolves both data.json and mindwtr.db using Mindwtr's platform paths (preferring XDG data on Linux).


Authentication

Desktop Local API requests always require the bearer token shown in Settings -> Advanced:

Authorization: Bearer <token>

The Bun helper requires a token too: it exits immediately unless MINDWTR_API_TOKEN is set, or you explicitly pass --dangerously-disable-auth (intended only for isolated compatibility testing).


Endpoints

MethodEndpointDescription
GET/healthHealth check → { ok: true }
GET/tasksList tasks
GET/tasks?status=nextFilter by status
GET/tasks?query=@workSearch tasks
GET/tasks?isFocusedToday=trueDesktop: filter Focus Today
GET/tasks?all=1Include done/archived
GET/tasks?deleted=1Include soft-deleted
POST/tasksCreate task
GET/tasks/:idGet single task
PATCH/tasks/:idUpdate task
DELETE/tasks/:idSoft delete task
POST/tasks/:id/completeMark as done
POST/tasks/:id/archiveMark as archived
POST/tasks/:id/restoreRestore a soft-deleted task
GET/projectsList projects
POST/projectsDesktop: create project
GET/projects/:idDesktop: get one project
PATCH/projects/:idDesktop: update project
DELETE/projects/:idDesktop: soft-delete project
POST/projects/:id/restoreDesktop: restore project
GET/areasList areas
GET/v1/areasCompatibility alias for areas
GET/sectionsHelper: list sections, optionally ?projectId=
POST/sectionsHelper: create section
GET/sections/:idHelper: get single section
PATCH/sections/:idHelper: update section
DELETE/sections/:idHelper: delete section
GET/search?query=...Search tasks + projects

Rows marked Desktop exist only in the desktop app's Local API. Rows marked Helper exist only in the Bun helper, which also answers them under a /v1/ prefix (/v1/sections).

The desktop Local API accepts isFocusedToday=true/1 and isFocusedToday=false/0. With true or 1, it returns tasks marked for Focus Today. With false or 0, it returns unmarked tasks, including older records without the field. Omit the parameter to leave the list unfiltered. Other values return 400.

query here is plain-text matching: the value is lowercased and tested as a substring against the task's title, description, status, tags, contexts, project and area ids, and support notes. It is not the operator language. status:, context:, tag:, due:<=7d, quoted phrases and -negation belong to the app's search box, the CLI's --query, and the MCP server's task listing; sent here they match as literal characters and will usually return nothing.

Response Shapes

Task (partial)

json
{
  "id": "uuid",
  "title": "Task title",
  "status": "inbox",
  "projectId": "uuid",
  "dueDate": "2026-01-25T12:00:00.000Z",
  "tags": ["#work"],
  "contexts": ["@email"],
  "createdAt": "2026-01-25T10:00:00.000Z",
  "updatedAt": "2026-01-25T10:00:00.000Z",
  "deletedAt": null
}

Project (partial)

json
{
  "id": "uuid",
  "title": "Project name",
  "status": "active",
  "color": "#94a3b8",
  "createdAt": "2026-01-25T10:00:00.000Z",
  "updatedAt": "2026-01-25T10:00:00.000Z",
  "deletedAt": null
}

Area

json
{
  "id": "uuid",
  "name": "Area name",
  "color": "#94a3b8",
  "icon": "briefcase",
  "order": 0,
  "createdAt": "2026-01-25T10:00:00.000Z",
  "updatedAt": "2026-01-25T10:00:00.000Z"
}

Create Task Body

json
{
  "input": "Call Alice",
  "title": "Alternative title",
  "props": {
    "status": "next",
    "contexts": ["@phone"],
    "tags": ["#errands"]
  }
}

Desktop uses title when present, otherwise input, and applies explicit props. The Bun helper additionally runs parseQuickAdd for input.

Desktop project writes

The built-in desktop API supports creating, reading, updating, soft-deleting, and restoring projects. Project creation requires title; areaId, color, status, isSequential, and order are optional. Updates accept the same editable fields. Create, read, update, and restore responses contain the saved project as { "project": { ... } }; delete returns { "ok": true }.

Send creation options inside props, for example { "title": "Plan the move", "props": { "isSequential": true } }; PATCH fields go directly in the request body. sequentialScope accepts project or section. Project status accepts active, someday, waiting, or archived. Set areaId to null to remove the area. Archiving and reactivating a project follow the app’s child-task and section rules. For PATCH, missing projects return 404, while deleted or purged projects return 409.

GET /projects/:id also returns a stored soft-deleted project, which lets a script inspect it before restoring it. DELETE /projects/:id soft-deletes the project and its live sections, and detaches live tasks instead of deleting them or changing their status. POST /projects/:id/restore restores the project and any children deleted by the same legacy cascade, but it does not reattach tasks detached by a current delete. Repeating delete or restore is safe and leaves the current state unchanged. Lifecycle changes to a purged project return 409.

Create a sequential project:

bash
curl -s -X POST "http://127.0.0.1:3456/projects" \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Plan the move","props":{"isSequential":true}}' | jq .

Change an existing project to parallel:

bash
curl -s -X PATCH "http://127.0.0.1:3456/projects/$PROJECT_ID" \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"isSequential":false}' | jq .

Desktop task triage

Use PATCH /tasks/:id with status to move an active task between inbox, next, waiting, someday, and reference. Continue using the dedicated /complete, /archive, and /restore actions for those operations. Terminal status values are not accepted by PATCH.

PATCH cannot reopen a done or archived task (409); reopen it in the app first. Restore a soft-deleted task with /restore before triaging it. Purged tasks cannot be triaged.

Moving a task to inbox, next, waiting, or someday inside an archived project returns 409. First reactivate the project with PATCH /projects/:id and {"status":"active"}, or move the task out of that project in the same task PATCH.

Move an Inbox task to Next Actions:

bash
curl -s -X PATCH "http://127.0.0.1:3456/tasks/$TASK_ID" \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"status":"next"}' | jq .

Examples

List next actions:

bash
curl -s 'http://127.0.0.1:3456/tasks?status=next' \
  -H 'Authorization: Bearer <token>' | jq .

Create with explicit props:

bash
curl -s -X POST 'http://127.0.0.1:3456/tasks' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"title":"Call Alice","props":{"status":"next","contexts":["@phone"],"tags":["#errands"]}}' | jq .

Complete a task:

bash
curl -s -X POST "http://127.0.0.1:3456/tasks/$TASK_ID/complete" \
  -H 'Authorization: Bearer <token>' | jq .

CLI Tool

A simpler command-line interface is also available:

bash
# Add a task
bun mindwtr:cli -- add "Call mom @phone #family"

# List active tasks
bun mindwtr:cli -- list

# List with filters
bun mindwtr:cli -- list --status next --query "due:<=7d"

# Read or update a task
bun mindwtr:cli -- get <taskId>
bun mindwtr:cli -- update <taskId> '{"status":"next"}'

# Complete a task
bun mindwtr:cli -- complete <taskId>

# Archive, delete, or restore
bun mindwtr:cli -- archive <taskId>
bun mindwtr:cli -- delete <taskId>
bun mindwtr:cli -- restore <taskId>

# Search
bun mindwtr:cli -- search "@work"

# List projects
bun mindwtr:cli -- projects

CLI Reference

CommandExampleNotes
addmindwtr:cli -- add "Call mom @phone"Uses quick-add parsing
listmindwtr:cli -- list --status nextSupports --all, --deleted, --query
getmindwtr:cli -- get <taskId>Prints full task JSON
updatemindwtr:cli -- update <taskId> '{"status":"next"}'Applies a JSON patch
searchmindwtr:cli -- search "@work due:<=7d"Searches tasks/projects
completemindwtr:cli -- complete <taskId>Marks task as done
archivemindwtr:cli -- archive <taskId>Marks task as archived
deletemindwtr:cli -- delete <taskId>Soft-deletes task
restoremindwtr:cli -- restore <taskId>Restores a deleted task
projectsmindwtr:cli -- projectsLists active projects

Security Notes

  • The server is intended to run on 127.0.0.1 (localhost). Don't expose it publicly unless you understand the risks.
  • Desktop API access requires the generated bearer token from Settings. Keep that token private.
  • If you need remote access to the Bun helper, set MINDWTR_API_TOKEN and place the server behind an authenticated reverse proxy.

See Also

Mindwtr is free, open source, and local-first.
Getting Things Done and GTD are registered trademarks of the David Allen Company. Mindwtr is not affiliated with, endorsed by, or sponsored by the David Allen Company.