# Canvas Medical analytics: getting your EHI into Snowflake, governed

**Canvas Medical has quietly become the system of record for a
generation of digital-health companies, and it earned the position
honestly: it is the rare EHR that developers choose rather than
endure. But its designers made a deliberate trade — velocity of care
delivery over the machinery of analytics — and so every Canvas
customer eventually rediscovers the same fact: the data is yours,
cleanly exported as FHIR, and so is the problem of doing anything
analytical with it. Lakewright closes that gap: it lands Canvas's
FHIR exports as governed Apache Iceberg tables inside your own
environment, masks protected health information before it ever
touches the warehouse, and generates the semantic views that let
Snowflake Cortex answer clinical and financial questions in plain
English.**

## The developer's EHR
Canvas's appeal is no mystery. An SDK, a plugin architecture, and a
FHIR-first API make it the obvious choice for companies that regard
their care model as software. The consequence of that focus is
equally unmysterious: Canvas ships no warehouse, no governance layer,
and no analytical model. This is not a defect so much as a division
of labour — one that leaves the customer holding the second half.

## An escape hatch is not a strategy
What Canvas provides, per its own documentation, is threefold: a
FHIR R4 API; an EHI bulk-export facility that emits newline-delimited
JSON across 32 resource types, from Condition and Observation to
Claim, Coverage, and CarePlan; and an SDK data module for in-instance
work. For analytics at any scale the bulk export is the workhorse,
and it behaves like one: population exports take real time, arrive
as flat files, and repeat themselves. A pile of NDJSON is data
portability in the legal sense. It is not a decision-support
capability, and the distance between the two is precisely where
analytically ambitious Canvas customers stall — typically after the
clinical lead asks for panel-level A1c trends and the CFO asks why
claim-to-collection is lengthening, in the same week.

## The arithmetic of landing it properly
The pattern is unglamorous and, for that reason, reliable. Exports
land in storage you control; Lakewright identifies each file as FHIR
by inspection, proposes one governed table per resource type, and —
only after a named human approves the plan — loads Iceberg tables
with patient identifiers masked or key-hashed at load. The raw
values never reach the warehouse. Every file is journalled by
content digest, which converts Canvas's slow, repetitive population
exports from a hazard into a routine: re-exports, overlaps, and
re-downloads land exactly nothing twice.

```bash
lakewright scan    s3://canvas-exports          # ndjson [fhir], per-resource feeds
lakewright approve plans/patient.plan.yaml      # your data owner signs; we cannot
lakewright load    plans/patient.plan.yaml --rest $ICEBERG_CATALOG
lakewright semantics plans/patient.plan.yaml --dialect snowflake
```

## One copy, every consumer
The result is a single governed copy in an open format. Snowflake
reads it as external Iceberg tables; Databricks, Trino, and DuckDB
read the same files; nothing is re-ingested and nothing is locked
in. More usefully, the Canvas data stops being an island: the same
engine lands X12 837 claims and 835 remittances from the
clearinghouse, scanned documents through OCR that runs on your own
hardware, and change data captured from the product database your
engineers actually operate. Clinical outcomes, denial rates, and
product usage — joined on patient identity, in one place, under one
audit trail. That combination, not any single feed, is the prize.

## What the machines may read
The semantic layer is generated, not hand-built. From what the scan
learned — resource types, column meanings, sensitivity — Lakewright
emits Snowflake `CREATE SEMANTIC VIEW` statements with the FHIR
provenance recorded in the comments. Applied, they give Cortex
Analyst something trustworthy to reason over; asked "which panels'
A1c worsened this quarter?", it answers from tables that were export
files the same morning. The arrangement has a compliance virtue that
deserves plain statement: Lakewright is self-hosted software running
inside your environment. There is no third-party processor, hence no
new business associate agreement, and the AI that touches your data
is whichever one you already trust, running where you already run it.

## FAQ
**Does this consume Canvas API quota?** No. The pattern reads
exported files you already possess; nothing polls the API. A live
FHIR-API connector is on the roadmap for teams that want one.
**Do we need a new BAA?** No — there is no third-party processor.
Your existing arrangements for Snowflake and infrastructure stand.
**What about the SDK data module?** Complementary. It serves
in-instance workflows; this pattern serves the warehouse. Use both.
**Single-Bundle JSON from the live API?** Bulk-export NDJSON is the
fully supported path today; Bundle support is in progress — say so
if it gates you.
**We also have faxes, claims EDI, and a product database.** That is
rather the point: one engine, one audit trail, all of it.
