Base URL, auth and conventions
Every endpoint below is served by the Kemory API. Paths are given exactly as the service exposes them; prepend your deployment's origin.
| Concern | Detail |
|---|---|
| Content type | application/json on request and response, except artifact upload (multipart) and artifact download (binary stream). |
| Versioning | REST lives under /api/v1, apart from the health probes under /health and the LocalFS artifact stream at /artifacts/{token}. The MCP transport is at /mcp/v1. Discovery documents — and the OAuth dynamic-client-registration endpoint they advertise — sit at the root, where the specifications require them. |
| Auth | A bearer token on Authorization, or an API key. See below. |
| Errors | 401 unauthenticated. A credential that resolves to no organisation is also rejected on some routes, but this is not enforced uniformly across the API today — do not rely on it either way. 403 authenticated but not permitted. 404 absent or outside your tenant, deliberately indistinguishable. Not uniformly, though — a number of by-id operations across memories, agents and permissions currently return 400 instead of 404 for a row that exists but is not yours, and the choice differs between verbs on the same resource. So do not read 400 as necessarily "your payload is malformed"; on a by-id call it may mean "gone, or not yours". |
Three ways to authenticate
This table describes the hosted deployment. A deployment running the single-user identity provider instead — the default in the community compose file — accepts a single static API key — sent either way, since a non-JWT credential arriving in the Authorization header is still treated as a key — and rejects real JWTs.
| Credential | Header | Used by |
|---|---|---|
| Keycloak RS256 bearer | Authorization: Bearer <jwt> | Human users in the dashboard; the OAuth connector path. |
| HS256 bearer | Authorization: Bearer <jwt> | Agents holding a signed service token. |
| API key | X-API-Key: <key> — or Authorization: Bearer <key>, which is what remote MCP clients configured for API-key auth actually send | Registered agents; the usual choice for MCP. |
20 of the operations below need no credential, each marked no auth in the tables. They are not only probes and public documents, so the categories are worth reading rather than assuming:
- Health — five probes.
- OAuth discovery — three
.well-knowndocuments, plus the dynamic client registration shim, which echoes a fixed pre-registered public PKCE client rather than creating one. - Artifact streams — three, carrying their own signature in the URL precisely so a binary can be fetched without a bearer.
- Code-claiming endpoints — the pair claim, and the join claim and validate. The code is the credential, so these are unauthenticated by design and are the security-interesting members of this list: a pair claim self-registers an agent.
- Telemetry ingest and the public plan catalogue.
The full list is generated from the same document as the endpoint tables, so it cannot fall behind the code:
| Method | Path | Why it is open |
|---|---|---|
| GET | /.well-known/kemory-cells | Kemory Cells |
| GET | /.well-known/oauth-authorization-server | Oauth Authorization Server |
| GET | /.well-known/oauth-protected-resource | Oauth Protected Resource |
| GET | /.well-known/oauth-protected-resource/mcp/v1 | Oauth Protected Resource Mcp |
| GET | /.well-known/openai-apps-challenge | Openai Apps Challenge |
| GET | /api/v1/artifacts/{artifact_id}/blob | Stream artifact binary (signed-URL auth — no bearer needed) |
| GET | /api/v1/billing/plans/public | Kemory's plan catalogue, unauthenticated |
| GET | /api/v1/chats/{chat_id}/artifacts/{artifact_id}/blob | Stream a binary artifact body (signed-URL auth, no bearer needed) |
| POST | /api/v1/join/{code}/claim | Claim a Kemory bypass/referral code (no auth — the code is the credential) |
| GET | /api/v1/join/{code}/validate | Validate a Kemory bypass/referral code (no auth — the code is the credential) |
| POST | /api/v1/pair/{code}/claim | Self‑register an agent using a pair code (called by the AI) |
| POST | /api/v1/telemetry | Ingest an opt-in anonymous CLI telemetry event |
| GET | /artifacts/{token} | Stream a LocalFS artifact by signed token |
| GET | /health/deep | Deep Health |
| GET | /health/history | Health History |
| GET | /health/live | Liveness |
| GET | /health/pipeline | Pipeline Health |
| GET | /health/ready | Readiness |
| GET | /health/retrieval | Retrieval Health |
| POST | /oauth/register | Oauth Register |
A credential is not the whole access boundary. Some Analytics and Consolidation operations additionally require an internal-staff identity and are not reachable with an API key or an agent token, however valid.
Scoping
Most tenant-scoped ORM reads have WHERE org_id = <caller's org> injected by a SQLAlchemy do_orm_execute listener, acting as a safety net over the filters handlers also apply. It is a net, not a universal guarantee: it covers registered models, SELECTs, and statements that expose a mapped entity — raw SQL and unmapped statements are the handler's own responsibility.
Memories default to owner-private, but for two of the four visibilities visibility is access control, not a label: a memory written org-public is readable by every user in the organisation, and one written team by every member of that team. So "scoped to their owner" is the default, not an invariant.
agent-private is the opposite trap — it is only a label. The tenancy filter treats it exactly like user-private, and no read path narrows it to the writing agent, so it does not hide a memory from the same user's other agents. Do not use it as an isolation boundary.
shared is the default namespace name, not an access tier — it gets no special handling in the gatekeeper or the tenancy filter. Agents authenticate as their user, so any namespace the user can reach is reachable by that user's agents; whether a given agent actually reads it is then decided by its own gatekeeper rules.
Compaction — the tier ladder
A memory is not stored once and left alone. It is served back at several levels, each a cheaper reading of the same content.
Only three of the rows below are states an individual memory occupies: L1, L2 and L3.1. Those are reported on the memory's own compression_tier response field — the supported surface, in preference to the internal metadata._compression_tier key — and the same three values work as a search filter. L1 is the default: nothing writes it, the key is simply absent until the pipeline promotes the memory. The remaining rows are not per-memory states at all, and live elsewhere entirely.
| Tier | What it is | Trigger | Stored where |
|---|---|---|---|
L1 | The raw observation as written — source of truth. Searchable (FTS + vector). | Default state; no tier key present. | The memory row |
L2 | AAAK field-aliased byte encoding — computed and measured at write time, then discarded. Only the tier, the achieved ratio and a timestamp persist; mode=aaak re-encodes on demand. | Asynchronously after write. | The memory row |
L2.1 | Rolling session digest — readable digest of older context plus the latest raw exchanges. See below. | Once a session exceeds the raw tail. | Its own session-digest table |
L3 | Narrative prose, in three flavours: per namespace, per session, and cumulative as of a session boundary. Below 5 source memories it is not prose at all — see the note under the table. | Namespace: ≥2 memories · session: ≥1 · cumulative: ≥2 up to the boundary. | Namespace flavour: a column on the namespace policy. Session and cumulative: their own session-summary table. |
L3.1 | LLM concept synthesis — clusters and directional merges, written back as content_type='concept' rows. Searchable, and hit by ordinary recall. | ≥3 memories, ≥1 cluster. | New memory rows, and the namespace policy's summary column — which is where the degraded L3.1-raw marker appears |
L4 | L3.1 plus Cognition OS graph entities, merged per request. Never stored. | On request. CogOS availability only decides whether the graph entities are populated — the response is returned either way, with a flag saying which. | Not stored |
L5 | Namespace merge detection. Advisory only. | Last stage of every compression run, where the enterprise plugin stage is loaded. It is controlled by an opt-out flag (on unless disabled), and the shipped community compose file disables it — so a stock community deployment produces no merge suggestions, while a self-hosted one not using that file will. | An advisory list on the namespace policy |
Read a namespace at a chosen level with GET /api/v1/namespaces/{namespace}/compressed: mode takes raw (L1), aaak (L2), concept (L3.1) or cognition (L4), and merge_mode selects the latest state or the full history. L3 narrative is not a /compressed mode — it is read through the namespace and session summary endpoints.
Below five memories, L3 is not prose
All three L3 flavours require at least 5 source memories to call a model. Below that they emit a deterministic, LLM-free bullet list of the source memories instead — so no summary tier guarantees prose.
Only the namespace flavour says so in its tier. The namespace summary tags the fallback L3-extractive. The session and cumulative rollups do not: they report the literal L3 whether the text is prose or a bullet list. A client that switches on the tier to decide how to render will get this wrong on session summaries — do not treat L3 on a session rollup as a promise of prose.
Tier strings a caller may encounter include L3, L3-extractive, L3.1, L3.1-raw, L3.1-chat, and on session digests L2.1 and L2.1-extractive. Some read paths also synthesise a value when the stored column is empty. Treat the set as open. Match on the exact base before the first hyphen, and on the -extractive / -raw suffix to detect a degraded variant — rather than switching on an exhaustive list. Do not match a bare L3 prefix: it also catches L3.1, which is concept synthesis, not narrative prose.
L3.1-raw is the degraded marker: a cluster formed but synthesis did not produce usable output — whether the backend was unreachable or simply returned nothing useful — so the stored text is unsynthesised source material rather than prose.
L5 deliberately excludes siblings
L5 compares this namespace's summary against the same user's other namespace summaries, and deliberately skips same-prefix siblings: project:alpha and project:beta are structurally distinct by design, not accidental duplicates, so they are never proposed for merge however similar their prose. A pair is flagged only above a cosine threshold and within a content-date window, and the result is advisory — nothing merges on its own.
L5 is not the top of the ladder. Despite the number it is a background duplicate-namespace detector, not a richer read. The cross-namespace "who is this user" paragraph is GET /api/v1/user/context?depth=l4.
"L4" labels two different reads. In this table it means concepts plus graph entities for one namespace. On user context, depth=l4 means a synthesis pass across namespace summaries. They share a name and a number and fail in different ways — read the one you are actually calling.
AAAK is a storage, transport and diagnostic dialect — not context compression. It saves bytes against compact JSON while often costing prompt tokens against readable text, and off-the-shelf gzip beats it losslessly by a wide margin. Reaching for aaak to shrink a prompt is a misreading of the tier. Semantic compaction is what reduces what a model must read: L3/L3.1, and L2.1 for a live session.
Why a namespace may not have summarised
Several gates guard L3, and they are the usual answer to "why has this namespace not summarised yet". Two are similarity checks, and they are genuinely different tests, often conflated:
- Novelty — is the new memory a near-duplicate of a memory already in the namespace? Above a high cosine bar the run is skipped.
- Coverage — is the new memory already represented in the existing summary, compared against the summary's own embedding at a lower bar?
Either can skip a run on its own, and neither means anything is broken: a namespace whose new material restates what is already summarised is correctly left alone rather than paying for a model call to say it again.
Note also that the sub-five-memory bullet-list fallback runs before any model call, and on a session rollup it is still tiered L3. On a session rollup, an L3 tier is therefore not evidence that a model produced the text. (The namespace flavour does distinguish the two — it tags the fallback L3-extractive.)
Not every empty summary is a gate doing its job. L3 also depends on the deployment's model configuration, and the write path is not the only route that produces summaries — some are generated by background and warm-up paths with different preconditions. If a namespace has no summary and the similarity gates above do not explain it, treat it as a threshold or configuration question — a namespace below the minimum memory count simply has nothing to summarise — and check docs/memory-management.md in the repository rather than inferring the cause from the tier alone.
L2.1 — the rolling session digest
This is the service most often described as "the last few messages plus a summary", and the one to reach for when the problem is prompt-window pressure during a live session rather than recall across the vault.
Kemory keeps the latest few exchanges verbatim — three by default — and compacts everything older into a single readable, token-budgeted digest covering the current objective, decisions, constraints and open loops. The raw tail is a per-call parameter clamped to 1–10 exchanges; 3 is the default, so the digest first appears once a session runs past its raw tail. It is deliberately prose rather than AAAK, because it is prompt-facing — the tier that exists to be read by a model is the one that must not be encoded for bytes. If no model backend is reachable, or the call fails or returns nothing, a deterministic extractive fallback runs instead.
Nothing is lost, only deferred
The digest carries retrieval hooks and the source ids behind its sections and facts, so an agent needing the exact wording of an older exchange can drill back to it on demand rather than being told a compressed approximation is all that survives. That is the difference between this and truncating a transcript.
| Tool | What it does |
|---|---|
kemory_get_session_context | The rolling digest plus the latest raw exchanges for one session. |
kemory_rehydrate_session_sources | Drill from a digest hook back to the exact raw sources behind it. |
MCP-only. There is no REST endpoint for the session digest — it is reached through the two tools above over POST /mcp/v1. A REST client wanting session context has to go through MCP. It is also not searchable: the digest is retrieved by session, not found by query.
Search summary — the service behind enhance
Not a separate service, which is why it is hard to find: it is POST /api/v1/search/unified with the summary opted in.
The call returns top ranked hits across memories, chats and text-native files and, when asked, a short narrative summary written over those hits.
As of writing, the Kora browser extension's enhance is built on it (extension behaviour described here lives in a separate repository, so treat it as how the client uses the endpoint today rather than part of the API contract): it searches on the user's draft plus recent conversation turns, takes the top handful, and appends them to the prompt as a delimited, provenance-tagged context block alongside the summary. Nothing rewrites the draft — the model is given context and left to decide.
The invariant that matters
The summary is written only over hits that were returned, and its sources array lists the ids it drew on. Treat that as a property of how the summary is produced rather than a guarantee about the response in your hand — see the caching caveat below, which is the case where it does not hold. This is what stops the summary asserting something the user cannot click through and verify — so a client that filters results after the fact must drop the summary too, or it outlives the hit it was describing. Note this is a contract the caller must honour, not something enforced for you: the constraint is applied when the summary is written, not re-checked afterwards.
The summary is opt-in because it costs a model call, and a caller that only needs a count or a list should not pay for prose.
Summaries are briefly cached per user and query — and the cache key is only the user and the query text. It does not include the result filters. So the same query issued with a narrower type filter or a smaller limit can be served a cached summary whose sources reference hits that are not in the response you are holding. If you rely on the sources-are-a-subset property, request without narrowing, or ignore the summary when you do narrow.
Degrading honestly
If the summary cannot be produced, the response carries results with no summary rather than failing. Treat it as decoration over the results, never as the result itself. A client may also fall back to a plain memory search when the unified endpoint is unavailable — in which case there is no summary at all, and the context block is hits only.
User context — what the vault knows about someone
GET /api/v1/user/context returns a cross-namespace summary for the authenticated user, assembled from the compressed tiers rather than by reading every memory. It is the fast "catch me up" read that agents call at the start of a session, and it takes a depth so a caller can ask for a cheaper or richer answer.
At depth=l4 it runs one synthesis pass across the per-namespace summaries. That pass is not stored — it is computed per request — and it degrades gracefully: if the model call fails, the response comes back without the synthesised layer rather than erroring. Treat the summary as an optional enrichment over the namespace data, not a guaranteed field.
There is also an entitlement gate ahead of the model call. It keys on the caller's resolved entitlement feature, not on a plan name: where that feature resolves to the basic tier, the request is downgraded to depth=l3 and answered with a degraded block, so the synthesis never runs. If entitlements fail to resolve at all the gate does not fire and the full pass runs. Both directions surprise people debugging a missing — or unexpectedly present — synthesis layer.
GET /api/v1/user/profile is the narrower, persisted view: static preferences plus a dynamic part maintained as memories accumulate.
Memories 24
Create, read, search and version memories. The core surface — everything else is either feeding this or reading from it.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/memories | Create a memory |
| POST | /api/v1/memories/aggregate | Aggregation queries over memories (count / sum / list / duration) |
| POST | /api/v1/memories/bulk | Bulk-create memories (backfill / import) |
| POST | /api/v1/memories/bulk-move | Preview or apply an audited memory namespace/tag move |
| GET | /api/v1/memories/export | Export all active memories as a streamed JSON download |
| POST | /api/v1/memories/facets | Count Memory Explorer facets exactly |
| POST | /api/v1/memories/purge | Purge (soft-delete) all memories, optionally scoped to a namespace |
| POST | /api/v1/memories/search | Search memories |
| DELETE | /api/v1/memories/{memory_id} | Delete a memory |
| GET | /api/v1/memories/{memory_id} | Get a memory |
| PUT | /api/v1/memories/{memory_id} | Update a memory |
| GET | /api/v1/memories/{memory_id}/history | Get memory provenance history |
| POST | /api/v1/memories/{memory_id}/rate | Rate a recalled memory (KMV-ANA-06) — thumbs up/down + reason |
| GET | /api/v1/memories/{memory_id}/similar | Find cosine-similar memories (dedup affordance) |
| GET | /api/v1/namespaces | List namespaces |
| GET | /api/v1/namespaces/hygiene | Count exact singleton and two-item namespace tail |
| GET | /api/v1/namespaces/{namespace}/compressed | Multi-level memory read (L1 raw / L2 AAAK / L3.1 concept / L4 cognition) |
| PUT | /api/v1/namespaces/{namespace}/description | Set a namespace's human-readable description (S9N-6188) |
| POST | /api/v1/namespaces/{namespace}/merge-into/{target} | Merge one namespace into another (memories, chats, artifacts, policy) |
| POST | /api/v1/namespaces/{namespace}/recompute | Force an on-demand consolidated-summary recompute (S9N-6188) |
| GET | /api/v1/namespaces/{namespace}/sessions/{session_id}/summary | Get per-session L3 rollup (session + cumulative-to-this-point) |
| GET | /api/v1/namespaces/{namespace}/summary | Get consolidated cross-session summary for a namespace |
| GET | /api/v1/namespaces/{namespace}/tags | List a namespace's second-tier segments (S9N-6612) |
| GET | /api/v1/namespaces/{namespace}/timeline | Unified time-ordered stream of a namespace's chats + memories (S9N-6392) |
Unified Search 1
One ranked search across memories, chats and text-native files, with an optional narrative summary. This is what the Kora extension's enhance uses.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/search/unified | Ranked search across memories, chats, and text-native files |
User Context 11
Cross-namespace summaries of who the caller is and what the vault knows, plus the account-level privacy controls (consent, encryption opt-in, crypto-shred).
| Method | Path | What it does |
|---|---|---|
| DELETE | /api/v1/user/chat-data | Crypto-shred: permanently erase the authenticated user's chat data (this org) |
| GET | /api/v1/user/consents | List the caller's recorded policy acceptances |
| POST | /api/v1/user/consents | Record acceptance of a policy document (idempotent per user+document+version) |
| GET | /api/v1/user/context | Cross-namespace context summary for the authenticated user |
| GET | /api/v1/user/encryption-status | Chat-content at-rest encryption coverage for the authenticated user |
| POST | /api/v1/user/encryption/enable | Opt in: enable chat-content encryption at rest for the authenticated user |
| POST | /api/v1/user/erasure-intent | Declare intent to erase — step 1 of 2 for the crypto-shred endpoints |
| DELETE | /api/v1/user/memory-data | Crypto-shred: permanently erase the authenticated user's memory data (this org) |
| GET | /api/v1/user/profile | Persisted user profile (static preferences + dynamic recent activity) |
| GET | /api/v1/user/telemetry-preference | KMV-ANA-13 — the caller's telemetry opt-in/out preference |
| PUT | /api/v1/user/telemetry-preference | KMV-ANA-13 — set the caller's telemetry opt-in/out preference |
Consolidation 6
Namespace consolidation policy and the stats behind it — the tier machinery described above, exposed for inspection and manual runs.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/admin/consolidate-all | Run consolidation across every active namespace (admin) |
| GET | /api/v1/namespaces/consolidation-stats | Consolidation stats across all namespaces |
| POST | /api/v1/namespaces/{namespace}/consolidate | Trigger an ad-hoc consolidation run for a namespace |
| GET | /api/v1/namespaces/{namespace}/consolidation-stats | Consolidation stats for a single namespace |
| GET | /api/v1/namespaces/{namespace}/policy | Read the consolidation policy for a namespace |
| PUT | /api/v1/namespaces/{namespace}/policy | Create or update the consolidation policy for a namespace |
Enrichment 3
Post-write enrichment of individual memories (entities, structure).
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/enrichment/batch | Enrich all pending memories |
| POST | /api/v1/memories/{memory_id}/enrich | Enrich a single memory |
| GET | /api/v1/memories/{memory_id}/enrichment | Get enrichment results for a memory |
Relations 4
Explicit relations between memories, detected conflicts, and the forgetting log.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/forgetting-log | Newest-first log of forgets, expiries and supersessions |
| POST | /api/v1/memories/resolve-conflict | Resolve a conflict: loser superseded by winner |
| GET | /api/v1/memories/{memory_id}/relations | List a memory's typed relations |
| POST | /api/v1/memories/{memory_id}/relations | Assert a typed relation from this memory to a target |
Projects 2
Explicit and implicit project aliases over namespaces.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/projects | List projects (explicit rows + implicit project:* namespaces) |
| POST | /api/v1/projects | Create an explicit project alias |
AI Chats 13
Captured conversations from ChatGPT, Claude, Gemini, Perplexity and Manus — upsert, list, classify and move.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/chats | List captured chats with filters |
| POST | /api/v1/chats | Idempotent upsert of a captured chat (+ turns + artifacts) |
| POST | /api/v1/chats/bulk | Bulk idempotent upsert of captured chats (retro-sync / import) |
| DELETE | /api/v1/chats/{chat_id} | Soft-delete a chat |
| GET | /api/v1/chats/{chat_id} | Get one chat, optionally with turns + artifacts |
| POST | /api/v1/chats/{chat_id}/artifacts/upload | Upload a binary artifact (file / audio / video / image) for a turn |
| GET | /api/v1/chats/{chat_id}/artifacts/{artifact_id}/blobno auth | Stream a binary artifact body (signed-URL auth, no bearer needed) |
| POST | /api/v1/chats/{chat_id}/classify | Suggest destination namespaces for a chat based on its content |
| GET | /api/v1/chats/{chat_id}/context | Stored session digest for one captured chat (read-only, no regeneration) |
| POST | /api/v1/chats/{chat_id}/move | Move a chat to a different namespace |
| PATCH | /api/v1/chats/{chat_id}/project-metadata | Enrich project identity without changing stored turns |
| GET | /api/v1/chats/{chat_id}/timeline | Unified cross-surface timeline for one chat (KMV-WA-E3-S2) |
| POST | /api/v1/chats/{chat_id}/turns:batch | Append (or upsert by source_turn_id) a batch of turns |
AI Chats — Merge 6
Merging chats into a shared namespace, splitting them out again, and the full assignment audit trail.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/chats/bulk-merge | Merge many chats into one namespace at once |
| POST | /api/v1/chats/{chat_id}/merge | Merge a chat into a target namespace (sets merged_namespace) |
| GET | /api/v1/chats/{chat_id}/namespace-history | Full chat↔namespace assignment audit for one chat |
| POST | /api/v1/chats/{chat_id}/split-out | Make a chat standalone with a unique namespace (KMV-CMERGE-S08) |
| GET | /api/v1/chats/{chat_id}/suggestions | Top-N merge candidates for a chat |
| POST | /api/v1/chats/{chat_id}/unmerge | Clear merged_namespace; restore original_namespace as active |
AI Chats — Mappings 4
User overrides that pin a chat to a namespace.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/chat-mappings | List own chat namespace mappings |
| POST | /api/v1/chat-mappings | Create a chat namespace mapping override |
| DELETE | /api/v1/chat-mappings/{mapping_id} | Delete a chat namespace mapping |
| PATCH | /api/v1/chat-mappings/{mapping_id} | Update a chat namespace mapping |
artifacts 8
Binary attachments on chat turns — files, audio, video, images.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/artifacts/upload | Upload a file to a namespace or memory (project files) |
| DELETE | /api/v1/artifacts/{artifact_id} | Delete an artifact |
| GET | /api/v1/artifacts/{artifact_id} | Get artifact metadata |
| GET | /api/v1/artifacts/{artifact_id}/blobno auth | Stream artifact binary (signed-URL auth — no bearer needed) |
| GET | /api/v1/memories/{memory_id}/artifacts | List artifacts attached to a memory |
| POST | /api/v1/memories/{memory_id}/artifacts/upload | Upload a file attached to a memory |
| GET | /api/v1/namespaces/{namespace}/artifacts | List artifacts for a namespace |
| GET | /artifacts/{token}no auth | Stream a LocalFS artifact by signed token |
Agents 10
Registered clients that hold credentials against the vault: MCP clients, CLI installs, the browser extension.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/agents | List user's agents |
| POST | /api/v1/agents | Register a new agent |
| DELETE | /api/v1/agents/{agent_id} | Delete a revoked agent (or revoke-and-delete with ?force=true) |
| GET | /api/v1/agents/{agent_id} | Get agent details |
| PATCH | /api/v1/agents/{agent_id} | Rename an agent's friendly display name |
| POST | /api/v1/agents/{agent_id}/approve | Approve a pending agent |
| POST | /api/v1/agents/{agent_id}/revoke | Permanently revoke an agent |
| POST | /api/v1/agents/{agent_id}/rotate-key | Rotate the API key for an agent |
| POST | /api/v1/agents/{agent_id}/suspend | Suspend an agent |
| POST | /api/v1/agents/{agent_id}/token | Generate JWT access token for an agent |
Extension Keys 4
Credentials issued specifically to the Kora browser extension.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/extension/keys | List own extension keys |
| POST | /api/v1/extension/keys | Mint (or rotate) a Chrome Extension API key |
| DELETE | /api/v1/extension/keys/{key_id} | Revoke an extension key |
| PATCH | /api/v1/extension/keys/{key_id} | Rename a device (edit its label) — S9N-6326 |
Pair 4
The quick-connect flow: a short-lived code exchanged for an API key.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/pair/setup/{client_id} | Per-client MCP setup block (pre-claim, placeholder key) — S9N-6314 |
| POST | /api/v1/pair/start | Mint a short‑lived pair code for quick‑connect setup |
| POST | /api/v1/pair/{code}/claimno auth | Self‑register an agent using a pair code (called by the AI) |
| GET | /api/v1/pair/{code}/status | Poll a pair code's claim status (originator only) |
MCP Server 5
The Model Context Protocol transport. POST /mcp/v1 is the live endpoint; the per-verb paths are deprecated.
| Method | Path | What it does |
|---|---|---|
| POST | /mcp/v1 | MCP Streamable HTTP transport (JSON-RPC 2.0) |
| POST | /mcp/v1/prompts/getdeprecated | [DEPRECATED] Fetch a versioned MCP prompt by name (use POST /mcp/v1) |
| POST | /mcp/v1/prompts/listdeprecated | [DEPRECATED] List available MCP prompts (use POST /mcp/v1) |
| POST | /mcp/v1/tools/calldeprecated | [DEPRECATED] Call an MCP tool (use POST /mcp/v1) |
| POST | /mcp/v1/tools/listdeprecated | [DEPRECATED] List available MCP tools (use POST /mcp/v1) |
MCP 6
Connected MCP client status.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/mcp/clients | List Mcp Clients |
| POST | /api/v1/mcp/clients/{client_slug}/connect | Start Mcp Connect |
| POST | /api/v1/mcp/clients/{client_slug}/revoke/complete | Complete Mcp Revoke |
| POST | /api/v1/mcp/clients/{client_slug}/revoke/start | Start Mcp Revoke |
| GET | /api/v1/mcp/org-selection | Get Mcp Org Selection |
| PUT | /api/v1/mcp/org-selection | Put Mcp Org Selection |
OAuth discovery 4
RFC 9728 protected-resource and authorization-server metadata, plus the dynamic client registration shim.
| Method | Path | What it does |
|---|---|---|
| GET | /.well-known/oauth-authorization-serverno auth | Oauth Authorization Server |
| GET | /.well-known/oauth-protected-resourceno auth | Oauth Protected Resource |
| GET | /.well-known/oauth-protected-resource/mcp/v1no auth | Oauth Protected Resource Mcp |
| POST | /oauth/registerno auth | Oauth Register |
Cells directory 1
Unauthenticated residency-cell directory — which cells exist and their public hosts, so clients route to the right region without a hardcoded list.
| Method | Path | What it does |
|---|---|---|
| GET | /.well-known/kemory-cellsno auth | Kemory Cells |
Identity 2
Who the bearer token belongs to.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/me | Identity, organisation, and team membership for the caller |
| GET | /api/v1/me/orgs | Organisations the caller can switch between (ADR-012) |
Permissions 5
Per-agent, per-namespace permission grants.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/permissions | List permission rules |
| POST | /api/v1/permissions | Create a permission rule |
| DELETE | /api/v1/permissions/{rule_id} | Delete a permission rule |
| GET | /api/v1/permissions/{rule_id} | Get a permission rule |
| PUT | /api/v1/permissions/{rule_id} | Update a permission rule |
Gatekeeper 3
Ordered rules evaluated on every access decision — lower priority number first, first match wins.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/gatekeeper/consent | List JIT consent requests |
| POST | /api/v1/gatekeeper/consent/{consent_id}/resolve | Resolve a JIT consent request |
| POST | /api/v1/gatekeeper/evaluate | Evaluate a permission request |
Teams 5
Kemory's own teams and membership. Independent of Core_Backend's teams; there is no sync between them.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/orgs/{org_id}/teams | List teams in an org |
| POST | /api/v1/orgs/{org_id}/teams | Create a team in an org |
| POST | /api/v1/teams/{team_id}/members | Add a member to a team |
| DELETE | /api/v1/teams/{team_id}/members/{user_id} | Remove a member from a team |
| PATCH | /api/v1/teams/{team_id}/members/{user_id} | Update a member's role / can_write flag |
Graph 2
The access graph — who can reach what.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/graph/access-map | Get agent-memory-namespace access graph |
| GET | /api/v1/graph/memory-graph | Memory graph: memories as nodes, typed relations as edges |
Audit & Governance 4
Audit trail over reads and writes.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/audit/logs | Query audit logs |
| GET | /api/v1/audit/rate-limit | Check rate limit status |
| POST | /api/v1/audit/validate-write | Validate a write operation |
| GET | /api/v1/audit/verify | Verify audit chain integrity |
Dashboard 6
Aggregates backing the dashboard home.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/dashboard/embedding-coverage | Embedding coverage for the calling user's own rows |
| GET | /api/v1/dashboard/memories-by-agent | Per-agent memories-written series (Analytics chart 4) |
| GET | /api/v1/dashboard/overview | Dashboard overview — counts, services, and recent activity |
| GET | /api/v1/dashboard/search-latency | Per-day search latency p50/p95 (Analytics chart 2) |
| GET | /api/v1/dashboard/summary-skips | Weekly optimiser summary-skip count (Analytics chart 6) |
| GET | /api/v1/dashboard/trends | Dashboard trends — per-day counts for tile sparklines + deltas |
Analytics 21
Storage, usage and quality analytics.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/analytics/activation | KMV-ANA-06 — activation flag/date for the caller's own account |
| GET | /api/v1/analytics/activation/cohort | KMV-ANA-06 — median time-to-useful-recall / time-to-cross-surface, by signup week |
| POST | /api/v1/analytics/activation/stages | Onboarding-stage timestamps for a batch of accounts (admin users list) |
| GET | /api/v1/analytics/agent-activity | Agent activity over time — reads/writes/denied per day |
| GET | /api/v1/analytics/exec-dashboard | KMV-ANA-12 — executive dashboard: health scorecard + segmentation + usage distribution + savings |
| GET | /api/v1/analytics/exec-dashboard/platform | KMV-ANA-12 — the exec dashboard pooled across EVERY org (platform scope) |
| GET | /api/v1/analytics/recall/memories | S9N-7207 — per-memory recall counts, or the memories never recalled in the window |
| GET | /api/v1/analytics/recall/summary | S9N-7207 — recall volume and TOKEN COST for the caller's own account |
| GET | /api/v1/analytics/savings | KMV-ANA-08 — cumulative modelled savings for the caller's own account, compaction vs routing |
| GET | /api/v1/analytics/scorecard | KMV-ANA-10 — the 5/5/5 scorecard: lead, lag, and health indicators, always together |
| GET | /api/v1/analytics/storage | Storage analytics — memories by tier + type, approximate size, totals |
| GET | /api/v1/analytics/usage-ladder | KMV-ANA-07 — per-day highest usage level reached, for the caller's own account |
| GET | /api/v1/analytics/usage-ladder/depth-frequency | KMV-ANA-12 — depth (level reached) x frequency (active days) grid, cohorted by account age |
| GET | /api/v1/analytics/usage-ladder/distribution | KMV-ANA-07 — active-days histogram at a given level, cohorted by account age |
| GET | /api/v1/analytics/usage-ladder/power-users | KMV-ANA-07 — power-user ratio (>=5/7 active) and dormant-connected ratio |
| GET | /api/v1/analytics/usage-ladder/progression | KMV-ANA-07 — % of accounts reaching usage_L1/usage_L2 within 30 days of first connect |
| GET | /api/v1/analytics/value-mode | KMV-ANA-11 — continuity / cross-AI / builder classification for the caller's own account |
| GET | /api/v1/analytics/value-mode/activation | KMV-ANA-11 — activation rate segmented by value mode |
| GET | /api/v1/analytics/value-mode/activation/platform | KMV-ANA-11 — activation rate by value mode, pooled across EVERY org |
| GET | /api/v1/analytics/value-mode/segment-mix | KMV-ANA-11 — org-wide segment mix, insurance-retained vs genuinely-dormant split |
| GET | /api/v1/analytics/value-mode/segment-mix/trend | KMV-ANA-11 — segment mix over time, one snapshot per week |
Usage 1
Plan usage summary.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/usage/summary | Metered usage for the authenticated user |
Telemetry 1
Opt-in CLI beacon.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/telemetryno auth | Ingest an opt-in anonymous CLI telemetry event |
Health 6
Liveness, readiness and deep dependency health.
| Method | Path | What it does |
|---|---|---|
| GET | /health/deepno auth | Deep Health |
| GET | /health/historyno auth | Health History |
| GET | /health/liveno auth | Liveness |
| GET | /health/pipelineno auth | Pipeline Health |
| GET | /health/readyno auth | Readiness |
| GET | /health/retrievalno auth | Retrieval Health |
billing 7
Kemory plans and checkout. Proxies Core_Backend.
These endpoints forward to Core_Backend. Kemory serves the route; the behaviour behind it is owned elsewhere.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/billing/checkout | Start a Stripe hosted Checkout for a Kemory plan |
| GET | /api/v1/billing/plans | Kemory's public plan catalogue |
| GET | /api/v1/billing/plans/publicno auth | Kemory's plan catalogue, unauthenticated |
| GET | /api/v1/billing/portal | Stripe Billing Portal URL for self-service billing |
| GET | /api/v1/billing/subscription | The caller's active Kemory subscription, if any |
| GET | /api/v1/billing/transactions | The org's Kemory invoice history |
| GET | /api/v1/billing/usage | Plan caps and how much of them the caller has used |
referral 2
Referral codes. Proxies Core_Backend.
These endpoints forward to Core_Backend. Kemory serves the route; the behaviour behind it is owned elsewhere.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/referral/invite | Invite someone to Kemory with the caller's referral link |
| GET | /api/v1/referral/me | The caller's Kemory referral link, stats and activity |
residency 3
Data-residency region selection. Proxies Core_Backend.
These endpoints forward to Core_Backend. Kemory serves the route; the behaviour behind it is owned elsewhere.
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/residency | Get Residency |
| POST | /api/v1/residency | Choose Residency |
| GET | /api/v1/residency/cell | Get Residency Cell |
join 2
Waitlist-bypass claim.
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/join/{code}/claimno auth | Claim a Kemory bypass/referral code (no auth — the code is the credential) |
| GET | /api/v1/join/{code}/validateno auth | Validate a Kemory bypass/referral code (no auth — the code is the credential) |
Ask 1
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/ask | Synthesized answer over retrieved memory, with its sources |
Kora 1
| Method | Path | What it does |
|---|---|---|
| POST | /api/kora/v1/chat/orchestrated/stream | Ask Kora — relays core-ai-backend's orchestrated chat stream |
Notifications 6
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/notifications | List the caller's notifications |
| GET | /api/v1/notifications/preferences | Per-family notification preferences |
| PUT | /api/v1/notifications/preferences | Update notification preferences |
| POST | /api/v1/notifications/read-all | Mark all as read, except Action required |
| GET | /api/v1/notifications/unread-count | Unread count for the header bell |
| POST | /api/v1/notifications/{notification_id}/read | Mark one notification as read |
OpenAI plugin verification 1
| Method | Path | What it does |
|---|---|---|
| GET | /.well-known/openai-apps-challengeno auth | Openai Apps Challenge |
Keeping this page honest
The endpoint tables on this page are generated from the running service's own OpenAPI document by scripts/generate_api_reference.py in the Kemory repository. They are not maintained by hand, because a reference covering this many operations drifts the moment someone adds a route — and a docs page that is quietly wrong is worse than no page at all, since it is trusted.
Regenerate after any route change. The narrative sections above are hand-written and describe behaviour a schema cannot express; they need a human when the behaviour changes.