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
- User asks the bot something → the agent calls
POST /retrieve. - Look at
strong_match: true → answer fromentries; false → extrapolate fromreasoning_patterns(matchingvariants[]to the asker's situation first). Ifstrong_matchisfalseandreasoning_patternsis empty, lean on any partialentriesor simply hedge / offer to check with the principal. - Compose the reply (the agent layer's job) and post it, keeping the
interaction_id. - User reacts 👍 / 👎 → the agent calls
POST /feedbackwith thatinteraction_id+ rating.
Notes
- A call always returns
200with a valid shape even whenmatches_found = 0(empty corpus →strong_match: false,entries: []). Handle the empty case gracefully. relevance_scoreis informational; truststrong_matchfor the confident-vs-extrapolate decision.- Certainty:
confidencehas 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 oldevolvingtake is down-weighted. You can also use it in phrasing (e.g. flag anevolvinganswer 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'sscope/department/context, else the canonicaltemplate. - Not exposed (by design): no ingestion/upload endpoint, no webhooks. Ingestion is fully owned by the Knowledge Layer.