HTTP API
Sadie’s HTTP surface is deliberately small. Most business logic lives in server actions under apps/app/server/*-actions.ts and is reachable only from React Server Components + useFormState. The routes on this page are the exceptions: things a browser, a Cloudflare scheduler, or a stress test would call directly.
POST /api/chat: streaming assistant
Section titled “POST /api/chat: streaming assistant”Primary chat endpoint. Takes a prompt, streams Sadie’s reply as server-sent events, then persists the final message and detected emotion.
Auth. Session cookie via requireSession. 404s if the target conversation is not the caller’s.
Request body
{ "conversationId": "string", "prompt": "string", "autoContinue": false}autoContinue: true skips inserting a user message. The dock’s quickAskSadie uses that path when it has already pre-created the conversation and redirected.
Response
Content-Type: text/event-stream. Each line is data: {...}\n\n. Chunk shape:
{ type: "token", value: "..." } // partial text, append in order{ type: "emotion", value: "curious" } // one of seven Sadie emotions{ type: "complete", value: "" } // end-of-stream sentinel{ type: "error", value: "..." } // surfaced errorsAfter the stream closes, the route runs any enabled chat-scope policies against the final text. A block match replaces the output with a short apology and flips the conversation’s emotion to serious. A rewrite match silently substitutes. Violations are persisted to policy_violations.
POST /api/compile/run: compile job trigger
Section titled “POST /api/compile/run: compile job trigger”Triggers the heavy compile pipeline: discourse opportunities, voice portrait rebuild, soul narrative, optional briefs, and (threshold-gated) agent evolution.
Auth. Either a valid session cookie (runs for that user) or Authorization: Bearer <SADIE_CRON_SECRET> (runs for body.userId if provided, otherwise all users). See apps/app/lib/cron.ts.
Request body
{ "kind": "agent_evolution", // optional: run the SEPL cycle directly "userId": "string", // cron only: target one user "withBriefs": false, // set true to generate up to 2 briefs "force": false // only with kind=agent_evolution; bypass XP threshold}Response
{ "ok": true, "users": [{ "userId": "...", "opportunities": 3, "briefs": 0 }], "durationMs": 2140}Writes a compile_runs audit row per user whether the run succeeds or fails.
POST /api/compile/wiki: wiki recompile
Section titled “POST /api/compile/wiki: wiki recompile”Rebuilds the wiki from sources. See apps/app/app/api/compile/wiki/route.ts. Same auth as /api/compile/run. Used by the onboarding compile step and as a periodic incremental refresh.
POST /api/compile/lint: wiki lint pass
Section titled “POST /api/compile/lint: wiki lint pass”Runs the wiki lint module against all entries, produces lint_findings. Same auth contract.
POST /api/compile/promote: preference to Soul promotion
Section titled “POST /api/compile/promote: preference to Soul promotion”Evaluates preference candidates against promotion thresholds, promotes the ones that cross, logs the rest. Same auth contract.
POST /api/compile/decay: Soul decay pass
Section titled “POST /api/compile/decay: Soul decay pass”Decays Soul items that stopped receiving reinforcing evidence. Same auth contract.
POST /api/policies/test: lint dry-run
Section titled “POST /api/policies/test: lint dry-run”Run the user’s active policies against a sample string without persisting anything. The Policies settings panel uses this to preview rewrites + blocks.
Auth. Session cookie.
Request body
{ "text": "string", "scope": "chat" // chat | studio | brief (default chat)}Response
{ "passed": true, "rewritten": "...", "violations": [ { "policyId": "...", "label": "...", "action": "rewrite", "matchedText": "...", "rewrittenTo": "..." } ]}POST /api/feeds/refresh-all: scheduler endpoint
Section titled “POST /api/feeds/refresh-all: scheduler endpoint”Polls every active feed, dedups across users by handle, fans results out to global_feed_items and user_feed_item_links.
Auth. Session cookie or Authorization: Bearer <SADIE_CRON_SECRET>. Cron callers can pass { "userId": "..." } in the body to narrow to one user; omit to hit all users.
Response includes the number of handles refreshed and any per-handle errors.
POST /api/sources/upload, POST /api/sources/url
Section titled “POST /api/sources/upload, POST /api/sources/url”File upload + URL ingestion for sources. Multipart and JSON respectively. Behind requireSession. Produce sources rows and kick off downstream compile work.
GET /api/auth/bootstrap?next=/path
Section titled “GET /api/auth/bootstrap?next=/path”The one and only cookie writer. On first visit, creates the owner user + session + onboarding row, sets the session cookie, and 302s to next. Anonymous direct loads of /today go through this route automatically via requireSession.
GET /api/internal/health
Section titled “GET /api/internal/health”Boot sanity check consumed by pnpm verify:health. Returns a small JSON shape with session + row counts. Not a public contract.
Not listed here
Section titled “Not listed here”Server actions under apps/app/server/*-actions.ts are not HTTP routes; they bind to form submissions and React Server Component invocations. If you want to call Sadie programmatically, prefer the routes above or write your own route handler that imports the same repos.