Use case · Healthcare
Onboard a new lab feed with the LOINC codes checked.
Every laboratory ships a different CSV. Map it once to lab_results_v1, have the LOINC codes checked before anything commits, and emit FHIR Observation — then reuse the mapping on every file that lab sends after.
The job
What you are actually trying to get done
A new laboratory joins the network and starts sending result files. Every lab formats differently, and the one thing you cannot get wrong is the code: a result filed against the wrong LOINC is a clinical error, not a data-quality one. The job is to onboard the feed once, prove the codes are well-formed before anything is written, and never have to think about that lab’s layout again.
Where it lands
Lab results
lab_results_v1 · risk: medium · FHIR Observation
Two templates cover this ground and the difference matters: lab_results_v1 is patient-linked and therefore medium risk; lab_result_catalog_v1 is the test-definition catalogue (LOINC, unit, reference range) with no patient in it and is low risk. Map the catalogue once, the results continuously.
Canonical columns
- patient_id
- loinc_code
- value
- unit
- taken_at
5 canonical columns · 4 required · 1 validated
Step by step
The whole run, one call at a time
- 1
Map the catalogue first
The lab’s test definitions carry no patient data, so this pass is low-risk and commits straight through. It gives you theloinc_code→ name → unit → reference-range table the results will be read against.template_id: lab_result_catalog_v1 - 2
Map the result feed
Wert,EinheitandEntnahmeare shipped hints onvalue,unitandtaken_at.PIDreachespatient_idon the fuzzy layer.LOINCis an exact hit.POST /v1/uploads/:id/match - 3
Check the codes, not just the shape
Theloinc_codevalidator is one of nineteen built-in types and is checked per row. A malformed or empty code lands inerrorswith its row index — before a single result is written anywhere.POST /v1/uploads/:id/validate - 4
Decide what a bad row does
Commit withskip_invalid_rows: trueand the good rows land while the rest come back inskipped; leave it off and a single bad code stops the batch. That is your clinical policy, expressed as one boolean.skip_invalid_rows - 5
Emit FHIR Observation
The template declaresfhir_resource: "Observation", so the bundle comes back withstatus: finaland the code as aCodeableConcept— LOINC by default, resolved throughCODING_SYSTEM_URIS.output: "fhir" - 6
Watch the layout for drift
When the lab adds a column,driftcomes back with an added/removed/common diff against the last confirmed layout — deterministic, no LLM, before the feed silently starts dropping a field.POST /v1/layouts/drift
In code
One shot: file in, validated rows out
POST /v1/transform runs upload, match and validate in one call and returns the transformed grid. Add output to attach a serialized file to the same envelope.
curl https://api.adaptivmapr.com/v1/transform \
-H "Authorization: Bearer $ADAPTIVMAPR_API_KEY" \
-F "file=@LAB_EXPORT_20260214.csv" \
-F "template_id=lab_results_v1" \
-F "skip_invalid_rows=true" \
-F "output=fhir"{
"ok": true,
"target_schema_id": "lab_results_v1",
"transformed": {
"headers": ["patient_id", "loinc_code", "value", "unit", "taken_at"],
"rows": [["P-4471", "718-7", "13.2", "g/dL", "2026-02-14"]]
},
"errors": [
{ "row": 9, "field": "loinc_code", "code": "loinc_code", "message": "not a well-formed LOINC code" }
],
"skipped": 4,
"file": { "format": "fhir", "filename": "lab_results_v1.fhir.json", "content_type": "application/fhir+json", "data": "…" }
}loinc_codeis one of 19 built-in validator types.SNOMED was removed in 2026-06— it required a SNOMED International Affiliate License and no template used it. There is nosnomed_codevalidator and there will not be one.lab_result_catalog_v1islowrisk andlab_results_v1ismedium: the difference is the patient link, and it is what decides whetherrequires_hitlcomes back true.- A commit records the confirmed mapping in
mapping_layouts, keyed by a sha256 fingerprint of the normalized header row.POST /v1/layouts/lookupreads it back — workspace-scoped, never shared across tenants. - Reference ranges that the results template does not declare are not silently dropped on the reshape path —
POST /v1/reshapecan carry pass-through columns into a produced sheet.
Where the work lands
Which layer resolves this file
| Layer | What it does here | Auto-accepts at | Cost |
|---|---|---|---|
| 1 · statistics | Every header this lab has sent before, once you have confirmed ~20 of them | ≥100 @ 95% · ≥20 @ 100% | Free · deterministic |
| 2 · heuristic | “Wert”, “Einheit”, “Entnahme”, “LOINC” — shipped hints, resolved at 1.00 | ≥ 0.85 | Free · deterministic |
| 3 · fuzzy | “PID” → patient_id, “RefMin” → ref_low on the catalogue template | ≥ 0.80 | Free · pure compute |
| 4 · semantic | An assay-specific label with no shared vocabulary | ≥ 0.78 | Cheap, cached · off without an embedding key |
| 5 · ai | One batched call over anything left, constrained to unclaimed fields | Model pick, constrained to the unclaimed column set | Metered · the only paid layer |
MAPR_EMBEDDING_API_KEY, MAPR_LLM_API_KEY) and fail soft to OFF, so an unconfigured deployment still maps on the deterministic layers rather than erroring.Before you commit
Review, cost and where it routes
It comes back flagged for review
lab_results_v1 is a medium-risk template, so PATCH /v1/uploads/:id/mappings returns requires_hitl: true and hitl_status: "pending_review" — automatically, from the template’s own risk field.
Honest limitThe flag is advisory in v1: it tells your importer to gate, it does not block the commit server-side. Native AgentGate queue integration is roadmap Q3 2026.
Usually just the flat fee
When every column resolves on layers 1–3 — the common case for a file you receive regularly — no model runs, so there are no AI tokens to bill. A small flat per-map fee ($0.0010) is drawn on every map — a fully deterministic one and a layout-cache re-map included. There is no free tier and no seats or contract; you top up a prepaid wallet from $10 and it draws down.
Rate cardAI tokens are billed only when a model actually ran, at provider cost ×2 (×0.5 with your own LLM key). A PHI-routed run multiplies the whole charge — flat fee included — by 1.2. See lib/pricing.ts and GET /v1/pricing.
PHI routing is opt-in, and gated
Standard is what an unconfigured workspace gets. Switch a run to PHI · enterprise and the layer-5 call goes to phi-cloud with X-PHI and a region pin, so a PHI-eligible in-region model handles it under the BAA.
GateLocked until the workspace accepts the BAA in-app (Settings → Security & Data, POST /v1/me/agreements). An explicit PHI ask without one is 403 agreement_required with a settings_url pointer — never a silent downgrade. PHI adds 20% to the whole map charge.
Questions
The things people actually ask
Does it validate that a LOINC code exists, or just its shape?
Why is the catalogue low-risk and the results medium?
The lab keeps changing its columns. What breaks?
Can I run this on a schedule instead of posting files?
Stop hand-mapping this file. Map it once.
Start with a $10 prepaid wallet. In schema-only mode only headers and up to three sample rows — each cell clamped to 80 characters — ever leave you.