HTTP API and its gates
Everything under /api/* on app.logishell.com is served by one worker.
The table below is not written by hand: it is rendered from the route
registry that lives next to the code (apps/ui/src/routes.manifest.ts), and
a test fails the build when the worker routes on a path the registry does
not know. Prefixes, not every route: /api/repos also covers
/api/repos/<id>/tree. A path belongs to the longest prefix that covers it,
so /api/llm/status is public while /api/llm/complete is not.
Three ways in
- Session. The browser cookie you get after signing in. This is the door for a person at a screen.
- Owner machine token.
Authorization: Bearer <APP_M2M_TOKEN>. There is one token per deployment and it always acts as the deployment owner; a wrong bearer is a 401, never a silent fall back to the cookie. - Both. Routes marked
bothaccept either: a bearer when one is presented, the session otherwise. This is how the runner on your machine, the LOG cube on the desktop and the owner MCP door reach the same API.
The owner MCP door talks to this API with the machine token, so it
sees exactly the groups marked m2m, both and admin. The personal MCP
door runs inside the same worker and calls the handlers directly with the
identity of your OAuth token; there is no fourth way in.
Route groups
<!-- api-doc:start -->
| prefix | gate | MCP | what |
|---|---|---|---|
/api | public | Catch-all for API paths this deployment does not serve: answers 503 not_deployed instead of the SPA shell. | |
/api/achievements | session | Achievements shelf and claims; the founder counter (/founders, /early) is public because the landing has no session. | |
/api/admin | admin | Admin LOG: notes queue (/notes), founders list, the first hundred by invite (/first-hundred) and game publish tokens; the MCP owner door reads and closes notes through the same gate. | |
/api/admin/me (GET) | public | Answers whether the current session is an admin; an anonymous caller gets {admin:false}, not 401. | |
/api/agent-grants | session | Revocable links that hand one agent to another person; only the owner mints and revokes them. | |
/api/agent-guest | guest-token | The receiving side of an agent grant: the token opens one agent on one machine, never /api/agents or the organization. | |
/api/agents | session | Conversational agents shelf and their turns; no m2m on purpose, a turn spends on behalf of the owner. | |
/api/apps | public | api_get | Apps & Plugins registry: the public shelf reads without a sign-in, publishing needs a session, and a self or org release is invisible to anyone it was not meant for. |
/api/artifact-docs | both | api_get | Artifacts by link: a document an AI session hands to the caller (report, spec, criteria), kept with its earlier versions and opened at /artifact/<id> under their sign-in. Personal: the owner is part of the key, so an id from another account answers 404. GET lists and reads, POST creates, PUT writes a new version; the body holds up to 512 KB. Not the data map at /api/artifacts, which only looks. |
/api/artifacts (GET) | both | The data map of the caller: one list of where their data physically lives, grouped by store, with the groups that have no inventory named rather than hidden. Read only by construction, the handler answers 405 to anything else. | |
/api/ask | both | api_get | Questions to people and agents; which door answered decides whether you may answer your own question. |
/api/assistant | both | LOG assistant chat and durable turns (/turn); m2m lets the desktop cube ask without an IDE window open. | |
/api/auth | public | Better Auth endpoints (sign-in, session, OAuth); /providers lists what this door offers, mail routes are rate limited. | |
/api/auth/apple/notifications (POST) | public | Apple's server-to-server notices (consent revoked, Apple ID deleted, mail forwarding toggled). Public by necessity: the address lives in Apple's console and nothing can be added to it, so the JWT signature over Apple's own keys IS the gate. | |
/api/auth/handoff (POST) | public | Sign-in handoff to another cookie jar (desktop window): /start and /redeem work without a session by design. | |
/api/auth/handoff/approve (POST) | session | Approves a handoff code; only a signed-in session can do it, because that session is what gets handed over. | |
/api/billing | session | Entitlements bought with money: the plan price list reads without a sign-in like the compute price list, everything else belongs to the signed-in person. | |
/api/board | both | api_get | Kanban board cards and columns; the owner m2m door lets MCP tools move cards. |
/api/book | session | Book of rules: types, rules and handle for the person behind the cookie. | |
/api/bought-access | session | The list of model access bought from a seller (address, format, model, requests per minute), the same on every device of the caller. Never the key: a record with a key-like field is refused, keys stay in the vault. PUT writes one record, the later edit wins; DELETE leaves a tombstone so another device does not bring it back. | |
/api/calendars | both | External calendars: connect Google and Microsoft accounts read-only (several per person), come back from consent, disconnect. Connecting needs a signed-in browser; events land in /api/time. | |
/api/calendars/accounts | both | api_get | Connected calendar accounts of the caller with their state (ok or needs_reauth) and last sync time; never tokens. |
/api/canvas | both | api_get | Whiteboard canvases, personal to the caller; m2m lets LOG draw without a browser. |
/api/card | session | The QR business card of the signed-in person. | |
/api/channel | both | Personal publishing channel and its posts. | |
/api/cloud | both | api_get | The cloud member of the fleet: GET /capacity says whether a short lived Cloudflare machine can take a run now; POST /runs {ref, command, cwd?, timeoutSec?} clones that ref of LogiShell/Monorepo read only and runs one command; GET /runs/{id} reads status and the output tail, /stream follows it, DELETE stops it. The machine is destroyed when the command ends. Owner and admins only (the account pays); otherwise owner_only, and not_deployed when this deploy has no sandbox. |
/api/connect/events (POST, OPTIONS) | public | LOG Connect event door for @logishell/connect: a batch of log.action / log.error, accepted from a connected Origin, a body signed with the project server key, or localhost with a dev key; rate limited per project, submit only. | |
/api/consent | session | Consent records of the signed-in person. | |
/api/ctx | both | Shared memory: contexts, topics, sessions, companion; the runner writes run traces here with the owner m2m token. | |
/api/delivery (GET) | admin | Delivery of LogiShell itself: CI runs and releases mirrored into D1 by the pipeline, read by the Delivery surface. A user’s own repository runs come from their forge through the runner, not from here. | |
/api/device-tokens | session | Machine tokens of the signed-in person: list and revoke, read authoritatively past the cookie cache; the token itself is never shown, only its hash is stored. | |
/api/diagnostic (POST) | public | Diagnostic report ingest from the desktop; no session, because the first breakages happen before sign-in. | |
/api/disk | both | api_get | Disk storage next to repos, behind the same two doors as /api/repos. |
/api/docs | session | Publishing a document to a link (/d/<id>): the snapshot, its list and its revocation. Session only on purpose: making a text public is a decision of the person at the screen, not of a machine token. | |
/api/documents | both | api_get | Docs pages of desks without a project, personal to the caller: the cloud mirror that makes them visible on another device and in the web app. |
/api/e (POST) | public | Product event ingest; a user id is attached only from a verified session, never from the body. | |
/api/errors | public | Errors caught automatically: surfaces post with the consent of the person and an optional session, owner machines post with the service bearer, a wrong bearer is a 401. | |
/api/feedback/embed (POST, OPTIONS) | public | Submit-only feedback widget door; the project id grants no read access and the Origin must be on the project allowlist. | |
/api/games | public | Games showcase: the catalog is public, publishing needs the author Bearer publish token minted by an admin. | |
/api/github/star | session | Tracks whether the signed-in person starred the repo. | |
/api/github/star/webhook (POST) | webhook-hmac | GitHub star webhook, verified with GITHUB_STAR_WEBHOOK_SECRET (sha256 HMAC). | |
/api/handwritten-notes | both | Handwritten notes captured from paper and their recognized text. | |
/api/hosted | session | Hosted services under run.logishell.com (Workers for Platforms): upload, list and remove the scripts of the signed-in person. | |
/api/integrations | both | Connected integrations of the caller (MCP servers, services) and their approvals. | |
/api/internal | internal | Node workers: saved-link capture (claims, uploads, gc behind SAVED_LINK_WORKER_TOKEN) and the GitHub token of the owner linked account for CI keepers (POST /api/internal/github/token, service bearer only, bound repositories). | |
/api/interpretations | both | api_get | Library interpretations of saved links and notes. |
/api/invites | session | Inviting people: the owner mints and revokes invitations, the invitee reads which profile was set for them; the role gate sits inside. | |
/api/island | session | iPhone Live Activity island: which machine to follow, searched across all spaces of the person. | |
/api/labs | session | Labs: published labs are readable without a session, writes resolve the cookie authoritatively. | |
/api/landing-session | public | One-bit credentialed CORS bridge for the landing: signed in or not, false for an anonymous visitor rather than 401. | |
/api/library/search | both | api_get | Search across the Library of the caller. |
/api/llm | both | LLM facade: complete, stream, routed and platform completions, usage and gateway receipts; the brain, keys and limits are chosen on the server. | |
/api/llm/status (GET) | public | Names which brains this deployment offers and carries no secret. | |
/api/market | session | Compute market orders of the signed-in person. | |
/api/mcp-apps | session | Own MCP servers of the signed-in person for apps on the canvas (LSH-631): connect by https address, list tools with their ui:// templates, call a tool, read a template; everything goes through the MCP gateway. | |
/api/me | session | Who am I and from which machine: account email and the name of the device token the request came with (lsh whoami). | |
/api/media | both | Evidence frames of QA proofs: named, previewed and searchable on a board card; the runner uploads them with the owner m2m token, the owner reads them, a stranger gets the same 404 as a made-up id. | |
/api/navigator/embed (POST, OPTIONS) | public | LOG Navigator door for the embeddable bubble: one LOG turn over the marks the page sends; the project key grants no read access and the Origin must be on the project allowlist. | |
/api/net | session | Network graph of people and organizations around the caller. | |
/api/network | session | Network editorial: the public page of a subject and its publication, behind the same door as the profile. | |
/api/nodes | both | api_get | Paired machines (device relay): list, pair, settings and proxy; approve, revoke and forget need owner or admin in an organization and are journaled (GET /audit, last 200); delivery control under /proxy/delivery needs the m2m door, a cookie is not enough. The live screen address of a machine is kept per person and machine (GET /live-screens, PUT and DELETE /{id}/live-screen): http and https only, never for a work zone. |
/api/nodes/claim (GET, POST) | public | Claiming a machine by QR: an unpaired machine asks for a code (start), polls for an owner (status), and accepts or declines the account a phone brought it. Public by necessity, the machine has no passport yet; held by a per-IP ceiling, a ten-minute TTL and a one-time code whose hash alone is stored. The phone half of the same door, GET and POST /claim/<code>, asks for a session itself. | |
/api/nodes/connect (GET) | public | The machine door: a headless node dials out with its pair code or per-device token in X-LB-Node-* headers, no cookie exists. | |
/api/note-sources | both | Sources of handwritten notes (scans, photos) and their upload. | |
/api/notes | both | api_get | Notes and the why-journal; m2m lets the local MCP door record decisions. |
/api/notifications | both | In-app notifications: list, create and mark read; m2m acts as the deploy owner, so a service (admin probe, node-watch) can wake the devices of the owner. | |
/api/onboarding | both | Onboarding links (/onboarding/<code>) and the rides through them. Split gate on purpose: creating, listing and revoking a link needs a session, while starting a ride and marking a step work without one, because at a stand the guest scans a QR before they are anybody. The lock on a ride is its unguessable id. | |
/api/orgs | session | Organizations of the signed-in person, served by the Network handler. | |
/api/profile | session | Profile of the signed-in person: what they show to others. | |
/api/push | both | Web push: public key, subscribe and unsubscribe; behind the same two doors as the inbox, so the owner m2m reaches it too. | |
/api/recent-tabs | both | api_get | Recently opened tabs of the caller, synced across surfaces. |
/api/recordings | both | Run and screen recordings; the runner uploads them with the owner m2m token because a terminal process has no cookie. | |
/api/reminders | both | api_get | Reminders at a set moment: create one for an event or on its own, answer it (done, snooze, cancel) and set quiet hours. The moment itself is kept by a per-person clock, so nothing here depends on an open window. |
/api/repos | both | api_get | Repositories: tree, files and git, proxied to the repo service with the caller stamped in. |
/api/rooms | both | Agent rooms, the executing side of a topic; an agent without a browser enters with the owner m2m token. | |
/api/saved-links | both | api_get | Saved links of the Library: save, tag, open, retry capture and delete. |
/api/search | session | Built-in LogiShell Search of the browser tab; behind the session so it is not an open proxy from our domain. | |
/api/seen | both | api_get | What this person has looked at: marks, the unseen queue, a check for agents. |
/api/services/brains | session | Owner side of hosted brains: create, key and revoke; agents reach /brains/:id/mcp with a key the owner minted. | |
/api/session-shares | session | Sharing a live terminal session: only a signed-in person mints and revokes, never the m2m owner token. | |
/api/sessions | session | Device Center: the sessions of the signed-in person, read authoritatively from the database and revocable. | |
/api/share | public | Share links to clips: GET peeks and POST redeems without any account, PUT (create) and DELETE (revoke) need a session. | |
/api/shared | session | The guest side of a shared session: the guest is a signed-in person with their own account, the token from the link is presented here. | |
/api/shared-agents | session | The receiving side of an agent handed to an account: the session is the key, so there is no token to forward and no expiry to remember. | |
/api/slate | both | api_get | Logbook pages (internal name Slate) and their artifacts. |
/api/store | session | Store: listings, purchases and the earn-only $LSH ledger of the signed-in person. | |
/api/surfaces | both | api_get | Surfaces composed from bricks, saved per caller. |
/api/talk | session | Messenger: connect, people, search, calls, room links and attachments (/files) for the signed-in person. | |
/api/talk/calls/status (GET) | public | Whether this deployment can call (hub, rooms, attachments, ICE, relay); carries no secret, the UI hides dead buttons by it. | |
/api/talk/guest | guest-token | Guest by link into a call room; what the guest may do next is decided by the guest boundary in TalkHub. | |
/api/themes | session | Themes shelf: browsing works without a session, applying and publishing answer 401 inside themesApi. | |
/api/time | both | api_get | Time tracking entries of the caller. |
/api/todo | both | api_get | Personal todo list: cards of the same board table under the reserved todo address, never part of a board; GET lists, POST {title, kind?} adds one line (the palette door). |
/api/vault | session | Personal vault proxied to the vault worker; the worker itself trusts only the stamp this gate adds. | |
/api/voice | session | Voice: realtime session tokens for the signed-in person. | |
/api/window-budgets | session | How much of the owner subscription each person may spend, counted per provider window; only the owner reads and sets it. | |
/api/work | both | api_get | Acceptance of AI work: submissions with evidence, read receipts, verdicts. |
/api/workspace-links | session | Where the cloud copy of a working copy lives, per person instead of per browser; the bytes stay in repo-service. |
Gates:
public: no sign-insession: signed-in browser session (cookie)m2m: owner machine token (Bearer APP_M2M_TOKEN), identity from M2M_OWNER_EMAILboth: session or owner m2madmin: ADMIN_EMAILS or owner m2minternal: service binding / internal secretguest-token: per-link guest tokenwebhook-hmac: HMAC-signed webhook
98 groups. A path belongs to the longest prefix that covers it. <!-- api-doc:end -->
Not on this page
Routes outside /api (/healthz, /mcp, /.well-known/*), an OpenAPI
document and per-route methods. Those come with the next step of the same
slice.