Reference

API reference

The contract for the agent layer. This is the only interface between the agent and the Knowledge Layer — read-only, JSON over HTTP. The agent never touches the corpus, DB, or ingestion; it asks questions and gets back ranked knowledge in the principal's voice.

Basics

Format JSON in and out — send Content-Type: application/json
Auth None in v1 (network-internal / localhost-bound)
Endpoints POST /retrieve · POST /feedback · GET /health

The API degrades rather than crashes: /retrieve fails per-request if embeddings are down, while /feedback and /health stay up.


1 · POST /retrieve — the one you'll actually use

Ask a question, get the most relevant pieces of the principal's knowledge plus their reasoning patterns.

You send

Field Type Notes
question string required — the user's question
asker string required — id of who's asking (for the interaction log)
top_n int optional, default 5, clamped to 1–20
POST /retrieve
{
  "question": "How should we decide whether to ship now or wait?",
  "asker": "<user-id>",
  "top_n": 5
}

You get back

Field Type Meaning
query string echo of the question
matches_found int how many entries returned (0 = empty / no match)
strong_match bool the key flag — see below
interaction_id string pass this to /feedback later
entries[] array ranked knowledge (best first) — fields below
reasoning_patterns[] array how the principal thinks — the few patterns most relevant to this question (ranked, not the whole set), and this list can be empty [] when no pattern fits. Each: {slug, description, template, variants[]}. Use these to reason in the principal's style when there's no strong match.

Each entry

Field Type Meaning
topic string short label
type string framework · decision · standard · philosophy · reaction
position string what the principal holds — the answer content
reasoning string why they hold it — use this to sound like them
reasoning_pattern string · null slug of the pattern this reflects (or null)
confidence string high · medium · low — reliability: high = stated explicitly & corroborated; medium = stated once or a clear inference; low = inferred
stability string how durable the position is: evergreen (a core principle, won't change), stable (holds for a long time), evolving (a current take, likely to shift). Already baked into ranking (stability-aware recency decay); you can also use it in phrasing.
tier string always public in v1 (private is never returned)
source object { channel: string\|null, date: "YYYY-MM-DD" }
relevance_score float 0–1 similarity to the question (1 = closest)

Each reasoning pattern

A reasoning pattern is a repeatable decision/judgment move (how the principal decides, prioritizes, evaluates, resolves uncertainty) — the lens the agent reasons from when strong_match is false.

Field Type Meaning
slug string stable id for the pattern, e.g. invert-conventional-wisdom
description string a plain one-line summary of what the reasoning move is (human-readable; template is the terse rule to reason with)
template string the applicable one-line move to reason with
variants[] array context-scoped applications of the same move (empty [] when the move is unconditional)

Each variant

The principal sometimes runs the same move the opposite way depending on context — this isn't a contradiction, it's a context-dependent application. If the asker's situation matches a variant's scope / department / context, prefer that variant's template over the canonical one; otherwise use the canonical template.

Field Type Meaning
template string the one-line move as it applies in this context
scope string · null the situation it applies under — e.g. "external", "new hires"
department string · null the department it applies to — e.g. "sales", "engineering"
context string · null any further qualifier — e.g. "client-facing"

Example response

200 OK
{
  "query": "How should we decide whether to ship now or wait?",
  "matches_found": 5,
  "strong_match": false,
  "interaction_id": "b1a127fb3381416b9fbccc6edbeba03a",
  "entries": [
    {
      "topic": "Timing of decision closure",
      "type": "framework",
      "position": "Don't collapse a decision too early ... but don't sit on the fence; once processed, commit.",
      "reasoning": "The restaurant-menu rule: scan once, narrow to 1-2, pick. Balances indecision vs premature closure.",
      "reasoning_pattern": "pendulum-swing",
      "confidence": "high",
      "stability": "stable",
      "tier": "public",
      "source": { "channel": "Daily Meeting", "date": "2026-06-19" },
      "relevance_score": 0.61
    }
  ],
  "reasoning_patterns": [
    {
      "slug": "invert-conventional-wisdom",
      "description": "Challenge the default approach; test the opposite before accepting the obvious.",
      "template": "Default: flip the conventional move and test the inverse.",
      "variants": [
        {
          "template": "With clients, stay conventional — save inversion for internal bets.",
          "scope": "external",
          "department": "sales",
          "context": "client-facing"
        }
      ]
    },
    {
      "slug": "ship-imperfect-iterate-with-signal",
      "description": "Deploy early to generate real usage signal instead of waiting for polish.",
      "template": "Not proven yet? Ship to get real data, then tighten.",
      "variants": []
    }
  ]
}

2 · POST /feedback

After the user reacts to an answer (👍 / 👎), record it against the interaction that produced it.

Field Type Notes
interaction_id string required — the id from the /retrieve that produced the answer
rating string required — exactly "helpful" or "not_helpful"
POST /feedback
{ "interaction_id": "b1a127fb3381416b9fbccc6edbeba03a", "rating": "helpful" }

200 OK
{ "status": "recorded" }

Errors: 422 if rating isn't one of the two values · 404 if the interaction_id is unknown.


3 · GET /health

Liveness / readiness check.

GET /health  →  200 OK
{
  "status": "ok",              // "ok" | "degraded" (degraded = embeddings down)
  "corpus_size": 164,          // active knowledge entries
  "embedding_service": true,
  "patterns_discovered": 4
}

Putting it together

  1. User asks the bot something → the agent calls POST /retrieve.
  2. Look at strong_match: true → answer from entries; false → extrapolate from reasoning_patterns (matching variants[] to the asker's situation first). If strong_match is false and reasoning_patterns is empty, lean on any partial entries or simply hedge / offer to check with the principal.
  3. Compose the reply (the agent layer's job) and post it, keeping the interaction_id.
  4. User reacts 👍 / 👎 → the agent calls POST /feedback with that interaction_id + rating.

Notes

  • A call always returns 200 with a valid shape even when matches_found = 0 (empty corpus → strong_match: false, entries: []). Handle the empty case gracefully.
  • relevance_score is informational; trust strong_match for the confident-vs-extrapolate decision.
  • Certainty: confidence has three levels (high/medium/low, reliability) — weight high-confidence positions more heavily, and surface honest "it depends" / "I'm inferring here" caveats on medium/low.
  • Durability: stability (evergreen/stable/evolving) is already baked into ranking — recency decay is stability-aware, so an old evergreen principle still ranks high while an old evolving take is down-weighted. You can also use it in phrasing (e.g. flag an evolving answer as "current thinking").
  • Context: context-dependence lives on the reasoning patterns, not on entries. Each pattern carries variants[] (context-scoped applications); pick the variant matching the asker's scope/department/context, else the canonical template.
  • Not exposed (by design): no ingestion/upload endpoint, no webhooks. Ingestion is fully owned by the Knowledge Layer.