Skip to documentation

Use cases by industry

Use case: revenue & guest ops

A hotel group runs on two things at once: a playbook — how it prices, upgrades, and hosts — and live numbers — what actually happened last weekend. A GM at the front desk is making a revenue call and a loyalty call in the same breath, with no data in front of them. This guide builds the assistant that closes that gap for Harbor View Hotels, a boutique group of waterfront and old-town properties, touching eight module slices: Intake to ingest the playbooks, Atlas to type the portfolio, Warrant to certify the rules that get quoted, Conduit to make the property management system answer live, Lens to answer with citations, Lineage to trace any answer to its source, Crucible to keep it accurate, and Ports to ship it as an agent.

One knowledge base, eight slices. Certified playbook (Warrant) and live PMS numbers (Conduit) both feed Lens; the dashed edge is the loop that keeps it honest — a regression the golden set catches steers the next round of curation.

Follow along in the seeded workspace

This walkthrough is a real, seedable demo. Run yarn demo:seed hospitality and the workspace appears as Harbor View Hotels (Hospitality) — every document, entity, named query, policy decision, and the analyst agent below is already there, so you can open it and reproduce each call. See the full catalog in Demo workspaces.

Prerequisites

  • A workspace key with kb:write, kb:read, data:read, data:run, agents:run, and runs:read, exported as $ELI_KEY. See Getting started.
  • Owner/admin on the workspace, to certify concepts and verify source documents.
  • An enabled chat model, so /query can generate — otherwise 409 no-model-configured.

1 · Intake — ingest the property playbooks

Harbor View's operating knowledge already exists as markdown — a page per property, the room-type specs, the guest-persona interviews, the loyalty program, and the weekly RevPAR review. Post them so their files, chunks, and FTS index commit as searchable, citable documents; graph enrichment follows as detached work. Start with the finding everything else will cite — the weekly RevPAR review:

POST/api/v1/documentsBearer · kb:write

Save markdown through the vault. The file, chunks, and full-text index commit before 201; embedding and graph enrichment continue as detached work, so the response contains stored-document metadata only.

Example requestbash
curl -s -X POST https://your-host/api/v1/documents \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "path": "revenue/revpar-review.md",
    "title": "Finding — Weekly RevPAR Review",
    "content": "# RevPAR Review\n\nOwned by [[Dahlia Moreau]], run weekly by [[Theo Nakamura]]. The [[RevPAR]] of each property is benchmarked against its [[Comp Set]]; anything trailing by more than 8% is escalated. Total revenue per available room includes [[The Anchor Room]] and [[Tidewater Cafe]] covers, pulled from the [[Property Management System]]."
  }'
Responsejson
{ "docId": "01KY30REVPARREVIEW00000000", "path": "revenue/revpar-review.md", "contentHash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", "title": "Finding — Weekly RevPAR Review" }

The rest of the corpus lands the same way — properties/harbor-view-waterfront.md, guests/anniversary-couple.md, loyalty/harbor-view-rewards.md — and detached extraction later supplies the typed concept graph used below. The 201response itself makes no graph-readiness promise. No markdown of your own? The same walkthrough works over any ingested corpus; see Ingest documents.

2 · Atlas — type the portfolio into a graph

Extraction reconciles names and relations against the workspace ontology Property, RoomType, GuestPersona, Outlet, Group, and LoyaltyTier — connected by typed relations (offers, operates, hosts, enrolled_in). Retrieval is now structured, not just text. Pull the neighborhood of the flagship, Harbor View Waterfront, to see it:

GET/api/v1/entities/{id}/neighborhoodBearer · kb:read or runs:read

The knowledge-graph frontier around one entity — canonical relations in both directions. This is Atlas: the typed portfolio the retriever traverses when a question names a property, room type, or group.

Example requestbash
curl -s "https://your-host/api/v1/entities/01KZPROPWATERFRONT00000000/neighborhood?depth=1" \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "entity": { "id": "01KZPROPWATERFRONT00000000", "name": "Harbor View Waterfront", "type": "Property", "authority": "machine_extracted", "mentionCount": 9 },
  "nodes": [
    { "id": "01KZROOMKINGWTRFRNT0000000", "name": "Harborview King", "type": "RoomType" },
    { "id": "01KZROOMGARDENSUITE0000000", "name": "Garden Suite", "type": "RoomType" },
    { "id": "01KZROOMBAYSTUDIO000000000", "name": "Bay Studio", "type": "RoomType" },
    { "id": "01KZOUTLETANCHORROOM000000", "name": "The Anchor Room", "type": "Outlet" },
    { "id": "01KZGROUPCOASTALTECH000000", "name": "Coastal Tech Summit", "type": "Group" }
  ],
  "edges": [
    { "src": "01KZPROPWATERFRONT00000000", "type": "offers", "dst": "01KZROOMKINGWTRFRNT0000000" },
    { "src": "01KZPROPWATERFRONT00000000", "type": "operates", "dst": "01KZOUTLETANCHORROOM000000" },
    { "src": "01KZPROPWATERFRONT00000000", "type": "hosts", "dst": "01KZGROUPCOASTALTECH000000" }
  ],
  "truncated": false
}

Extraction is a starting point, not a source of truth

Notice the Waterfront lands at authority machine_extracted. That is a candidate the graph inferred — good enough to retrieve on, not good enough for an assistant to state without hedging. Promoting the entities that matter is the next slice.

3 · Warrant — certify the rules reps quote

Some facts are quoted at the front desk and in the revenue meeting, and those have to be vouched for. The rule that governs the whole RevPAR review — benchmark each property against its comp set weekly, escalate anything trailing by more than 8% — is exactly that kind of fact. In Warrant, propose its canonical definition as a reviewable change, then certify it — certified is the only tier the assistant states plainly.

POST/api/v1/change-requestsBearer · kb:write

Propose the canonical definition as a reviewable change rather than editing silently. Approval promotes authority to at least curated and records the reviewer on an append-only revision.

Example requestbash
curl -s -X POST https://your-host/api/v1/change-requests \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "kind": "update",
    "entityId": "01KZCOMPSETBENCH0000000000",
    "payload": { "description": "Comp Set benchmark: the RevPAR of each property is compared to its comp set weekly; any property trailing the comp set by more than 8% is escalated to Dahlia Moreau." },
    "note": "Locking the comp-set escalation rule the RevPAR review runs on"
  }'
Responsejson
{ "id": "01KZCR00HOSPITALITY0000000", "entityId": "01KZCOMPSETBENCH0000000000", "kind": "update", "status": "open" }
POST/api/v1/entities/{id}/certifyBearer · kb:write

Owner/admin sign-off — authority certified. Also starts the review clock: nextReviewAt lands at now + the workspace's reviewIntervalDays, so a certified rule can't quietly go stale.

Example requestbash
curl -s -X POST https://your-host/api/v1/entities/01KZCOMPSETBENCH0000000000/certify \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"note": "Signed off by Dahlia Moreau (VP, Revenue & Guest Experience)"}'
Responsejson
{ "authority": "certified", "lifecycleStatus": "published", "version": 2, "nextReviewAt": "2027-01-18T09:10:00.000Z" }

Then verify the source document itself, so its provenance carries a governance tier the next two slices report on:

POST/api/v1/documents/{id}/verifyBearer · kb:write

Glean-style document verification. Stamps the verification tier, the verifier, and the next re-verify date — the governance block Lineage reports and the manifest counts.

Example requestbash
curl -s -X POST https://your-host/api/v1/documents/01KY30REVPARREVIEW00000000/verify \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"verification":"certified","note":"Current as of the latest RevPAR review"}'
Responsejson
{ "id": "01KY30REVPARREVIEW00000000", "verification": "certified", "verifiedAt": "2026-07-22T09:12:00.000Z", "nextVerifyAt": "2026-12-29T00:00:00.000Z" }

Certification is what separates a stated answer from an abstention downstream. Once the comp-set rule and the RevPAR review are certified, a backed question like "Which room type has the highest ADR at Harbor View Waterfront?" resolves to a policy verdict of answer — while an unbacked one like "How many guests will we have on New Year's Eve next year?" comes back abstain_with_pointers. Same corpus, different warrant. You will see both verdicts in the Lens slice.

Curate in the UI too

Everything here is also a click: the Curation page shows the open change-request queue, the review-due list, and a certify button per concept. The API and the UI write the same append-only revisions.

4 · Conduit — make the property system answer live

The docs say the Waterfront has the highest RevPAR in the group — but "what was it last weekend?" is a number no document holds. That number lives in the Property Management System, wired in as the read-only Conduit connector harborview-pms. It exposes governed named queries bound onto entities; list them, then run one. Every run writes an append-only data_calls row and returns a dataCallId — the [Dn] anchor you can cite:

GET/api/v1/data/queriesBearer · data:read

List the workspace's governed named queries (SELECT-only templates with typed params). Nothing executes here — these are the reusable domain components bound onto Atlas entities.

Example requestbash
curl -s https://your-host/api/v1/data/queries \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "items": [
    { "slug": "occupancy-by-property", "title": "Occupancy & RevPAR by property", "connectorName": "harborview-pms", "params": [ { "name": "start_date", "type": "date", "required": true }, { "name": "end_date", "type": "date", "required": true } ] },
    { "slug": "adr-by-room-type", "title": "ADR by room type", "connectorName": "harborview-pms", "params": [ { "name": "property", "type": "string", "required": true } ] },
    { "slug": "group-block-pickup", "title": "Group block pickup", "connectorName": "harborview-pms", "params": [ { "name": "group_name", "type": "string", "required": true } ] }
  ],
  "total": 3
}
POST/api/v1/data/queries/{slug}/runBearer · data:run

Execute a named query with validated params. Read-only, row-capped, timed, and provenance-logged. Params are bound, never interpolated. Returns a dataCallId — the [Dn] provenance anchor.

Example requestbash
curl -s -X POST https://your-host/api/v1/data/queries/occupancy-by-property/run \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"params":{"start_date":"2026-07-18","end_date":"2026-07-19"}}'
Responsejson
{
  "dataCallId": "01KY30PMSOCCUPANCY00000000",
  "connectorName": "harborview-pms",
  "queryTitle": "Occupancy & RevPAR by property",
  "columns": ["property", "occupancy_pct", "adr_usd", "revpar_usd"],
  "rows": [
    { "property": "Harbor View Waterfront", "occupancy_pct": 86.4, "adr_usd": 251.20, "revpar_usd": 217.04 },
    { "property": "Harbor View Cove", "occupancy_pct": 79.1, "adr_usd": 223.80, "revpar_usd": 177.03 },
    { "property": "Harbor View Old Town", "occupancy_pct": 68.5, "adr_usd": 198.40, "revpar_usd": 135.90 }
  ],
  "rowCount": 3,
  "truncated": false,
  "durationMs": 58,
  "executedAt": "2026-07-22T09:15:00.000Z"
}

Queries are bound to entities

occupancy-by-property is bound to the Property Management System entity, adr-by-room-type to Harbor View Waterfront, and group-block-pickup to Coastal Tech Summit. That binding is what lets the next slice run the right query automatically when a question names an entity — the model fills the params, it never writes SQL.

5 · Lens — ask, and get a cited answer

Now the payoff. Lens retrieves, detects entities, grounds one generation, and returns an answer with inline [Sn] citations and a policy decision that says how much to trust it. Start with a purely document-grounded question straight from the golden set:

POST/api/v1/queryBearer · agents:run

Run the grounded query pipeline: retrieval + entity detection + one generation + a groundedness check + the answer-policy engine. Returns a cited answer with a policy verdict. The workspace comes from the key.

Example requestbash
curl -s -X POST https://your-host/api/v1/query \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Who runs the weekly RevPAR review for the coastal properties?"}'
Responsejson
{
  "answer": "The weekly RevPAR review is run by Theo Nakamura, the revenue manager for the coastal properties, and is owned by Dahlia Moreau, VP of Revenue & Guest Experience [S1].",
  "claims": [
    { "text": "Theo Nakamura runs the weekly RevPAR review", "citations": ["S1"] },
    { "text": "It is owned by Dahlia Moreau", "citations": ["S1"] }
  ],
  "sources": [
    { "id": "S1", "docId": "01KY30REVPARREVIEW00000000", "docTitle": "Weekly RevPAR Review", "docPath": "revenue/revpar-review.md", "chunkId": "01KY30REVPARCHUNK000000000" }
  ],
  "entities": [ { "id": "01KZCOMPSETBENCH0000000000", "name": "Comp Set", "type": "concept", "authority": "certified" } ],
  "policy": { "verdict": "answer", "confidence": 0.73, "reasons": ["confidence_above_answer_floor"] },
  "groundedness": { "verified": 2, "total": 2 },
  "model": { "chat": "claude-sonnet-4-5", "judge": "claude-haiku-4-5" }
}

Documents answer the who; the hotel's hardest questions are about live numbers. Ask the flagship one with includeData: trueand Lens fuses the two layers — the detected entity's bound query runs as a governed [Dn] lookup alongside the document [Sn] citations:

POST/api/v1/queryBearer · agents:run

Grounded answer fusing the semantic and data layers: the Property Management System's bound occupancy-by-property query runs as a [Dn] lookup, cited beside the certified RevPAR review. This is the number nobody has to look up by hand.

Example requestbash
curl -s -X POST https://your-host/api/v1/query \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"What was RevPAR at Harbor View Waterfront last weekend?","includeData":true}'
Responsejson
{
  "answer": "RevPAR at Harbor View Waterfront over the July 18-19 weekend was $217.04 [D1] — the highest in the group and comfortably ahead of its comp set, which the review benchmarks weekly [S1].",
  "claims": [
    { "text": "Waterfront weekend RevPAR was $217.04", "citations": ["D1"] },
    { "text": "RevPAR is benchmarked weekly against the comp set", "citations": ["S1"] }
  ],
  "dataCalls": [ { "id": "D1", "dataCallId": "01KY30PMSOCCUPANCY00000000", "queryTitle": "Occupancy & RevPAR by property", "executedAt": "2026-07-22T09:15:00.000Z" } ],
  "sources": [ { "id": "S1", "docId": "01KY30REVPARREVIEW00000000", "docTitle": "Weekly RevPAR Review", "docPath": "revenue/revpar-review.md" } ],
  "entities": [ { "id": "01KZPROPWATERFRONT00000000", "name": "Harbor View Waterfront", "type": "Property" } ],
  "policy": { "verdict": "answer", "confidence": 0.81, "reasons": ["confidence_above_answer_floor"] }
}

The policy block is the trust signal, and it is the whole point of a revenue assistant. A backed number returns answer at 0.81. A judgment call with thin evidence — "Should we overbook the Garden Suites for the Vega-Marsh Wedding?" — returns answer_with_caveat at 0.43, hedged on purpose. And a demand forecast the corpus can't support — "How many guests will we have on New Year's Eve next year?" — returns abstain_with_pointers at 0.1 rather than inventing a number. That abstain-by-default behavior is exactly what the answer policy is for.

6 · Lineage — trace the answer to its source

The VP reviewing the assistant asks the fair question: where is this coming from, and can I trust it? The answer cited S1 → docId 01KY30REVPAR…. Pull that document's lineage and read the whole trail in one call — origin, governance, and the downstream answers it is grounding right now:

GET/api/v1/documents/{id}/lineageBearer · kb:read or runs:read

The unified per-document lineage trail: origin provenance, governance, the audit trail, derived knowledge, and downstream impact. This is how you prove an answer's pedigree end to end.

Example requestbash
curl -s https://your-host/api/v1/documents/01KY30REVPARREVIEW00000000/lineage \
  -H "Authorization: Bearer $ELI_KEY" | jq '{origin: .origin.kind, verification: .governance.verification, verifier: .governance.verifiedBy, citedBy: .downstream.citedByAnswers}'
Responsejson
{
  "origin": "upload",
  "verification": "certified",
  "verifier": "Dahlia Moreau",
  "citedBy": 11
}

Closed loop: the answer traces to revenue/revpar-review.md, the document is certified (verified by Dahlia Moreau in the Warrant slice), and it is currently grounding 11answers — so an edit to it has a known blast radius. The VP didn't have to trust the model; they read the pedigree. The full trail also lists the derived concepts (like the certified Comp Set rule) and the dependent cache entries that would invalidate if the review changed.

Same trail, three surfaces

The workspace UI renders this exact DocumentLineage as a visual trail on the document page (via the BFF at GET /api/w/{workspaceId}/documents/{docId}/lineage), and an agent can pull it mid-run with the kb_document_lineage MCP tool.

7 · Crucible — lock the accuracy in

Rates, blocks, and inventory change every day, and a wrong number in a revenue meeting is expensive. Capture the questions the team actually asks as a golden set so Crucible can prove the assistant still answers them after every content change. Anchor a golden item to the RevPAR review, and seed a provenance qrel from the chunk that should ground it:

POST/api/v1/qrelsBearer · kb:write

Author a graded relevance judgment for a golden item. source=provenance derives the highest-grade judgment from the item's anchored citation — deterministic, no judge model needed.

Example requestbash
curl -s -X POST https://your-host/api/v1/qrels \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "queryId": "01KZGOLDENREVPARREVIEW0000",
    "chunkId": "01KY30REVPARCHUNK000000000",
    "docId": "01KY30REVPARREVIEW00000000",
    "grade": 3,
    "source": "provenance"
  }'
Responsejson
{ "id": "01KZQRELHOSPITALITY0000000", "queryId": "01KZGOLDENREVPARREVIEW0000", "grade": 3, "source": "provenance" }

Run the "Revenue & guest Q&A baseline" set from the evals workspace, then read the metrics for the run. Retrieval and abstention numbers are deterministic; RAGAS numbers carry the judge model that produced them:

GET/api/v1/evals/runs/{id}/metricsBearer · kb:read or runs:read

Retrieval, RAGAS, and abstention metrics for one run — overall and by facet. The ruler for 'did the assistant get worse?' after a rate change or a re-certified rule.

Example requestbash
curl -s https://your-host/api/v1/evals/runs/01KZRUNHOSPBASELINE0000000/metrics \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "runId": "01KZRUNHOSPBASELINE0000000",
  "n": 24,
  "retrieval": { "recallAtK": 0.78, "ndcgAt10": 0.72, "mrr": 0.81, "hitAtK": 0.92 },
  "ragas": { "faithfulness": 0.90, "responseRelevancy": 0.85, "contextPrecision": 0.68 },
  "abstention": { "truthfulness": 0.64, "abstainRate": 0.12, "aurc": 0.071 }
}

When faithfulness drops after a rate update, the golden set names exactly which question regressed — and promoting that miss back into the set turns it into a permanent test. That is the dashed loop in the diagram: Crucible's regressions steer the next round of Warrant curation. Compare two configs behind a two-gate significance test in Measure & compare.

8 · Ports — ship it as the analyst agent

A grounded, measured pipeline is only useful if the team can reach it where they work. The seeded workspace ships it as an agent revenue-guest-analyst, the "Revenue & Guest Experience Analyst" — whose system prompt tells it to cite every claim and abstain without verified backing. Kick off a durable run:

POST/api/v1/agents/{slug}/runsBearer · agents:run

Start a durable agent run. Admits under concurrency and monthly-cost caps, then enqueues; returns immediately with a runId and a stream URL to reattach to. The loop runs on the worker, using the same grounded /query pipeline and the connector's bound live queries.

Example requestbash
curl -s -X POST https://your-host/api/v1/agents/revenue-guest-analyst/runs \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input":"Summarize RevPAR versus comp set by property this week, and flag any group blocks at risk before cutoff."}'
Responsejson
{ "runId": "01KZAGENTRUNANALYST0000000", "status": "queued", "stream": "/api/v1/runs/01KZAGENTRUNANALYST0000000/stream" }

That same substrate runs the workspace's scheduled checks, and the seeded pack already carries two live alerts: a revpar.comp_set_lag warning — "Harbor View Old Town is trailing its comp set RevPAR by 11% this week (shoulder season)" — and a group.pickup_lagging notice — "Coastal Tech Summit block pickup is at 62% with 21 days to cutoff at Harbor View Waterfront". The analyst doesn't just answer when asked; it watches the comp-set rule you certified in slice 3 and raises a flag when a property falls behind.

The leverage

Eight slices, one knowledge base, one audit trail. A GM gets a fast, cited answer to "what was RevPAR last weekend?"; the VP can trace any answer to a certified source and see its blast radius; the numbers come live from the property management system with [Dn]provenance; and the whole thing is measured, so accuracy is a number you watch rather than a hope. Swap "hotels" for any operation that runs on a playbook plus live data and the shape is identical — the platform doesn't care what the business is, only that every answer is grounded, governed, traceable, and measured.

Keep going

See the other end-to-end walkthroughs — Sales enablement and Automated reports — or open this workspace with yarn demo:seed hospitality and see the whole platform at a glance in the Capability map.