Every answer shows its work
eli.ai turns your documents and live systems into one knowledge layer for chat, agents, and APIs. Every claim links to the document or the database call behind it — and when the evidence is not there, it says so instead of guessing.S
Self-hostable on a laptop or a single VPS — Postgres and two Node processes, no managed services required.S
answers when the evidence is there
Is anything stuck on the Order Platform right now?
Yes — 14 orders are stuck totalling $8,120 D1, all older than 24 hours. Per the incident runbook, requeue them from the fulfilment console S1.
- S1
- Order Platform incident runbook · § Requeue
- D1
- order-backlog · 4 rows · 41 ms · ok
and stops when it is not
Which vendor should we consolidate onto next quarter?
I cannot answer that from this workspace. The vendor comparison is a draft and no certified source covers next quarter — here is what does exist.
abstained · reason: no_certified_source
How it works
Four steps from your documents to an answer you can check
- 01
Connect
Point eli.ai at the wikis, vaults and channels your team already writes in. Sync is read-only and incremental.
- 02
Model
Entities and relations are extracted with provenance, then reviewed and merged against an ontology you control.
- 03
Bind
Attach parameterized, read-only queries to the entities they describe, so answers can reach live numbers safely.
- 04
Ask
Chat, agents and the API answer from documents and live systems together — with citations, or an abstention.
The system
What the eight modules add up to
eli.ai sits between the things your team writes down and the systems that hold your numbers, and treats the two as one governed knowledge layer. An answer drawn from it can cite a paragraph in a runbook and the exact query behind a live figure in the same sentence, decline when neither backs the question, and stay traceable and measurable long after it was given.S
Both halves of an answer
One claim can carry a Source Citation S1 to the paragraph it came from and a Data Citation D1 to the query that fetched the number. The two registries are separate and each resolves on its own.
A refusal is a result
When no certified source covers the question, the answer policy abstains and points at what does exist instead of filling the gap. Every verdict is recorded, so you can read what was declined and why.S
Checked afterwards, not asserted
Lineage keeps the trail from an answer back to the document revision behind it. Crucible scores the same pipeline against golden sets, and a configuration change ships only when the difference is statistically significant.S
Documents enter at Intake. Live rows enter at Conduit. Everything converges on Lens and leaves through Ports. Lineage records the path; Crucible measures the result and feeds it back.
in
Confluence, SharePoint, Slack, markdown vaults
Intake
Syncs your sources read-only and re-chunks only what changed.
Atlas
Extracts entities and relations, each with the evidence for it.
Warrant
Reviews and certifies before a fact is allowed to count.
Conduit
Runs the governed query and hands over the rows D1.
Lens
Retrieves, then answers with its sources cited S1 — or abstains.
Ports
Delivers it to people and machines alike.
out
chat, durable agents, REST, MCP, SDK
Crucible
Scores those answers against golden sets. The configuration that wins ships back into Lens.
Lineage
Records every step above — origin, revisions, what was derived from what, and which answers depended on it.
What each one contributes
In pipeline order — the same order the reference below uses. One line each; the endpoints and imports come after.
- Intake
- Pulls in what your team already writes — wikis, vaults, Slack, SharePoint — read-only and incrementally, re-processing only what actually changed.
- Atlas
- Turns those pages into a typed graph of the things you talk about, carrying the sentence that evidences every relation.
- Warrant
- Decides what counts as certified, and who has to approve a change before a fact gets promoted.
- Lineage
- Keeps the receipts: where a fact came from, what changed since, and what breaks if you deprecate the document behind it.
- Conduit
- Attaches allow-listed, read-only queries to the entities they describe, so an answer can quote a live number and show the call that fetched it D1.
- Lens
- Retrieves across full text, vectors and the graph, then answers with the documents cited S1 — or clarifies, flags a conflict, or abstains.
- Crucible
- Measures that pipeline against concept-anchored golden sets, so a change to it is judged on recall, groundedness and abstention rather than on impressions.
- Ports
- Hands the whole thing to your people and your machines: the workspace UI, durable agents, a versioned REST API, an MCP server and a typed SDK.
They chain in that order, but the order describes them rather than constrains you. Each module owns its own endpoints, its own SDK subpath and its own scope, so adopting one costs a key and a call rather than the platform — and nothing above has to be switched on first. The reference below takes them one at a time and shows exactly that: the input you hand over, the endpoint, the import.
Flows across the modules
Every job is a different path through the same eight
Each row below runs on the platform in the diagram above. Read across a row for the modules a job needs and the order it needs them in; read down a column for how often a module is actually on the path. Breadth follows scope — a focused job runs on four of the eight, a whole-workspace walkthrough runs on all of them, and it is the same machine either way.S
Research synthesisPattern
Ingest papers, reports and field notes, connect them in a concept graph, and trace every synthesised claim back to the document it came from.
Builds the result: Intake (1), Atlas (2), Lens (3). Acts on it afterwards: Lineage (4). Not used: Warrant, Conduit, Crucible and Ports.
Support deflectionPattern
Answer a customer in their own words from the help centre and ticket history — citing the canonical answer, and abstaining rather than bluffing.
Builds the result: Intake (1), Warrant (2), Lens (3). Acts on it afterwards: Crucible (4). Not used: Atlas, Lineage, Conduit and Ports.
Compliance & audit trailsPattern
Certify policies with named owners and review SLAs, then hand an auditor append-only provenance, a blast radius and a programmatic trail.
Builds the result: Warrant (1). Acts on it afterwards: Lineage (2), Crucible (3), Ports (4). Not used: Intake, Atlas, Conduit and Lens.
Sales enablement
A rep on a live call gets the current price, the certified positioning and the security posture — cited, never guessed.
Builds the result: Intake (1), Warrant (2), Lens (3). Acts on it afterwards: Lineage (4), Crucible (5). Not used: Atlas, Conduit and Ports.
Automated reports
The same client report every Monday — current numbers, defensible definitions, a reproducible PDF — with nobody assembling it by hand.
Builds the result: Conduit (1), Warrant (2), Lens (3). Acts on it afterwards: Crucible (4), Ports (5). Not used: Intake, Atlas and Lineage.
Matter research
State a certified controlling authority plainly, read the live matters database, and decline when a settlement call has no authority on file.
Builds the result: Intake (1), Atlas (2), Warrant (3), Conduit (4), Lens (5). Acts on it afterwards: Lineage (6), Crucible (7). Not used: Ports.
Incident response
A 2am page becomes a cited incident answer carrying live open-incident counts, abstaining when no record backs the claim — then ships as an assistant.
Builds the result: Intake (1), Atlas (2), Warrant (3), Conduit (4), Lens (5). Acts on it afterwards: Lineage (6), Crucible (7), Ports (8).
The fourteen industry walkthroughs — one per seeded demo workspace, each runnable locally — all follow the long path, Intake through Ports, because touring a whole workspace touches everything in it. The shorter rows above are the useful lesson: a job takes the modules it needs, and the rest stays switched off.
The platform
Eight modules, and any one of them on its own
Each module owns one job, one set of endpoints under /api/v1, and one entry point in the SDK. Together they are a single path from a document to a cited answer. Separately, each is something you can adopt without the other seven.S
You do not have to adopt the platform to use a module
There is no base install and no required order. Run Lens over documents you never modelled in Atlas. Run Conduit against your warehouse and never ingest a document at all. The minimum to use one module is a workspace API key carrying that module's scope, and that module's endpoint — nothing else has to be configured.S
Reactone subpath, one module in your bundle
import { createEliTransport } from "@eli-ai/client/core";
import { EliProvider } from "@eli-ai/react/provider";
import { EliQuery } from "@eli-ai/react/query";
import "@eli-ai/react/styles.css";
// Only the query module is in your bundle. No
// provider holds a key — a same-origin route
// injects it server-side.
const transport = createEliTransport({
fetch: proxyFetch,
});
<EliProvider transport={transport}>
<EliQuery initialMode="ask" />
</EliProvider>HTTPone key, one endpoint
# Conduit alone, against your own warehouse.
# Nothing was ingested, modelled or governed
# first, and no other module is switched on.
curl -L -X POST \
https://eli.ai/api/v1/data/queries/order-backlog/run \
-H "Authorization: Bearer eli_sk_..." \
-H "Content-Type: application/json" \
-d '{"params":{"status":"stuck"}}'
# { "dataCallId": "01K7V...", "rows": [...],
# "rowCount": 14, "durationMs": 41 }one subpath per module — @eli-ai/react/{query, documents, graph, governance, data, agents, quality, connectors} · plus /provider and /hooks
The headless client mirrors the same split — createDataClient from @eli-ai/client/data imports no other capability module, and no React.
Intake
Sync Confluence, SharePoint, Slack and markdown vaults read-only, incrementally, and re-process only where content actually changed.
- API
- /api/v1/connectors
- SDK
- @eli-ai/react/connectors
Inputa source: non-secret config plus a credential, encrypted at write
{ "type": "confluence", "config": { "baseUrl": "https://acme.atlassian.net/wiki", "spaceKeys": ["ENG", "OPS"] }, "credential": { "email": "svc@acme.com", "apiToken": "..." } }APIcreate the source, then enqueue a crawl — scope kb:write
curl -L -X POST https://eli.ai/api/v1/connectors \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d @source.json # 201 { "id": "01K7Q...", "type": "confluence", "hasCredential": true } curl -L -X POST https://eli.ai/api/v1/connectors/01K7Q.../sync \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d '{"kind":"incremental"}' # 202 { "enqueued": true, "sourceId": "01K7Q...", "kind": "incremental" }Reactthe source list, with per-source sync
import { EliConnectors } from "@eli-ai/react/connectors"; <EliConnectors canSync onSyncQueued={(result) => toast(`sync queued: ${result.sourceId}`)} />Atlas
Extract a typed entity graph with provenance on every relation — and a deterministic fallback that works with no model at all.
- API
- /api/v1/entities · /api/v1/relations
- SDK
- @eli-ai/react/graph
Inputan entity, or an edge carrying the sentence that evidences it
{ "name": "Order Platform", "type": "system", "aliases": ["OP", "order-svc"], "description": "Checkout, capture and fulfilment hand-off." } { "srcEntityId": "01K7R...", "dstEntityId": "01K7S...", "type": "depends_on", "evidence": "Capture calls the ledger synchronously." }APIwrite an entity (kb:write), then walk the frontier (kb:read)
curl -L -X POST https://eli.ai/api/v1/entities \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d @entity.json # 201 the canonical entity # 409 { "error": "conflict", "existingId": "01K7R..." } curl -L "https://eli.ai/api/v1/entities/01K7R.../neighborhood?depth=2" \ -H "Authorization: Bearer eli_sk_..." # { "entity": {...}, "nodes": [...], "edges": [...], "truncated": false }Reactbrowse entities and their neighbourhood
import { EliKnowledgeGraph } from "@eli-ai/react/graph"; <EliKnowledgeGraph initialEntityId="01K7R..." onEntitySelect={(entity) => router.push(`/e/${entity.id}`)} />Warrant
Promote facts from extracted to certified through gated change requests, with owners, review SLAs and a versioned manifest.
- API
- /api/v1/change-requests · /api/v1/manifest
- SDK
- @eli-ai/react/governance
Inputa proposal — kind, target, and the payload validated for that kind
{ "kind": "update", "entityId": "01K7R...", "payload": { "description": "Owns capture. Fulfilment moved to Logistics in Q3." }, "note": "Scope changed after the Q3 split." }APIpropose, then decide — scope kb:write (kb:read to list)
curl -L -X POST https://eli.ai/api/v1/change-requests \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d @request.json # 201 curl -L -X POST https://eli.ai/api/v1/change-requests/01K7T.../approve \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d '{"note":"Confirmed with the owner."}' curl -L https://eli.ai/api/v1/manifest -H "Authorization: Bearer eli_sk_..." # counts, governance distribution, hub concepts, review staleness # GET /api/v1/export/skos returns the same concepts as SKOS/PROV-O JSON-LDReactthe review queue, gated on what the caller may do
import { EliGovernance } from "@eli-ai/react/governance"; <EliGovernance canPropose canReview={viewer.isSteward} onRequestReviewed={(id, decision) => audit(id, decision)} />Lineage
Trace any document to what it produced, and see what breaks before you deprecate it.
- API
- /api/v1/documents
- SDK
- @eli-ai/react/documents
Inputa markdown document — the thing an answer later cites as S1
# Order Platform runbook ## Requeue Orders stuck longer than 24 hours are requeued from the fulfilment console. Escalate to #order-platform after two failed requeues.APIwrite it (kb:write), then ask what it produced (kb:read)
curl -L -X POST https://eli.ai/api/v1/documents \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" \ -d '{"path":"runbooks/order-platform.md","content":"# Order Platform runbook\n..."}' # 201 { "docId": "01K7U...", "path": "...", "contentHash": "...", "title": "..." } curl -L https://eli.ai/api/v1/documents/01K7U.../lineage \ -H "Authorization: Bearer eli_sk_..." # { "document", "governance", "origin", "revisions", # "audit", "derived", "downstream" }Reactthe trail for one document, origin through blast radius
import { EliDocumentLineage } from "@eli-ai/react/documents"; <EliDocumentLineage documentId="01K7U..." onLoaded={(lineage) => setImpact(lineage.downstream)} />Conduit
Bind graph entities to allow-listed, parameterized read-only SQL so an answer can quote a live number and show the call that fetched it.
- API
- /api/v1/data/queries
- SDK
- @eli-ai/react/data
Inputa named query: SELECT/WITH only, typed params, row-capped
slug: order-backlog sql: select id, total_cents, created_at from orders where status = $1 and created_at < now() - interval '24 hours' params: [{ "name": "status", "type": "string", "required": true }]APIrun it against your own warehouse — scope data:run; the
dataCallIdis the D1 anchorcurl -L -X POST https://eli.ai/api/v1/data/queries/order-backlog/run \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" \ -d '{"params":{"status":"stuck"}}' # { "dataCallId": "01K7V...", "connectorId": "01K7W...", # "columns": ["id","total_cents","created_at"], "rows": [...], # "rowCount": 14, "durationMs": 41, "truncated": false }Reactthe query catalogue, with governed execution
import { EliLiveData } from "@eli-ai/react/data"; <EliLiveData initialQuerySlug="order-backlog" canExecute={viewer.canRunData} onResult={(result) => setRows(result.rows)} />Lens
Retrieve across full-text, vectors and the graph, then answer, ask for clarification, surface a conflict, or abstain.
- API
- /api/v1/query · /api/v1/search
- SDK
- @eli-ai/react/query
Inputa question, and how hard the answer policy should hold the line
{ "question": "Is anything stuck on the Order Platform right now?", "includeData": true, "maxSources": 8, "policyMode": "enforce" }APIone call, evidence attached — scope agents:run (kb:read for search)
curl -L -X POST https://eli.ai/api/v1/query \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d @question.json # { "answer": "...", # "claims": [{ "text": "...", "citations": ["S1", "D1"] }], # "sources": [...], "dataCalls": [...], "entities": [...], # "groundedness": { "verified": 2, "total": 2 }, # "policy": { "verdict": "answer", ... } } curl -L "https://eli.ai/api/v1/search?q=requeue&limit=10&rerank=true" \ -H "Authorization: Bearer eli_sk_..." # retrieval only, no model # every verdict is kept: GET /api/v1/policy/decisions?verdict=abstainReactsearch and ask in one surface; EliSearch drops the ask tab
import { EliQuery } from "@eli-ai/react/query"; <EliQuery initialMode="ask" searchLimit={10} onAnswer={(answer) => setSources(answer.sources)} />Crucible
Measure quality against concept-anchored golden sets, and gate config changes on statistical significance rather than impressions.
- API
- /api/v1/qrels · /api/v1/evals/runs
- SDK
- @eli-ai/react/quality
Inputa golden item: must/should assertions and whether citing is required
{ "question": "How do we requeue stuck orders?", "assertions": [ { "text": "Requeue from the fulfilment console", "kind": "must" }, { "text": "Escalate after two failures", "kind": "should" } ], "citations": [{ "docId": "01K7U...", "mode": "required" }], "difficulty": "medium", "tags": ["runbook"] }APIjudge, read the metrics, then compare two runs properly
curl -L -X POST https://eli.ai/api/v1/qrels \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" \ -d '{"queryId":"01K7X...","chunkId":"01K7Y...","grade":3}' # append-only, 0-3 curl -L https://eli.ai/api/v1/evals/runs/01K7Z.../metrics \ -H "Authorization: Bearer eli_sk_..." # { "retrieval": { "recallAtK", "ndcgAt10", "mrr", ... }, # "abstention": { "abstentionRate", "meanTruthfulness", "aurc" }, # "errorClasses": {...}, "quality": {...} } curl -L "https://eli.ai/api/v1/evals/runs/compare?a=01K7Z...&b=01K80...&metric=truthfulness" \ -H "Authorization: Bearer eli_sk_..." # significant only when the 95% bootstrap CI excludes 0 AND p < 0.05Reactrun metrics and the policy-decision log
import { EliQuality, EliPolicyDecisions } from "@eli-ai/react/quality"; <EliQuality initialEvalRunId="01K7Z..." /> <EliPolicyDecisions surface="chat" verdict="abstain" limit={20} />Ports
Reach all of it from durable agents, a versioned API, an MCP server, and a typed SDK.
- API
- /api/v1/agents/{slug}/runs · /api/mcp
- SDK
- @eli-ai/react/agents
Inputan agent slug and the run input; the conversation is optional
POST /api/v1/agents/ops-summary/runs { "input": "Draft the weekly ops summary for the Order Platform.", "conversationId": "01K81..." }APIqueued durably, reattachable — scope agents:run
curl -L -X POST https://eli.ai/api/v1/agents/ops-summary/runs \ -H "Authorization: Bearer eli_sk_..." \ -H "Content-Type: application/json" -d @run.json # 202 { "runId": "01K82...", "status": "queued", # "stream": "/api/v1/runs/01K82.../stream" } # the run continues on the worker; reattach to the stream at any time claude mcp add --transport http eli https://eli.ai/api/mcp \ --header "Authorization: Bearer eli_sk_..."Reactstart a run, watch its steps, approve what needs a human
import { EliAgentRunner, EliRunDetail } from "@eli-ai/react/agents"; <EliAgentRunner initialAgentSlug="ops-summary" canApprove /> <EliRunDetail runId="01K82..." canCancel />
For developers
Reachable from whatever you already run
Workspace-scoped API keys, structured JSON with the citation registries intact, and a native MCP server — so an external agent can use your knowledge base without a bespoke integration.S
The typed client, instead of curl
npm install @eli-ai/client
import { createEliClient } from "@eli-ai/client";
const eli = createEliClient({
baseUrl: "https://eli.ai",
apiKey: () => process.env.ELI_API_KEY,
});
const answer = await eli.query.ask({
question: "Is anything stuck right now?",
includeData: true,
});Mount it in an MCP client
claude mcp add --transport http eli \
https://eli.ai/api/mcp \
--header "Authorization: Bearer eli_sk_..."
# Your agent can now search the graph, read
# documents, and run governed queries.Put your knowledge to work without losing track of it
Start in the hosted app, or follow the quickstart and run the whole thing on your own infrastructure.