MemoDocumentation
EN

API Reference

Memo Backend runs a REST API on localhost:8090 (default). Every /api/* route is also aliased under /api/v1/* (see route() in internal/webserver/server.go).

Authentication

Local (localhost) connections are open, no token required. Remote access (LAN, ngrok, or Tailscale) requires the access token shown in Settings on every request — this was previously optional and is now enforced (v3.3.3 security fix). The mobile app already sends it; a custom tool talking to the remote API directly needs to add it too.

For a self-hosted server, this token model sits alongside a full account system: token, password, token+password, or (opt-in, loudly warned about) none auth mode, selectable per-server. A server can host more than one account — an admin plus any number of user accounts — each with its own password and its own set of seven granular permissions (Models, Memory, Agent, Calendar, WhatsApp, Telegram, Routines). Permission checks are enforced per-request: requirePermission (GET/HEAD-exempt) or requirePermissionStrict (no exemption, used for Memory) wrap the relevant handlers below. See the Accounts & Permissions section and Self-Hosting.

Developer API Gateway (Anthropic-compatible)

POST /v1/messages implements the server side of Anthropic's Messages API wire format (internal/anthropicapi/), so tools that only know how to talk to Anthropic — most notably Claude Code via ANTHROPIC_BASE_URL — can point at Memo instead. Model selection uses a type/model-id format (local/qwen2.5, openai/gpt-4o, ...). See Sidebar → Developer for the base URL/token/live request log.

POST /v1/models and POST /v1/chat/completions (internal/openaiapi/) are the OpenAI-compatible sibling of the same gateway, for tools that only speak OpenAI's wire format. Both this and the Anthropic-compatible endpoint above now enforce the configured API key for any non-loopback caller (v4.5.0 security fix) — previously, with "Require API Key" left off and remote access on, either could be reached by anything else able to reach the port, no credential at all.

This list below is not exhaustive — there are 180+ registered endpoints as of v4.6.0. It groups the major ones by area; see internal/webserver/server.go's route(...) calls for the full, current list.

Endpoints

Chat

Method Endpoint Description
POST /api/send Send a standard JSON message (non-streaming)
POST /api/send/stream SSE streaming response. Body: message, optional chat_id, optional options {auto_permission, web_tools} — this client's own settings for this one request (they never change a backend-wide flag; absent = follow the backend)
POST /api/send_file File/image message (Multipart)
GET /api/chats List all sessions
POST /api/chats/new Create new session
POST /api/chats/switch Switch active session
POST /api/chats/delete Delete session
GET /api/messages Get a chat's history. Pass chat_id — without it the call acts on the global active chat, which another client can switch at any moment. /api/messages/update and /api/messages/delete take chat_id too
GET /api/chats/streaming {"chat_ids": [...]} — every chat that is generating a reply right now (the sidebar's "still working" spinner; covers background tasks, WhatsApp/Telegram replies and other browser tabs)
GET /api/status System status + memory count
POST /api/incognito Toggle incognito mode
GET/PUT /api/system-prompt Get/update system prompt
GET /api/context?chat_id= The context gauge (v4.6.0): {provider, model, window, used, used_real, percent, categories:[{key,tokens}], auto_compact_enabled, auto_compact_pct, summarized_messages, limits:[{label,remaining_percent,reset_at}], limits_vendor}. used is the provider's own prompt + reply token count when it reported one (used_real), a len/3 estimate otherwise; categories (messages, summary, system, memory, skills, tools, current) come from the last prompt Memo assembled and are scaled to the real count. limits lists the account's allowance windows for a Subscriptions model (5h, 7d, or one unlabeled per-model figure) and is [] otherwise. Read-only; answers from memory and the quota cache, never a model call

Provider errors. An error chunk (and the saved message) is a plain sentence in the UI language with the HTTP status as a trailing (HTTP n) tag, not the vendor's raw text — App.FriendlyError, applied at the SSE boundary and when the message is saved. The raw text is classified first (the quota card parses its reset time) and logged.

Marker chunks on a chat SSE stream (/api/send/stream, /api/send/file/stream)

A streamed chunk is ordinary reply text unless its finish_reason names one of these markers. Clients must treat an unknown marker as "ignore", never print it.

finish_reason content Meaning
heartbeat empty Sent after 10 s of silence so a long, quiet thinking phase is not mistaken for a dropped connection. A client's own timeout should be an idle timeout that these reset
agent_event JSON A tool event (call, result, permission request) — render as a badge, never as text. Parse defensively
browser_frame JSON {screenshot, timestamp} A fresh screenshot of the interactive browser after a page-changing tool. Streams to the live panel only and is never saved into the chat history
quota_exhausted JSON QuotaSignal The turn failed because the allowance behind the model ran out; arrives just before the error chunk
quota_low JSON QuotaSignal The turn worked but 10% or less is left; sent once per allowance window

QuotaSignal (internal/models/quota_signal.go): kind (exhausted | low), provider, model, vendor, remaining_percent (0–100, -1 unknown), reset_at (RFC 3339, empty when unknown), window (5h, 7d …). The quota markers are added in one place, Server.withQuotaSignals; CLI-agent streams, WhatsApp streams and the Self-Driving loop are not wrapped. A plain chat turn is ended by silence (300 s with no chunk, first word included) plus a 30-minute cap, not by a fixed total length.

Memory

Method Endpoint Description
GET /api/memory/files List memory files
DELETE /api/memory/files Delete a memory file
POST /api/memory/clear Clear all memory
GET /api/memory/known-facts Every currently pinned fact ("what Memo knows about you") — same shape as the debug-search results, no query needed
GET /api/memory/conversation?limit=&offset= A page of ordinary (non-pinned) conversation memories: {results, total}
POST /api/memory/delete-by-ids {ids: [...]} — delete exactly those records (never a pattern); returns {deleted: n}
POST /api/memory/pinned/update {id, content, tags} — rewrite one pinned fact
POST /api/memory/explicit/save, /api/memory/explicit/delete Save / remove an explicit "remember this" memory (works with no embedding model configured)

Models

Method Endpoint Description
GET /api/models/local List downloaded .gguf files
DELETE /api/models/local Delete a local model
POST /api/models/start Start a model (spawn llama-server)
POST /api/models/stop Stop running model
GET /api/models/status Model runtime status
GET /api/gpu GPU detection info (NVIDIA/AMD/Metal)
POST /api/models/search Search HuggingFace for GGUF files
POST /api/models/download Start model download
GET /api/models/download/progress Download progress stream
GET /api/models/llama/check Check if llama.cpp binary exists

External Providers

Method Endpoint Description
GET /api/providers List all provider configs
PUT /api/providers Add/update a provider config
DELETE /api/providers Delete a provider config
POST /api/providers/test Test provider connection
GET /api/providers/active Get active provider type
PUT /api/providers/active Set active provider
GET /api/providers/effort-levels Reasoning-effort levels for the active model, if supported
POST /api/openrouter/connect OAuth connect flow for OpenRouter
GET /api/kilo/models Live model list from Kilo Code (app.kilo.ai), free models flagged
GET /api/opencode-zen/models Live model list from OpenCode Zen, free models flagged (-free id suffix)

Accounts & Permissions (self-hosted)

Method Endpoint Description
GET /api/setup/status Whether a self-hosted server has an admin account yet
POST /api/setup/create-admin First-run: create the admin account
POST /api/setup/create-device Pair a new device/token under an account
GET/POST /api/accounts List accounts / create a new account
GET/PUT/DELETE /api/accounts/{id} Get/update/delete one account
PUT /api/accounts/{id}/password Change an account's password
GET/PUT /api/accounts/{id}/permissions Get/update an account's 7 granular permissions

Agent Mode

Method Endpoint Description
GET /api/agent/enabled Get agent mode status
PUT /api/agent/enabled Enable/disable agent mode
POST /api/agent/permission Respond to a permission request
GET /api/agent/permissions List permanent permissions
DELETE /api/agent/permissions Revoke (with ?id=) or clear all permissions

WhatsApp

Method Endpoint Description
GET /api/whatsapp/status Pairing/connection status
POST /api/whatsapp/start Start the client, generate a QR pairing code
POST /api/whatsapp/stop Stop the client
POST /api/whatsapp/logout Log out and clear the paired session
POST /api/whatsapp/send Send a message
GET /api/whatsapp/search Search WhatsApp message history
GET /api/whatsapp/chats List recent chats
GET /api/whatsapp/messages Get messages for a chat
GET /api/whatsapp/avatar Fetch a contact's avatar
GET /api/whatsapp/stats Message/contact counters
PUT /api/whatsapp/chat-mode Configure the dedicated WhatsApp-only chat executor
POST /api/whatsapp/chat-stream SSE stream for the WhatsApp-only chat mode
POST /api/whatsapp/self-chat-assistant Enable/configure the self-chat assistant (message your own number, get a full Memo assistant back)

Telegram

Method Endpoint Description
GET /api/telegram/status Bot connection/owner-link status
POST /api/telegram/connect Connect with a bot token, start long-polling
POST /api/telegram/stop Stop the client without clearing the token
POST /api/telegram/disconnect Disconnect and clear the stored token/owner link

Image generation

Method Endpoint Description
GET /api/image/config {"auto_route": bool, "default_provider": "", "default_model": ""} — whether a picture asked for in chat goes to an image model on its own, and the default image model API-key providers use (a Subscriptions account always uses its own account's image model)
PUT/POST /api/image/config Change any of those; omitted fields keep their value. A default image model needs both a provider and a model (or neither) and the provider must be configured — otherwise 400 with the reason. Admin-only write.

Self-Driving Task Loop

Method Endpoint Description
GET/POST /api/tasklists List task lists / create one from a Task.md
GET/PUT/DELETE /api/tasklists/{id} Get/update/delete a task list
POST /api/tasklists/{id}/plan Trigger a planning turn (planner mode)
POST /api/tasklists/{id}/approve-plan Approve a pending Plan.md
GET /api/tasks/running Live view of the currently-running task list
GET /api/tasks/events SSE stream of task-loop activity
POST /api/tasks/{id}/pause, /resume, /cancel, /skip, /inject Control a running task list from outside the model (pause/resume/cancel current item, skip it, or inject a message)
GET/PUT /api/taskloop/settings Persistent task-loop configuration

Code Mode

Method Endpoint Description
GET/POST /api/code-mode/prompt Get/set the system prompt for a Code Mode sub-mode (plan/auto/build)
POST /api/code-mode/prompt/reset Reset a sub-mode's prompt to its default

Desktop Mascot

Method Endpoint Description
GET /api/mascot/activity App-wide "what is Memo doing right now" activity signal the mascot window polls (also consumed by the terminal REPL's spinner)

Live Mode v2 (native audio-to-audio)

Method Endpoint Description
GET/PUT /api/livemode/engines List/select the configured voice engine (Google Live, OpenAI Realtime, local fallback)
GET /api/livemode/engines/models Live model list for the selected engine
POST /api/livemode/session Start/manage a Live Mode v2 session
GET/PUT /api/livemode/active Get/set whether Live Mode is the active surface for a chat

Orchestra Mode

Method Endpoint Description
GET /api/orchestra/config Get orchestra configuration
PUT /api/orchestra/config Update orchestra configuration

Cloud Sync

Method Endpoint Description
GET/PUT /api/sync/settings Get/update Google Drive sync settings

Config

Method Endpoint Description
GET/PUT /api/config/llama Get/update llama configuration
GET /api/image?path= {data: <base64>} for an image a chat message stored. Only files under the backend's own image directories are served (.. and everything else is refused). Clients must load chat images through this, never from a local path — the file is on the backend's disk
POST /api/models/embedding/start Start embedding server
POST /api/models/embedding/stop Stop embedding server

Routines

Method Endpoint Description
GET/POST /api/routines List / create routines
POST /api/routines/parse Turn a plain-language description into a routine config
GET/PUT/DELETE /api/routines/{id} Get/update/delete a routine
POST /api/routines/sync-offset Resync a device's timezone offset for its routines

Proactive Learning & Self-Insight

Method Endpoint Description
GET/PUT /api/proactive/settings Get/update proactive learning level
GET /api/proactive/pending Poll for a pending suggestion
POST /api/proactive/respond Accept/dismiss/suppress a suggestion
GET /api/proactive/patterns List learned patterns
POST /api/proactive/patterns/forget Forget one pattern
POST /api/proactive/clear Clear all patterns
GET /api/memory/insight /insight — summarize recent mood/memory patterns

Live Mode / Text-to-Speech

Method Endpoint Description
POST /api/tts/synthesize Synthesize speech for a chat reply
POST /api/tts/filler Short "thinking" filler sound
GET/PUT /api/tts/providers List/select TTS provider (local Piper or external)
POST /api/tts/providers/test Test an external TTS provider
GET /api/tts/voices List downloadable/local Piper voices
POST /api/tts/voices/download Download a voice
POST /api/tts/voices/select Select active voice

🖧 Memo Swarm (beta)

Method Endpoint Description
GET /api/swarm/status Current room/worker status
POST /api/swarm/host/create Create a Swarm room (become Host)
POST /api/swarm/host/workers/add/remove/reorder/share Manage joined workers and their compute share
POST /api/swarm/host/start/stop/close Control the Swarm session
POST /api/swarm/join/leave Join/leave a room with a room code

Usage Stats

Method Endpoint Description
GET /api/stats/usage Requests, tokens, avg tok/s, per-model breakdown, 30-day history, and the prompt-cache split: total_cached_prompt_tokens, total_cache_write_tokens plus cached_prompt_tokens / cache_write_tokens on each per-model and per-category row. A zero means "the provider reported nothing", not a measured 0%

Claude Code / Codex CLI Providers (beta)

Method Endpoint Description
GET /api/cli/status Whether claude/codex are installed, with version
GET /api/cli/running CLI jobs currently running in the background
POST /api/chats/project {chat_id, project_path} — turns an EMPTY chat into a project (agent) chat, or back into a plain one with "". 409 once the chat has messages, 404 for an unknown chat. Same permission as /api/agent/chat
GET /api/cli/commands The CLI's own / commands (project/personal/skill/built-in)
GET/PUT /api/chats/cli-provider, /api/chats/cli-workdir, /api/chats/cli-model Per-chat CLI provider/working-dir/model
POST /api/send/cli-stream Send a message to a CLI-provider chat (SSE)

Project folders (.memo/) — see PROJECT_FOLDER.md

Method Endpoint Description
POST /api/projects/status {path} → {root, id, name, set_up, declined, allowed, reason, enabled, should_ask}. Writes nothing. Same permission as /api/agent/chat
POST /api/projects/setup {path} → creates .memo/ and the starter files (never overwrites). 403 for the home folder, Memo's own folders, or a foreign .memo
POST /api/projects/decline {path, declined?} → remembers "never set up Memo in this folder" (declined:false undoes it)
POST /api/projects/permissions {path} → the project's .memo/permissions.json rules and their trust state on this device. Writes nothing
POST /api/projects/permissions/save Write the rules to .memo/permissions.json. A file with even one bad rule is rejected as a whole
POST /api/projects/permissions/trust Approve the file's current content on this device (trust on first use). A file that arrives with a clone, or changes later, is ignored until approved again

Developer API Gateway

Method Endpoint Description
GET/PUT /api/dev-gateway/config Get/update the gateway's config (API key requirement, memory integration) — shared by both the Anthropic- and OpenAI-compatible endpoints
GET /api/dev-gateway/models List type/model-id selectable models across local + configured providers
GET /api/dev-gateway/logs Live request log
GET /api/dev-gateway/claude-code-cli Claude Code CLI connection helper/status
POST /api/dev-gateway/token/rotate Rotate the gateway's API key

Subscriptions & model selector

Method Endpoint Description
GET / POST /api/subscriptions Subscriptions (the bundled CLIProxyAPI sidecar): state (bundled?, running?, accounts, models, sign-in in flight) and the actions login (provider: antigravity | claude | codex), cancel_login, logout. POST is admin-only; GET never starts the sidecar and waits at most ~2 s for quota figures
GET /api/providers/model?name=<provider> That provider's live models. For Subscriptions each model may carry remaining (0–1), reset_at and quota_window. Answers from a quota cache at once; add fresh=1 (the picker's background refresh) to wait up to 3 s for current figures. Needs the models permission — it spends a stored key
PUT /api/providers/model {name, model, activate} — switch only that provider's model (every other setting survives); activate also makes it the active provider, so a selector needs one call
GET /api/providers/models Model list for a key the caller supplies (never a stored one)

Quota comes from the vendor with the token in the credential file, which goes only to the vendor — never to this API, logs or UI. Antigravity reports it per model; Codex and Claude meter the account in windows, so the tightest window is applied to all of that vendor's models. The Claude parser is written from the endpoint's known shape and has not been verified against a live Claude sign-in.

Skills

Method Endpoint Description
GET /api/skills/list List installed skills
POST /api/skills/install Install a skill
DELETE /api/skills/remove/{name} Remove a skill
GET /api/skills/get/{name} Get one skill's manifest
GET / PUT /api/skills/active-list?chat_id=, /api/skills/active ({chat_id, names}) Get/set the skills active in one chat. Activation is per chat; a new chat starts with none

Interactive Browser Pane

Beta: with Settings › Beta Features off, navigate/click/scroll/type answer {error: "...beta feature..."} and status reports no session; close stays allowed.

The live browser panel next to the chat drives one isolated Chromium session (internal/browserengine/). All session routes need the agent (tool-execution) permission, accept only http, https and a blank page (no file://), and answer {screenshot_base64, url, error} so the panel can repaint from a single round trip. GET/PUT /api/browser, POST /api/browser/install and GET /api/browser/install/progress manage the optional browser install.

Method Endpoint Description
POST /api/browser/session/navigate {url} — an address without a scheme is accepted
POST /api/browser/session/click {x, y} in the screenshot's pixel space
POST /api/browser/session/type {text, enter} — typed into whatever the last click focused
POST /api/browser/session/scroll {dx, dy}
GET /api/browser/session/status {active, url}
POST /api/browser/session/close End the session

Mail (admin only) — see DIGITAL_TWIN_AND_MAIL.md, MAIL_SECURITY.md

Every mail route needs an admin session except stats; settings is readable by anyone but writable only by an admin. Mail is off until an account is connected.

Method Endpoint Description
GET/DELETE /api/mail/accounts List connected accounts; DELETE ?id= disconnects one
GET/POST /api/mail/autodiscover ?email= → IMAP/SMTP settings from the built-in presets (Gmail, Outlook, …) or Thunderbird's autoconfig database
GET/POST /api/mail/oauth/start Begin the Gmail / Outlook sign-in; returns the URL and a state
GET/POST /api/mail/oauth/status ?state= → whether the sign-in finished
POST /api/mail/oauth/manual {state, callback_url, email} — finish a sign-in by pasting the redirect URL (headless and remote setups)
POST /api/mail/accounts/password Connect any IMAP/SMTP account with a password
GET /api/mail/inbox ?account_id=&limit= → the account's recent message summaries
GET /api/mail/message ?account_id=&seq_num= → one full message
GET /api/mail/attachment ?account_id=&seq_num=&part=&filename= → the attachment bytes
GET /api/mail/search ?account_id=&q=&limit= → search the mailbox
POST /api/mail/send Send a message. The outgoing secret scan runs first; a match answers 422 with a reason code, never the secret
POST /api/mail/draft Save a draft in the account's Drafts folder (same secret scan)
POST /api/mail/sync Poll the connected accounts now instead of waiting for the next 60-second poll. Returns {ok, stats}
GET /api/mail/stats The watcher's counters (messages evaluated, filtered, blocked by the security shield). Reason codes only, never message text
POST /api/mail/security/check Dry-run the inbound injection shield and the outbound scan on pasted text. Sends nothing, stores nothing, counts nothing
GET/PUT /api/mail/settings Watcher and triage settings

Digital Twin (admin only) — see DIGITAL_TWIN_AND_MAIL.md, DIGITAL_TWIN_APPROVAL.md

One route per operation under /api/twin/{operation}. state and activity are GET; every other operation is a POST with a JSON body of at most 32 KB and unknown fields are rejected. An unknown operation answers 404.

Method Endpoint Description
GET /api/twin/state Everything the Digital Twin panel shows: preferences (autonomy, radar, briefing), reply model, contacts, prepared actions, waiting commitments, briefing cards, audit log
GET /api/twin/activity The latest autonomous decisions
GET /api/twin/events Server-sent change signals such as autonomy:action_pending and autonomy:action_executed, resumable with Last-Event-ID. They carry ids and states only, never a subject, recipient or body; after a reconnect fetch /api/twin/activity for the truth
POST /api/twin/preferences Save preferences, including briefing: {morning, morning_time, pre_meeting, pre_meeting_minutes, sections[], custom[{title,prompt}], channels[], mail_account, model{provider,model}}
POST /api/twin/reply_model {provider, model} — the model that writes reply drafts (independent of the decision model)
POST /api/twin/contact · /merge · /contact_timeline Create or edit a contact (mail + WhatsApp aliases, per-person policy never/draft_only/full_auto), merge two contacts, read one person's timeline across channels
POST /api/twin/approve_action · /send_action · /hold_action · /cancel_action Owner decisions on a prepared reply. A second approval answers 409
POST /api/twin/twin_commitments · /resolve_commitment List open "waiting on" and "my promise" records; mark one done
POST /api/twin/briefing_read {key} — mark a briefing card as read
POST /api/twin/check_availability · /git_project_status · /dev_status Read-only evidence tools: masked free/busy, a registered project's git state, unfinished work across projects
POST /api/twin/task_from_action Turn a message into an idle Self-Driving task, only on the owner's request

Telemetry (anonymous daily summary) — see TELEMETRY.md

On by default in official builds; off in source builds unless enabled. The reads are open; the writes are admin only.

Method Endpoint Description
GET /api/telemetry/status Whether sharing is on, the person's answer, the install count state
GET/POST /api/telemetry/preview The exact report that would be sent, before anything is sent
POST /api/telemetry/consent `{share: true
POST /api/telemetry/reset-id Make a new random install ID
POST /api/telemetry/delete Ask for the data held under this install ID to be deleted

In-app updates — see AUTO_UPDATE.md

Method Endpoint Description
GET /api/update/check ?force= → whether a newer Memo version (or engine) is published; force skips the cache
GET/POST /api/update/status The state of the current download or install and the result of the last attempt
POST /api/update/apply Admin only. Download, verify the signed manifest, swap the files in and restart

Chat sub-agents — see CHAT_SUBAGENTS.md

Needs the agent (tool-execution) permission.

Method Endpoint Description
GET /api/subagents ?chat_id= → the chat's sub-agents (running, finished, history)
GET/PUT /api/subagents/settings How many run at once (1–16, default 3)
GET /api/subagents/{id} ?chat_id= → one sub-agent: its task, live output and tool calls
POST /api/subagents/{id}/{send|cancel|close|merge|resume} Send it a follow-up, stop it, close it, merge its isolated copy of the project, or resume an interrupted review

Backup / Export / Wipe

Method Endpoint Description
POST /api/export Full .memo backup (now includes calendar, routines, task lists, permissions, skills, machine.key)
POST /api/import Restore from a .memo backup
POST /api/wipe Factory reset (all internal DBs closed before file removal, fixed on Windows)

Remote Access

Method Endpoint Description
GET/PUT /api/remote-access LAN/ngrok/Tailscale config, requires the access token on remote requests

Gating. Every new route needs an explicit decision: destructive actions (export, import, wipe, uninstall, shutdown) are admin-only; state-changing admin settings whose GET is read ambiently are admin-write with a redacted GET; a GET that carries a credential is redacted for callers without the permission. The single-user desktop (no credential) is never affected.

For detailed JSON payloads, refer to internal/webserver/handlers_flutter.go, the other handlers_*.go files, and internal/webserver/server.go.


Source document