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.

The platform is eight composable systems. Adopt the slices you need — retrieval, live data, approvals, eval, lineage, or the API — and leave the rest off. The same handoffs still produce one cited result.

composable systems
8
documented API operations
92

Run the whole machine on a laptop or a single VPS: one Postgres, a web process, and a worker. No managed services required. Docker Compose locally, or the same two processes in production — your documents, graph, and connectors stay on infrastructure you control. Deploy it yourself.

Grounded chatverified 2/2

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

Composition / grounded answer

  1. Intake
  2. Atlas
  3. Warrant
  4. Lens
  5. Ports

Five systems, in the order they hand off. The annotated plate is below.

Composition is the product.

Documents, live data, policy, evaluation, lineage, and programmatic surfaces participate in one result. Each system is a slice you can adopt or self-host on its own. Composition is how those slices become one answer. Follow a path to see where each handoff happens.

One machine, four documented paths

Path note 01 / grounded answer

A source-backed answer, in five moves.

Question
What is the approved procedure for requeueing stuck orders?
Result
Open the fulfilment console, select orders older than 24 hours, and requeue them from the incident panel S1.
source-backed
  1. 01Intake

    Source arrives. Normalize source material and preserve source permissions.

  2. 02Atlas

    Evidence connects. Connect evidence through typed entities and relations.

  3. 03Warrant

    Claims narrow. Apply certification, lifecycle, and content ACL policy.

  4. 04Lens

    An answer forms. Retrieve, rerank, answer, clarify, or abstain.

  5. 05Ports

    The result leaves. Deliver the result to a person or programmatic caller.

Lineage records the source revision and answer trail; Crucible measures groundedness and abstention behavior.

What composing them produces

  • 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.

    • Lens
    • Conduit
  • 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.

    • Warrant
    • Lens
  • 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.

    • Lineage
    • Crucible
  • Humans stay in the loop

    Consequential agent actions suspend into an approvals inbox — draft, rationale, and three verbs: approve, reject, or edit. A per-tool review policy earns autopilot over time: every autopiloted call is logged and risk-scored, a sample gets a retrospective verdict, and overturns tighten the policy on their own.

    • Warrant
    • Ports
  • One engine for every number

    Matrices, deal configurations, pricing rules and estimation factors live as versioned structured records. Prices come from rules, estimates come out as ranges with visible assumptions, and report figures freeze their source values — so a number a buyer sees is the number delivery reports.

    • Atlas
    • Conduit

Every mechanism, and what it changes.

Select a piece of the platform to read the mechanism your engineers can verify, and the outcome everyone else can check. Both lines describe the same shipped code — and the systems each piece composes with.

Lens

Cited answers, honest abstentions

the mechanism
Hybrid retrieval with reranking and graph fusion, ACL-filtered before ranking. A citation policy validates every claim, retries with named gaps, then abstains rather than shipping uncited — span-level offsets included.
what it changes
Answers you can forward to a customer or an auditor without re-checking them — and a refusal you can trust instead of a confident guess.

composes with

Every job is a different path.

Each row below runs on the machine above. Read a path for the systems a job needs and the order it needs them in: the chain that builds the result, then what happens to it once it exists. 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.

  • 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.

    1. Intake
    2. Atlas
    3. Lens
    then
    1. Lineage
  • 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.

    1. Intake
    2. Warrant
    3. Lens
    then
    1. Crucible
  • 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.

    1. Warrant
    then
    1. Lineage
    2. Crucible
    3. Ports
  • Sales enablement

    A rep on a live call gets the current price, the certified positioning and the security posture — cited, never guessed.

    1. Intake
    2. Warrant
    3. Lens
    then
    1. Lineage
    2. Crucible
  • Automated reports

    The same client report every Monday — current numbers, defensible definitions, a reproducible PDF — with nobody assembling it by hand.

    1. Conduit
    2. Warrant
    3. Lens
    then
    1. Crucible
    2. Ports
  • Matter research

    State a certified controlling authority plainly, read the live matters database, and decline when a settlement call has no authority on file.

    1. Intake
    2. Atlas
    3. Warrant
    4. Conduit
    5. Lens
    then
    1. Lineage
    2. Crucible
  • 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.

    1. Intake
    2. Atlas
    3. Warrant
    4. Conduit
    5. Lens
    then
    1. Lineage
    2. Crucible
    3. Ports
  • Sales AI desk

    One deal record drives the deck, the proposal, the price and the contract; estimates ship as ranges, claims trace to the matrices, and consequential sends wait for a human — until autopilot is earned.

    1. Intake
    2. Atlas
    3. Warrant
    4. Conduit
    5. Lens
    then
    1. Lineage
    2. Crucible
    3. Ports

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 systems it needs, and the rest stays switched off.

Eight systems, and any one alone.

Each system 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.

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.

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.

Read each system as the code you would ship — or as the data flowing through it.

  • IntakeConnect & ingest

    Bring it in

    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
    Intake reference

    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}`)}
    />
  • AtlasThe semantic layer

    Connect it

    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
    Atlas reference

    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}`)}
    />
  • WarrantGovern & certify

    Govern it

    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
    Warrant reference

    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-LD

    Reactthe 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)}
    />
  • LineageProvenance & traceability

    Trace it

    Trace any document to what it produced, and see what breaks before you deprecate it.

    API
    /api/v1/documents
    SDK
    @eli-ai/react/documents
    Lineage reference

    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)}
    />
  • ConduitThe live-data layer

    Query it

    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
    Conduit reference

    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 dataCallId is the D1 anchor

    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...", "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)}
    />
  • LensRetrieve & answer

    Answer it

    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
    Lens reference

    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=abstain

    Reactsearch 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)}
    />
  • CrucibleEvaluate & measure

    Measure it

    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
    Crucible reference

    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.05

    Reactrun metrics and the policy-decision log

    import { EliQuality, EliPolicyDecisions } from "@eli-ai/react/quality";
    
    <EliQuality initialEvalRunId="01K7Z..." />
    <EliPolicyDecisions surface="chat" verdict="abstain" limit={20} />
  • PortsProgrammatic surfaces

    Ship it

    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
    Ports reference

    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 />

Executable surface / Ports

One engine, three surfaces.

People use the workspace, applications call the REST API, and agents mount the MCP server. All three execute the same governed pipeline under one workspace-scoped key: structured JSON with both citation registries intact, 92 documented operations with a live console, and a native MCP server — so an external agent can use your knowledge base without a bespoke integration.

Same key, same scopes, same provenance

Applications send POST /api/v1/query. MCP clients connect to /api/mcp and invoke the eli_query tool. Two transports that reach the same governed query pipeline, and get back the same cited answer.

Applications

the typed client — one POST /api/v1/query, key held server-side

// npm install @eli-ai/client
import { createEliClient } from "@eli-ai/client";

const eli = createEliClient({
  baseUrl: "https://eli.ai",
  apiKey: () => process.env.ELI_API_KEY,
});

// one POST /api/v1/query — scope agents:run
const answer = await eli.query.ask({
  question: "Is anything stuck right now?",
  includeData: true,
});
// answer.claims[0].citations -> ["S1", "D1"]
// answer.policy.verdict     -> "answer" | "abstain"

Agents

mount the workspace over streamable HTTP — POST /api/mcp

claude mcp add --transport http eli \
  https://eli.ai/api/mcp \
  --header "Authorization: Bearer eli_sk_..."

# Ten read-only tools — kb_search, kb_graph_query,
# kb_data_lookup, eli_query — under the same key,
# the same scopes, and the same row-level security.

Colophon / start here

Every answer shows its work

Open the hosted workspace and ask it something you can check, or run the quickstart on your own Postgres and two Node processes. Either way, the first cited answer is the test.