Schema Care

FHIR Coverage Resource From Insurance Cards, Field by Field

By Schema CarePublished Updated

To build a FHIR Coverage resource from insurance cards, put the member ID in subscriberId or a member-number identifier, the group, plan and pharmacy numbers in class entries, the cardholder in subscriber and the patient in beneficiary. Then supply what the card never prints, such as status and a payor reference, from your own records.

Treat this as a mapping guide for your own integration. Schema Care produces no Coverage resources: it offers no API, SDK, webhooks, FHIR, X12 or 275 output, and it does not read insurance cards. The card, family, plan and every identifier below are invented, and the server is fhir.example.com.

Insurance card fields mapped onto FHIR Coverage elements

Each printed insurance card field has one home in the FHIR R4 Coverage resource. The member ID goes to subscriberId or identifier, the payer to payor, the group and plan to class, the cardholder to subscriber, the patient to beneficiary, and a dependent suffix to dependent. Copays are the only field with a doubtful home.

The HL7 FHIR R4 Coverage resource page defines each element. Three are required in the base resource: status (1..1), beneficiary (1..1) and payor (1..*). Everything else is optional, which is why a card alone can fill most of a Coverage but never all of it.

The fictional card for this post belongs to a family plan. The subscriber is Marcus Example; the patient is his daughter, Ava Example, age 9, who has her own card with a dependent suffix.

Printed card fieldExample value on the cardCoverage element
Member IDEXM55120934subscriberId, and identifier with type MB
Dependent suffix02dependent
Member nameAVA EXAMPLEbeneficiary (a reference to her Patient)
SubscriberMARCUS EXAMPLEsubscriber (a reference to a RelatedPerson)
Group numberEXG-40018class with type group
Plan nameEX Gold HMOclass with type plan, and class.name
RxBIN, RxPCN, RxGroup999002, EXRX2, EXRXG40class with types rxbin, rxpcn, rxgroup
Payer nameExample Health Planpayor (a reference to an Organization)
Network labelEX Selectnetwork
CopaysOffice $20, Specialist $40Possibly costToBeneficiary; see below

How the card fields themselves come out of a photo, with confidence scores and nulls, is described in the guide to an insurance card OCR API. Which FHIR resource fits each kind of medical document, beyond cards, is covered in the section on which FHIR resources match each medical document type.

The annotated Coverage, as an example

The JSON below is the fictional card above turned into a Coverage. The table after it labels each element with the card field it came from.

{
  "resourceType": "Coverage",
  "id": "ex-cov-ava",
  "identifier": [{
    "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/v2-0203", "code": "MB" }] },
    "system": "https://fhir.example.com/sid/example-health-plan-member",
    "value": "EXM55120934-02"
  }],
  "status": "active",
  "subscriber": { "reference": "RelatedPerson/ex-rp-marcus" },
  "subscriberId": "EXM55120934",
  "beneficiary": { "reference": "Patient/ex-pat-ava" },
  "dependent": "02",
  "relationship": {
    "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", "code": "child" }]
  },
  "payor": [{ "reference": "Organization/ex-org-example-health-plan" }],
  "class": [
    { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-class", "code": "group" }] }, "value": "EXG-40018" },
    { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-class", "code": "plan" }] }, "value": "EX-GOLD-HMO", "name": "EX Gold HMO" },
    { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-class", "code": "rxbin" }] }, "value": "999002" },
    { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-class", "code": "rxpcn" }] }, "value": "EXRX2" },
    { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-class", "code": "rxgroup" }] }, "value": "EXRXG40" }
  ],
  "network": "EX Select"
}
ElementCame from
identifier.valueMember ID plus dependent suffix, as printed on Ava's card
statusNot on the card; set by your system after an eligibility check
subscriberSubscriber name line
subscriberIdMember ID without the suffix
beneficiaryMember name line, matched to Ava's existing Patient record
dependentDependent suffix 02
relationshipNot on the card; from the intake form's relationship question
payorPayer name and logo, matched to an Organization you maintain
class entriesGroup, plan name and the three pharmacy fields
networkNetwork label

Which Coverage class types hold group, plan and Rx numbers?

The FHIR Coverage class element holds the insurer's group, plan and pharmacy numbers as a list of typed entries. Each entry has a required type code, a required value string and an optional name. Use the code group for the group number, plan for the plan, and rxbin, rxpcn, rxgroup and rxid for the pharmacy fields.

The codes come from the HL7 FHIR coverage class value set, which takes every code from the code system http://terminology.hl7.org/CodeSystem/coverage-class. It lists eleven codes:

CodeHL7 definitionCard field it suits
groupAn employee groupGroup number
subgroupA sub-group of an employee groupOnly if the card labels it
planA specific suite of benefitsPlan name or plan code
subplanA subset of a specific suite of benefitsOnly if the card labels it
classA class of benefitsOnly if the card labels it
subclassA subset of a class of benefitsOnly if the card labels it
sequenceA sequence number for a short-term continuance of coverageOnly if the card labels it
rxbinThe pharmacy benefit manager's Business Identification NumberRxBIN
rxpcnA pharmacy benefit manager's Processor Control NumberRxPCN
rxidA pharmacy benefit manager's member IDRx ID, when it differs from the member ID
rxgroupA pharmacy benefit manager's group numberRxGroup

The Coverage page shows class.type bound to these codes at extensible strength, so use them whenever one fits. Because class.value is required, never add a class entry for a field the card leaves blank. A card with no group number gets no group entry, not an entry with an empty value.

Keep the medical group and the RxGroup apart. They are different numbers with different codes, and a card can print both. When the card prints a plan name but no plan code, put the printed name in both value and name, or use your own stable plan code in value, and record which you chose.

Subscriber, beneficiary and relationship in a Coverage resource

In a Coverage resource, subscriber is the person who holds the policy, beneficiary is the patient it covers, and relationship says how the patient is related to the subscriber. On a dependent's card, the subscriber is usually the parent or spouse and the beneficiary is the patient. When they are the same person, use self.

The Coverage page defines subscriber as a reference to a Patient or RelatedPerson, and beneficiary as a reference to a Patient only. When the subscriber is not a patient of your practice, a RelatedPerson is the right target. The HL7 FHIR R4 RelatedPerson page requires a patient reference on every RelatedPerson, so Marcus is recorded in relation to Ava:

{
  "resourceType": "RelatedPerson",
  "id": "ex-rp-marcus",
  "patient": { "reference": "Patient/ex-pat-ava" },
  "relationship": [{
    "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", "code": "FTH" }]
  }],
  "name": [{ "family": "Example", "given": ["Marcus"] }],
  "birthDate": "1985-06-21"
}

That example is fictional, and the subscriber's birth date usually comes from the intake form, not the card.

The relationship element on Coverage uses the HL7 subscriber relationship codes: child, parent, spouse, common, other, self and injured. These describe the beneficiary's relationship to the subscriber, so Ava's Coverage says child, not parent. Getting that direction wrong is a common mapping error.

Who counts as the subscriber when a child, a spouse or a guardian is involved is explained in the section on who the subscriber on an insurance card is.

Should copays printed on the card become Coverage costToBeneficiary?

Copays printed on an insurance card can become costToBeneficiary entries, but only when the text is clear about the service and the amount. Short labels like "Office $20" map cleanly. Labels like "Spec 20%/$40 after ded" carry conditions the element cannot express, so leave those out rather than store a value that misstates the benefit.

The Coverage page defines costToBeneficiary as optional and repeating. Each entry has an optional type, a required value[x] that is either a Quantity (for a percentage) or Money, and optional exception entries. The type is bound at extensible strength to the HL7 coverage copay type codes, which include gpvisit, spvisit, emergency, inpthosp, televisit, urgentcare, copaypct, copay, deductible and maxoutofpocket.

The two copays on the fictional card would look like this:

"costToBeneficiary": [
  { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", "code": "gpvisit" }] },
    "valueMoney": { "value": 20.00, "currency": "USD" } },
  { "type": { "coding": [{ "system": "http://terminology.hl7.org/CodeSystem/coverage-copay-type", "code": "spvisit" }] },
    "valueMoney": { "value": 40.00, "currency": "USD" } }
]

Ask three questions before writing a copay:

  • Does the label name one service type? "Office" fits gpvisit; "Visit" alone may not.
  • Is it one amount? A range or "after deductible" is a condition, not a value.
  • Will anyone read it? If billing always uses the eligibility response for patient cost, a card copay adds a second, older answer.

Printed copays are a snapshot from the day the card was made. The payer's eligibility response describes current benefits, so prefer it whenever you have it.

Coverage resource gaps: required elements an insurance card does not print

A Coverage resource needs some elements that no insurance card prints. The card shows no status, nothing a payor reference can point to on its own, and often no coverage period. US Core adds a required relationship. Fill these from eligibility results, the intake form and your own payer table, never from guesses.

Each gap has a better source:

ElementRequired?Why the card falls shortWhere to get it
status1..1 in base R4Cards carry no statusactive after an eligibility check confirms it; otherwise draft
payor1..* in base R4The card has a name, not a resourceAn Organization you maintain for each payer, matched from the printed name
beneficiary1..1 in base R4The card has a name, not a Patient recordYour patient matching step
relationship1..1 in US CoreThe card may not sayThe intake form's relationship-to-insured question
periodOptionalThe card may print no datesThe eligibility response, or leave it out
typeOptional, Must Support in US CorePlan type is often only impliedYour payer table, using the US Core payer type codes

Use draft honestly. The Coverage status codes are active, cancelled, draft and entered-in-error, so a Coverage built from a card that nobody has verified is fairly described as draft until a check confirms it.

When a new card replaces one already on file, end the old Coverage instead of overwriting it. The process for that is in the section on how updated patient insurance on file should replace the old coverage.

Cards often arrive inside a paper intake packet rather than as a photo. Schema Care reads that route: the insurance, demographics, history and consent checkbox sections of patient intake forms, each field scored and the uncertain ones sent to a reviewer, as listed with the other document types on the homepage. Your integration would then build the Coverage. The intake packet itself can be modeled as a FHIR QuestionnaireResponse, which the guide to FHIR QuestionnaireResponse for intake forms covers.

Validating a card-derived Coverage resource against US Core

Validate a card-derived Coverage resource against the US Core Coverage profile, which adds rules the base resource does not have. US Core requires a relationship, exactly one payor, and a member ID in either identifier or subscriberId. It also marks group and plan class entries, coverage type and period as Must Support.

The US Core Coverage profile, in US Core version 9.0.0 (STU 9), lists what each Coverage must have and what systems must support:

RuleUS Core 9.0.0 requirement
status1..1
beneficiary1..1, a reference to a US Core Patient
relationship1..1, extensible binding to the subscriber relationship codes
payor1..1, a reference to a US Core Organization, Patient or RelatedPerson
Member IDConstraint us-core-15: a member ID in Coverage.identifier or Coverage.subscriberId SHALL be present
identifier:memberidType MB from the v2-0203 code system
class:group, class:planMust Support, each with type and value
type, periodMust Support

Common failures with card-derived resources:

  • No relationship. The card did not say, and nobody asked. Ask on the intake form.
  • Two payor references. Base R4 allows several; US Core allows one.
  • A payor pointing at a placeholder. The Organization must exist and meet the US Core Organization profile.
  • An empty class value. A blank group number produced a group entry with no value.
  • The member ID only in a note. It must be in identifier or subscriberId to satisfy us-core-15.

Run the validation when the resource is built, before it reaches a server, and send failures to a person with the card image beside them. If coverage documents later travel with a claim as supporting paperwork, that exchange uses a different standard, described in the guide to X12 275 claim attachments.

All posts in APIs, FHIR and Integration