Same steps. Different tables.
Krusgraph turns tables without declared keys into an explained graph, then into what people actually use.
It reads table and column names, works out the keys and links, and gives every one the rule that produced it. You check the plan against the rows, then create what you need. The steps are the same in every industry; what differs is the tables.
- 1Tablesno declared keys
- 2Explained graph
plan_backend, thenvalidate_plan - 3What people use
- GraphQL APIan API for GraphQL item in your workspace, for an app or for questions in Claude
- Fabric IQ ontology draftentity types, keys and relationships (preview)
- Semantic modelfor Power BIplanned
- SQL viewsfor the joins a rule cannot expressplanned
- Data agentthat answers over the same modelplanned
Ten industries
Each one has the same four parts: typical tables, what Krusgraph finds in them, what you build, and where the value is.
- Retail & e-commercecustomers, orders, order_lines, products, categories, returns
- Manufacturingitems, bill_of_materials, work_orders, machines, quality_checks
- SaaS & subscriptionsaccounts, plans, subscriptions, invoices, invoice_lines, usage_events
- Professional servicesclients, consultants, projects, time_entries, invoices
- Financial services: AMLcustomers, accounts, account_holders, transactions, counterparties, alerts, kyc_reviews
- Financial crime preventioncustomers, cases, alerts, screening_hits, sanctions_lists, devices, logins
- Pharmaceutical researchstudies, sites, subjects, visits, adverse_events, compounds, assays, results
- Environment & sustainabilitysites, meters, meter_readings, activities, emission_factors, suppliers, purchases
- Property & real estateproperties, units, tenants, leases, maintenance_requests
- Educationstudents, teachers, courses, enrolments, grades
Retail & e-commerce
Who bought what, on which order line, and what came back.
Typical tables
From names alone, as the engine plans them: 6 tables, 6 with keys, 6 relationshipscustomers
- customer_
id key · K2 - country
- created_
at
- customer_
orders
- order_
id key · K2 - customer_
id customers R1 - order_
date - status
- order_
order_
lines - order_
id key · K5 - line_
no key · K5 - product_
id products R1 - quantity
- unit_
price
- order_
products
- product_
id key · K2 - sku
- category_
id categories R1 - list_
price
- product_
categories
- category_
id key · K2 - parent_
category_ categories? confirmid - name
- category_
returns
- return_
id key · K2 - order_
id orders R1 - line_
no - reason
order_id + line_noorder_lines R3- return_
What Krusgraph finds
- R1
orders.customer_id→customers: the column is named after the table. The same rule linksorder_lines.product_id→productsandproducts.category_id→categories. - K5
order_lineshas no key column of its own. The line rule (K5) takes the reference to its header plus the line number,order_id + line_no, andreturns.order_id + line_no→order_linesfollows by R3. - to confirm
categories.parent_category_idpoints back at its own table, and no rule links a table to itself by name. Confirm it once and the API gets aparent_categoryfield.
What you build
- A returns desk in Lovable over the GraphQL API: an order, its lines and products, and what came back, in one nested query.
- Questions in Claude through
query_graphql, such as which categories see the most returns. - A Power BI semantic model from the same keys (planned).
Where the value is
- A working read API over the order tables as soon as the plan is accepted, without writing a schema by hand.
- The line-level key is stated once, so returns are not joined to whole orders and counted once per line.
- Every link carries its rule. That is the documentation when the data moves to another team.
Manufacturing
Work orders on machines, what they make, what goes into it, and whether it passed.
Typical tables
From names alone, as the engine plans them: 5 tables, 5 with keys, 5 relationshipsitems
- item_
no key · K2 - description
- unit
- item_
type
- item_
bill_
of_ materials - parent_
item_ key · K4no - component_
item_ key · K4no - quantity
- parent_
work_
orders - work_
order_ key · K2id - item_
no items R1 - machine_
id machines R1 - planned_
start - status
- work_
machines
- machine_
id key · K2 - line_
name - model
- machine_
quality_
checks - quality_
check_ key · K2id - work_
order_ work_orders R1id - checked_
at - result
- quality_
What Krusgraph finds
- R1
itemsgetsitem_noas its key (K2 also reads_no), andwork_orders.item_no→itemsfollows.work_orders.machine_id→machinesandquality_checks.work_order_id→work_ordersthe same way. - R4
bill_of_materialspoints atitemstwice, throughparent_item_noandcomponent_item_no. Both are linked by R4, the weaker rule for a role in front of a table's name, asparent_itemandcomponent_itemfields. Check them before you build. - K4
bill_of_materialshas no key column and starts with those two references, so the junction rule (K4) makesparent_item_no + component_item_noits key.validate_plancounts duplicates and NULLs in it before anything is built.
What you build
- A shop-floor app over GraphQL: a work order with its item, machine and quality checks.
- One level of a bill of materials per query. A full explosion is recursive, and that stays a SQL job.
- Questions in Claude, such as which machines had failed checks last week.
Where the value is
- The two-way bill of materials is a classic wrong join. Both roles carry the weaker rule, so they get reviewed instead of assumed.
- A first backend over exported production tables, with no keys declared in the lakehouse.
- The rules and confirmations are the handover note: the next person sees why each link exists.
SaaS & subscriptions
Accounts on plans, invoices with lines, and a stream of usage events.
Typical tables
From names alone, as the engine plans them: 6 tables, 5 with keys, 7 relationshipsaccounts
- account_
id key · K2 - name
- owner_
email - created_
at
- account_
plans
- plan_
id key · K2 - name
- monthly_
price
- plan_
subscriptions
- subscription_
id key · K2 - account_
id accounts R1 - plan_
id plans R1 - billing_
account_ accounts R4id - started_
at
- subscription_
invoices
- invoice_
id key · K2 - account_
id accounts R1 - subscription_
id subscriptions R1 - issued_
at - total
- invoice_
invoice_
lines - invoice_
id key · K5 - line_
no key · K5 - description
- amount
- invoice_
usage_
events - event_
id - subscription_
id subscriptions R1 - event_
type - occurred_
at
no key · K3
- event_
What Krusgraph finds
- R1
subscriptions.account_id→accountsandsubscriptions.plan_id→plans;invoices.subscription_id→subscriptionsas well. - R4
subscriptions.billing_account_id→accountsby R4, the weaker rule for a role in front of a table's name, as abilling_accountfield. Probably right, for example a parent company paying for a subsidiary, but only you know: check it, or confirm another target. - K5
invoice_linesgetsinvoice_id + line_noby the line rule (K5).usage_eventshasevent_id, which is not named after the table, so it gets no key (K3). It still points atsubscriptions, but nothing can point at it until you give the key (K1).
What you build
- A self-service billing page in Lovable: an account, its subscriptions, invoices and invoice lines.
- Usage per plan, asked in Claude and answered through the API without writing SQL.
- A semantic model for revenue reporting from the same keys (planned).
Where the value is
- The using account and the paying account are kept apart from the start, not discovered in a wrong revenue figure later.
- Time to a first working API is the time it takes to read and accept a plan.
- The plan doubles as a data dictionary for new engineers.
Professional services
Clients, projects, consultants, and the hours that end up on invoices.
Typical tables
From names alone, as the engine plans them: 5 tables, 5 with keys, 6 relationshipsclients
- client_
id key · K2 - name
- sector
- client_
consultants
- consultant_
id key · K2 - name
- grade
- manager_
id consultants? confirm
- consultant_
projects
- project_
id key · K2 - client_
id clients R1 - lead_
consultant_ consultants R4id - name
- project_
time_
entries - time_
entry_ key · K2id - consultant_
id consultants R1 - project_
id projects R1 - work_
date - hours
- invoice_
id invoices R1
- time_
invoices
- invoice_
id key · K2 - client_
id clients R1 - issued_
at - amount
- invoice_
What Krusgraph finds
- R1
time_entries.project_id→projects,time_entries.consultant_id→consultantsandtime_entries.invoice_id→invoices. An emptyinvoice_idis an unbilled hour:validate_plancounts rows with a value and orphans separately. - R4
projects.lead_consultant_id→consultantsby R4, a role in front of the table's name, as alead_consultantfield.consultants.manager_idnames no table at all, so it stays unlinked until you confirm it; then it ismanager. - K2Every table here has a key by name (
client_id,project_id,time_entry_id, …), so the API can fetch any single row by its key.
What you build
- A utilisation app: hours per consultant and project, with the client one level up.
- Questions in Claude, such as which projects have hours that are not invoiced yet.
- A Fabric IQ ontology draft (preview) with Client, Project, Consultant and Time entry entity types.
Where the value is
- Hours to invoices is the join that decides revenue. Its rule and its orphan count are visible before anything is built.
- A backend for an internal tool without a modelling project first.
- A new controller reads the plan instead of reverse-engineering old reports.
Financial services: AML
Anti-money laundering: customers, the accounts they hold, transactions, counterparties, alerts and KYC reviews.
Typical tables
From names alone, as the engine plans them: 7 tables, 7 with keys, 8 relationshipscustomers
- customer_
id key · K2 - name
- country
- risk_
rating
- customer_
accounts
- account_
id key · K2 - product_
type - opened_
at - status
- account_
account_
holders - customer_
id key · K4 - account_
id key · K4 - holder_
role - since
- customer_
transactions
- transaction_
id key · K2 - account_
id accounts R1 - counterparty_
id counterparties R1 - beneficiary_
account_ accounts R4id - amount
- booked_
at
- transaction_
counterparties
- counterparty_
id key · K2 - name
- country
- bank_
code
- counterparty_
alerts
- alert_
id key · K2 - transaction_
id transactions R1 - customer_
id customers R1 - scenario
- raised_
at
- alert_
kyc_
reviews - kyc_
review_ key · K2id - customer_
id customers R1 - reviewed_
at - outcome
- kyc_
What Krusgraph finds
- R1
transactions.account_id→accounts,transactions.counterparty_id→counterpartiesandalerts.transaction_id→transactions. - K4
account_holdersis a bridge with two outgoing references:account_holders.customer_id→customersandaccount_holders.account_id→accounts, both R1. The junction rule (K4) makescustomer_id + account_idits key: one row per holding. - R4
transactions.beneficiary_account_id→accountsby R4, the weaker rule for a role in front of a table's name, as abeneficiary_accountfield. The plan says which rule made it, so it is checked rather than trusted.
What you build
- One explained model that investigators and an agent in Claude query the same way.
- Questions such as all transactions between accounts held by the same customer in the last 90 days: one nested GraphQL query from customer through
account_holders, accounts and transactions to the beneficiary account, with the rule or confirmation behind each step. - A read-only investigation app in Lovable over the same API.
Where the value is
- Fewer wrong joins in investigations: the bridge and the beneficiary are each stated once, with their rules.
- A documented model for model-risk and audit review: every link has a rule or a recorded confirmation, and
validate_plancounts orphans before anyone relies on it.
Financial crime prevention
Fraud, sanctions screening and KYC: alerts and cases from teams that usually keep separate tables.
Typical tables
From names alone, as the engine plans them: 7 tables, 6 with keys, 6 relationshipscustomers
- customer_
id key · K2 - name
- onboarded_
at
- customer_
cases
- case_
id key · K2 - customer_
id customers R1 - linked_
case_ cases? confirmid - case_
type - status
- case_
alerts
- alert_
id key · K2 - case_
id cases R1 - customer_
id customers R1 - source_
system - raised_
at
- alert_
screening_
hits - screening_
hit_ key · K2id - customer_
id customers R1 - list_
entry_ id - match_
score
- screening_
sanctions_
lists - list_
entry_ id - list_
name - entry_
name - published_
at
no key · K3
- list_
devices
- device_
id key · K2 - fingerprint
- first_
seen
- device_
logins
- login_
id key · K2 - customer_
id customers R1 - device_
id devices R1 - ip_
address - logged_
in_ at
- login_
What Krusgraph finds
- R1
logins.device_id→devices,logins.customer_id→customersandalerts.case_id→cases: fraud signals, alerts and cases meet at the customer. - R2
sanctions_listshas one row per list entry, keyed bylist_entry_id, which no key rule reads (K3). Give it as the key (K1) andscreening_hits.list_entry_id→sanctions_listsfollows by R2. - to confirm
cases.linked_case_idties a case to an earlier one. It points back at its own table, which no rule links by name, so you confirm it; then each case has alinked_casefield.
What you build
- One connected view for fraud, sanctions and AML analysts: a customer with cases, alerts, screening hits and logins.
- Questions in Claude, such as which customers with an open screening hit also have a fraud alert this month.
- A Fabric IQ ontology draft (preview) as a shared vocabulary across the teams.
Where the value is
- Teams that work in separate tables share one model and see the same joins.
- Explained links are defensible to auditors: each one says which rule made it, or that someone confirmed it.
validate_planshows orphans, such as screening hits for customers missing fromcustomers, first.
Pharmaceutical research
Clinical studies and lab work: subjects at sites, visits, adverse events, compounds and assay results.
Typical tables
From names alone, as the engine plans them: 8 tables, 8 with keys, 9 relationshipsstudies
- study_
id key · K2 - compound_
id compounds R1 - phase
- start_
date
- study_
sites
- site_
id key · K2 - country
- investigator
- site_
subjects
- subject_
id key · K2 - study_
id studies R1 - site_
id sites R1 - arm_
code - enrolled_
at
- subject_
visits
- subject_
id subjects R1 - visit_
no key · K2 - visit_
date - visit_
type
- subject_
adverse_
events - adverse_
event_ key · K2id - subject_
id subjects R1 - visit_
no visits R1 - severity_
code - meddra_
code
- adverse_
compounds
- compound_
id key · K2 - code_
name - modality
- compound_
assays
- assay_
id key · K2 - assay_
type - unit
- assay_
results
- result_
id key · K2 - assay_
id assays R1 - subject_
id subjects R1 - visit_
no visits R1 - value
- result_
What Krusgraph finds
- R1
visits.subject_id→subjects,results.assay_id→assays,subjects.site_id→sitesandstudies.compound_id→compounds. - K1From names alone,
visitsgetsvisit_noas its key (K2) andresults.visit_no→visits(R1). Both are wrong: visit numbers repeat per subject, andvalidate_planshows the duplicates. The engine does not suggest the fix here; you givesubject_id + visit_no(K1). The single-column links go, andresultsandadverse_eventsreachvisitsby R3 on both columns. - to confirmCoded columns such as
adverse_events.severity_code,adverse_events.meddra_codeandsubjects.arm_codepoint at code lists. No rule links them and nothing is guessed: add the code-list tables, or confirm each link.
What you build
- An agent in Claude that answers research questions through the explained plan, such as which compounds had adverse events at more than one site.
- A study overview app: a subject with visits, results and adverse events.
- A semantic model for study reporting (planned).
Where the value is
- Fewer confident-but-wrong joins: the visit key above is that kind of mistake, caught before an answer depends on it.
- Orphans are counted first: results without an assay, adverse events without a visit.
- Regulated use, such as GxP work, needs the organisation's own validation; Krusgraph does not provide it.
Environment & sustainability
Sites, energy meters and readings, activities with emission factors, and purchases from suppliers (Scope 3).
Typical tables
From names alone, as the engine plans them: 7 tables, 6 with keys, 6 relationshipssites
- site_
id key · K2 - name
- region
- site_
meters
- meter_
id key · K2 - site_
id sites R1 - energy_
type
- meter_
meter_
readings - meter_
id meters R1 - read_
at - value_
kwh
no key · K3
- meter_
activities
- activity_
id key · K2 - site_
id sites R1 - emission_
factor_ emission_factors R1id - activity_
type - quantity
- period_
start
- activity_
emission_
factors - emission_
factor_ key · K2id - activity_
type - valid_
from - kg_
co2e_ per_ unit
- emission_
suppliers
- supplier_
id key · K2 - name
- country
- supplier_
purchases
- purchase_
id key · K2 - supplier_
id suppliers R1 - site_
id sites R1 - spend_
category - amount
- purchased_
at
- purchase_
What Krusgraph finds
- R1
activities.emission_factor_id→emission_factors: the factor behind each activity is an explained join.meters.site_id→sitesandmeter_readings.meter_id→metersas well. - R1
purchases.supplier_id→suppliersandpurchases.site_id→sites, for spend-based Scope 3 per site and supplier. - K3
meter_readingshas no key, which is fine for rows that are read, not pointed at. Where a factor is picked by activity type and date range instead of an id, that is not a key relationship: Krusgraph does not infer it, and it belongs in a SQL view.
What you build
- A site app over GraphQL: a site with its meters, activities and purchases.
- Questions in Claude, such as emissions per site and quarter with the factor behind each figure.
- SQL views for date-range factor matching (planned); until then, those are written by hand.
Where the value is
- Traceable numbers for sustainability reporting: every figure has a path back to its source tables, and every step has a rule.
- Each activity points at the factor row it used, so a changed factor shows up as a changed link.
- It documents joins. It does not make a report meet any reporting standard.
Property & real estate
Properties split into units, leased to tenants, with maintenance requests.
Typical tables
From names alone, as the engine plans them: 5 tables, 5 with keys, 5 relationshipsproperties
- property_
id key · K2 - address
- city
- property_
units
- unit_
id key · K2 - property_
id properties R1 - floor
- area_
m2
- unit_
tenants
- tenant_
id key · K2 - name
- contact_
email
- tenant_
leases
- lease_
id key · K2 - unit_
id units R1 - tenant_
id tenants R1 - start_
date - monthly_
rent
- lease_
maintenance_
requests - maintenance_
request_ key · K2id - unit_
id units R1 - reported_
by_ tenants R4tenant_ id - status
- opened_
at
- maintenance_
What Krusgraph finds
- R1
units.property_id→properties(the plural is understood),leases.unit_id→unitsandleases.tenant_id→tenants. - R4
maintenance_requests.reported_by_tenant_id→tenantsby R4, a role in front of the table's name, as areported_by_tenantfield. Check it before you build. - K2All five tables have a key by name, so the API can fetch any unit, lease or request by its key.
What you build
- A caretaker app in Lovable: a unit with its lease, tenant and open requests. Creating requests from the app needs a warehouse or SQL database source; lakehouse sources are read-only through the API.
- Questions in Claude, such as which properties have the most open requests per unit.
- A Fabric IQ ontology draft (preview) with Property, Unit, Lease and Tenant entity types.
Where the value is
- A first working backend for a property app without a modelling phase.
- Tenant, lease and unit are joined once, so reports stop guessing which tenant belongs to which unit.
- When the app changes hands, the plan explains every link.
Education
Students enrolled in courses each term, taught by teachers, with grades.
Typical tables
From names alone, as the engine plans them: 5 tables, 5 with keys, 6 relationshipsstudents
- student_
id key · K2 - name
- cohort
- student_
teachers
- teacher_
id key · K2 - name
- department
- teacher_
courses
- course_
id key · K2 - teacher_
id teachers R1 - title
- credits
- course_
enrolments
- student_
id key · K4 - course_
id key · K4 - term
- enrolled_
at
- student_
grades
- grade_
id key · K2 - student_
id students R1 - course_
id courses R1 - term
- grade
student_id + course_idenrolments R3- grade_
What Krusgraph finds
- R1
courses.teacher_id→teachers,enrolments.student_id→studentsandenrolments.course_id→courses. - K4
enrolmentshas no key column and starts with two references, so the junction rule (K4) makesstudent_id + course_idits key, andgradesreaches it by R3. If a student can retake a course in another term, that key is wrong, andvalidate_planshows the duplicates. - K1Give
student_id + course_id + term(K1). Thengrades.student_id + course_id + term→enrolmentsfollows by R3 on all three columns.
What you build
- A course overview app: a course, its teacher and the students enrolled this term.
- Questions in Claude, such as grades per course and term.
- A semantic model for term reports from the same keys (planned).
Where the value is
- A three-column key is easy to half-write in a join and then double-count. Stating it once avoids that.
- A working API for a course tool without declaring keys in the lakehouse first.
- Documentation for next term's administrator, generated with the API.
For Fabric consultancies
The same engine runs on every client's tables, so the first conversation can start from their own schema instead of a slide. The rules and the plan are open source, and every key and link says why it is there, which makes a review with the client's data owners short and concrete.
To discuss a pilot, write to hello@krusgraph.com.
Scenarios are illustrative; they describe how the tool works on typical table designs, not customer projects.