LiteTextShare API Docs
LiteTextShare (by Lite Lake / ่ฝปๆน) pickup-code mode API reference. This page is for humans; AI agents should fetch the raw Markdown linked below.
TextShare API (LiteTextShare Backend)
Text express-cabinet (TextShare) backend API โ the single source of truth for frontend (BFF) and AI agent integration. Any change to this document MUST land in the same release as the corresponding API change.
- Base URL (REST, via BFF proxy):
https://textshare.litelake.com/api/** - Collaborative editing (WebSocket, direct connection, NOT via BFF):
wss://ws.textshare.{env}/collab/{noteId}?token={editToken}โ thewsUrlbase is returned by the create/verify responses, clients never assemble it.
Conventions
- All REST endpoints are
POST+application/json(except/docs/*and the WebSocket endpoint, which areGET). - Unified response envelope:
{ "code": number, "message": string, "data": T }. Success:code = 200. - Business errors use HTTP 200 and are distinguished by
body.code(4101/4103/4104). Rate limiting uses real HTTP 429 +Retry-After; storage outage (fail-closed) uses real HTTP 503. - All requests SHOULD carry the
X-Device-Idheader (UUID fromlocalStorage). Invalid/missing values degrade brute-force protection to IP-only dimension (no error).
POST /api/public/notes โ create note and issue pickup code
Create a note with locally-written initial content (write first, generate code
afterwards). The note is immediately active.
Request body:
{
"mode": "md",
"initState": "<base64(V1 update of the local Y.Doc)>"
}
mode:txt|md(whitelist; anything else โ 4103)initState: base64 of the local Y.Doc'sY.encodeStateAsUpdate()(V1). An empty string means an empty document. The server applies the update to an empty doc, folds it, and measures the plain-text byte length.
Response data:
{
"noteId": "cz9fkq2wm4xv...",
"pickupCode": "K7M2XQ",
"mode": "md",
"expiresAt": "2026-09-16T08:00:00Z",
"wsUrl": "wss://ws.textshare.{env}/collab",
"editToken": "9f2c8e...64 hex chars"
}
Errors: 4103 invalid mode / initState not applicable (HTTP 200),
4104 text exceeds 128 KB (HTTP 200), 429 rate limited (12/hour +
30/day per IP, real HTTP 429 + Retry-After), 503 storage unavailable
(fail-closed).
POST /api/public/pickup/verify โ exchange pickup code for collaboration entry
Request body:
{ "pickupCode": "K7M2XQ" }
The code is normalized server-side: strip spaces/hyphens, uppercase,
Oโ0, I/Lโ1. Response data has the same shape as the create response
(pickupCode echoes the normalized value).
Errors: 4101 pickup code invalid or expired โ unified message to prevent
enumeration (HTTP 200; each failure increments per-IP and per-device counters).
Brute-force protection: 5 failures / 10 minutes on EITHER dimension blocks the
caller with real HTTP 429 + Retry-After. Counters are not reset by success.
Redis outage โ real HTTP 503 (fail-closed).
GET /collab/{noteId}?token={editToken} โ collaboration WebSocket
Yjs collaboration endpoint served by a Hocuspocus-protocol-compatible server
(@hocuspocus/provider interop; every frame carries the VarString(docName)
prefix). Direct browser connection (never via BFF; gateway forwards per-host).
Handshake authorization (performed before the WebSocket upgrade):
noteIdexists,status = active, not expired (Redis-cached 60 s + MySQL fallback; expired/not-found โ rejected with 401, business code 4102)editTokenis valid and bound to thisnoteId(Redis; miss or Redis failure โ rejected, fail-closed, business code 4105)- Per-IP connection count < 10, peers per room < 16, single update <= 768 KB, single frame <= 1 MB
After the upgrade the server runs the y-protocols sync handshake
(SyncStep1/2), broadcasts awareness (cursors/presence), and persists every
commit as an incremental V1 update (append-then-compact into MySQL:
notes.snapshot + note_updates log). Documents expire 48 h after creation;
expiry closes rooms forcefully (clients receive a close event).
There are no read-only connections: possessing the token grants edit rights (v1 product definition).
GET /docs/api.md, /docs/api.en.md, /docs/api.zhs.md
This document (embedded in the binary; en is the default/fallback, zhs is
Simplified Chinese). Served as text/markdown with Cache-Control: no-cache.
GET /api/health, /api/ready
Framework built-in liveness/readiness probes (gateway healthcheck target).
Error code table
| body.code | Meaning | HTTP status |
|---|---|---|
| 200 | success | 200 |
| 400 | invalid arguments (binding) | 200 |
| 4101 | PICKUP_NOT_VALID (unified anti-enumeration message) | 200 |
| 4102 | NOTE_NOT_FOUND / expired (WS Authorize; REST does not reach it) | 200 |
| 4103 | INVALID_ARGUMENT (mode / initState) | 200 |
| 4104 | NOTE_TOO_LARGE (> 128 KB plain text after fold) | 200 |
| 4105 | EDIT_TOKEN_INVALID (WS handshake rejected) | 200 |
| 429 | RATE_LIMITED | 429 + Retry-After |
| 503 | STORAGE_UNAVAILABLE (Redis outage, fail-closed) | 503 |
| 500 | internal error | 500 |
Note: the baseline rate-limit middleware on /api/public write methods
(30/min/IP) responds with its own body shape when triggered; clients must
treat 429 by HTTP status code, not by body shape.