{"routes":[{"m":"GET","p":"/api/settings","app":"shared","s":"Horizon view settings: horizonDays (filter window) and activeArea."},{"m":"GET","p":"/api/prefs","app":"shared","s":"The per-app preferences blob ({global, horizon, luminate, pupil, loupe, refract}). Absent keys mean the client-side default.","n":"horizon.customCategories = [{id,label,icon}]; horizon.defaultArea/defaultImportance/defaultCategory; horizon.wakeHour (snooze mornings, default 8); loupe.zeroCatchRuns (default 5)."},{"m":"GET","p":"/api/attachments/{key}","app":"luminate","s":"Download a stored file (a Luminate PDF note's source PDF). key comes from a note's pdfKey."},{"m":"GET","p":"/api/bootstrap/horizon","app":"horizon","s":"Everything Horizon boots with in one call: pending tasks, checklists (recurring templates), checklist items, metric logs, areas, settings, prefs, and — with ?date — loupeToday.","q":[{"name":"date","type":"string","desc":"Local YYYY-MM-DD; enables loupeToday (Loupe checklists due that day, if the token also has loupe).","required":false}],"n":"Ships PENDING tasks only; done tasks come from GET /api/tasks?done=1."},{"m":"GET","p":"/api/tasks","app":"horizon","s":"All tasks (absorbed tasks never appear).","q":[{"name":"done","type":"string","desc":"\"1\" = completed only, \"0\" = open only, omitted = both.","required":false}],"n":"done=0 includes subtasks stranded open inside already-completed parents (left by clients that completed parents without cascading); the app hides any task with a done ancestor, so filter those out to match what the person sees. Task fields: id, parentId (dwell parent), title, notes, importance 1-5, area (domain id), category, followUp (due date YYYY-MM-DD), done, completedAt, festers, isMetric/metricUnit, snoozeUntil (epoch ms), timerStartedAt/timerMode/timerDurationSec/timerElapsedSec/timerAutoDone, campaign, checklistItemId, loupeChecklistId, inboxToken, createdAt."},{"m":"POST","p":"/api/tasks","app":"horizon","s":"Create a task.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"title","type":"string","desc":"Required. ≤500 chars. Renders **bold** *italic* __underline__ ~~strike~~ only.","required":true},{"name":"notes","type":"string","desc":"Markdown.","required":false},{"name":"importance","type":"integer","desc":"1-5, default 3.","required":false},{"name":"area","type":"string","desc":"Domain id, default \"personal\".","required":false},{"name":"category","type":"string","desc":"todo|storage|idea|event|note|<custom id>, default todo.","required":false},{"name":"followUp","type":"string","desc":"Due date YYYY-MM-DD.","required":false},{"name":"parentId","type":"string","desc":"Dwell parent task id.","required":false},{"name":"festers","type":"boolean","desc":"Default true.","required":false},{"name":"isMetric","type":"boolean","desc":"A numeric reading task.","required":false},{"name":"metricUnit","type":"string","desc":"","required":false},{"name":"createdAt","type":"integer","desc":"Epoch ms.","required":false}],"n":"snoozeUntil/timer fields are not accepted on create — PATCH them after."},{"m":"PATCH","p":"/api/tasks/{id}","app":"horizon","s":"Patch any of: parentId, title, importance, area, category, followUp, notes, done, completedAt, festers, isMetric, metricUnit, timerStartedAt, timerMode, timerDurationSec, timerElapsedSec, timerAutoDone, campaign, snoozeUntil, remindAt, loupeChecklistId, checklistItemId, lastFesterDate.","n":"Completing the way the app does: {done:true, completedAt:now} AND clear timer (timerStartedAt/timerMode/timerDurationSec null, timerElapsedSec = seconds run) AND snoozeUntil null. A task with open subtasks should only be completed together with them. Timer: timerMode \"up\"|\"down\", timerStartedAt epoch ms, timerDurationSec for countdowns."},{"m":"DELETE","p":"/api/tasks/{id}","app":"horizon","s":"Delete a task and every descendant. 409 if the task itself is absorbed into a Loupe checklist."},{"m":"GET","p":"/api/tasks/search","app":"horizon","s":"Semantic search over open and done tasks (meaning, not keywords). Empty array on any search-index failure.","q":[{"name":"q","type":"string","desc":"Query.","required":true}]},{"m":"POST","p":"/api/tasks/ai-sort","app":"horizon","s":"\"Help Me Sort\": classify up to 40 task titles into named groups. Streams SSE (assign / done events). Only classifies — moving tasks is the caller's job.","b":[{"name":"titles","type":"array","desc":"Task titles in order (≤50; the app sends 40).","required":true},{"name":"hint","type":"string","desc":"How to group, in the person's words.","required":false},{"name":"existingGroups","type":"array","desc":"Group names from earlier chunks, so everything files into one taxonomy.","required":false}],"n":"Costs a Workers AI call. Curated agents usually do this reasoning themselves."},{"m":"POST","p":"/api/tasks/estimate","app":"horizon","s":"\"Estimate for me\": a countdown duration suggestion for a task title, anchored on this account's own recorded timer runtimes.","b":[{"name":"title","type":"string","desc":"","required":true}]},{"m":"GET","p":"/api/tasks/{id}/inbox-token","app":"horizon","s":"The task's anonymous-inbox token and QR SVG (null if it isn't a receiver)."},{"m":"POST","p":"/api/tasks/{id}/inbox-token","app":"horizon","s":"Make the task an anonymous-inbox receiver, or rotate its token (old links stop working). Public page: /inbox?t=<token>; submissions land as subtasks immediately."},{"m":"DELETE","p":"/api/tasks/{id}/inbox-token","app":"horizon","s":"Stop the task receiving anonymous submissions."},{"m":"GET","p":"/api/inbox-submissions","app":"horizon","s":"Anonymous-inbox submissions (newest 100): text, status, receiverTaskId, taskId."},{"m":"POST","p":"/api/inbox-submissions/{id}/{accept|dismiss}","app":"horizon","s":"Decide a submission still marked pending (accept = create it as a subtask of its receiver). New submissions land as subtasks immediately, so this only matters for old rows."},{"m":"GET","p":"/api/checklists","app":"horizon","s":"Horizon recurring templates (not Loupe checklists): named groups of items that spawn tasks on a recurrence."},{"m":"POST","p":"/api/checklists","app":"horizon","s":"Create a recurring template.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":true},{"name":"area","type":"string","desc":"","required":false},{"name":"category","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/checklists/{id}","app":"horizon","s":"Patch name, area, category."},{"m":"DELETE","p":"/api/checklists/{id}","app":"horizon","s":"Delete a template and its items."},{"m":"GET","p":"/api/checklist-items","app":"horizon","s":"Items of recurring templates."},{"m":"POST","p":"/api/checklist-items","app":"horizon","s":"Add a recurring item.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"checklistId","type":"string","desc":"","required":true},{"name":"title","type":"string","desc":"","required":true},{"name":"importance","type":"integer","desc":"1-5","required":false},{"name":"recurrence","type":"string","desc":"JSON string or null. {\"type\":\"weekdays\",\"days\":[1,3,5]} (0=Sunday) | {\"type\":\"interval\",\"n\":2,\"anchor\":\"scheduled\"|\"complete\"} | {\"type\":\"monthly\",\"day\":15|\"last\"} | {\"type\":\"yearly\",\"month\":0-11,\"day\":1-31}.","required":false},{"name":"festers","type":"boolean","desc":"","required":false},{"name":"isMetric","type":"boolean","desc":"Spawned task records a number.","required":false},{"name":"metricUnit","type":"string","desc":"","required":false}],"n":"Recurrence is evaluated client-side while a Horizon tab is open — there is no server cron."},{"m":"PATCH","p":"/api/checklist-items/{id}","app":"horizon","s":"Patch checklistId, title, importance, recurrence, lastAutoAddedDate, lastCompletedDate, festers, isMetric, metricUnit."},{"m":"DELETE","p":"/api/checklist-items/{id}","app":"horizon","s":"Delete a recurring item."},{"m":"GET","p":"/api/metric-logs","app":"horizon","s":"Numeric readings recorded against metric items."},{"m":"POST","p":"/api/metric-logs","app":"horizon","s":"Record a reading.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"checklistItemId","type":"string","desc":"The metric item.","required":true},{"name":"value","type":"number","desc":"Must be a JSON number.","required":true},{"name":"taskId","type":"string","desc":"The spawned task the reading belongs to.","required":false},{"name":"createdAt","type":"integer","desc":"Epoch ms.","required":false}]},{"m":"DELETE","p":"/api/metric-logs/{id}","app":"horizon","s":"Delete a reading."},{"m":"POST","p":"/api/shared-lists","app":"horizon","s":"Create a printable/public check-off list snapshot of tasks; returns its /list?t= token URL.","b":[{"name":"items","type":"array","desc":"≤200 of {text (required), taskId, due YYYY-MM-DD, importance 1-5, notes}. A taskId links check-offs back to the real task.","required":true},{"name":"title","type":"string","desc":"Sheet heading.","required":false}],"n":"The link is public to anyone holding it and has no revoke UI — create only when the person asks."},{"m":"GET","p":"/api/areas","app":"horizon","s":"Domains (user-facing name for areas): id, label, color, sortOrder. Also readable with refract."},{"m":"POST","p":"/api/areas","app":"horizon","s":"Create a domain.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"label","type":"string","desc":"","required":true},{"name":"color","type":"string","desc":"#rrggbb","required":true},{"name":"sortOrder","type":"integer","desc":"","required":false}]},{"m":"PATCH","p":"/api/areas/{id}","app":"horizon","s":"Patch label, color (#rrggbb), sortOrder."},{"m":"DELETE","p":"/api/areas/{id}","app":"horizon","s":"Delete a domain. 400 while any task still uses it — move those tasks first."},{"m":"GET","p":"/api/notes","app":"luminate","s":"All notes, newest-edited first: id, title, body (markdown), folderId, pdfKey, pdfPageCount, createdAt, updatedAt."},{"m":"POST","p":"/api/notes","app":"luminate","s":"Create a note.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"title","type":"string","desc":"≤300 chars.","required":false},{"name":"body","type":"string","desc":"Markdown. __x__ = underline in this dialect.","required":false},{"name":"folderId","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/notes/{id}","app":"luminate","s":"Patch title, body, folderId. Returns updatedAt."},{"m":"DELETE","p":"/api/notes/{id}","app":"luminate","s":"Delete a note, its PDF, annotations, and scan suggestions."},{"m":"GET","p":"/api/notes/search","app":"luminate","s":"Semantic search over notes.","q":[{"name":"q","type":"string","desc":"","required":true}]},{"m":"POST","p":"/api/notes/{id}/pdf","app":"luminate","s":"Upload the source PDF for a PDF note (raw body, Content-Type application/pdf, ≤25MB)."},{"m":"POST","p":"/api/notes/{id}/pdf-scan","app":"luminate","s":"\"Scan for tasks\": extract up to 6 task suggestions from text lines of a PDF note (one AI call).","b":[{"name":"lines","type":"array","desc":"[{page, y, text}] — the app sends ≤250 per call.","required":true},{"name":"append","type":"boolean","desc":"Keep earlier suggestions (chunked scans).","required":false}]},{"m":"GET","p":"/api/note-folders","app":"luminate","s":"Folders: id, name, parentId, sortOrder."},{"m":"POST","p":"/api/note-folders","app":"luminate","s":"Create a folder.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":true},{"name":"parentId","type":"string","desc":"","required":false},{"name":"sortOrder","type":"integer","desc":"","required":false}]},{"m":"PATCH","p":"/api/note-folders/{id}","app":"luminate","s":"Patch name, parentId, sortOrder."},{"m":"DELETE","p":"/api/note-folders/{id}","app":"luminate","s":"Delete a folder. Its notes and subfolders move up to its parent — never deleted."},{"m":"GET","p":"/api/note-annotations","app":"luminate","s":"PDF margin annotations: noteId, page, x, y (PDF points), text."},{"m":"POST","p":"/api/note-annotations","app":"luminate","s":"Add a margin annotation.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"noteId","type":"string","desc":"","required":true},{"name":"page","type":"integer","desc":"","required":true},{"name":"y","type":"number","desc":"PDF point-space.","required":true},{"name":"x","type":"number","desc":"","required":false},{"name":"text","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/note-annotations/{id}","app":"luminate","s":"Patch text, x, y."},{"m":"DELETE","p":"/api/note-annotations/{id}","app":"luminate","s":"Delete an annotation."},{"m":"GET","p":"/api/note-pdf-suggestions","app":"luminate","s":"Task suggestions from PDF scans."},{"m":"PATCH","p":"/api/note-pdf-suggestions/{id}","app":"luminate","s":"Patch accepted, dismissed, taskId."},{"m":"GET","p":"/api/bootstrap/pupil","app":"pupil","s":"Everything Pupil boots with: members, certifications, member certifications, goals, notes, activity, prefs."},{"m":"GET","p":"/api/pupil-members","app":"pupil","s":"Members: id, name, phone, payRate, availability (JSON string), notes, hiredDate."},{"m":"POST","p":"/api/pupil-members","app":"pupil","s":"Create a member.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":false},{"name":"phone","type":"string","desc":"","required":false},{"name":"payRate","type":"string","desc":"","required":false},{"name":"availability","type":"string","desc":"JSON string, the 7-day grid object.","required":false},{"name":"hiredDate","type":"string","desc":"YYYY-MM-DD","required":false}]},{"m":"PATCH","p":"/api/pupil-members/{id}","app":"pupil","s":"Patch name, phone, payRate, availability, notes, hiredDate. Pay rate / availability / hired date changes log a timeline activity."},{"m":"DELETE","p":"/api/pupil-members/{id}","app":"pupil","s":"Delete a member and all of their goals, notes, certifications, activity."},{"m":"GET","p":"/api/pupil-certifications","app":"pupil","s":"Certification types: id, name."},{"m":"POST","p":"/api/pupil-certifications","app":"pupil","s":"Create a certification type.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":true}]},{"m":"PATCH","p":"/api/pupil-certifications/{id}","app":"pupil","s":"Rename."},{"m":"DELETE","p":"/api/pupil-certifications/{id}","app":"pupil","s":"Delete a type and every member's record of it."},{"m":"GET","p":"/api/pupil-member-certifications","app":"pupil","s":"Who holds what: memberId, certificationId, certifiedDate, expiresDate."},{"m":"POST","p":"/api/pupil-member-certifications","app":"pupil","s":"Record a certification for a member.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"memberId","type":"string","desc":"","required":true},{"name":"certificationId","type":"string","desc":"","required":true},{"name":"certifiedDate","type":"string","desc":"","required":false},{"name":"expiresDate","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/pupil-member-certifications/{id}","app":"pupil","s":"Patch certifiedDate, expiresDate."},{"m":"DELETE","p":"/api/pupil-member-certifications/{id}","app":"pupil","s":"Remove."},{"m":"GET","p":"/api/pupil-goals","app":"pupil","s":"Goals: memberId, text, dueDate, done, completedAt."},{"m":"POST","p":"/api/pupil-goals","app":"pupil","s":"Create a goal.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"memberId","type":"string","desc":"","required":true},{"name":"text","type":"string","desc":"","required":true},{"name":"dueDate","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/pupil-goals/{id}","app":"pupil","s":"Patch text, dueDate, done, completedAt."},{"m":"DELETE","p":"/api/pupil-goals/{id}","app":"pupil","s":"Delete a goal."},{"m":"GET","p":"/api/pupil-notes","app":"pupil","s":"Dated note cards: memberId, body, createdAt."},{"m":"POST","p":"/api/pupil-notes","app":"pupil","s":"Add a note.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"memberId","type":"string","desc":"","required":true},{"name":"body","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/pupil-notes/{id}","app":"pupil","s":"Patch body."},{"m":"DELETE","p":"/api/pupil-notes/{id}","app":"pupil","s":"Delete a note."},{"m":"GET","p":"/api/pupil-activity","app":"pupil","s":"Single-line timeline entries (server-written only)."},{"m":"POST","p":"/api/pupil-imports","app":"pupil","s":"Idempotent import of one dated artifact; deduped by member+date+text, same member+date with new text = an edit.","b":[{"name":"memberId","type":"string","desc":"","required":true},{"name":"artifactType","type":"string","desc":"note | goal","required":true},{"name":"text","type":"string","desc":"","required":true},{"name":"date","type":"string","desc":"YYYY-MM-DD","required":false}]},{"m":"GET","p":"/api/bootstrap/loupe","app":"loupe","s":"Everything Loupe boots with: folders, checklists, items, last 30 days of runs, items of open runs, prefs.","q":[{"name":"date","type":"string","desc":"Local YYYY-MM-DD.","required":false}]},{"m":"GET","p":"/api/loupe-folders","app":"loupe","s":"Deck folders (same semantics as Luminate folders)."},{"m":"POST","p":"/api/loupe-folders","app":"loupe","s":"Create a folder.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":true},{"name":"parentId","type":"string","desc":"","required":false},{"name":"position","type":"integer","desc":"","required":false}]},{"m":"PATCH","p":"/api/loupe-folders/{id}","app":"loupe","s":"Patch name, parentId (cycle-checked), position."},{"m":"DELETE","p":"/api/loupe-folders/{id}","app":"loupe","s":"Delete a folder; contents move up to its parent."},{"m":"GET","p":"/api/loupe-checklists","app":"loupe","s":"{checklists:[…]}: id, name, pausePoint, mode (do_confirm|read_do|null), kind (normal|non_normal), cadence, cadenceDays, dueTime, version, archived, position, folderId."},{"m":"POST","p":"/api/loupe-checklists","app":"loupe","s":"Create a checklist (id is the only required field).","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"name","type":"string","desc":"","required":false},{"name":"pausePoint","type":"string","desc":"","required":false},{"name":"mode","type":"string","desc":"do_confirm | read_do; omit = not chosen (no default by design).","required":false},{"name":"kind","type":"string","desc":"normal | non_normal","required":false},{"name":"cadence","type":"string","desc":"none|daily|weekdays|weekly|monthly","required":false},{"name":"cadenceDays","type":"string","desc":"CSV: weekly 0-6 (0=Sun), monthly 1-31.","required":false},{"name":"dueTime","type":"string","desc":"HH:MM","required":false},{"name":"folderId","type":"string","desc":"","required":false},{"name":"position","type":"integer","desc":"","required":false}]},{"m":"PATCH","p":"/api/loupe-checklists/{id}","app":"loupe","s":"Patch name, pausePoint, mode, kind, cadence, cadenceDays, dueTime, archived, position, folderId. version is not patchable — cut a revision."},{"m":"DELETE","p":"/api/loupe-checklists/{id}","app":"loupe","s":"Delete a checklist with its items, runs and revisions. Absorbed tasks are released back to Horizon first."},{"m":"GET","p":"/api/loupe-checklist-items","app":"loupe","s":"{items:[…]}: checklistId, challenge, response, note, position, killer, sourceTaskId.","q":[{"name":"checklistId","type":"string","desc":"Only this checklist.","required":false}]},{"m":"POST","p":"/api/loupe-checklist-items","app":"loupe","s":"Add one item (appends unless position given).","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"checklistId","type":"string","desc":"","required":true},{"name":"challenge","type":"string","desc":"","required":false},{"name":"response","type":"string","desc":"","required":false},{"name":"note","type":"string","desc":"","required":false},{"name":"killer","type":"boolean","desc":"","required":false},{"name":"position","type":"integer","desc":"","required":false}]},{"m":"POST","p":"/api/loupe-checklists/{id}/items/bulk","app":"loupe","s":"Add up to 50 items in one request, at the end or after afterItemId.","b":[{"name":"items","type":"array","desc":"[{challenge (required), response, note, killer}]","required":true},{"name":"afterItemId","type":"string","desc":"","required":false}]},{"m":"PATCH","p":"/api/loupe-checklist-items/{id}","app":"loupe","s":"Patch challenge, response, note, position, killer."},{"m":"DELETE","p":"/api/loupe-checklist-items/{id}","app":"loupe","s":"Remove an item; an absorbed source task is released first."},{"m":"POST","p":"/api/loupe-checklist-items/{id}/release","app":"loupe","s":"Send an absorbed item back to Horizon as the task it came from, and drop the item."},{"m":"POST","p":"/api/loupe-checklists/{id}/absorb","app":"loupe","s":"Turn Horizon tasks into items; the tasks are hidden (absorbedAt), never deleted or completed.","b":[{"name":"taskIds","type":"array","desc":"≤50 ids. Tasks with subtasks must not be absorbed.","required":true}]},{"m":"GET","p":"/api/loupe-checklists/{id}/stats","app":"loupe","s":"Evidence for the CURRENT version: runs, finished, median duration, per-item skip rate and catches."},{"m":"GET","p":"/api/loupe-checklist-runs","app":"loupe","s":"{runs:[…]} newest first: version, mode, runDate, startedAt, finishedAt, stoppedAt, stopReason, abandoned, taskId.","q":[{"name":"checklistId","type":"string","desc":"","required":false},{"name":"limit","type":"integer","desc":"1-200, default 50.","required":false}]},{"m":"POST","p":"/api/loupe-checklist-runs","app":"loupe","s":"Start a run (idempotent per checklist + runDate while open). Freezes item text.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"checklistId","type":"string","desc":"","required":true},{"name":"runDate","type":"string","desc":"The person's local YYYY-MM-DD.","required":false},{"name":"taskId","type":"string","desc":"A hex to complete when the run finishes.","required":false}],"n":"Runs are the person's instrument — an agent running them defeats the point. Read-only use is the norm."},{"m":"PATCH","p":"/api/loupe-checklist-runs/{id}","app":"loupe","s":"Close a run: finishedAt | stoppedAt + stopReason | abandoned. Closed runs are immutable. A finish completes a linked hex unless it holds open subtasks (taskCompleted:false, reason has_open_children); a stop never does."},{"m":"DELETE","p":"/api/loupe-checklist-runs/{id}","app":"loupe","s":"Delete a run and its items."},{"m":"GET","p":"/api/loupe-checklist-run-items","app":"loupe","s":"Frozen run items: runId, state (pending|checked|skipped), caught, caughtNote, deviationReason.","q":[{"name":"runId","type":"string","desc":"","required":true}]},{"m":"PATCH","p":"/api/loupe-checklist-run-items/{id}","app":"loupe","s":"Answer an item on an OPEN run (409 once closed): state, checkedAt, caught, caughtNote, deviationReason."},{"m":"GET","p":"/api/loupe-run-annotations","app":"loupe","s":"Corrections appended to closed runs.","q":[{"name":"runId","type":"string","desc":"","required":true}]},{"m":"POST","p":"/api/loupe-run-annotations","app":"loupe","s":"Append a correction to a closed run.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"runId","type":"string","desc":"","required":true},{"name":"text","type":"string","desc":"","required":true}]},{"m":"GET","p":"/api/loupe-checklist-revisions","app":"loupe","s":"{revisions:[…]}: version, itemCount, note, snapshot (JSON), evidence (JSON).","q":[{"name":"checklistId","type":"string","desc":"","required":false}]},{"m":"POST","p":"/api/loupe-checklist-revisions","app":"loupe","s":"Cut a revision: snapshot items + evidence at the current version, then version+1.","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"checklistId","type":"string","desc":"","required":true},{"name":"note","type":"string","desc":"What changed and why.","required":false}]},{"m":"GET","p":"/api/bootstrap/refract","app":"refract","s":"Everything Refract boots with: entries, questions, suggested tasks, areas, check-ins, insights, this week's achievements (hallItems), prefs."},{"m":"GET","p":"/api/refract-entries","app":"refract","s":"Weekly entries, newest first: weekStart (Monday), worstPart, bestPart, bothering, proud, summary, feedback, locked, per-answer summaries."},{"m":"POST","p":"/api/refract-entries","app":"refract","s":"Create the entry for a week (a race with another device returns the existing row).","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"weekStart","type":"string","desc":"Monday YYYY-MM-DD.","required":true}]},{"m":"PATCH","p":"/api/refract-entries/{id}","app":"refract","s":"Patch worstPart, bestPart, bothering, proud (refused until the week's Sunday), locked (false = unlock)."},{"m":"DELETE","p":"/api/refract-entries/{id}","app":"refract","s":"Delete a week's entry with its questions and suggestions."},{"m":"POST","p":"/api/refract-entries/{id}/generate","app":"refract","s":"\"Reflect\": 3 follow-up questions + 3-5 suggested to-dos from the reflection and related history (one AI call). Refused on a locked entry."},{"m":"POST","p":"/api/refract-entries/{id}/refract","app":"refract","s":"\"Refract\": lock the entry and write summary, feedback, and per-answer summaries (one AI call)."},{"m":"GET","p":"/api/refract-questions","app":"refract","s":"Follow-up questions: entryId, prompt, answer."},{"m":"PATCH","p":"/api/refract-questions/{id}","app":"refract","s":"Patch answer."},{"m":"GET","p":"/api/refract-suggested-tasks","app":"refract","s":"Suggested to-dos: entryId, title, rationale, whenHint, accepted, dismissed, taskId."},{"m":"PATCH","p":"/api/refract-suggested-tasks/{id}","app":"refract","s":"Patch accepted, dismissed, taskId. Accepting in the app = create a Horizon task (category \"refract\", notes prefixed \"**When:** <whenHint>\") then PATCH accepted+taskId."},{"m":"DELETE","p":"/api/refract-suggested-tasks/{id}","app":"refract","s":"Delete a suggestion."},{"m":"GET","p":"/api/refract-checkins","app":"refract","s":"Daily check-ins: weekStart, dayKey (mon-sat), prompt (frozen), answer."},{"m":"POST","p":"/api/refract-checkins","app":"refract","s":"Answer a day's check-in (unique per week+day).","b":[{"name":"id","type":"string","desc":"New row id — generate a UUID. Ids are client-generated everywhere in VISION.","required":true},{"name":"weekStart","type":"string","desc":"Monday YYYY-MM-DD.","required":true},{"name":"dayKey","type":"string","desc":"mon|tue|wed|thu|fri|sat","required":true},{"name":"answer","type":"string","desc":"≤500 chars.","required":true}]},{"m":"PATCH","p":"/api/refract-checkins/{id}","app":"refract","s":"Patch answer."},{"m":"POST","p":"/api/refract-surface/run","app":"refract","s":"Run the 8am/4pm/midnight surfacing pass (achievement sentence, Insights, narrative revision) now. AI calls; no-op when nothing's new."}]}