Skip to content

Cloud API

Mindwtr Cloud exposes a small bearer-token API for sync, task automation, and attachment transfer. It is designed for self-hosted deployments and uses the same token namespace as the self-hosted cloud backend.

The two ways into Mindwtr data: an AI assistant calls the MCP server, which runs the shared core package; the apps send a bearer token to the self-hosted cloud server, which serves the sync document, record endpoints, attachments and a calendar feed. Only the health check needs no token.

Open the interactive diagram

Authentication

Send a bearer token on every /v1/* request:

http
Authorization: Bearer <token>

Use MINDWTR_CLOUD_AUTH_TOKENS or MINDWTR_CLOUD_AUTH_TOKENS_FILE in production. MINDWTR_CLOUD_ALLOW_ANY_TOKEN=true is only for controlled automation and caps new namespaces with MINDWTR_CLOUD_ANY_TOKEN_MAX_NAMESPACES.

Health

text
GET /health

Returns server health without authentication.

Snapshot Sync

text
GET /v1/data
PUT /v1/data

GET /v1/data returns the authenticated namespace snapshot. If the namespace does not exist and writes are allowed, the server creates an empty snapshot.

PUT /v1/data validates the uploaded AppData, merges it with the existing namespace using the core sync algorithm, validates the merged result, and writes it back. It is not a forced overwrite. A successful response returns { ok: true, stats, clockSkewWarning }, where stats is the same merge-stats shape used by local sync diagnostics.

Tasks

text
GET /v1/tasks
POST /v1/tasks
GET /v1/tasks/:id
PATCH /v1/tasks/:id
DELETE /v1/tasks/:id
POST /v1/tasks/:id/complete
POST /v1/tasks/:id/archive

List query parameters:

ParameterPurpose
queryCase-insensitive text search across task title and metadata.
statusOne task status: inbox, next, waiting, someday, reference, done, or archived.
all=1Include completed tasks.
deleted=1Include soft-deleted tasks.
isFocusedTodaytrue/1 returns only tasks starred for today's focus; false/0 returns only the rest. Omit for no filter.
limit, offsetPage size and start offset.

Create accepts either title or quick-add input, plus optional props. Patch accepts task fields supported by the cloud validation layer and bumps sync revision metadata.

On create, props accepts these task fields: status, projectId, sectionId, areaId, description, priority, dueDate, startTime, relativeStartOffset, reviewAt, recurrence, showFutureRecurrence, contexts, tags, checklist, attachments, assignedTo, location, energyLevel, timeEstimate, timeSpentMinutes, taskMode, textDirection, isFocusedToday, pushCount, repeatReminderMinutes, suppressMindwtrReminders, and viewSectionIds. title is a top-level field, not a prop. Patch accepts the same fields plus title, order, orderNum, boardOrder, and focusOrder. Server-managed fields such as id, createdAt, updatedAt, completedAt, deletedAt, and rev are never client-writable. The lists come from the cloudWrite flag in packages/core/src/task-sync-schema.ts, so that file is the current reference.

assignedTo is a single person's name as a string, for example "Alex", not an array of person IDs. Use null or "" to clear it. REST task creation and updates reject arrays, objects, numbers, and booleans with HTTP 400. Search ignores malformed non-string assignments already in storage without rewriting them.

Mindwtr Cloud 1.2.8 and later return a strong ETag on GET /v1/tasks/:id and GET /v1/projects/:id. To update the version you read, send that tag in If-Match on the corresponding PATCH. The server checks the current record inside its write lock and returns 412 Precondition Failed without writing if the record changed, including an attachment-only change. Read the record again and rebuild your update before retrying. Omitting If-Match keeps the existing unconditional patch behavior.

For automation, put status inside props and omit dueDate when no deadline is set. attachments must be an array of attachment objects, not a URL string. For a link attachment, supply an ID, kind: "link", title, URI, and creation/update timestamps as shown below. Generate a unique ID for each new attachment and keep it when editing that link. You can also put a URL in props.description if you only need it in the task notes.

json
{
  "title": "JIRA-123 - Follow up",
  "props": {
    "status": "next",
    "attachments": [
      {
        "id": "890b7cda-e1ec-43de-a0d1-60b5385b7f8d",
        "kind": "link",
        "title": "JIRA-123",
        "uri": "https://jira.example.com/browse/JIRA-123",
        "createdAt": "2026-09-13T12:00:00.000Z",
        "updatedAt": "2026-09-13T12:00:00.000Z"
      }
    ]
  }
}

Capture

text
POST /v1/capture

Capture creates an Inbox task from a transcription, an audio recording, or both. The body can be multipart form data, JSON, or plain text, but only multipart form data can carry audio. See Capture webhook for the fields, the responses, and the Pebble Index 01 setup.

text
POST /v1/capture-tokens
GET /v1/capture-tokens
DELETE /v1/capture-tokens/:id
  • POST /v1/capture-tokens creates a capture-only token for the account. The body is { "label": "Pebble ring" }, and the label is optional. It returns 201 with id, token, label and createdAt. The token is shown once.
  • GET /v1/capture-tokens lists the account's capture-only tokens with id, label and createdAt. No secrets.
  • DELETE /v1/capture-tokens/:id revokes one.

All three need the full bearer token. A capture-only token is accepted only on POST /v1/capture and is rejected with 403 on every other route.

Projects, Areas, and Sections

text
GET /v1/projects
POST /v1/projects
GET /v1/projects/:id
PATCH /v1/projects/:id
DELETE /v1/projects/:id

GET /v1/areas
POST /v1/areas
GET /v1/areas/:id
PATCH /v1/areas/:id
DELETE /v1/areas/:id

GET /v1/sections
POST /v1/sections
GET /v1/sections/:id
PATCH /v1/sections/:id
DELETE /v1/sections/:id

All list endpoints accept limit, offset, and deleted=1. Sections also accept projectId.

Reference fields must point to live records. A project areaId must reference a live area. Use areaId: null to clear a project area; areaId: "" is invalid. A section projectId must reference a live project.

Deleting areas, projects, and sections uses tombstones and server-side repair to keep the snapshot valid for sync.

text
GET /v1/search?query=<text>

Search returns live tasks and projects in separate arrays. It supports the shared limit and offset parameters, plus independent cursors:

ParameterPurpose
taskLimit, taskOffsetPage the task result set.
projectLimit, projectOffsetPage the project result set.

The response includes taskTotal, projectTotal, and the effective cursor values.

Attachments

text
GET /v1/attachments/:path
HEAD /v1/attachments/:path
PUT /v1/attachments/:path
DELETE /v1/attachments/:path

POST /v1/attachments/orphans
DELETE /v1/attachments/orphans

Attachment paths are resolved inside the authenticated token namespace. Uploads enforce the configured byte limit and the core attachment validation rules.

The orphan cleanup endpoint scans the namespace for files no longer referenced by data.json. It skips files modified in the last five minutes so an upload racing with a later snapshot write is not removed.

Calendar Feed

text
GET /v1/calendar/feed
POST /v1/calendar/feed
DELETE /v1/calendar/feed

GET /v1/calendar/:token.ics

The first three require the bearer token and read, rotate, and revoke the namespace's feed token. They return { "feed": { "token", "path", "createdAt" } }, or { "feed": null } when nothing is published. POST returns 404 for a namespace that has never synced data.

GET /v1/calendar/:token.ics takes no Authorization header — the token in the URL is the credential — and returns text/calendar for that namespace. An unknown token is a 404.

The feed carries one event per scheduled task (startTime) and one per deadline (dueDate), matching what the app's Calendar view shows: completed, archived, reference, deleted, and non-active-project tasks are excluded, and a task scheduled and due on the same day appears once. Events carry a stable UID and the task title only — no description, checklist, attachments, tags, or project metadata.

MCP Adapter

The published mindwtr-mcp helper can use a self-hosted Cloud endpoint as a backend. Configure it with --cloud-url and --cloud-token or the MINDWTR_MCP_CLOUD_URL / MINDWTR_MCP_CLOUD_TOKEN environment variables.

Cloud-backed MCP mode reads /v1/data and exposes read tools for tasks, projects, sections, areas, and people. With --write, it routes task, project, section, and area mutations through the per-resource REST endpoints above; it stays read-only by default and does not turn Mindwtr Cloud itself into a hosted MCP service.

cancelledAt

cancelledAt is an optional ISO timestamp with a timezone on Task and Project. Setting it archives the commitment without marking it completed. A cancelled task has no completedAt and generates no next recurring occurrence. A project cancellation preserves completed steps and cancels its remaining actions. Upgrade every writer before using this field with sync. See Cancelling a commitment.

Use PATCH /v1/tasks/:id or PATCH /v1/projects/:id with { "cancelledAt": "2026-09-07T12:00:00.000Z" }. An explicit active status clears cancellation and reactivates the record; sending cancelledAt: null alone only clears the outcome marker. Creation accepts the field in props.

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.