Skip to documentation

Use cases by industry

Use case: property operations

A property manager's hardest question is a simple one: what's actually true right now?What's the real occupancy at a community, which emergency work order has blown its SLA, and how much can rent go up at renewal without stepping on the Fair Housing Act. This guide builds that answer machine end to end on a workspace you can seed in one command, touching seven module slices: Intake to ingest the property files, Atlas to type the portfolio graph, Warrant to certify the numbers, Conduit to wire the live property-management database, Lens to answer with citations, Lineage to trace any answer, and Crucible to keep it correct as occupancy and maintenance move.

Seed this workspace and follow along

Everything below is grounded in a real seeded workspace. Run yarn demo:seed realestate and it appears as Beacon Property Group (Real Estate) — a residential manager of ~1,200 units across Maple Court Apartments, Harborview Lofts, and Cedar Ridge Townhomes, with the documents, the property database, and the ops agent already wired. See the demo workspaces index for the full list.

One knowledge base, seven slices. The dashed edge is the loop that keeps it honest: a regression the golden set catches steers the next round of curation.

Prerequisites

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

1 · Intake — ingest the property files

The knowledge already exists as scattered files: a firm overview, the operations roster, one markdown page per property, tenant records, the Standard Residential Lease, vendor sheets, and the Work Order Triage runbook. Post them as documents so their text becomes durably searchable before detached graph enrichment. Start with the property that anchors the rest of this guide, Maple Court:

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": "properties/maple-court-apartments.md",
    "title": "Property — Maple Court Apartments",
    "content": "# [[Maple Court Apartments]]\n\nManaged by [[Trevor Lang]]. A 320-unit garden-style community; the portfolio largest asset by unit count. Current occupancy runs near the 95% target. Home of [[The Alvarez Household]] in [[Unit 4B]]. Rent roll and occupancy tracked in the [[Property Management System]]; escalations flow through [[Work Order Triage]]."
  }'
Responsejson
{ "docId": "01KYMAPLECOURTDOC00000001", "path": "properties/maple-court-apartments.md", "contentHash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", "title": "Property — Maple Court Apartments" }

Repeat for the rest of the pack — tenants/okafor-household.md, operations/work-order-triage.md, systems/property-management-system.md, and the others. As each lands, Atlas can reconcile names and relations into a graph. That work is detached from the 201response; the seeded pack already has the structure used by the next slice.

2 · Atlas — type the portfolio graph

Text alone isn't structure. This workspace ships an ontology of six entity types — Property, Unit, Tenant, Lease, Vendor, and Manager — tied by manages, contains, occupies, and services relations. So "Maple Court" isn't a string; it's a Property a Manager manages and that contains the unit a Tenant occupies. Read the anchor entity and its edges:

GET/api/v1/entities/{id}Bearer · kb:read

Fetch one entity with its ontology type, authority tier, and typed relations. Extraction lands new entities at authority machine_extracted — a starting point Warrant will promote.

Example requestbash
curl -s https://your-host/api/v1/entities/01KYENTPROPERTYMAPLE00001 \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "id": "01KYENTPROPERTYMAPLE00001",
  "name": "Maple Court Apartments",
  "type": "Property",
  "authority": "machine_extracted",
  "relations": [
    { "type": "manages", "direction": "in", "entity": { "name": "Trevor Lang", "type": "Manager" } },
    { "type": "contains", "direction": "out", "entity": { "name": "Unit 4B", "type": "Unit" } },
    { "type": "services", "direction": "in", "entity": { "name": "Rapid Response Plumbing", "type": "Vendor" } }
  ],
  "sourceDocs": ["properties/maple-court-apartments.md"]
}

The graph is the retrieval index

Because retrieval is structured and not just fuzzy text, a question about Maple Court can pull in Trevor Lang, Unit 4B, and The Alvarez Household as related context. Explore and edit the same graph on the Entities page.

3 · Warrant — certify the numbers

Occupancy targets and SLAs are the numbers managers quote in reviews and to owners; they have to be vouched for, not machine-guessed. In Warrant, propose the canonical definition of the Occupancy Report concept as a reviewable change, then certify it — certified is the only tier the assistant states without hedging:

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 the 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": "01KYENTOCCUPANCYRPT000001",
    "payload": { "description": "Portfolio stabilized-occupancy target is 95%; any property under 92% triggers a leasing review with Nadia Osei." },
    "note": "Lock the occupancy target and review threshold managers report against"
  }'
Responsejson
{ "id": "01KYCROCCUPANCY0000000001", "entityId": "01KYENTOCCUPANCYRPT000001", "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 stale occupancy definition can't quietly slip through.

Example requestbash
curl -s -X POST https://your-host/api/v1/entities/01KYENTOCCUPANCYRPT000001/certify \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"note": "Signed off by Nadia Osei, Director of Property Operations"}'
Responsejson
{ "authority": "certified", "lifecycleStatus": "published", "version": 2, "nextReviewAt": "2027-01-18T09:10:00.000Z" }

Then verify the source document so its provenance carries a governance tier — the same Maple Court page Lineage will report on later:

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/01KYMAPLECOURTDOC00000001/verify \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"verification":"certified","note":"Occupancy and manager of record confirmed with Trevor Lang"}'
Responsejson
{ "id": "01KYMAPLECOURTDOC00000001", "verification": "certified", "verifiedAt": "2026-07-22T09:12:00.000Z", "nextVerifyAt": "2026-12-29T00:00:00.000Z" }

Certification is what earns a confident answer downstream. Ask "What is the current occupancy at Maple Court Apartments?" and the policy engine returns verdict answer (confidence 0.8, confidence_above_answer_floor) — precisely because the number is now backed by a certified concept and a verified source.

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 — pick whichever fits the reviewer.

4 · Conduit — wire the live property database

Documents describe the portfolio; they don't hold live occupancy. That's in the Property Management System — the book of record for units, leases, rent, and work orders. Wire a read-only data connector named beacon-pms with a small, named, parameterized query catalog, so the model runs vetted SQL and never freehand:

POST/api/v1/connectorsBearer · kb:write

Create a read-only Postgres data connector with a fixed catalog of named queries. The credential is stored encrypted and masked on every read; every query is capped at maxRows and only these slugs can run.

Example requestbash
curl -s -X POST https://your-host/api/v1/connectors \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "beacon-pms",
    "kind": "postgres",
    "config": { "host": "localhost", "port": 54330, "database": "beacon_pms", "user": "app", "sslMode": "disable" },
    "credential": "app",
    "maxRows": 200,
    "queries": [
      {
        "slug": "occupancy-summary",
        "title": "Occupancy by property",
        "sqlTemplate": "select p.name as property, count(*) filter (where u.status = 'occupied') as occupied_units, count(*) as total_units, round(100.0 * count(*) filter (where u.status = 'occupied') / nullif(count(*),0), 1) as occupancy_pct from units u join properties p on p.id = u.property_id group by p.name order by occupancy_pct asc",
        "params": []
      },
      { "slug": "rent-roll-by-property", "params": [{ "name": "property", "type": "string", "required": true }] },
      { "slug": "open-work-orders", "params": [] }
    ]
  }'
Responsejson
{ "id": "01KYCONNBEACONPMS00000001", "name": "beacon-pms", "kind": "postgres", "status": "active", "hasCredential": true, "queries": ["occupancy-summary", "rent-roll-by-property", "open-work-orders"] }

Bind queries to entities

Each query is bound to the concept it answers for, so retrieval knows when to reach for live data: Property Management Systemoccupancy-summary("Live occupancy by property"), Maple Court Apartments rent-roll-by-property (the property param is filled by the model from the question), and Work Order Triage open-work-orders. When a manager asks about occupancy, Lens knows to run occupancy-summary and cite the row it used.

5 · Lens — a manager asks, and gets a cited answer

Now the payoff. A manager asks in plain language; Lens retrieves the certified docs, runs the bound occupancy-summary query, and returns an answer with inline [Sn] citations, an atomic claims breakdown, and a policy decision that says how much to trust it:

POST/api/v1/queryBearer · agents:run

Run the grounded query pipeline: retrieval + entity detection + bound live queries + one generation + a groundedness check + the answer-policy engine. Returns a cited answer with a policy decision. 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":"What is the current occupancy at Maple Court Apartments?"}'
Responsejson
{
  "answer": "Maple Court Apartments is at 95% occupancy — 304 of 320 units occupied — right at the portfolio's stabilized target [S1]. Its on-site property manager is Trevor Lang [S2].",
  "claims": [
    { "text": "Maple Court occupancy is 95% (304 of 320 units)", "citations": ["S1"] },
    { "text": "The portfolio stabilized-occupancy target is 95%", "citations": ["S2"] },
    { "text": "Trevor Lang manages Maple Court Apartments", "citations": ["S2"] }
  ],
  "sources": [
    { "id": "S1", "kind": "connector_query", "connector": "beacon-pms", "query": "occupancy-summary", "row": { "property": "Maple Court Apartments", "occupied_units": 304, "total_units": 320, "occupancy_pct": 95.0 } },
    { "id": "S2", "docId": "01KYMAPLECOURTDOC00000001", "docTitle": "Property — Maple Court Apartments", "docPath": "properties/maple-court-apartments.md", "chunkId": "01KYMAPLECOURTCHK00000001" }
  ],
  "entities": [
    { "id": "01KYENTPROPERTYMAPLE00001", "name": "Maple Court Apartments", "type": "Property", "authority": "curated" },
    { "id": "01KYENTOCCUPANCYRPT000001", "name": "Occupancy Report", "type": "concept", "authority": "certified" }
  ],
  "policy": { "decision": "answer", "confidence": "high", "score": 0.8, "reasons": ["confidence_above_answer_floor"] },
  "groundedness": { "verified": 3, "total": 3 },
  "model": { "chat": "claude-sonnet-4-5", "judge": "claude-haiku-4-5" }
}

The number comes straight from the live database (S1) and the manager-of-record from the certified doc (S2) — no hallucinated occupancy. The real test is a question the assistant must not answer. Ask it to make a screening call on a protected basis, and the policy engine abstains before retrieval even runs:

POST/api/v1/queryBearer · agents:run

The same pipeline, a fair-housing-risky question. A pre-generation policy stage catches it and returns abstain_with_pointers — a refusal plus a pointer to the governing document, never an invented eligibility judgment.

Example requestbash
curl -s -X POST https://your-host/api/v1/query \
  -H "Authorization: Bearer $ELI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Should I deny this applicant based on their neighborhood?"}'
Responsejson
{
  "answer": "I can't help decide an application on that basis. Screening on neighborhood or other protected characteristics risks violating the Fair Housing Act, and I have no verified, permissible criteria to answer from. Apply the documented screening criteria consistently and route the file to Gwen Fowler.",
  "policy": { "decision": "abstain_with_pointers", "stage": "pre", "score": 0.05, "reasons": ["fair_housing_risk", "no_relevant_chunks"] },
  "pointers": [
    { "docTitle": "Standard Residential Lease", "docPath": "leases/standard-residential-lease.md" }
  ]
}

Two questions, two very different behaviors from one pipeline: a high-confidence, dual-sourced answer for the occupancy question, and a hard abstain_with_pointers for the fair-housing one. The same pipeline is a native MCP tool, so it drops straight into a manager's Claude or the ops app.

6 · Lineage — trace the answer back to its source

Nadia Osei, reviewing the assistant, asks the fair question: where is this coming from, and can I trust it? The occupancy answer cited S2 → docId 01KYMAPLECOURT…. 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/01KYMAPLECOURTDOC00000001/lineage \
  -H "Authorization: Bearer $ELI_KEY" | jq '{origin: .origin, verification: .governance.verification, derived: .derived.entities, citedBy: .downstream.citedByAnswers}'
Responsejson
{
  "origin": {
    "kind": "document",
    "path": "properties/maple-court-apartments.md",
    "ingestedVia": "POST /api/v1/documents",
    "ingestedAt": "2026-07-22T09:05:12.004Z"
  },
  "verification": "certified",
  "derived": ["Maple Court Apartments", "Trevor Lang", "Unit 4B", "The Alvarez Household"],
  "citedBy": 9
}

There it is, closed loop: the answer traces to a certified markdown document, that document derived the Maple Court Apartments property and its manager Trevor Lang, and it is currently grounding 9answers — so an edit to it has a known blast radius. Nadia didn't have to trust the model; she read the pedigree. The full trail also lists the dependent cache entries that would invalidate if the page 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. One shape, three surfaces.

7 · Crucible — lock the accuracy in

Occupancy and maintenance move every day, and a wrong answer to an owner or a resident is expensive. Capture the questions managers actually ask as a golden set so Crucible can prove the assistant still answers them after every content and data change. Anchor the golden item "Which property manager runs Maple Court Apartments?" (answer: Trevor Lang) and seed a provenance qrel from the Maple Court 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": "01KYGOLDENMAPLEMGR0000001",
    "chunkId": "01KYMAPLECOURTCHK00000001",
    "docId": "01KYMAPLECOURTDOC00000001",
    "grade": 3,
    "source": "provenance"
  }'
Responsejson
{ "id": "01KYQRELMAPLE000000000001", "queryId": "01KYGOLDENMAPLEMGR0000001", "grade": 3, "source": "provenance" }

The seed ships this golden set as Property Ops Q&A baseline and a portfolio-ops-analystagent (the "Portfolio Ops Analyst") that runs it — cited on every claim, abstaining without verified backing, and refusing fair-housing-risky judgments. Read the metrics for the seeded run:

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?'

Example requestbash
curl -s https://your-host/api/v1/evals/runs/01KYRUNPROPOPS00000000001/metrics \
  -H "Authorization: Bearer $ELI_KEY"
Responsejson
{
  "runId": "01KYRUNPROPOPS00000000001",
  "config": "Property Ops Q&A baseline",
  "n": 24,
  "retrieval": { "recallAtK": 0.78, "ndcgAt10": 0.74, "mrr": 0.81, "hitAtK": 0.95 },
  "ragas": { "faithfulness": 0.92, "responseRelevancy": 0.86, "contextPrecision": 0.69 },
  "abstention": { "truthfulness": 0.64, "abstainRate": 0.14, "aurc": 0.071 }
}

When a metric drops after a data or content change, 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 with a two-gate significance test in Measure & compare.

The leverage

Seven slices, one knowledge base, one audit trail. A manager gets a fast, cited occupancy number pulled live from the property database; a fair-housing-risky screening question is refused, not answered; the SLA-breached no-heat emergency at Harborview surfaces as a critical alert instead of sitting in a queue; and Nadia can trace any answer to a certified source and see its blast radius. The whole thing is measured, so accuracy is a number you watch rather than a hope. Swap "property operations" for any regulated, data-heavy domain and the shape is identical — the platform doesn't care what the records are about, only that every answer is grounded, governed, traceable, and measured.

Keep going

Automate the deliverable side of this with Automated reports, end to end, browse the other seeded demo workspaces, or see the whole platform at a glance in the Capability map.