Skip to content

HTTP API ​

这一页由 docs/gen-api.py 从服务端的 OpenAPI 生成。 说明是各个接口函数文档字符串的第一句(英文)。

适用版本

这一页对应 main 分支的服务端。Docker 镜像 latest(v0.1.0)比它旧,没有之后加的接口,比如 /api/todos/{todo_id}/mark 和 /api/lang。

认证 ​

  • 除了 GET /api/health,每个请求都要带一个租户的 token:请求头 Authorization: Bearer <token>,或者 cookie token。
  • 任何一个 GET 页面带上 ?token=<token> 打开一次,服务器会设置一年有效的 cookie,再 303 跳转到去掉 token 的地址。
  • 没有有效 token:HTTP 返回 401,WebSocket 以 4401 关闭。
  • tenants.json 里只有一个租户、且它的 token 为空时,不做认证。
  • 写固件的接口(POST /api/firmware、PATCH /api/firmware/{model}/{version})另外要 X-Firmware-Token: <FIRMWARE_TOKEN>。

不在下表里的 ​

  • 请求体是原始字节、不在 OpenAPI 里的:POST /api/chunks 的请求体是一个完整的音频文件,POST /api/firmware 的请求体是一个固件包。
  • GET /ws:WebSocket,服务器向网页推送这个租户的更新。
  • 网页本身的路由(/、/live、/conversations 等)和 /assets/*,见网页与 App。

接口 ​

参数写法:名字? 表示可选;括号里是位置(query、path)和类型。

/api/firmware ​

方法说明参数
POSTOne DFU package (lr.sh pkg) as the body; the model is the build's own (CONFIG_BT_DIS_MODEL), read by lr_release.py from the build directory the package came from.model (query, string)
version (query, string)
commit (query, string)
built (query, string)
notes (query, string)
GETA model's packages, newest first: the app's firmware page lists them with their notes.model (query, string)

/api/firmware/{model}/{version} ​

方法说明参数
PATCHNew notes for a published version (lr_release.py --version); the package stays as it is.model (path, string)
version (path, string)
notes (query, string)

/api/firmware/latest ​

方法说明参数
GETThe newest package of a model (what the app compares with its recorder's revision), or 404.model (query, string)

/api/firmware/{model}/{version}.zip ​

方法说明参数
GET(无说明)model (path, string)
version (path, string)

/api/addresses ​

方法说明参数
GET(无说明)—

/app.apk ​

方法说明参数
GETThe Android client, for installing from the phone's browser.—

/api/app/latest ​

方法说明参数
GETWhat /app.apk is: the Android app compares versionCode with its own build number and offers the update.—

/api/chunks ​

方法说明参数
POSTOne self-contained audio file (the apps' Opus or AAC pieces), start its device time in unix ms, n the 16 kHz samples the client wrote into it (the iPhone's AAC: the decoder adds some at both ends).device (query, string)
start (query, integer)
n? (query, integer)
hold? (query, integer)

/api/chunks/end ​

方法说明参数
POSTThe recorder stopped at device time t (unix ms): the open conversation closes once its audio is processed.device (query, string)
t (query, integer)

/api/hold/release ​

方法说明参数
POSTThe phone no longer has the recorder (unbound, or another bound in its place): the older audio on its card will not come from it, so finalization stops waiting for it now, not HOLD_MAX_S after the phone last said the hold.device (query, string)

/api/device-status ​

方法说明参数
POSTWhat a client read from a BLE recorder (battery, card, firmware, model): each one kept, the newest shown with the device's pipeline.device (query, string)
body: JSON object

/api/device-log ​

方法说明参数
POSTA batch of a device's events, from the phone that read them; one sent again replaces the one kept.device (query, string)
body: JSON DeviceEvent[] {src, seq, at, data}
GETA device's events, status reports and audio by the minute (audio.minutes) in [since, until).device (query, string)
since (query, string)
until? (query, string)

/api/calls ​

方法说明参数
GETThe ASR and LLM calls since since, by kind and engine (db.calls), and the StepFun balance (db.balance).since (query, string)

/api/perf ​

方法说明参数
GETA day's (YYYY-MM-DD) performance rows in this tenant's store, oldest first (perf.day): {stalls, samples, requests, pieces}; the event loop's stalls and the process's minutes are in the first tenant's only.day (query, string)

/api/upgrades ​

方法说明参数
GETA device's firmware updates begun since since, oldest first: what its app logged joined with the recorder's DFU and BOOT records (db.upgrades).device (query, string)
since (query, string)

/api/client-config ​

方法说明参数
GETWhat the apps ask on start and while recording: piece lengths per app state (the server changes them without a new build; short pieces in front make drafts quick, long ones in the background save requests).—

/api/app-setup ​

方法说明参数
GETFor the web page's QR: the token to hand the app (its tenant's, the one the page sent), so it doesn't need typing in by hand.—

/api/events ​

方法说明参数
GET(无说明)—

/api/live ​

方法说明参数
GETEach device's live analysis of its open conversation.—

/api/live/{device} ​

方法说明参数
POST立即总结 (the live page's button): the device's live analysis now; 409 with why not.device (path, string)

/api/health ​

方法说明参数
GETNo token and no tenant (Tenants lets it through): for reverse proxies and docker-compose.yml's healthcheck.—

/api/status ​

方法说明参数
GET(无说明)—

/api/lang ​

方法说明参数
GETWhat the models write this tenant's summaries, recaps and answers in (tenant.py): {"lang": "zh"} or "en".—
PUTThe language from the next model call on; existing summaries keep theirs (重新生成 rewrites one).body: JSON object

/api/sentences ​

方法说明参数
GET(无说明)date? (query, string)

/api/conversations ​

方法说明参数
GET(无说明)date? (query, string)

/api/days ​

方法说明参数
GETDays that have sentences, newest first.—

/api/calendar ​

方法说明参数
GETPer day with sentences: closed conversations, their speech minutes and the sentence count, for the day picker and the calendar page.—
方法说明参数
GETdb.search; before is the start of the last one already shown.q? (query, string)
tag? (query, string)
who? (query, string)
after? (query, string)
before? (query, string)
limit? (query, integer)

/api/ask ​

方法说明参数
POSTA question about my own past answered from this app's data (ask.py), in 10-30 s: {answer, sources, calls, usage}.body: JSON object

/api/ask/voice ​

方法说明参数
POSTA question said aloud (the ask sheet's hold-to-talk: what the browser's MediaRecorder wrote, webm, ogg or mp4) as text, {text}, for the page to show and ask with /api/ask; "" when nothing was heard.—

/mcp ​

方法说明参数
GETNo server-sent stream and no sessions to end (2026-07-28).—
POSTRemote MCP for the owner's agents (mcp_server.py), with the same token as every other client: stateless, JSON only.—
DELETENo server-sent stream and no sessions to end (2026-07-28).—

/api/recap ​

方法说明参数
GETA day's recap, the newest one when no day is given; null when there is none.date? (query, string)

/api/todos/due ​

方法说明参数
GETMy todos due today or before, not marked done or wrong, with how stale each is (db.due_todos).—

/api/todos/{todo_id}/mark ​

方法说明参数
PUT完成 (done) or 不对 (wrong) on one todo, by its id (db.todo_ids); a mark again replaces it.todo_id (path, string)
body: JSON object
DELETEThe undo: the todo is open again.todo_id (path, string)

/api/todos/marks ​

方法说明参数
POSTMany todos at once: {ids, mark}, mark done or wrong, or null to drop their marks (the bulk undo).body: JSON object

/api/recap/{date} ​

方法说明参数
POSTWrite a day's recap again (15-20 s), e.g. after its summaries were redone.date (path, string)

/api/tags ​

方法说明参数
GET(无说明)—

/api/conversation/{conv_id} ​

方法说明参数
GET(无说明)conv_id (path, string)

/api/conversation/{conv_id}/feedback ​

方法说明参数
POSTA correction in the owner's words (wrongly greyed as media, a wrong speaker, a bad summary); collected and read back in batches to fix the prompt, never acted on one by one.conv_id (path, string)
body: JSON object

/api/feedback ​

方法说明参数
GETEvery correction, newest first, with the conversation's time and title for the read-back.—

/api/conversation/{conv_id}/summarize ​

方法说明参数
POST(无说明)conv_id (path, string)

/api/names ​

方法说明参数
GETNames already in use, most conversations first: suggestions for a rename.—

/api/conversation/{conv_id}/rename ​

方法说明参数
POSTOne label renamed; with a name the conversation has already, a merge; to 我, its voice learnt too (learn_me).conv_id (path, string)
body: JSON object

/api/speakers/undo ​

方法说明参数
POSTTakes back a rename, merge or 拆开 by the undo id its answer gave: the rows, the participants, the voices, pins and name hints as they were, the summaries redone, what the renamed sentences taught the people bank (the ledger, bank.py) both ways, the centroid a 「这是我」 added to 我's voiceprint dropped. 409 when the id is unknown (taken back already, too old, the server restarted) or a conversation changed since.body: JSON object

/api/speakers ​

方法说明参数
GETThe speakers page's cards of people (named, or OTHERS_MIN_S of speech over OTHERS_MIN_CONVS conversations) and how many other voices there are; ?others=1 the cards of those.others? (query, boolean)

/api/speakers/{gid} ​

方法说明参数
GET(无说明)gid (path, string)

/api/speakers/{gid}/halves ​

方法说明参数
GETThe two-half test of a card (bank.halves): mixed, and its two halves when it sounds like two people.gid (path, string)

/api/speakers/name ​

方法说明参数
POSTEvery member voice of a group (or of one of its suggestions) takes the name, in all of its conversations; with voices (conv|label), only those.body: JSON object

/api/speakers/detach ​

方法说明参数
POSTA voice that is not this speaker: a named one goes back to a fresh S<n> of its conversation; either way it is kept apart from the group from now on.body: JSON object

/api/speakers/{gid}/split ​

方法说明参数
POST拆开: the voices of a card a request picks (chosen: a half from the two-half test) out of it into a card of their own, in one transaction, with an undo.gid (path, string)
body: JSON object

/api/conversation/{conv_id}/speakers ​

方法说明参数
GET(无说明)conv_id (path, string)

/api/aliases ​

方法说明参数
GET(无说明)—
POSTAnother way a person is called (A哥 for A): hints with it suggest the name.body: JSON object

/api/person/{name} ​

方法说明参数
GETEverything about one named speaker: their conversations newest first with what they said there, the todos they own, and what I promised them.name (path, string)

/api/person/{name}/profile ​

方法说明参数
POSTMade (or made again) now: one chat call, some seconds.name (path, string)

/api/voice-clip/{name} ​

方法说明参数
GET(无说明)name (path, string)

/api/self ​

方法说明参数
GETThe card while it is up: candidates most likely first (none preselected), each with a tier instead of a %.—
POSTThe answer to the card: a candidate's index becomes 我's voiceprint (owner-redimnet2.npz), taken by every pipeline from its next conversation on: the conversations before keep their labels and summaries. "none" (都不是) and "later" (以后再说) put the card away until selfvoice.due brings it again.body: JSON object

/api/audio ​

方法说明参数
GETWAV of [start, end] (+0.2 s each side) cut from the uploaded pieces of device (any when not given) covering it.start (query, string)
end (query, string)
device? (query, string)

/api/listen ​

方法说明参数
GETListening in on a device (the live page polls it): its audio after after (unix ms, the X-After of the answer before; none: its newest piece) as WAV, X-Start the first piece's start; 204 when there is nothing newer.device (query, string)
after? (query, integer)

/api/stars ​

方法说明参数
GETThe starred sentences as they read now (a rename shows), the newest star first, with their conversation's start and title. ponytail: a star whose row a replay re-cut (another start, another id) drops out of the list.—

/api/sentences/{sid}/star ​

方法说明参数
PUT(无说明)sid (path, string)
body: JSON object
DELETE(无说明)sid (path, string)

/api/sentences/{sid}/speaker ​

方法说明参数
POSTThis sentence was said by to.sid (path, string)
body: JSON object