Skip to content

HTTP routes

Generated from the route constants and dispatcher in packages/web/src/server/index.ts, with handler contracts from server/follow.ts, server/bundle-route.ts, and server/logs-route.ts.

Every /api/threads/:threadId… route also reads the X-Enso-Project header (#395): a page names a new thread’s project with it, and an id that is no registered project is a 400 before the route runs.

Parser limitation: discovery recognizes top-level const *_ROUTE = /…/, const *_ROUTE = threadRoute(…), const *_ROUTE = promptRoute() and const *_ROUTE = new RegExp( declarations (only threadRoute derives its path; every other form states it in its metadata) plus direct request.method branches using url === … or url.startsWith(…). Statuses, input shapes, classes, acquisition, and self-logging are intentionally explicit checked metadata rather than inferred behavior.

Method Rendered path Handler / factory Input shape Success Named error statuses Class Builds / acquires? Logs itself? Source
GET /api/about createAboutRoute → about none 200 EnsoBundleAbout JSON 403 untrusted origin observation no no packages/web/src/server/index.ts:1542
packages/web/src/server/bundle-route.ts:169
POST /api/logs respondToBrowserLogs → receiveBrowserLogs body: EnsoBrowserLogBatch 204 empty 400 malformed batch; 403 untrusted origin; 413 body too large; 500 body-read failure browser-log-ingest no no packages/web/src/server/index.ts:1564
packages/web/src/server/logs-route.ts:29
GET /api/logs/bundle* createBundleRoute → bundle query: optional thread id/prefix 200 Markdown attachment 400 malformed thread filter; 403 untrusted origin observation no no packages/web/src/server/index.ts:1549
packages/web/src/server/bundle-route.ts:98
GET /api/logs/stats createLogStatsRoute → stats none 200 EnsoDayStats JSON 403 untrusted origin observation no no packages/web/src/server/index.ts:1558
packages/web/src/server/logs-stats-route.ts:62
GET /api/logs/tail* followLogTail query: optional level, thread id/prefix, process, since duration, from byte offset with the day it belongs to 200 SSE stream of EnsoLogTailFrame; a read the server cannot do ends it with an error event 400 unknown level, malformed thread filter, malformed since or from; 403 untrusted origin observation no no packages/web/src/server/index.ts:1553
packages/web/src/server/logs-tail-route.ts:152
GET /api/mode respondWithFreshThreadMode none 200 permission-mode JSON 403 untrusted origin static no no packages/web/src/server/index.ts:1530
GET /api/projects respondWithProjects none 200 EnsoProject[] JSON — the launch project first, then the registered ones 403 untrusted origin observation no no packages/web/src/server/index.ts:1333
POST /api/projects respondToProjectRegistration → ProjectRegistry.register body: EnsoProjectRegistration — an absolute path strictly under a root the ENSO_HOME config declares (projects.roots), optional title 201 EnsoProject JSON when registered; 200 when the directory already is a project 400 malformed body; 403 untrusted origin; 413 body too large; 422 refused, with the reason — not absolute, missing, not a directory, a root itself, outside every root (resolved through symlinks), or inside a hidden directory; 500 handler failure control no yes packages/web/src/server/index.ts:1337
DELETE /api/projects/:projectId respondToProjectRemoval → ProjectRegistry.remove path: projectId (sixteen hex digits) 204 empty — the registration is forgotten; its sessions stay on disk and are served again if the directory is registered again 403 untrusted origin; 404 no registered project with that id; 409 the launch project, or a thread in the project is running control no yes packages/web/src/server/index.ts:140
GET /api/projects/browse* respondWithBrowse → ProjectRegistry.browse query: optional path — a directory inside a declared root; absent, the roots themselves 200 EnsoDirectoryListing JSON — the directories under it that are inside a root and not hidden (symlinks resolved), which already are projects, and whether this one may be added 403 untrusted origin; 422 refused, with the reason — not absolute, missing, not a directory, outside every root, or inside a hidden directory observation no no packages/web/src/server/index.ts:1341
GET /api/prompts respondWithPrompts → PromptLibrary.list none 200 EnsoLibraryPromptListing JSON — the library’s prompts by name without their bodies, each with its bytes and estimated tokens, and every .md file it refused, with the reason naming it 403 untrusted origin; 404 a server started without a library; 500 handler failure observation no no packages/web/src/server/index.ts:1456
DELETE /api/prompts/:id respondToPromptRemoval → PromptLibrary.remove path: id; header: If-Match: <ETag> (the version read) or If-Match: * 204 empty — the file is gone; a thread that ran it keeps the text its log recorded 400 If-None-Match, a list, or a weak tag; 403 untrusted origin; 404 no such file, or a server started without a library; 412 the file changed on disk since it was read; 428 no If-Match; 500 handler failure control no yes packages/web/src/server/index.ts:138
GET /api/prompts/:id respondWithPrompt → PromptLibrary.read path: id — the file name under ENSO_HOME/prompts without .md, lowercase words joined by hyphens 200 EnsoLibraryPrompt JSON — its frontmatter fields, its body, the file’s bytes and the body’s estimated tokens — with an ETag header, the sha256 of the file’s bytes, which a later replace or delete of this version names 403 untrusted origin; 404 no such file, or a server started without a library; 422 the file is refused, with the reason naming it — unreadable, not UTF-8, no frontmatter, an unknown key, no name, a category that is not system, append or session; 500 handler failure observation no no packages/web/src/server/index.ts:136
PUT /api/prompts/:id respondToPromptWrite → PromptLibrary.write path: id; headers: exactly one precondition — If-None-Match: * (create: no file of that name), If-Match: <ETag> (replace the version read), If-Match: * (replace whatever is there); body: EnsoLibraryPromptWrite — name, description, category (system, append or session) and the markdown body 200 EnsoLibraryPrompt JSON with its new ETag — the file as read back after it was written whole (staged, then linked into place for a create, renamed over for a replace) 400 malformed body, or both headers, a list, or a weak tag; 403 untrusted origin; 404 a server started without a library; 412 the precondition does not hold, with the reason naming the file — it already exists (a refused file included), changed on disk since it was read, or is gone; 413 body too large; 428 no precondition — never an unconditional write; 500 the file does not read back, or a disk failure control no yes packages/web/src/server/index.ts:137
GET /api/stored-threads respondWithStoredThreads none 200 EnsoStoredThread[] JSON 403 untrusted origin; 500 handler failure observation no no packages/web/src/server/index.ts:1538
GET /api/threads respondWithThreads none 200 EnsoThreadSummary[] JSON 403 untrusted origin observation no no packages/web/src/server/index.ts:1534
GET /api/threads/:threadId respondWithThread path: threadId 200 JSON 400 malformed thread id; 403 untrusted origin; 404 no live session observation no no packages/web/src/server/index.ts:81
POST /api/threads/:threadId/abort respondToAbort → abortThread path: threadId; no body 200 EnsoAbortReceipt JSON 400 malformed thread id; 403 untrusted origin; 404 no live session; 409 nothing running control no yes packages/web/src/server/index.ts:106
packages/web/src/server/follow.ts:895
GET /api/threads/:threadId/changes respondWithThreadChanges path: threadId 200 EnsoThreadChanges JSON — git’s state in the thread’s cwd and every file that differs from its last-push baseline (#452): unpushed commits, staged and unstaged edits, deletions, renames and untracked files, each with its unified diff unless binary or too large; truncated past 500 files. A diff git cannot produce reads as unavailable. Read-only, no fetch 400 malformed thread id; 403 untrusted origin; 404 no session, or its project is no longer served; 500 handler failure observation no no packages/web/src/server/index.ts:101
POST /api/threads/:threadId/command respondToHostCommand → runHostCommand path: threadId; body: EnsoHostCommandRun — model with provider/id, thinking with a level, login with provider/authType, logout with a provider, compact with nothing; a view row (context, fork) is the page’s to do, never run 204 empty for a setting (the change arrives on the follow as model); 202 empty for a flow or action (login, logout, compact) — started, its questions and progress arrive on the follow as dialogs and notify lines 400 malformed id/body; 403 untrusted origin; 409 thread busy; 413 body too large; 422 unknown command, model or level, a provider without credentials, or a view (/context, /fork) the page does itself; 500 handler failure control yes yes packages/web/src/server/index.ts:110
GET /api/threads/:threadId/commands respondWithThreadCommands path: threadId 200 EnsoThreadCommands JSON — source says live, remembered or none 400 malformed thread id; 403 untrusted origin observation no no packages/web/src/server/index.ts:87
POST /api/threads/:threadId/configure respondToConfigure → configureThread path: threadId; body: EnsoSessionConfiguration — optional systemPrompt (a library prompt id, system or append, or null for pi’s default; absent leaves the session’s as it is), model (provider/id), thinking (a level that model supports) and mode; applied in that order under one lease, a value equal to the current one left alone 204 empty — every step applied (the changes arrive on the follow as model and permission-mode) 400 malformed id/body; 403 untrusted origin; 409 thread busy; 413 body too large; 422 JSON EnsoConfigurationRefusal — the step that refused (systemPrompt, model, thinking, mode) and the reason, the steps before it applied — a system prompt is refused by name when its id is not a library name, its file is missing, unreadable or unparseable, a session prompt, or empty, or the session already sent a request (before it, a new choice — pi’s default included — replaces the last); 500 handler failure control yes yes packages/web/src/server/index.ts:112
GET /api/threads/:threadId/context respondWithThreadContext path: threadId 200 EnsoThreadContext JSON — pi’s branch, request by request, and its context meter 400 malformed thread id; 403 untrusted origin; 404 no session; 500 handler failure observation no no packages/web/src/server/index.ts:91
GET /api/threads/:threadId/context/:request respondWithContextRequest path: threadId, request (a turn number or next) 200 EnsoContextRequestDetail JSON — one request’s context, element by element 400 malformed thread id; 403 untrusted origin; 404 no session or no such turn; 500 handler failure observation no no packages/web/src/server/index.ts:93
POST /api/threads/:threadId/continue respondToContinue → continueThread → ThreadRuntime.continueRun path: threadId; no body 202 empty — the torn tail is continued from the stored state with no new user message; the run arrives on the follow and its settled releases the thread 400 malformed thread id; 403 untrusted origin; 404 no thread under that id, live or stored; 409 thread busy; 422 nothing to continue — the thread does not end on an unanswered prompt, an unfinished tool call, or tool results with no reply, or this pi build has no run lifecycle to continue in; 500 handler failure control yes yes packages/web/src/server/index.ts:127
packages/web/src/server/follow.ts:440
POST /api/threads/:threadId/dialog respondToDialog → answerThreadDialog path: threadId; body: DialogAnswer 204 empty 400 malformed id/body; 403 untrusted origin; 404 no live question; 413 body too large; 422 answer refused; 500 handler failure control no yes packages/web/src/server/index.ts:105
packages/web/src/server/follow.ts:518
GET /api/threads/:threadId/eval respondWithThreadEval path: threadId 200 EnsoSessionEval JSON — the mechanical timeline and scorecard (#441): the branch’s events off pi’s entries, the queued prompts and interrupts off the day files the branch spans 400 malformed thread id; 403 untrusted origin; 404 no session; 500 handler failure observation no no packages/web/src/server/index.ts:97
GET /api/threads/:threadId/events respondWithThreadEvents path: threadId 200 EnsoThreadEvents JSON 400 malformed thread id; 403 untrusted origin; 404 no live session observation no no packages/web/src/server/index.ts:83
GET /api/threads/:threadId/follow followThread path: threadId 200 SSE stream 400 malformed thread id; 403 untrusted origin; 500 handler failure observation no no packages/web/src/server/index.ts:103
packages/web/src/server/follow.ts:202
POST /api/threads/:threadId/fork respondToFork → forkThreadAt → AgentHost.forkThread path: threadId; body: EnsoForkRequest — entryId, the pi entry id of one of the thread’s user messages; the fork is pi’s /fork at position before, taken from the session file 200 EnsoForkReceipt JSON — the new thread id (its session file names the source as parentSession) and the forked message’s text, for the new composer 400 malformed id/body, or an entry that is no user message; 403 untrusted origin; 404 no session file in a served project, or no such entry; 409 thread busy; 413 body too large; 500 handler failure control no yes packages/web/src/server/index.ts:125
packages/web/src/server/follow.ts:851
GET /api/threads/:threadId/git respondWithThreadGit path: threadId 200 EnsoThreadGit JSON — the thread’s cwd and git’s state there (#453): repository, branch, upstream, the last-push baseline and the rule that chose it, ahead/behind, and changed files by where they sit; not-a-repository or unavailable with git’s reason otherwise. Read-only, no fetch 400 malformed thread id; 403 untrusted origin; 404 no session, or its project is no longer served; 500 handler failure observation no no packages/web/src/server/index.ts:99
GET /api/threads/:threadId/history respondWithThreadHistory path: threadId 200 EnsoThreadHistory JSON 400 malformed thread id; 403 untrusted origin, or a session stored in a directory that is no project here (named, #378); 404 no session — an id nobody has used, which a page starts; 500 handler failure observation no no packages/web/src/server/index.ts:85
GET /api/threads/:threadId/manifest respondWithThreadManifest path: threadId 200 EnsoForkPoint JSON — identity, then run conditions, off pi’s branch 400 malformed thread id; 403 untrusted origin; 404 no session; 500 handler failure observation no no packages/web/src/server/index.ts:95
POST /api/threads/:threadId/mode respondToModeChange → changeThreadMode path: threadId; body: EnsoPermissionModeChange 204 empty; the change arrives on the follow as permission-mode 400 malformed id/body; 403 untrusted origin; 409 thread busy; 413 body too large; 422 mode not offered; 500 handler failure control yes yes packages/web/src/server/index.ts:108
packages/web/src/server/follow.ts:551
POST /api/threads/:threadId/prompt respondToPrompt → promptThread path: threadId; body: EnsoPromptBody 202 EnsoPromptReceipt JSON 400 malformed id/body or unavailable command; 403 untrusted origin; 409 the thread was taken and released again while the prompt waited on its build (rare; #444); 413 body too large; 500 handler failure control yes yes packages/web/src/server/index.ts:104
packages/web/src/server/follow.ts:340
GET /api/threads/:threadId/stats respondWithThreadStats path: threadId 200 EnsoThreadStats JSON — pi’s session for the numbers, the log’s day files for the times 400 malformed thread id; 403 untrusted origin; 404 no session; 500 handler failure observation no no packages/web/src/server/index.ts:89
GET /api/threads/:threadId/system-prompt respondWithSystemPromptPreview → HostedThread.previewSystemPrompt path: threadId; query: optional prompt — a library prompt id (system or append); absent is pi’s default 200 EnsoSystemPromptPreview JSON — pi’s rendering of the next request’s system prompt with that prompt, section by section (who wrote each: the library or pi), the tool definitions sent beside it, and the total the Context tab prices a first request made with it at; the thread’s own choice is untouched 400 malformed thread id; 403 untrusted origin; 404 a prompt named on a server without a library; 409 thread running; 422 the prompt is refused, with the reason naming it — missing, unreadable, not UTF-8 or unparseable, a session prompt, or empty; 500 pi’s rendering does not split back into its sections (the prompt’s own text is matched whole, so it cannot cause this), or handler failure observation yes no packages/web/src/server/index.ts:119
POST /api/threads/:threadId/system-prompt respondWithDraftPreview → HostedThread.previewSystemPrompt path: threadId; body: EnsoSystemPromptDraft — an unsaved prompt’s mode (system or append), text and name 200 EnsoSystemPromptPreview JSON — the same preview a saved prompt gets, for the editor’s breakdown while it types; nothing is recorded 400 malformed id/body; 403 untrusted origin; 409 thread running; 413 body too large; 500 pi’s rendering does not split back into its sections (the draft’s text is matched whole, so it cannot cause this), or handler failure observation yes no packages/web/src/server/index.ts:123

Detected 26 regex route constants and 12 literal dispatcher routes; metadata is exhaustive for both sets.