BreethDocs v0.1
REST API

GET /v1/graph/*

Stream the graph skeleton at canvas scale, fetch rich per-node detail on demand.

The graph endpoints split the team's memory graph into two layers:

  • A light skeleton that paints the canvas — every node, every edge, but only the fields a renderer needs (uuid, name, labels, degree, cognitive pattern).
  • A rich per-node detail payload — summary, knot narrative, neighbours with full intent_meta, mentioning episodes — fetched lazily when the user focuses a node.

The list endpoints stream as NDJSON so the canvas paints incrementally as rows arrive; the per-node endpoint returns plain JSON.

Scope required: read (implicit on every key)


GET /v1/graph/nodes

NDJSON stream of light node skeletons. One JSON object per line, sentinel as the last line.

GET /v1/graph/nodes?query=&limit=2000&offset=0
Authorization: Bearer ck_live_...
Query paramTypeDefaultNotes
querystring""Case-insensitive substring filter on name.
limitint, 1–500002000Hard cap is 50K rows per request. Page with offset for larger graphs.
offsetint, ≥ 00Skip the first N rows (sorted by degree descending).

Response — 200 OK · application/x-ndjson

Each line is one of:

{"u":"a7b1…","n":"Sridhar","l":["Person"],"d":12,"k":1,"ks":47.3}
{"u":"d04e…","n":"Rust","l":["Tech"],"d":8,"k":0,"ks":null}

{"_end": 38}
FieldMeaning
uEntity uuid.
nEntity name (canonical id for force-graph rendering).
lLabels, excluding the base Entity label.
dDegree — total connected edges (in + out, undirected).
k1 if the entity is a knot (has been through synthesis), else 0.
ksKnot score (null for non-knots).
_endSentinel on the final line. Carries the row count.

NDJSON stream of light edges.

GET /v1/graph/links?include_retracted=false&limit=5000&offset=0
Authorization: Bearer ck_live_...
Query paramTypeDefaultNotes
include_retractedboolfalseSet true to include edges with retracted_at IS NOT NULL.
limitint, 1–1000005000Hard cap 100K per request.
offsetint, ≥ 00Page through larger result sets.

Response — 200 OK · application/x-ndjson

{"u":"e1c8…","s":"Sridhar","t":"Rust","p":"preference","r":0}
{"u":"f7a2…","s":"Sridhar","t":"Go","p":"preference","r":0}

{"_end": 412}
FieldMeaning
uEdge uuid.
sSource entity name.
tTarget entity name.
pcognitive_pattern from the edge's intent_meta (null if the edge wasn't intent-annotated).
r1 if retracted, else 0.
_endSentinel — row count.

why_connected, director_vision, and edge_kind are not in this payload — fetch them via /v1/graph/nodes/{name}/details when the user focuses a node.


GET /v1/graph/nodes/{name}/details

Rich payload for a single entity: the entity itself, every neighbour with full intent_meta, and the mentioning episodes. Fired on canvas-node click.

GET /v1/graph/nodes/Sridhar/details
Authorization: Bearer ck_live_...

Name is URL-encoded. Names are unique within (team, project), so this is a single-row lookup.

Response — 200 OK · application/json

{
  "entity": {
    "uuid": "a7b1…",
    "name": "Sridhar",
    "labels": ["Person"],
    "summary": "Founder of Breeth. Backend leaning, ships fast.",
    "edge_count": 12,
    "episode_count": 24,
    "space": "individual",
    "member_id": null,
    "created_at": "2025-12-04T18:22:10Z",
    "knot_narrative": "Sridhar is the founder-engineer hub …",
    "knot_score": 47.3,
    "knot_synthesized_at": 1746012345.1,
    "knot_model": "anthropic/claude-haiku-4.5"
  },
  "neighbors": [
    {
      "peer": "Rust",
      "direction": "out",
      "fact": "Sridhar prefers async Rust over Go for IO-heavy services",
      "cognitive_pattern": "preference",
      "intent_meta": {
        "edge_kind": "preference",
        "cognitive_pattern": "preference",
        "why_connected": "Tail latency matters more than ramp-up time.",
        "director_vision": "Bet on languages with strong async stories for v1."
      },
      "edge_uuid": "e1c8…"
    }
  ],
  "episodes": [
    { "uuid": "ep_…", "name": "api_1778775104758", "valid_at": "2026-05-13T22:15:04Z" }
  ]
}
FieldMeaning
entity.knot_*Populated only when this entity is a knot. knot_narrative is the pre-synthesized one-paragraph framing — rendered inline; zero live LLM cost.
neighbors[].direction"out" means entity → peer; "in" means peer → entity.
neighbors[].intent_metaAll four intent fields are present when the edge was written with intent extraction. Older / un-annotated edges return null.
episodes[]The top mentioning episodes (capped at ~20).

Errors

HTTPSlugWhen
404not_foundNo entity with this name in the caller's (team, project).

Streaming clients

curl — print rows as they land

curl -sN https://api.thebreeth.com/v1/graph/nodes \
  -H "Authorization: Bearer $KEY"

-N disables curl's output buffering; otherwise you only see the stream after the connection closes.

Python — incremental parse

import httpx, json

with httpx.stream(
    "GET",
    "https://api.thebreeth.com/v1/graph/nodes",
    headers={"Authorization": f"Bearer {KEY}"},
    timeout=None,
) as r:
    for line in r.iter_lines():
        if not line:
            continue
        row = json.loads(line)
        if "_end" in row:
            print(f"done — {row['_end']} nodes")
            break
        print(row["n"], "deg", row["d"])

Node / browser — Web Streams API

const res = await fetch("/api/graph/nodes", {
  headers: { Authorization: `Bearer ${KEY}` },
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
let buf = "";
while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += decoder.decode(value, { stream: true });
  let idx: number;
  while ((idx = buf.indexOf("\n")) >= 0) {
    const line = buf.slice(0, idx).trim();
    buf = buf.slice(idx + 1);
    if (!line) continue;
    const row = JSON.parse(line);
    if ("_end" in row) continue;
    onNode(row); // paint immediately
  }
}

Scaling notes

  • Compact field names (u/n/l/d/k/ks) cut wire size ~25% vs the legacy /v1/graph/entities payload. Drop the prose (summary, knot_narrative, intent_meta.why_connected) from the list to keep that budget intact at canvas scale.
  • No layout precompute — clients run their own force-directed simulation. For graphs > 1.5K nodes, switch to a WebGL renderer (Cosmograph etc.); the wire format is unchanged.
  • Per-node detail is cached in the dashboard via an in-memory Map<name, NodeDetails> so the inspector's "Show details" expansion never re-fetches the same node.

Legacy endpoints

GET /v1/graph/{entities,edges,episodes} (regular JSON arrays, no streaming) remain available. They predate the v2 split and ship the full prose payload. Prefer the streamed endpoints above for any new integration; the legacy routes will receive no further enhancements.

On this page