Skip to content
The English translation is still being built: some pages may lag behind the German ones.
HTTP API

HTTP API

Every client of samudo, the web UI included, uses this API and nothing else. The command line covers all of it, so samu <command> --format json is the quickest way to see a response. The wire types are the Rust crate samu-api (serde only, versioned on its own); JSON examples of every type are in crates/samu-api/tests/fixtures/.

Conventions

  • Ids are UUIDv7, timestamps ISO 8601 in UTC with Z, lengths of time ISO 8601 durations (PT15M, P2D).
  • A date is {"date": "2026-11-20"} (a whole day, no zone) or {"at": "2026-11-20T09:00:00+01:00[Europe/Berlin]"} (an instant with its zone).
  • Fields a reader does not know are kept; enum values from a newer version read as unknown and are never written back.
  • Errors are a status code with {"error": "not_found", "message": "…"}; kinds include unauthorized, not_found, offline (a replica without its server).

Signing in

POST /login
{"account": "private", "password": "…", "device": "laptop"}

answers with a session token and its expiry, and sets a cookie for browsers. Send the token as Authorization: Bearer <token>. With "kind": "agent" the session is an agent’s: what it writes is marked as such. GET /session shows the session and the account’s zone and settings; POST /logout ends it.

Pairing: POST /pair/code (with the password again) makes a one-time code for ten minutes; POST /pair trades it for a session on the new device.

Reading

Route
GET /availableopen tasks with availability, every reason, state and next state; filters: available only, today, project, all, scope, perspective
GET /available/projects, /available/foldersprojects (stuck, review due) and folders
GET /forecastthe forecast sections, each row with why it is there
GET /objects/{id}one object of any kind
GET /objects/{id}/logbookits history
GET /changes?after=<seq>every object changed after a change number, for a client’s cache
GET /habits, /habits/{id}habit status, streaks, entries
GET /appointmentsappointments of some days, repetitions unfolded
GET /clocks/report, GET /reporttime and the report
GET /scopes/scheduledwhich scope the schedules choose now, and until when
GET /blobs/{sha256}the file behind an attachment
GET /exportthe whole account (see Self-hosting)
GET /health{"status": "ok", "version": …, "api": …}, without a session

Writing

Nearly every write is a list of changes to POST /changes. One change is one of:

{"op": "create", "object": "<id>", "kind": "task", "value": {…whole object…}, "hlc": …}
{"op": "set",    "object": "<id>", "field": "due", "value": {"date": "2026-11-20"}, "hlc": …}
{"op": "add",    "object": "<id>", "field": "tags", "element": "<tag id>", "position": 0, "hlc": …}
{"op": "remove", "object": "<id>", "field": "tags", "element": "<tag id>", "hlc": …}
{"op": "delete", "object": "<id>", "hlc": …}

set with null clears a field; a property is the field properties.<key>. create is idempotent by id, so a client makes the id itself and may send the same change twice. Changes to states and completion may carry a note for the logbook.

hlc is a hybrid logical clock stamp, {"wall": <ms>, "counter": n, "device": "laptop"}. The server decides per field: the later stamp wins. Each change gets an outcome:

{"outcome": "applied", "seq": 185}
{"outcome": "superseded", "by": {"wall": …, "counter": 0, "device": "phone"}}
{"outcome": "rejected", "reason": "field \"due\" expects {\"date\"} or {\"at\"}"}

Writes with logic of their own have their own routes:

Route
POST /capturea line of quick capture: preview, create, or apply to a task
POST /objects/{id}/deletedelete with what hangs on it
PUT /blobs/{sha256}upload a file; then create an attachment that names it
POST /rendera note or comment as a document tree
PUT /calendars/{id}/source, POST /calendars/{id}/refresh, POST /calendars/discovercalendar sources
POST /appointments/{id}/editchange an appointment or occurrences of one
GET, POST /hookswebhooks of the account
POST /hooks/{name}a signed delivery from outside, no session

Live

GET /events is a stream of server-sent events. A changed event carries ids only, never content:

{"seq": 184, "gone": ["…"], "added": ["…"], "changed": ["…"], "config": false}

A client loads what it shows again; config: true means the account’s settings changed. A reset event ({"seq": 190}) means: load everything you show from scratch.

Web UI routes

/ and /lib/… serve the web UI from the binary; /o/{id} opens an object in it.