Capability · FHIR
Already mapped. Now emit it as FHIR.
You mapped the file already. Ask for FHIR and the same run hands you R4 resources instead of rows — no second integration.
- R4 resource types
- 14
- Not a second product
- A flag
The job
Your data is mapped. The other system wants FHIR.
What changes
fhir_resource mapping, so when the template changes the resource follows it.What you get
Not a demo. The thing that runs every day.
Resources
Straight to an R4 Bundle
Resolved rows become FHIR resources wrapped in a collection Bundle, served with the right content type. There is no intermediate transform for you to build and keep in sync.
HowrowsToFhirBundle() in lib/fhir.ts returns { resourceType: "Bundle", type: "collection", entry: [...] }, served as application/fhir+json.
Coverage
Fourteen resource types implemented
Patient, Observation, Medication, Appointment, Coverage, Practitioner, Claim, Claim.item, Encounter, Condition, Immunization, Organization, Location and AllergyIntolerance.
HowSUPPORTED_FHIR_RESOURCES is the whitelist, and it is checked before anything is built — a template pointing at a resource type we have not implemented is refused with fhir_resource_unsupported, naming the type.
Same engine
One cascade, FHIR at the end
You map to the template’s fields exactly as you would for a CSV export. Asking for FHIR is a flag on the output, not a different product.
Howemit() in lib/emit/index.ts treats fhir as one of nine output formats alongside json, csv, tsv, xml, sql, xlsx, xlsm and parquet.
Three doors
From a document, a file, or a commit
Turn an extracted PDF into resources, convert a spreadsheet in one shot, or emit a reviewed upload at commit time. Same output, wherever the rows came from.
Howoutput: "fhir" is accepted on POST /v1/convert, POST /v1/transform and POST /v1/uploads/{id}/commit — and on commit it can be delivered to a webhook rather than returned inline.
Validated
Checked before it is emitted
The codes inside a resource go through the same commit-time validators as any other field, so a malformed code is flagged rather than shipped inside a resource that looks well-formed.
Howloinc_code, icd10_code and fhir_reference run in validateRowsDetailed() before the bundle is assembled. An Observation with an unparseable LOINC code never gets built.
Honest
It refuses rather than invents
Ask for FHIR from a schema that has no resource mapping and you get a specific refusal — never a half-built resource with guessed fields.
How422 fhir_unavailable when the schema declares no fhir_resource, naming the schema. On the commit path the same case is 400 fhir_not_supported.
Reference
The templates that already carry a mapping
For healthcare workloads, mapping to your columns is only half the job — you often need FHIR out the other side. Any template that declares a fhir_resource can emit its resolved rows directly as R4 resources in a Bundle. The emit path itself is vertical-agnostic: it is a general output mode layered on the same cascade, not a walled-off healthcare gate.
- 01
Map to the template
Nothing special happens here. Your columns land on the template’s fields through the same six-layer cascade a CSV export would use.
- 02
Validate the codes first
loinc_code,icd10_codeandfhir_referencerun in the commit-time validator pass BEFORE assembly — an Observation with an unparseable LOINC code never gets built. - 03
Build against the whitelist
The template’s
fhir_resourceis checked againstSUPPORTED_FHIR_RESOURCES. A type we have not implemented is refused by name rather than emitted half-built. - 04
Serve, or deliver
A collection Bundle served as application/fhir+json — or, from the commit path, POSTed to your webhook HMAC-signed with a deterministic idempotency key.
| Template | FHIR resource | Risk |
|---|---|---|
| patient_demographics_v1 | Patient | medium |
| lab_results_v1 | Observation | medium |
| lab_result_catalog_v1 | Observation | low |
| drug_formulary_v1 | Medication | medium |
| claims_line_items_v1 | Claim.item | medium |
| appointment_log_v1 | Appointment | low |
| insurance_contracts_v1 | Coverage | low |
| provider_directory_v1 | Practitioner | low |
Try it
A scanned intake form, out as a Patient
Ask for FHIR on a template that carries a resource mapping, and the response is a Bundle instead of flat rows — served as application/fhir+json.
curl https://api.adaptivmapr.com/v1/convert \
-H "Authorization: Bearer $MAPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": { "format": "pdf", "data_base64": "JVBERi0xLjcK…" },
"schema": { "ref": "patient_demographics_v1" },
"output": "fhir",
"mode": "full-data",
"phi_mode": true
}'{
"resourceType": "Bundle",
"type": "collection",
"entry": [
{ "resource": {
"resourceType": "Patient",
"name": [{ "given": ["Ada"], "family": "Lovelace" }],
"birthDate": "1815-12-10",
"gender": "female",
"telecom": [{ "system": "email", "value": "ada@example.org" }]
} }
]
}- The implemented set is
Patient,Observation,Medication,Appointment,Coverage,Practitioner,Claim,Claim.item,Encounter,Condition,Immunization,Organization,LocationandAllergyIntolerance. - FHIR emit is metered like any other map — the flat per-map fee, plus AI tokens only if the cascade actually needed the metered layer to place a column.
- On the commit path,
output: "fhir"can be POSTed to your webhook instead of returned, HMAC-signed and carrying a deterministic idempotency key so a retry is safe to dedupe.
The surface
Every route this page actually has.
FHIR is not its own endpoint. It is one of nine values of `output`, accepted on every surface that produces rows — so the door you already use is the door.
- POST
/v1/convertA document in, a Bundle out — the scanned-intake-form path. Needs the PHI entitlement, because unstructured input is full-data.transform scope - POST
/v1/transformA spreadsheet or pasted text to a Bundle in one shot, up to 10 000 rows inline.transform scope - POST
/v1/uploads/{id}/commitEmit a reviewed upload as FHIR — returned inline, or delivered to your webhook instead.commit scope - POST
/v1/reshapeUntangle a messy workbook first, then emit the clean result as R4 resources.commit scope - GET
/v1/templatesWhich templates declare a fhir_resource, and which resource type each one builds.no key
Limits & failure modes
What it refuses to do — and the code it says it with.
The whole point of this surface is that it refuses rather than invents. A half-built resource with guessed fields is worse than an error, because it validates downstream and is wrong.
422 fhir_unavailableThe schema you named declares no fhir_resource.
The refusal names the schema. Add a fhir_resource to your own schema — it just has to name one of the fourteen implemented types.
400 fhir_not_supportedThe same case, on the commit path.
Two codes because the two planes answer in their own vocabulary; both refuse before anything is built.
fhir_resource_unsupportedA template points at an R4 resource type outside the implemented fourteen.
Checked against the whitelist first, and the refusal names the type — so you learn what is missing rather than getting an empty resource.
type: "collection"You need a transaction or batch Bundle to POST at a FHIR server.
We emit a collection Bundle. Wrap it yourself, or write the mapped rows through a destination connector instead.
loinc_formatA code fails its format check inside a row destined for a resource.
Flagged in the validator pass, before assembly. The bundle is never built around a code that already failed.
R4 onlyYou are on STU3, or already moving to R5.
The emit path targets R4, which is what the fourteen implemented resource types are built against. We would rather name the version than imply all of them.
What it costs
One prepaid wallet, drawn down per call.
No free tier. Top up from $10 — the balance is shared across the phi-cloud suite — and every operation draws it down at the rate below. An optional $6/mo plan grants a credit that resets each billing cycle instead; overage falls back to the wallet.
Every FHIR emit
$0.001
Metered exactly like any other map — the flat per-map fee. Asking for FHIR instead of CSV costs nothing extra by itself.
Bundle assembly
Free
Pure compute. The only AI cost on this path is the cascade’s metered layer, and only if a column could not be placed deterministically.
From a PHI document
+20%
A scanned form is full-data and PHI-routed, so the whole charge carries the uplift — flat fee, extraction tokens and all.
A PHI/enterprise-routed run multiplies the whole charge by 1.2 (+20%), flat fee included — and only when the run genuinely got that routing. PHI stays locked until the workspace accepts the BAA in Settings → Security & Data. Full pricing
Questions
The ones asked before signing.
What it costs, what leaves your network, and the claims we will not make.
Do I have to write the FHIR mappings myself?
Is FHIR only for the healthcare pack?
Which resources are supported?
Are the values inside a resource validated?
Keep reading
The rest of the same engine.
One key, from a messy file to your schema.
Top up a $10 prepaid wallet and start mapping. In schema-only mode only your headers and three clamped rows ever leave you.