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
3456or 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:
Settings -> Advanced -> Enable local API serverDefault URL:
http://127.0.0.1:3456From the repo root with Bun:
bun install
MINDWTR_API_TOKEN=replace-with-a-strong-token bun run mindwtr:api -- --port 4317 --host 127.0.0.1Options
| Option | Default | Description |
|---|---|---|
--port <n> | 4317 | Server port |
--host <host> | 127.0.0.1 | Bind address |
--data <path> | Platform default | Override data.json location |
--db <path> | Platform default | Override mindwtr.db location |
--dangerously-disable-auth | off | Run with no token at all — unsafe, and not browser-safe even on loopback |
Environment Variables
| Variable | Description |
|---|---|
MINDWTR_DATA | Override data.json location (if --data is omitted) |
MINDWTR_DB_PATH | Override mindwtr.db location (if --db is omitted) |
MINDWTR_API_TOKEN | Required. Bearer token the helper demands on every request; it refuses to start without one unless --dangerously-disable-auth is passed |
MINDWTR_API_CORS_ORIGIN | Optional. 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
| Method | Endpoint | Description |
|---|---|---|
GET | /health | Health check → { ok: true } |
GET | /tasks | List tasks |
GET | /tasks?status=next | Filter by status |
GET | /tasks?query=@work | Search tasks |
GET | /tasks?isFocusedToday=true | Desktop: filter Focus Today |
GET | /tasks?all=1 | Include done/archived |
GET | /tasks?deleted=1 | Include soft-deleted |
POST | /tasks | Create task |
GET | /tasks/:id | Get single task |
PATCH | /tasks/:id | Update task |
DELETE | /tasks/:id | Soft delete task |
POST | /tasks/:id/complete | Mark as done |
POST | /tasks/:id/archive | Mark as archived |
POST | /tasks/:id/restore | Restore a soft-deleted task |
GET | /projects | List projects |
POST | /projects | Desktop: create project |
GET | /projects/:id | Desktop: get one project |
PATCH | /projects/:id | Desktop: update project |
DELETE | /projects/:id | Desktop: soft-delete project |
POST | /projects/:id/restore | Desktop: restore project |
GET | /areas | List areas |
GET | /v1/areas | Compatibility alias for areas |
GET | /sections | Helper: list sections, optionally ?projectId= |
POST | /sections | Helper: create section |
GET | /sections/:id | Helper: get single section |
PATCH | /sections/:id | Helper: update section |
DELETE | /sections/:id | Helper: 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)
{
"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)
{
"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
{
"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
{
"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:
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:
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:
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:
curl -s 'http://127.0.0.1:3456/tasks?status=next' \
-H 'Authorization: Bearer <token>' | jq .Create with explicit props:
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:
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:
# 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 -- projectsCLI Reference
| Command | Example | Notes |
|---|---|---|
add | mindwtr:cli -- add "Call mom @phone" | Uses quick-add parsing |
list | mindwtr:cli -- list --status next | Supports --all, --deleted, --query |
get | mindwtr:cli -- get <taskId> | Prints full task JSON |
update | mindwtr:cli -- update <taskId> '{"status":"next"}' | Applies a JSON patch |
search | mindwtr:cli -- search "@work due:<=7d" | Searches tasks/projects |
complete | mindwtr:cli -- complete <taskId> | Marks task as done |
archive | mindwtr:cli -- archive <taskId> | Marks task as archived |
delete | mindwtr:cli -- delete <taskId> | Soft-deletes task |
restore | mindwtr:cli -- restore <taskId> | Restores a deleted task |
projects | mindwtr:cli -- projects | Lists 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_TOKENand place the server behind an authenticated reverse proxy.