Memory graph & Asset Browser API
The memory graph endpoint, the read-only Kumiho proxy, artifact serving, and the typed asset write API.
This page documents the gateway endpoints that expose Kumiho graph memory to clients: a single aggregated memory graph payload for the Memory Auditor, a read-only generic Kumiho proxy for arbitrary GET endpoints, artifact body serving for rendering files on disk, and the typed Asset Browser write API for mutating projects, spaces, items, revisions, artifacts, bundles, and edges.
Use these endpoints when you want to read or edit the knowledge graph programmatically instead of through the dashboard. The dashboard’s Assets & memory explorer is built entirely on top of them. For the underlying data model — spaces, items, revisions, krefs, edges, and provenance — see Graph model: spaces, items & provenance. For how agents read and write memory through MCP tools instead of REST, see Kumiho memory tools.
Authentication
Section titled “Authentication”Every route on this page requires the pairing bearer token:
Authorization: Bearer <token>Obtain the token through the pairing flow in Pairing & authentication. A missing or invalid token returns 401. Most routes share the gateway’s default 64 KiB JSON body cap and request timeout; the exceptions (the memory graph timeout, the artifact body and content-edit size caps) are called out below.
How requests reach Kumiho
Section titled “How requests reach Kumiho”Revka does not store the graph itself — persistence is Kumiho’s job. Every route here delegates to a KumihoClient that reaches Kumiho over one of two transports, chosen automatically:
| Transport | What it is | When it’s used |
|---|---|---|
| SDK bridge | A loopback-only Python sidecar (kumiho_sdk_bridge.py) that exposes the Kumiho Python SDK over 127.0.0.1 for lower-latency reads | Preferred when the Kumiho venv is present |
| Hosted FastAPI | Direct HTTP to the Kumiho control plane at [kumiho] api_url (default https://api.kumiho.cloud) | Fallback when the bridge is unavailable, returns 501 kumiho_sdk_bridge_unsupported, or returns a 5xx |
The bridge is enabled by default and disabled by setting REVKA_KUMIHO_SDK_BRIDGE=0 (also accepts false, no, off). It is materialized to ~/.revka/kumiho/kumiho_sdk_bridge.py on first use, listens on an ephemeral loopback port, restarts automatically if it exits, and logs to ~/.revka/logs/kumiho-sdk-bridge.stdout.log and kumiho-sdk-bridge.stderr.log. Health-check and per-request timeouts are 10 seconds each. If the venv at ~/.revka/kumiho/venv/ is absent, the bridge simply fails to start and all reads fall back to hosted FastAPI — there is no hard error. See Kumiho setup for installing the sidecar.
Read responses are served from a token-scoped cache with a 10-second fresh TTL and a 120-second stale window: a cached value can be up to 10 s old, and a stale value keeps serving for 2 minutes after Kumiho goes offline. Write methods (POST/PUT/DELETE) are never retried — Kumiho has no idempotency keys, so a retried create could duplicate data.
Memory graph
Section titled “Memory graph”GET /api/memory/graph returns nodes, edges, spaces, and stats in a single payload, ready for the dashboard’s Obsidian-style force-directed memory explorer.
GET /api/memory/graph?project=CognitiveMemory&limit=100&kinds=decision,fact&space=CognitiveMemory/Skills&sort=recent&search=gRPCAuthorization: Bearer <token>Query parameters
Section titled “Query parameters”| Param | Type | Default | Meaning |
|---|---|---|---|
project | string | [kumiho] memory_project (CognitiveMemory) | Kumiho project to read |
limit | int | 100 (max 500) | Maximum number of items to include |
kinds | string | — | Comma-separated kind filter, e.g. decision,fact,preference |
space | string | — | Space-path prefix filter |
sort | string | recent | recent (newest first) or name |
search | string | — | Fulltext query; restricts the result set to matching items |
Response
Section titled “Response”{ "nodes": [ { "id": "CognitiveMemory/Decisions/chose-grpc", "name": "chose-grpc", "kind": "decision", "space": "CognitiveMemory/Decisions", "created_at": "2026-06-18T09:12:00Z", "title": "Chose gRPC over REST", "summary": "...", "revision_kref": "kref://CognitiveMemory/Decisions/chose-grpc?r=2" } ], "edges": [ { "source": "...", "target": "...", "edge_type": "DERIVED_FROM", "metadata": {} } ], "spaces": ["CognitiveMemory", "CognitiveMemory/Skills"], "stats": { "total_items": 42, "total_edges": 18, "kinds": { "decision": 10, "fact": 32 } }}stats.total_items reflects the full filtered set before the limit truncation, so it can exceed nodes.length.
Edges only appear when both endpoints are already present in the returned node set; self-edges and cross-set edges are filtered out, and duplicate (source, edge_type, target) triples are deduplicated. This keeps the graph renderable without dangling references.
Generic Kumiho proxy
Section titled “Generic Kumiho proxy”GET /api/kumiho/{*path} is a transparent read-only proxy to any Kumiho FastAPI GET endpoint. Query parameters are forwarded verbatim; the response body is returned as JSON.
GET /api/kumiho/items?space_path=/CognitiveMemory/SkillsGET /api/kumiho/revisions/latest?item_kref=kref://CognitiveMemory/my-skillAuthorization: Bearer <token>Only GET is proxied — there is no write path here by design. To mutate the graph, use the typed Asset Browser write API or the resource-specific routes documented in Agents, skills & teams API.
Revka adds two observability headers so clients can see how the request was served:
| Header | Values | Meaning |
|---|---|---|
X-Revka-Cache | hit · stale | Whether the response came from cache, and whether it was the stale fallback |
X-Revka-Kumiho-Transport | sdk-bridge · fastapi | Which transport answered |
GET requests make up to 3 attempts (2 retries) with 500 ms / 1500 ms jittered backoff on transient upstream errors (502, 503, 504, 520, 522, 524) within a 15-second budget. Upstream HTML error pages (for example a Cloudflare 502 splash) are stripped before reaching the client.
Artifact body serving
Section titled “Artifact body serving”GET /api/artifact-body streams the raw bytes of the local file referenced by a Kumiho artifact’s location field. The Asset Browser and the Workflow Runs artifact viewer use it to render text, image, and video artifacts without re-implementing file I/O.
GET /api/artifact-body?location=file:///home/user/.revka/workspace/artifacts/kumiho/report.mdAuthorization: Bearer <token>| Param | Type | Required | Meaning |
|---|---|---|---|
location | string | yes | The artifact’s location value: an absolute path, optionally file://-prefixed; ~ is expanded |
The response carries best-effort Content-Type (guessed from the extension), Content-Disposition: inline, and Cache-Control: private, max-age=60. The maximum served size is 256 MiB; larger files return 413.
Security and fallback
Section titled “Security and fallback”The path is checked against the workspace security policy: the location must be absolute and resolve inside the workspace or an allowed root, otherwise the request returns 403. Relative paths return 400.
If no file is found on disk, the gateway attempts a revision-metadata fallback: it looks up the artifact by location in Kumiho and reconstructs the body from revision metadata — either a base64 payload (content_base64, data_base64, payload_base64, body_base64) or a text field (content, body, text, markdown, …) for text-like artifacts. When this path is taken, the response includes an X-Revka-Artifact-Source: revision-metadata header. Workflow YAML definitions are deliberately excluded from this fallback so they are never served as artifact bodies.
Asset Browser write API
Section titled “Asset Browser write API”The /api/assets/* routes are the typed write surface behind the dashboard’s Assets page. Because the generic proxy is read-only, every mutation — creating spaces, adding revisions, editing artifact content — goes through these routes. They share a 2 MiB body limit (larger than the default 64 KiB) and require the bearer token.
Endpoints
Section titled “Endpoints”POST /api/assets/projectsPOST /api/assets/spacesPOST /api/assets/itemsPOST /api/assets/items/deprecatePOST /api/assets/revisionsPOST /api/assets/revisions/deprecatePOST /api/assets/revisions/publishPOST /api/assets/revisions/tags — tag a revisionDELETE /api/assets/revisions/tags — untag a revisionGET /api/assets/bundlesPOST /api/assets/bundlesGET /api/assets/bundles/membersPOST /api/assets/bundles/membersDELETE /api/assets/bundles/membersPOST /api/assets/edgesGET /api/assets/dependency-graphPOST /api/assets/artifactsPOST /api/assets/artifacts/deprecatePUT /api/assets/artifacts/content — edit artifact contentCreating graph entities
Section titled “Creating graph entities”The create routes mirror the Kumiho hierarchy: a project contains spaces, a space contains items, an item has a series of revisions, and a revision can carry artifacts. Each create returns the created entity wrapped under a key named for its type ({"item": {...}}, {"revision": {...}}, and so on).
POST /api/assets/projects{ "name": "CognitiveMemory", "description": "optional" }
POST /api/assets/spaces{ "parent_path": "/CognitiveMemory", "name": "Decisions" }
POST /api/assets/items{ "space_path": "/CognitiveMemory/Decisions", "item_name": "chose-grpc", "kind": "decision", "metadata": {} }
POST /api/assets/revisions{ "item_kref": "kref://CognitiveMemory/Decisions/chose-grpc", "metadata": { "title": "Chose gRPC" } }
POST /api/assets/edges{ "source_kref": "<rev kref>", "target_kref": "<rev kref>", "edge_type": "DERIVED_FROM", "metadata": {} }Edges connect revision krefs, not item krefs, and the edge_type is one of the provenance types described in Graph model: spaces, items & provenance (DERIVED_FROM, DEPENDS_ON, REFERENCED, CONTAINS, CREATED_FROM, BELONGS_TO).
Creating artifacts
Section titled “Creating artifacts”POST /api/assets/artifacts attaches an artifact to a revision. It can register an existing file by location, validate that the file exists, or write the content to disk for you:
POST /api/assets/artifacts{ "revision_kref": "kref://CognitiveMemory/Decisions/chose-grpc?r=2", "name": "NOTES.md", "location": "", "content": "# Notes\n...", "write_file": true, "overwrite": false, "validate_exists": false}| Field | Type | Meaning |
|---|---|---|
revision_kref | string | Revision the artifact belongs to (must include an exact ?r=N selector) |
name | string | Artifact filename |
location | string | Absolute path or file:// URI; leave empty with write_file to auto-generate one under workspace/artifacts/kumiho/... |
content | string | Body to write (when write_file is true); capped at 1 MiB |
write_file | bool | Write content to location on disk before registering |
overwrite | bool | Allow overwriting an existing file; otherwise an existing file returns 409 |
validate_exists | bool | For link-only artifacts, fail with 400 if the file is missing |
Any path written or linked is checked against the workspace security policy; paths outside the workspace and allowed roots return 403.
Editing artifact content
Section titled “Editing artifact content”PUT /api/assets/artifacts/content edits an artifact’s body in place and is capped at 1 MiB:
PUT /api/assets/artifacts/content{ "artifact_kref": "<artifact kref>", "revision_kref": "<revision kref>", "content": "updated body" }The behavior depends on whether the revision is published:
- Unpublished revision — the file is rewritten in place. The response has
"created_revision": false. - Published revision — published history is immutable, so the gateway creates a new revision, writes the edited content to a new versioned file (
name.rN.ext), and copies the other artifacts forward. The response has"created_revision": trueand acopied_artifactscount.
{ "revision": { "...": "the (possibly new) revision" }, "artifact": { "...": "the edited artifact" }, "created_revision": true, "copied_artifacts": 3}Publishing and tagging revisions
Section titled “Publishing and tagging revisions”POST /api/assets/revisions/publish { "kref": "<revision kref>" }POST /api/assets/revisions/tags { "kref": "<revision kref>", "tag": "milestone" }DELETE /api/assets/revisions/tags { "kref": "<revision kref>", "tag": "milestone" }Publish tags the revision published. Tag and untag accept any tag except current — moving or removing the current tag is high-risk and rejected with 403 (it must be done through kumiho_patch_apply).
Bundles and members
Section titled “Bundles and members”Bundles are Kumiho’s grouping construct (a team, a canon set, a session’s captures). List them, list their members with resolved item detail, and add or remove members:
GET /api/assets/bundles?project=Revka&space_path=/Revka/TeamsGET /api/assets/bundles/members?bundle_kref=<bundle kref>POST /api/assets/bundles/members { "bundle_kref": "...", "item_kref": "...", "metadata": {} }DELETE /api/assets/bundles/members { "bundle_kref": "...", "item_kref": "..." }Dependency graph
Section titled “Dependency graph”GET /api/assets/dependency-graph walks edges outward from a revision for the canvas dependency view:
GET /api/assets/dependency-graph?revision_kref=<kref>&direction=both&depth=2&edge_type=all&node_limit=100| Param | Type | Default | Meaning |
|---|---|---|---|
revision_kref | string | required | Center node to walk from |
direction | string | both | upstream/outgoing, downstream/incoming, or both |
depth | int | 1 (max 3) | How many edge hops to traverse |
edge_type | string | all | Restrict to one edge type; all or empty means no filter |
node_limit | int | 100 (max 200) | Stop expanding after this many nodes; sets truncated: true when hit |
The response includes the resolved nodes (with their artifacts and incoming/outgoing edges), the deduplicated edge list, and the effective direction, depth, node_limit, and truncated flag.
Kumiho SDK bridge & client CRUD
Section titled “Kumiho SDK bridge & client CRUD”The transport split described in How requests reach Kumiho is implemented by the SDK bridge (a process-local Python sidecar) and the KumihoClient, which together back every route on this page plus the agents, skills, teams, and workflow routes elsewhere in the API. They are internal — you never call them directly — but their behavior is worth knowing because it shapes what you observe:
- Authentication uses two tokens.
KUMIHO_SERVICE_TOKENauthenticates hosted FastAPI calls;KUMIHO_AUTH_TOKENauthenticates the bridge. They may differ.revka onboardwrites both to~/.revka/.env. See Kumiho setup. - Reads retry; writes do not. The retry and stale-cache behavior above applies to GET. Create/update/delete are issued once.
- The cache is token-scoped. Cache keys are
(token_hash, url), so one account’s cached reads can never leak to another.
The KumihoClient wraps the full Kumiho REST surface that these endpoints compose: items (create_item, list_items, search_items, deprecate_item), revisions (create_revision, get_latest_revision, tag_revision, batch_get_revisions), spaces and projects (ensure_space, create_project), edges (create_edge, list_edges), artifacts (create_artifact, get_artifacts, get_artifacts_by_location), and bundles (create_bundle, add_bundle_member, list_bundle_members). The /api/assets/* routes are thin, validated wrappers over exactly these calls, which is why creating an entity through the API behaves identically to creating it through an agent’s MCP tools.
Related pages
Section titled “Related pages”- Graph model: spaces, items & provenance — the data model behind every entity here
- Kumiho graph memory — the operator-facing guide
- Kumiho setup — installing the sidecar and configuring tokens
- Assets & memory explorer — the dashboard built on these endpoints
- Agents, skills & teams API — resource-specific write routes that also persist to Kumiho
- Gateway API overview — auth, body limits, and shared conventions