recordist

Local API

The desktop app listens on http://127.0.0.1:47321 while it is running. It binds to the loopback interface only, rejects any other Host header, and requires a bearer token on every request. The MCP gateway and the Chrome extension are both clients of this API; you can be one too.

Authentication

Authorization: Bearer <contents of api_token>

The token is written to api_token in the data folder on first launch, readable by your user only. It is local and personal: never commit it, share it, or send it anywhere. Requests without a valid token receive 401.

Endpoints

All requests and responses are JSON. Errors return { "error": { "code": "…", "message": "…" } } with an HTTP status.

Method Path Purpose
GET /v1/health { ok, version, recording: { active, meeting_id? } }
GET /v1/meetings?q=&limit=&offset=&from=&to= List meetings; q runs a full-text search
GET /v1/meetings/:id One meeting with notes, action items and markers
GET /v1/meetings/:id/transcript?format=json|md|srt|vtt|txt Transcript in the format you choose
POST /v1/meetings/:id/notes { kind, template_id? } regenerates notes (Pro)
PATCH /v1/meetings/:id Update title, tags, starred, attendees
DELETE /v1/meetings/:id Delete the meeting: database rows and audio
GET /v1/search?q= Full-text search across transcript segments and notes, with snippets
POST /v1/recording/prompt { source_app, title?, url?, attendees? } shows the confirm-to-record card; returns { prompt_id }
POST /v1/recording/start { source_app, title? } starts a recording (Pro; requires Allow remote start)
POST /v1/recording/stop Stops the current recording
POST /v1/recording/marker { label? } bookmarks the current moment
POST /v1/import { path, title? } transcribes an existing audio file (WAV natively, other formats through ffmpeg; regular files up to 4 GB with a known audio extension). Requires Allow remote start
GET /v1/ledger The Privacy Ledger: every outbound request the app has made
POST /v1/pair { code } exchanges a 6-digit pairing code for a token (see Extension pairing)
GET /v1/events Server-Sent Events stream

Nothing here can make the app record without a person having chosen it: the prompt endpoint only asks, and the start endpoint is refused unless Allow remote start is on.

Meeting JSON

{
  "id": "01J…", "title": "Weekly sync", "source_app": "meet",
  "started_at": 1725270000000, "ended_at": 1725273600000, "duration_ms": 3600000,
  "status": "ready", "language": "en", "attendees": [{ "name": "Ana" }], "tags": ["sales"],
  "starred": false,
  "notes": [{ "id": "…", "kind": "summary", "content_md": "…" }],
  "action_items": [{ "id": "…", "text": "Send deck", "owner": "You", "done": false }],
  "markers": [{ "id": "…", "at_ms": 120000, "label": "pricing" }]
}

status moves through recording, transcribing, summarising and ready.

Transcript JSON

{ "meeting_id": "…", "segments": [{ "speaker": "You", "channel": "mic", "start_ms": 0, "end_ms": 2100, "text": "Hi all" }] }

channel is mic (you) or sys (everyone else). The remote channel is one speaker today.

Events

GET /v1/events streams Server-Sent Events. The event name is the type; the data is { type, data, at }.

Type Data
recording.started { meeting_id, source_app, title, started_at }
recording.stopped { meeting_id, status }
recording.paused { paused }
segment.new a transcript segment
meeting.updated, meeting.ready, meeting.deleted { meeting_id }
prompt.shown the pending prompt
prompt.answered { prompt_id, accepted, answer }
marker.added the marker
notes.progress { meeting_id, stage, provider? }
models.loading, models.loaded { model }
pair.completed {}
error { message }

Changing the port

If something else already uses port 47321, the app logs “address in use” (Settings → Diagnostics → Logs). Set general.api_port in settings.json and restart; the gateway reads the same file. See the settings reference.