This document reflects the target assistant-first API direction agreed for the next work phase. Some endpoints described here replace current routes that still exist in code today; the workboard will track that migration explicitly.
All routes are SvelteKit. Page routes return SSR HTML. Form actions use application/x-www-form-urlencoded. JSON API routes use application/json.
Protected app routes are grouped under src/routes/(app) and enforced by src/routes/(app)/+layout.server.ts. Public auth routes remain outside that group at /signin, /auth/*, and /debug/auth/*.
See docs/schema.md for persisted-table reference and docs/ARCHITECTURE.md for runtime subsystem boundaries.
Home page. Loads all notes and links for the D3 force graph.
Server load returns:
{
nodes: { id, slug, title, status, category }[],
links: { source: string, target: string }[]
}Unified notes repository surface with integrated search, category filtering, and orphan detection.
Query params:
| Param | Type | Description |
|---|---|---|
q |
string | Title or category substring for repository search |
tags |
string | Comma-separated tag list |
category |
string | Category filter |
orphans |
string | Optional truthy flag to focus orphan notes |
Server load returns:
{
notes: { id, title, slug, tags, category, status, createdAt }[],
orphanIds: string[],
q?: string,
tagsParam?: string,
category?: string,
showOrphans?: boolean
}Notes:
/notesis the unified browsing, search, and management surface for existing notes.- The first implementation pass may keep filtering on the client over the loaded notes dataset.
orphanIdsis server-computed from notes with no incoming and no outgoingnote_links.- New note creation now originates from
/chat; the dedicated/notes/newpage has been removed.
Download all notes as a zip archive of Markdown files.
Response: application/zip binary with Content-Disposition: attachment; filename="techy-notes-YYYY-MM-DD.zip"
Each note is serialised to {slug}.md with YAML frontmatter (title, tags, aliases, category, status) followed by the body.
Errors: 500 on DB or zip failure
Note detail page.
Path param: slug — URL-safe note identifier
Server load returns:
{
note: Note,
htmlBody: string,
outgoing: { id, title, slug, status }[],
incoming: { id, title, slug, status }[]
}Errors: 404 if slug not found
Page behaviour:
- The note detail page may expose a
Read note/Stopcontrol in the header action area; seedocs/PWA-SPEECH.mdfor read-aloud behavior.
Legacy direct edit route for an existing note. The assistant-first direction shifts primary authoring into /chat, but direct edit may remain temporarily during migration.
Server load returns: { note: Note }
Errors: 404 if slug not found
Revision history list for a note, ordered most-recent first.
Server load returns:
{
note: { id, title, slug },
revisions: NoteRevision[]
}Errors: 404 if slug not found
Full content of a specific revision, rendered as HTML.
Server load returns:
{
note: { id, title, slug },
revision: NoteRevision,
htmlBody: string
}Errors: 404 if slug or revisionId not found, or revisionId does not belong to this note
Primary assistant surface for conversation and note authoring.
Server load returns:
{
providers: {
id: 'anthropic' | 'openai' | 'openrouter',
label: string,
models: { id: string, label: string }[],
defaultModel: string
}[],
defaultProvider: 'anthropic' | 'openai' | 'openrouter',
defaultModel: string,
notes: {
id: string,
title: string,
slug: string,
aliases: string[],
category: string | null,
status: 'stub' | 'growing' | 'mature',
updatedAt: string
}[],
conversations: { id: string, title: string | null, createdAt: string, updatedAt: string }[],
conversation: null,
messages: []
}GET /chat starts with an empty transcript. The first successful POST /api/assistant/respond call creates the app-owned conversation and returns its conversationId.
Resumed route-compatible page data shape:
{
// same provider, model, note, and conversations fields as above
conversation: {
id: string,
title: string | null,
createdAt: string,
updatedAt: string
},
messages: {
id: string,
role: 'user' | 'assistant',
content: string,
createdAt: string
}[]
}Page behaviour:
- Standard conversation stays available at all times.
- The page shows persisted conversations through a secondary Notebook Index surface so an older chat can be resumed without leaving
/chat. - The Notebook Index behaves like a slide-over/drawer overlay rather than a second primary app rail.
- Entering
/chatmay open the Notebook Index for quick resume; closing it does not reset the active transcript. - While already on
/chator/chat/[conversationId], activating the main appChatnav item toggles the Notebook Index surface instead of forcing a route reload. - Starting a new chat does not require deleting older conversations.
- After a successful assistant response, the conversation list updates immediately in-place (insert or reorder by
updatedAt) without requiring a page refresh. - The composer remains a single unified entry point rather than a primary mode switcher.
- The initial chat mode defaults to inference-first
Auto. - The UI may expose compact create/update override controls near the composer, but inference is the default routing path.
- The chat surface may render those override controls inside the composer chrome rather than as a separate top toolbar.
/chatis the sole note-authoring entry point for new notes; there is no dedicated new-note page.- If the assistant detects a strong match to an existing note, the page may surface that note inline and offer research or review actions without forcing an immediate update flow.
- Assistant transcript content may be rendered as markdown for readability rather than displayed as flat plain text.
- For topic-learning prompts in
Automode that do not strongly match an existing saved note, the response may include a lightweightcreateOfferCTA so the user can explicitly open the normal create-note draft flow from the same turn. - The page may render create/update proposals as editable inline draft panels and delete proposals as explicit confirmation cards.
- The page keeps the full saved transcript as canonical history, but submits a bounded replay window when continuing a conversation (currently the most recent 5 user+assistant exchanges).
- When loaded from
/chat/[conversationId], the page rebuilds the submittedmessagespayload from the saved transcript and includes the app-ownedconversationIdon subsequent respond calls. - Provider/model options come from the server-side registry in
src/lib/server/ai/models.ts. - The UI should continue to initialize provider/model state from those server-supplied defaults rather than introducing visual-only overrides.
- Provider and model selection should be presented through one consolidated assistant settings dialog to avoid clipped popovers and poor wrapping behavior.
- Respond-time prompt grounding also includes the shared canonical note-category list and a bounded deterministic list of existing lower-case note tags so create/update drafts reuse established taxonomy when possible.
- Assistant proposals must follow the standard note skeleton from
docs/NOTES.md:Overview,Description,Key Concepts,Connections, andResources, with only the approved optional sections allowed betweenKey ConceptsandConnections. - Assistant prompts also keep
Overviewbrief, treatDescriptionas the main explanatory section, prefer evergreen explanation over release-churn unlessVersion Notesis warranted, and ban deprecated default headings likeCurrent Status,Notable Features,Quick Examples, andIndustry Usage. - Conversational prompts should bias toward concise, digestible answers rather than long article-style replies.
- The initial
/chatselection defaults to OpenAI withgpt-5-mini. - The current OpenAI chat allowlist includes
gpt-5.2,gpt-5-mini,gpt-4o, andgpt-4o-mini. The current OpenAI default isgpt-5-mini. - The current Anthropic default is
claude-haiku-4-5-20251001. - The current OpenRouter allowlist includes
nvidia/nemotron-3-super-120b-a12b:free. The current OpenRouter default isnvidia/nemotron-3-super-120b-a12b:free. - Assistant responses may include a structured proposal for
create_note,update_note, ordelete_note. - Assistant responses may also include
createOffermetadata for conversational learning turns that are eligible to become notes. - Create/update proposals render as editable draft panels in chat before save.
- Delete proposals render as explicit confirmation UI.
- Live web citations may be shown in chat review as a collapsed sources disclosure, but are not persisted as dedicated source metadata.
- The chat UI should cap visible source links to a small primary set rather than dumping the full research result list into the thread.
- Persisted chat history stores the app-owned transcript, not provider-managed hidden conversation state.
- Resuming a conversation reconstructs the
messagespayload from saved chat history before calling the assistant again. - The response payload also includes
routing, which exposes the resolved mode, matched note, target note, and override source so the chat UI can show the selected branch without inferring it client-side. - The chat composer may expose a voice input toggle. Voice input transcribes speech into composer text and then uses this same endpoint when the user sends, so no assistant request contract changes are required.
- Assistant messages may expose a read-aloud control that speaks the assistant message content only. Proposal editors, confirmation controls, and citation disclosures are not included in readback by default.
- Voice controls are capability-detected. Unsupported browser speech features should disable the relevant control with clear user-facing feedback instead of failing silently.
Resume a previously saved assistant conversation.
Path param: conversationId — UUID of the saved chat conversation
Server load returns:
{
conversation: {
id: string,
title: string | null,
createdAt: string,
updatedAt: string
},
messages: {
id: string,
role: 'user' | 'assistant',
content: string,
createdAt: string
}[],
providers: {
id: 'anthropic' | 'openai' | 'openrouter',
label: string,
models: { id: string, label: string }[],
defaultModel: string
}[],
defaultProvider: 'anthropic' | 'openai' | 'openrouter',
defaultModel: string,
notes: {
id: string,
title: string,
slug: string,
aliases: string[],
category: string | null,
status: 'stub' | 'growing' | 'mature',
updatedAt: string
}[],
conversations: { id: string, title: string | null, createdAt: string, updatedAt: string }[]
}Notes:
- This route renders the same chat interface contract as
/chat, preloaded with owned conversation metadata and transcript rows. - The saved transcript is the canonical source of resumed chat state.
- If the conversation is missing or is not owned by the signed-in user, the route redirects to
/chat. - Provider-specific conversation IDs or hidden memory are not part of the route contract.
- The saved transcript remains the product source of truth; runtime model replay is currently limited to the most recent 5 user+assistant exchanges.
Daily coding-practice workspace.
Server load returns:
{
dailyProblem: PracticeProblem | null,
recentProblems: {
id: string,
title: string,
source: string,
sourceUrl: string,
difficulty: string | null,
dailyDate: string | null,
status: 'not_started' | 'in_progress' | 'completed' | 'skipped',
completedAt: string | null
}[],
stats: {
completed: number,
inProgress: number,
streakDays: number
}
}Page behaviour:
- Shows the current locally stored daily problem when available.
- Provides an authenticated control to fetch today's LeetCode daily challenge.
- Provides a manual JSON import fallback when automated fetch is unavailable or disabled.
- Links out to the source problem for final solving/submission.
- Does not load or persist tutor conversation history.
Focused practice workspace for one stored problem.
Path param: problemId - UUID of the stored practice problem
Server load returns:
{
problem: PracticeProblem,
progress: {
status: 'not_started' | 'in_progress' | 'completed' | 'skipped',
attempts: number,
notes: string,
codeSnapshot: string | null,
completedAt: string | null,
updatedAt: string
}
}Page behaviour:
- Renders problem statement, examples, constraints, topic tags, and source link from local storage.
- Lets the user update progress, attempts, notes, and an optional code snapshot.
- Sends current problem context and the latest user question/code to the transient tutor endpoint.
- Keeps tutoring turns in client state only; refreshes may clear them.
Batch-import notes from uploaded Markdown files. Files must have a YAML frontmatter block at the top.
Form encoding: multipart/form-data
Form fields:
| Field | Required | Description |
|---|---|---|
files |
yes | One or more .md files |
Behaviour:
- Parses
title,tags,aliases,category,statusfrom YAML frontmatter; falls back to first# Headingfor title. categorymust resolve to one of the canonical categories fromdocs/NOTES.md; invalid category strings are reported per file and skipped rather than persisted.- If a note with the same title already exists it is updated. Otherwise a new note is inserted.
[[wikilinks]]in each imported note are synced tonote_linksafter all upserts complete.
Success: Returns { importResult: { imported: number, errors: { file: string, message: string }[] } }
Errors:
400— no files provided- Per-file validation errors for malformed markdown/frontmatter, invalid category values, or slug/title conflicts
Legacy direct update action for an existing note.
Behaviour:
- Re-syncs
note_linksand preserves revision history. - Rejects non-canonical
categoryvalues with400before any revision snapshot or note update is written.
Legacy direct delete action for an existing note.
Success: Redirects 303 to /notes
Errors: 404 — note not found
Legacy assistant endpoint. Superseded by /api/assistant/respond (now live). Can be removed now that /api/assistant/commit (ASSIST-007) has landed and the mutation boundary is established.
Request body:
{ "query": "string" }Response (200):
{
"matchedNote": { "id": "...", "title": "...", "slug": "...", "url": "..." },
"summary": "string",
"possibleGaps": ["string"],
"newTopicIdeas": ["string"]
}matchedNote is null when no note closely matches the query. possibleGaps and newTopicIdeas are assistant-generated suggestions for expanding the graph.
Authenticated endpoint that fetches the current LeetCode daily challenge, normalizes it, and upserts it into practice_problems.
Request body:
{}Response (200):
{
problem: PracticeProblem,
source: 'leetcode',
fetchedAt: string,
created: boolean
}Behaviour:
- Runs only server-side for the authenticated user.
- Uses no LeetCode credentials or browser automation in the first pass.
- Fails closed if the unofficial LeetCode fetch path changes, times out, or is disabled.
- Does not persist raw fetch payloads unless a future debugging mode explicitly adds bounded diagnostics.
Errors: 401 unauthenticated, 502 upstream fetch/parse failure, 503 fetch disabled, unavailable, or practice storage migration pending
Authenticated manual JSON import fallback for a practice problem.
Request body:
{
source: string,
sourceSlug?: string,
sourceUrl: string,
title: string,
difficulty?: string,
dailyDate?: string,
promptMarkdown: string,
examples?: unknown,
constraints?: unknown,
topicTags?: string[]
}Response (200):
{ problem: PracticeProblem, created: boolean }Behaviour:
- Validates required source, URL, title, and prompt fields.
- Sends imported data through the same normalized upsert path as daily fetch.
- Allows personal-use JSON produced outside Techy without making the deployed app depend on the automated fetch path.
Errors: 400 invalid JSON or missing fields, 401 unauthenticated, 503 practice storage migration pending
Authenticated endpoint for updating per-user practice progress.
Request body:
{
problemId: string,
status: 'not_started' | 'in_progress' | 'completed' | 'skipped',
attempts?: number,
notes?: string,
codeSnapshot?: string | null
}Response (200):
{
progress: {
problemId: string,
status: 'not_started' | 'in_progress' | 'completed' | 'skipped',
attempts: number,
notes: string,
codeSnapshot: string | null,
completedAt: string | null,
updatedAt: string
}
}Behaviour:
- Upserts one progress row for the authenticated user and problem.
- Sets
completedAtwhen status first becomescompleted; clears it only if the user explicitly moves out of completed status. - Does not store tutor messages.
Errors: 400 invalid status or payload, 401 unauthenticated, 404 problem not found
Authenticated transient OpenRouter tutoring endpoint for a stored problem.
Request body:
{
problemId: string,
message: string,
code?: string,
hintLevel?: 'nudge' | 'pattern' | 'approach' | 'review',
model?: string
}Response (200):
{
reply: string,
model: string
}Behaviour:
- Loads problem context from local storage and sends only the current user turn/code to OpenRouter.
- Uses a practice-specific prompt that prefers stepwise guidance over full solutions.
- Does not create a
conversation, appendconversation_messages, or persist any practice tutor transcript. - Requires
OPENROUTER_API_KEY.
Errors: 400 invalid payload, 401 unauthenticated, 404 problem not found, 503 OpenRouter unavailable or unconfigured
Primary assistant endpoint for conversation, live research, and proposal generation.
Target direction: the assistant resolves intent server-side from the conversation and optional UI overrides. The current runtime still accepts the legacy explicit mode contract during migration, but the intended steady state is intent inference first, override second.
Request body:
{
"messages": [
{ "role": "user", "content": "Create a note about SvelteKit adapters" }
],
"override": "create",
"provider": "anthropic",
"model": "claude-opus-4-6",
"conversationId": "optional-existing-conversation-id",
"topicCache": {
"sveltekit adapters": {
"summary": "...",
"citations": [
{ "title": "SvelteKit docs", "url": "https://..." }
]
}
}
}| Field | Required | Description |
|---|---|---|
messages |
yes | Active conversation replay payload for the current request. When resuming, this is rebuilt from saved history and may be windowed (current policy: most recent 5 user+assistant exchanges). |
override |
no | Optional hard override for the assistant router: "chat" | "create" | "update". The default path is server-side inference from the conversation. |
provider |
yes | "anthropic" | "openai" | "openrouter" |
model |
yes | A server-approved model identifier for the chosen provider |
conversationId |
no | UUID of an existing owned conversation to continue. When omitted, the endpoint creates a new conversation for the authenticated user and returns its id. |
topicCache |
no | Ephemeral per-conversation research cache used to avoid re-researching the same topic repeatedly during an active runtime |
noteId |
conditional | UUID of the selected note to compare. Required only when the resolved intent is "update" and no strong exact title/alias note match can be resolved from the latest user turn. |
mode |
temporary | Legacy compatibility field during migration. Its semantics match override, and when both are present override wins. |
Response (200):
{
"assistantMessage": {
"content": "SvelteKit adapters determine how the app is deployed...",
"citations": [
{ "title": "SvelteKit docs", "url": "https://..." }
]
},
"createOffer": null,
"routing": {
"intent": "create",
"resolvedMode": "create",
"override": "create",
"overrideSource": "override",
"matchedNote": null,
"targetNote": null,
"noteId": null,
"latestUserMessage": "Create a note about SvelteKit adapters"
},
"proposal": {
"type": "create_note",
"draft": {
"title": "SvelteKit Adapters",
"body": "# SvelteKit Adapters\n\n...",
"tags": ["framework"],
"aliases": [],
"category": "Frameworks & Libraries",
"status": "growing",
"aiGenerated": true,
"aiModel": "claude-opus-4-6",
"aiPrompt": "Create a note about SvelteKit adapters"
},
"linkedNotePatches": [
{
"noteId": "existing-note-id",
"title": "SvelteKit",
"updatedBody": "... [[SvelteKit Adapters]] ..."
}
]
},
"topicCache": {
"sveltekit adapters": {
"summary": "...",
"citations": [
{ "title": "SvelteKit docs", "url": "https://..." }
]
}
},
"conversation": {
"id": "saved-conversation-id",
"title": "Create a note about SvelteKit adapters",
"updatedAt": "2026-04-13T19:22:41.000Z"
},
"conversationId": "saved-conversation-id"
}Behaviour:
- The assistant remains conversational regardless of the resolved intent.
- The endpoint requires an authenticated session and uses the session user id as the conversation owner.
- If
conversationIdis omitted, the endpoint creates a new conversation row before calling the assistant. - If
conversationIdis provided, it must identify an existing conversation owned by the authenticated user. - The latest incoming user message is appended before the provider call; the generated assistant message content is appended after a successful response.
- The response includes conversation metadata (
conversation) plusconversationIdso the client can update Notebook Index ordering and labels immediately without waiting for a page reload. - The endpoint is stateless with respect to provider-managed hidden conversation memory.
- The endpoint contract is provider-agnostic, but the adapters may differ internally: Anthropic currently uses the Messages API, OpenAI currently uses the Responses API, and OpenRouter currently uses Chat Completions compatibility.
- Prompt assembly starts from one shared assistant identity and layers routed skill instructions for conversation, create, update, and explicit-delete behavior on top.
- The router resolves whether the turn is best treated as chat, create, or update based on the latest user turn plus any explicit override.
routing.overrideSourceis"override","mode", or"none"so the UI can tell whether the resolved branch came from the new override field, the legacy compatibility alias, or pure inference.routing.matchedNotereports a conservative exact-match note hit from the latest user turn when one exists.routing.targetNotereports the effective note target for update flows, whether it came from an explicitnoteIdselection or inferred title/alias match.- Intent inference is conservative. Strong exact-title or alias matches may route into note-review behavior; weaker similarity should stay conversational and ask or suggest instead of silently picking a note target.
- A prompt like "teach me about Django" should remain conversational even if a
Djangonote exists. The server injects the matched note body for that chat turn so the assistant can summarize what is already saved and offer to research more or review it for updates without forcing anupdate_noteproposal. - A conversational learning turn without a strong saved-note match may return
createOffermetadata instead of an immediatecreate_notedraft. The client can use that offer to append an explicit create follow-up turn and then render the normal editable draft panel from the resultingcreate_noteproposal. - When the resolved intent is create, the assistant may return a
create_noteproposal while still answering conversationally about the topic. - For note proposals,
draft.categoryis expected to be one of the canonical categories documented indocs/NOTES.md. The DB still storescategoryastext, but the product contract treats it as a controlled vocabulary. - For note proposals,
draft.tagsremain flexibletext[]values, but the assistant should prefer already-established tags from the graph when they fit rather than inventing near-duplicates. - When the resolved intent is update, the server looks up the effective target note title and saved note body using either the selected
noteIdor a strong exact title/alias match, uses that title as the research topic, and injects both title and saved body into the system prompt alongside live research context. Empty saved bodies are still passed through explicitly so the assistant can treat them as incomplete notes rather than asking the user to restate the note. - Update proposals are server-normalized before being returned to the client: the selected
noteIdis attached to the proposal payload and the canonical saved note title remains the update target. delete_noteproposals remain explicit-intent only. The prompt layer only exposes a delete target when the latest user turn clearly asks to delete/remove a specifically selected or strongly matched saved note.- Existing-note detection is proposal-first, not mutation-first. Finding a related note may change the assistant's response framing, but it never commits changes without the explicit commit step.
- Live web research is performed for create and update-style turns, including update review flows, so the comparison is always grounded in current information.
- If the same topic is already known in the current conversation cache, the assistant should reuse that context rather than re-run the same live research.
- Citations are review-only and are not persisted as dedicated DB metadata in this phase.
- Chat-mode responses should be biased toward concise markdown-friendly output, while create/update flows may still use larger output budgets for full draft generation.
- OpenAI GPT-5-family requests may include adapter-level reasoning configuration without changing this endpoint contract.
- No provider-side conversation identifier is part of this endpoint contract. The returned
conversationIdis the app-owned persisted transcript id. - When chat history is resumed, the caller rebuilds
messagesfrom the saved transcript and currently sends only the most recent 5 user+assistant exchanges. topicCacheis an ephemeral optimization and is not intended to be the persisted source of truth for chat history.
Errors:
400— invalid body, missing messages, invalid provider/model combination, invalid selectednoteId, or update routing with neither a selected note nor a strong exact match401— missing app session, or invalid or missing provider API key404— providedconversationIdwas not found for the authenticated user429— provider rate limit exceeded500— assistant orchestration or provider failure
Authenticated fallback endpoint for speech-to-text when browser speech recognition is unavailable or unreliable.
This endpoint is optional for v1. If no server transcription provider is configured, the route may return 503 and the UI should rely on browser speech recognition only. See docs/PWA-SPEECH.md for the broader speech feature plan.
Request: multipart/form-data
| Field | Required | Notes |
|---|---|---|
audio |
yes | Short microphone recording from the current user interaction |
mimeType |
no | Browser-reported MIME type when available |
language |
no | Optional BCP 47 language hint, defaulting to English-oriented provider behavior |
Response:
{
transcript: string
}Behaviour:
- Requires the same authenticated session boundary as the rest of the app.
- Processes one short uploaded clip and returns plain transcript text.
- Does not persist raw audio, generated transcript metadata, provider request ids, or timing data.
- Provider selection stays server-side via environment configuration so the client contract does not change if the fallback provider changes.
Errors:
400— missing or invalid audio upload401— unauthenticated413— audio payload too large for the configured limit503— no transcription provider is configured500— transcription provider or server failure
Persist a confirmed assistant proposal.
Request body:
{
"proposal": {
"type": "create_note",
"draft": {
"title": "SvelteKit Adapters",
"body": "# SvelteKit Adapters\n\n...",
"tags": ["framework"],
"aliases": [],
"category": "Frameworks & Libraries",
"status": "growing",
"aiGenerated": true,
"aiModel": "claude-opus-4-6",
"aiPrompt": "Create a note about SvelteKit adapters"
},
"linkedNotePatches": [
{
"noteId": "existing-note-id",
"updatedBody": "... [[SvelteKit Adapters]] ..."
}
]
}
}Proposal types:
create_noteupdate_notedelete_note
Behaviour:
- This endpoint is the assistant mutation boundary for confirmed create, update, and delete proposals.
- Create/update drafts must use a canonical note category; non-canonical category strings are rejected with
400. create_notevalidates the assistant body skeleton, normalizes approved section headings, inserts the note, parses[[wikilinks]], and syncsnote_linksimmediately.- If
linkedNotePatchesare included, the commit also updates those existing note bodies to include the new[[wikilink]]and re-syncs theirnote_linksrows before returning. update_notevalidates the replacement body skeleton, stores a revision snapshot before updating the note, and re-syncs links.delete_noteremoves the note after explicit UI confirmation.
Response (200/201):
{
"result": {
"type": "create_note",
"note": { "id": "...", "slug": "sveltekit-adapters", "title": "SvelteKit Adapters" }
}
}Errors:
400— invalid proposal shape or non-canonical category in a create/update draft404— target note not found for update/delete, or alinkedNotePatchestarget ID does not exist at commit time409— title/slug conflict on create500— DB or sync failure
Legacy helper endpoint. No longer the intended product entry point once the assistant-first flow lands.
Request body:
{ "topic": "string", "provider": "claude" }Behaviour:
- Accepts
provider: "claude" | "chatgpt"; defaults to"claude". - Returns
{ body, model }, wheremodelreflects the current provider default. - The current defaults are
claude-haiku-4-5-20251001for Anthropic andgpt-5-minifor OpenAI.
Legacy helper endpoint. No longer the intended product entry point once the assistant-first flow lands.
Request body:
{ "topic": "string", "provider": "claude" }Behaviour:
- Accepts
provider: "claude" | "chatgpt"; defaults to"claude". - Generates and immediately persists a note, then syncs
note_linksand returns note metadata plusnextNoteIdeas. - The saved note writes
ai_modelusing the current provider default. - The current defaults are
claude-haiku-4-5-20251001for Anthropic andgpt-5-minifor OpenAI.
Auth.js actions are handled by the catch-all at src/routes/auth/[...auth]/+server.ts. The custom sign-in UI is a normal SvelteKit page at src/routes/signin/+page.svelte.
| Method | Path | Description |
|---|---|---|
GET |
/signin |
Custom sign-in page |
GET |
/auth/callback/github |
GitHub OAuth callback — do not call directly |
POST |
/auth/signout |
Sign out |
GET |
/signin?error=... |
Sign-in page with auth error message |
GET / POST |
/debug/auth/login |
Debug-only bypass login |
GET / POST |
/debug/auth/logout |
Clears the debug bypass session cookie |