Every API here is available over the APIs.io API and to AI agents over MCP.
{"openapi":"3.0.3","info":{"title":"marginalia public API","version":"0.1.7","description":"Public read + chat API for marginalia (https://marginalia.polycode.co.uk). No auth today; the API is public. Generated from route-descriptors.mjs.","license":{"name":"AGPL-3.0-only"}},"servers":[{"url":"https://marginalia.polycode.co.uk","description":"production"},{"url":"https://ci-{slug}.marginalia.polycode.co.uk","description":"CI branch","variables":{"slug":{"default":"main"}}}],"paths":{"/api/graphs/default":{"get":{"operationId":"getDefaultGraph","summary":"Resolve the current default graph id.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/graphs":{"get":{"operationId":"listGraphs","summary":"List memory graphs (past + current). `?all=1` bypasses the margin cap.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"all","in":"query","required":false,"schema":{"type":"string"}}]},"post":{"operationId":"createPrivateGraph","summary":"(Tier-1) Create a private graph (archived-from-birth, owner-only, cap 16). Returns the one-time API key.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/status":{"get":{"operationId":"getStatus","summary":"Deployment status: caps, default graph, bedrock_enabled, mode, version.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/usage":{"get":{"operationId":"getUsage","summary":"Bedrock usage drill-down for a scope.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"scope","in":"query","required":false,"schema":{"type":"string"}},{"name":"scopeId","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/usage/all":{"get":{"operationId":"getUsageAll","summary":"Every LLM call or web search in a calendar month.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"month","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/budget":{"get":{"operationId":"getBudget","summary":"Today + month-to-date spend vs caps, plus Tavily credits.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"query","required":false,"schema":{"type":"string"}},{"name":"sessionUuid","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/diverts":{"get":{"operationId":"getDiverts","summary":"The graph's standing mechanical auto-diverts (Reflex) + today's avoided-turn ROI.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/graph/{graphId}/activity":{"get":{"operationId":"getActivity","summary":"Recent synth turns for a graph.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/session/{sessionUuid}/history":{"get":{"operationId":"getSessionHistory","summary":"Turn history for a session.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionUuid","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/session/{sessionUuid}/meta":{"get":{"operationId":"getSessionMeta","summary":"Session meta (introduction, searchability).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionUuid","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/insights":{"get":{"operationId":"getInsights","summary":"Latest hourly insights for a graph.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/daily-summary":{"get":{"operationId":"getDailySummary","summary":"Latest daily typed-edge prose summary (markdown) for a graph.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/entities":{"get":{"operationId":"getEntities","summary":"Extracted OWL types view: domain classes, object properties, and individuals.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graph/{graphId}/insights/refresh":{"post":{"operationId":"refreshInsights","summary":"Regenerate a graph's insights now; returns the fresh snapshot.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graph/{graphId}/turn/{turnId}":{"get":{"operationId":"getTurn","summary":"A single turn by id.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}},{"name":"turnId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/sessions":{"get":{"operationId":"getSessionsSearch","summary":"Full-text search across session introductions.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/sessions/log":{"get":{"operationId":"getSessionsLog","summary":"Combined visitor + socials message log: per-session channel, shortened ids, created time, message count.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graph","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/visitor/{visitorId}":{"get":{"operationId":"getVisitor","summary":"Pseudonymous visitor meta (label, creation time).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"visitorId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/projects":{"get":{"operationId":"getProjects","summary":"List a graph's projects (default: the current graph).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graph","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/projects/{projectId}":{"get":{"operationId":"getProjectDetail","summary":"Project detail: file map, log, suggestions.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/projects/{projectId}/file":{"get":{"operationId":"getProjectFile","summary":"Read a project file by relpath.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}},{"name":"relpath","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/project/{projectId}/op":{"post":{"operationId":"postProjectOp","summary":"(Admin for the shared graph; owner for a private graph) Project lifecycle op: { action: \"reopen\"|\"conclude\"|\"archive\"|\"delete\", note? }.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/openapi.json":{"get":{"operationId":"getOpenapi","summary":"This OpenAPI document.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/chat":{"post":{"operationId":"postChat","summary":"Async one-turn chat: returns 202 + a task id; poll /api/chat/result. sessionUuid is optional — minted and returned when omitted (reuse it to keep conversation context).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["userMessage"],"properties":{"graphId":{"type":"string","description":"Target graph id; omit for the default graph."},"sessionUuid":{"type":"string"},"userMessage":{"type":"string"},"volatile":{"type":"boolean","default":false},"visitorId":{"type":"string","description":"Optional pseudonymous visitor id."}}}}}}}},"/api/v1/chat/completions":{"post":{"operationId":"postMechanicalCompletion","summary":"OpenAI-shaped MECHANICAL completion shim: { messages } → grammar Formulate → SPARQL Solve → template Render. Token-free (usage all 0); the `marginalia` block carries the receipt (sparql, sources) or a stated miss. graphId via X-API-Key else the shared default.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/chat/result":{"get":{"operationId":"getChatResult","summary":"Poll an async chat task: working | completed (with reply) | failed.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"task","in":"query","required":false,"schema":{"type":"string"}}]}},"/api/keys":{"post":{"operationId":"postKeys","summary":"(Tier-1, logged-in) Provision a private graph + mint an API key (shown once). Send { action: \"delete\", hash } to delete one of your own keys.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}},"get":{"operationId":"getKeys","summary":"(Tier-1) List the caller's private graphs + their API-key metadata (hash, issued_at, last-4 suffix; never plaintext).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/keys/whoami":{"get":{"operationId":"whoamiKey","summary":"(X-API-Key) Which graph does this key resolve to? Key-authed self-discovery: { graph_id, issued_by, suffix }. Never echoes the key.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/api/test/seed":{"post":{"operationId":"postTestSeed","summary":"Prime behaviour-test fixtures into the sandbox graph (sandbox-only).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/flag":{"post":{"operationId":"postFlag","summary":"Flag a memory node for operator review.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target":{"type":"string"},"reason":{"type":"string"},"note":{"type":"string"}}}}}}}},"/api/visitor/{visitorId}/label":{"post":{"operationId":"postVisitorLabel","summary":"Set a visitor's screened, unverified label.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"visitorId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["label"],"properties":{"label":{"type":"string"}}}}}}}},"/api/graph/{graphId}/session/{sessionUuid}/introduction":{"post":{"operationId":"postSessionIntroduction","summary":"Set a visitor's session introduction.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}},{"name":"sessionUuid","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"introduction":{"type":"string"},"searchable":{"type":"boolean"}}}}}}}},"/api/graph/{graphId}/export":{"post":{"operationId":"postExport","summary":"Export a graph (stub).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graphs/{graphId}/repo":{"post":{"operationId":"setRepo","summary":"(Owner of a private graph; admin for a public graph) Bind the graph to a source repo: { repo_owner, repo_slug, repo_url?, default_branch?, intent_path?, license?, summary_branch?, summary_path?, agent_config? }. Mirrors onto graph meta for the showcase + the Phase-18 scoper.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graph/{graphId}/summary":{"get":{"operationId":"getRepoSummary","summary":"(Owner/admin/X-API-Key) Build the compact screened showcase seed (summary.json shape) from the live graph — top-N degree-ranked summaries + themes + provenance.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/graphs/default/clear":{"post":{"operationId":"clearUserDefault","summary":"(Tier-1) Clear the caller's per-user default graph (revert to the shared graph).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/me/default-graph":{"post":{"operationId":"setMyDefaultGraph","summary":"(Tier-1+) Set the caller's default graph: { graphId } — a private graph you own, or the shared default. Keyed A2A/chat requests that name no graph land here.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graphs/{graphId}/delete":{"post":{"operationId":"deletePrivateGraph","summary":"(Owner) Delete a private graph + its tree, keys, and default pointer.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graphs/{graphId}/label":{"post":{"operationId":"renamePrivateGraph","summary":"(Owner of a private graph; admin for any graph) Rename a graph (single-token, screened).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graphs/{graphId}/default":{"post":{"operationId":"setUserDefault","summary":"(Owner) Set this private graph as the caller's login default.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/graphs/{graphId}/prompt-note":{"get":{"operationId":"getPromptNote","summary":"(Owner of a private graph; admin for a public graph) Read the graph's free-text prompt note.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}]},"post":{"operationId":"setPromptNote","summary":"(Owner of a private graph; admin for a public graph) Set { note } — free text (max 2000 chars, guardrail-screened) injected into the graph's system prompt. Empty note clears it.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/admin/work-item":{"post":{"operationId":"postWorkItem","summary":"(Admin) Create or comment on a GitLab work item: { action: \"create\", title, description, labels? } or { action: \"comment\", iid, body }. REST mirror of the work_item_* chat tools.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/admin/turn":{"post":{"operationId":"postAdminTurn","summary":"(Admin) Trigger an autonomous turn now: { graphId? } — async-invokes the turn lambda with force (bypasses the cadence gate); graphId targets one graph, else the default.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/admin/delivery":{"post":{"operationId":"postDelivery","summary":"(Admin) GitLab delivery-loop action: { action: assign | mr_review | mr_comment | mr_comment_assign | mr_merge | mr_close | pipeline_status | issue_close, iid?, ... }. REST mirror of the delivery chat tools — the admin acts as themself (no caps/footer/merge-policy gate).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/hooks/gitlab/{graphId}":{"post":{"operationId":"postGitlabHook","summary":"(Connector secret) GitLab webhook receiver for a bound supervisor graph — X-Gitlab-Token constant-time-validated against the binding; kept events are shaped and queued for ingest.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/hooks/github/{graphId}":{"post":{"operationId":"postGithubHook","summary":"(GitHub HMAC) GitHub webhook receiver for a bound graph — X-Hub-Signature-256 constant-time-verified against the org webhook secret in SSM (/marginalia/{env}/github-webhook-secret); kept events (push/PR/issues/comment/discussion/release) shaped and queued for ingest. 401 on bad signature or an unbound graph.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"parameters":[{"name":"graphId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}},"/api/hooks/push":{"post":{"operationId":"postHooksPush","summary":"(X-API-Key) Generic collector: batched { messages: [{ label, body, source_refs?, actor? }] } shaped and queued into the key's bound graph (plain-git history, CI pushes).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Bad request"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}}}}}}