$bulk-member-match operation for HealthLake
AWS HealthLake supports the $bulk-member-match operation for processing multiple member match requests asynchronously. This operation enables healthcare organizations to efficiently match hundreds of members' unique identifiers across different healthcare systems using demographic and coverage information in a single bulk request. This capability is essential for large-scale payer-to-payer data exchange, member transitions, and CMS compliance requirements and follows the FHIR specification
Note
The $bulk-member-match operation is based on an underlying FHIR specification that is currently experimental and subject to change. As the specification evolves, the behavior and interface of this API will be updated accordingly. Developers are advised to monitor AWS HealthLake release notes and the relevant FHIR specification updates to stay informed of any changes that may impact their integrations.
This operation is particularly useful when you need to:
Process member matching at scale during open enrollment periods
Facilitate bulk member transitions between payers
Support large-scale CMS compliance data exchange requirements
Efficiently match member cohorts across healthcare networks
Minimize API calls and improve operational efficiency for high-volume matching scenarios
Usage
The $bulk-member-match operation is an asynchronous operation invoked on Group resources using the POST method:
POST [base]/Group/$bulk-member-match
After submitting a bulk match request, you can poll the job status using:
GET [base]/$bulk-member-match-status/{jobId}
Supported parameters
HealthLake supports the following FHIR $bulk-member-match parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
|
Patient |
Yes |
Patient resource containing demographic information for the member to be matched. |
|
Coverage |
Yes |
Coverage resource that will be used for matching against existing records. |
|
Coverage |
No |
Coverage resource to be linked during the matching process. |
|
Consent |
Yes |
Consent resource for authorization purposes is also stored. This is different than the individual |
POST request to submit bulk member match job
The following example shows a POST request to submit a bulk member match job. Each member is wrapped in a MemberBundle parameter containing the required MemberPatient, CoverageToMatch, and Consent resources, along with an optional CoverageToLink.
POST [base]/Group/$bulk-member-match Content-Type: application/fhir+json { "resourceType": "Parameters", "parameter": [ { "name": "MemberBundle", "part": [ { "name": "MemberPatient", "resource": { "resourceType": "Patient", "identifier": [ { "system": "http://example.org/patient-id", "value": "patient-0" } ], "name": [ { "family": "Smith", "given": ["James"] } ], "gender": "male", "birthDate": "1950-01-01" } }, { "name": "CoverageToMatch", "resource": { "resourceType": "Coverage", "status": "active", "identifier": [ { "system": "http://example.org/coverage-id", "value": "cov-0" } ], "subscriberId": "sub-0", "beneficiary": { "reference": "Patient/patient123" }, "relationship": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", "code": "self" } ] }, "payor": [ { "reference": "Organization/org123" } ] } }, { "name": "Consent", "resource": { "resourceType": "Consent", "status": "active", "scope": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/consentscope", "code": "patient-privacy" } ] }, "category": [ { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "IDSCL" } ] } ], "patient": { "reference": "Patient/patient123" }, "performer": [ { "reference": "Patient/patient123" } ], "sourceReference": { "reference": "http://example.org/DocumentReference/consent-source" }, "policy": [ { "uri": "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular" } ], "provision": { "type": "permit", "period": { "start": "2024-01-01", "end": "2025-12-31" }, "actor": [ { "role": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/provenance-participant-type", "code": "performer" } ] }, "reference": { "identifier": { "system": "http://hl7.org/fhir/sid/us-npi", "value": "9876543210" }, "display": "Old Health Plan" } }, { "role": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ParticipationType", "code": "IRCP" } ] }, "reference": { "identifier": { "system": "http://hl7.org/fhir/sid/us-npi", "value": "0123456789" }, "display": "New Health Plan" } } ], "action": [ { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/consentaction", "code": "disclose" } ] } ] } } }, { "name": "CoverageToLink", "resource": { "resourceType": "Coverage", "status": "active", "identifier": [ { "system": "http://example.org/coverage-link-id", "value": "cov-link-0" } ], "subscriberId": "new-sub-0", "beneficiary": { "reference": "Patient/patient123" }, "relationship": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship", "code": "self" } ] }, "payor": [ { "identifier": { "system": "http://hl7.org/fhir/sid/us-npi", "value": "0123456789" }, "display": "New Health Plan" } ] } } ] } ] }
Completed job response with output
When the job completes, the response includes job metadata and a FHIR Parameters resource containing three Group resources that categorize the match results.
{ "datastoreId": "datastoreId", "jobId": "jobId", "status": "COMPLETED", "submittedTime": "2026-03-20T18:45:26.321Z", "numberOfMembers": 3, "numberOfMembersProcessedSuccessfully": 3, "numberOfMembersWithCustomerError": 0, "numberOfMembersWithServerError": 0, "output": { "resourceType": "Parameters", "meta": { "profile": [ "http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/pdex-parameters-multi-member-match-bundle-out" ] }, "parameter": [ { "name": "MatchedMembers", "resource": { "resourceType": "Group", "id": "group1", "text": { "status": "generated", "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Matched members group</div>" }, "contained": [ { "resourceType": "Patient", "id": "1", "identifier": [ { "system": "http://example.org/patient-id", "value": "patient-0" } ], "name": [ { "family": "Smith", "given": ["James"] } ], "gender": "male", "birthDate": "1950-01-01" } ], "type": "person", "actual": true, "code": { "coding": [ { "system": "http://hl7.org/fhir/us/davinci-pdex/CodeSystem/PdexMultiMemberMatchResultCS", "code": "match", "display": "Matched" } ] }, "quantity": 1, "member": [ { "entity": { "extension": [ { "url": "http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/base-ext-match-parameters", "valueReference": { "reference": "#1" } } ], "reference": "Patient/patient123" } } ] } }, { "name": "NonMatchedMembers", "resource": { "resourceType": "Group", "id": "Group2", "text": { "status": "generated", "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Non-matched members group</div>" }, "contained": [ { "resourceType": "Patient", "id": "1", "identifier": [ { "system": "http://example.org/patient-id", "value": "patient-501" } ], "name": [ { "family": "Carter", "given": ["Emily"] } ], "gender": "female", "birthDate": "1985-06-15" } ], "type": "person", "actual": true, "code": { "coding": [ { "system": "http://hl7.org/fhir/us/davinci-pdex/CodeSystem/PdexMultiMemberMatchResultCS", "code": "nomatch", "display": "Not Matched" } ] }, "quantity": 1, "member": [ { "entity": { "extension": [ { "url": "http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/base-ext-match-parameters", "valueReference": { "reference": "#1" } } ], "reference": "Patient/patient123" } } ] } }, { "name": "ConsentConstrainedMembers", "resource": { "resourceType": "Group", "id": "group3", "text": { "status": "generated", "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Consent constrained members group</div>" }, "contained": [ { "resourceType": "Patient", "id": "1", "identifier": [ { "system": "http://example.org/patient-id", "value": "patient-502" } ], "name": [ { "family": "Nguyen", "given": ["David"] } ], "gender": "male", "birthDate": "1972-11-22" } ], "type": "person", "actual": true, "code": { "coding": [ { "system": "http://hl7.org/fhir/us/davinci-pdex/CodeSystem/PdexMultiMemberMatchResultCS", "code": "consentconstraint", "display": "Consent Constraint" } ] }, "quantity": 1, "member": [ { "entity": { "extension": [ { "url": "http://hl7.org/fhir/us/davinci-pdex/StructureDefinition/base-ext-match-parameters", "valueReference": { "reference": "#1" } } ], "reference": "Patient/123" } } ] } } ] } }
How HealthLake classifies members into output Groups
Every member submitted in a $bulk-member-match request is evaluated through a sequential pipeline. The outcome of each step determines which output Group the member is placed in.
Structural validation — Does the MemberBundle conform to required profiles? On failure: Error (not in any Group).
Patient matching — Can HealthLake find patients matching the submitted demographics? On failure: NonMatchedMembers.
Coverage confirmation — Can HealthLake narrow to exactly one patient with valid CoverageToMatch? On failure: NonMatchedMembers.
Consent evaluation — Is the submitted Consent honorable right now? (status = active, period covers current date, performer can be validated). On failure: ConsentConstrainedMembers.
Success — All checks pass. Consent stored in datastore. Member placed in MatchedMembers.
Key principle: A member can only appear in one destination. The first failing step determines placement. Members who fail Steps 2 or 3 are never placed in ConsentConstrainedMembers — that Group is exclusively for members who matched successfully but whose consent cannot be honored.
Consent evaluation details (Step 4):
Check 1 — Consent status: Is
Consent.statusequal to "active"? If not → ConsentConstrainedMembers.Check 2 — Provision period: Does
provision.periodcover the current date? If current date is beforeperiod.startor afterperiod.end→ ConsentConstrainedMembers.Check 3 — Performer validation: Can the
Consent.performerreference be validated? If the referenced resource is not found in the datastore or is not associated with the matched patient → ConsentConstrainedMembers.
All checks must pass for the member to be placed in MatchedMembers and for the Consent to be stored.
Coverage matching behavior
During member matching, only CoverageToMatch is validated against the responding payer's datastore. CoverageToLink belongs to the new/requesting payer and is not validated against the old payer's datastore. Including CoverageToLink in the request will not affect matching results.
Each Patient + Coverage combination in the request is processed independently. The same patient can be submitted multiple times with different coverage plans, and each entry receives its own result based on its specific coverage.
Consent performer reference handling
The new payer may send a temporary or local patient reference in Consent.performer (for example, the same reference used in Consent.patient). HealthLake resolves these references automatically:
If
Consent.performercontains the same local reference asConsent.patient, HealthLake replaces it with the actual matched patient reference after matching succeeds.HealthLake supports performer references of type Patient, RelatedPerson, Practitioner, PractitionerRole, and Organization (both direct references and logical identifier references).
If performer validation fails (resource not found or not associated with the matched patient), the member is placed in ConsentConstrainedMembers rather than returning an error.
Output Group resources
The completed job returns a Parameters resource containing three Group resources:
- MatchedMembers Group
-
Contains Patient references for all successfully matched members whose consent is active and valid at the time of the request. The Consent resource is created and stored in the datastore for each matched member. This Group is instantiated in the datastore and can be used directly with
$davinci-data-export. - NonMatchedMembers Group
-
Contains references to members where no unique match was found. A member is placed here when no patient in the datastore matches the provided demographics, no valid coverage exists for any matched patient candidate, or multiple patients match demographics and multiple have valid coverage (ambiguous).
- ConsentConstrainedMembers Group
-
Contains Patient references for members who were successfully matched (demographics and coverage confirmed) but whose consent cannot be honored at the time of the request. The Consent resource is not stored for consent-constrained members. The matched member identity (MemberIdentifier and MemberId) is still included so the requesting payer knows who was constrained.
The Group.quantity field contains the total count of members in each of their respective groups.
Group member references:
Group.member.entity.reference— For MatchedMembers and ConsentConstrainedMembers, contains the Patient ID of the matched member in the responding payer's system. For NonMatchedMembers, references the contained input Patient.Group.member.entity.extension (base-ext-match-parameters)— Contains the Patient ID from the original input request (the ID submitted by the requesting payer, derived fromPatient.id,Coverage.beneficiary.reference, orConsent.patient.reference).
Consent-Patient linkage
Important
The stored Consent resource retains the patient reference exactly as submitted by the requesting payer. HealthLake does not automatically update the Consent's patient field to point to the matched Patient in the receiving datastore.
To link a stored Consent to the matched Patient, use the job output: each member in the MatchedMembers Group has a member.entity.reference pointing to the matched Patient and a member.entity.extension (base-ext-match-parameters) pointing to the contained input Patient. Cross-reference these with the Consent's patient field to build the mapping in your application layer.
What gets stored vs. what is transient
The following table documents what HealthLake persists to the datastore during $bulk-member-match processing and what exists only in the job response:
| Resource | Stored? | Queryable via REST? | Notes |
|---|---|---|---|
MemberPatient (input) |
No |
No |
Used only for matching; not persisted |
CoverageToMatch (input) |
No |
No |
Used only for coverage confirmation |
CoverageToLink (input) |
No |
No |
Not validated against the datastore; belongs to the new payer |
Consent (matched members) |
Yes |
Yes — GET [base]/Consent/{id} |
Stored as-received from requesting payer |
Consent (constrained members) |
No |
No |
Not stored. Member identity still included in response. |
MatchedMembers Group (output) |
Yes |
Yes — GET [base]/Group/{id} |
Instantiated; usable with $davinci-data-export |
NonMatchedMembers Group |
No |
No |
Job response only |
ConsentConstrainedMembers Group |
No |
No |
Job response only |
Integration with $davinci-data-export
The MatchedMembers Group resource returned by $bulk-member-match can be directly used with the $davinci-data-export operation to retrieve bulk member data:
POST [base]/Group/{matched-group-id}/$davinci-data-export GET [base]/Group/{matched-group-id}
This integration enables efficient workflows where you first identify matched members in bulk, then export their complete health records using the resulting Group resource.
Using $member-remove before export
If you need to exclude specific members from export after matching (for example, a member revokes consent between matching and export), use $member-remove on the MatchedMembers Group.
Important
Removing a member via $member-remove marks the member as inactive in the Group, but $davinci-data-export only excludes inactive members after the Group is updated to status "final". If you call $davinci-data-export on a Group that still has the default status, removed members may still appear in export results.
Workflow:
POST [base]/Group/{id}/$member-remove— mark members inactivePUT [base]/Group/{id}— update Group status to "final"POST [base]/Group/{id}/$davinci-data-export— export now excludes removed members
Performance characteristics
The $bulk-member-match operation is designed for high-volume processing and runs asynchronously:
Concurrency: Maximum of 5 concurrent operations per data store.
Scalability: Handles up to 500 members per request (5 MB payload limit).
Parallel operations: Compatible with concurrent import, export, or bulk-delete operations.
Authorization
The API uses SMART on FHIR authorization protocol with the following required scopes:
system/Patient.read— Required to search and match patient resources.system/Coverage.read— Required to validate coverage information.system/Group.write— Required to create result Group resources.system/Organization.read— Conditional, required if coverage references organizations.system/Practitioner.read— Conditional, required if coverage references practitioners.system/PractitionerRole.read— Conditional, required if coverage references practitioner roles.system/Consent.write— Conditional, required if consent resources are provided.
The operation also supports AWS IAM Signature Version 4 (SigV4) authorization for programmatic access.
Validation rules
The following validation rules are applied to each MemberBundle at Step 1. Members that fail validation are reported as errors and do not appear in any output Group.
MemberPatient
| Field | How HealthLake uses it | Validation failure if... |
|---|---|---|
| Demographic search | Missing |
| Demographic search | Missing (at least one required) |
| Demographic search | Missing |
| Demographic search; if absent, Birth Sex extension used | Neither gender nor Birth Sex present (hrex-pat-1) |
| Included in search when present; improves confidence | Never causes failure (optional) |
CoverageToMatch / CoverageToLink
| Field | How HealthLake uses it | Validation failure if... |
|---|---|---|
| Confirms coverage is actionable | Missing |
| Links coverage to a patient candidate | Missing |
| Disambiguation when multiple candidates exist | Missing, or more than one payor |
| Confirms subscriber-beneficiary relationship | Missing |
| Primary disambiguation key | Neither present |
Consent
| Field | How HealthLake uses it | Validation failure if... |
|---|---|---|
| Confirms consent scope is patient-privacy | Missing or no patient-privacy code |
| Confirms disclosure classification | Missing |
| Identifies the consent subject | Missing |
| Identifies who is agreeing | Missing |
| Documents the consent source | Missing |
| Determines data sharing scope | Missing or URI not ending in #regular or #sensitive |
| Must be "permit" per HRex Consent profile | Missing or not "permit" (including "deny") |
| Evaluated at Step 4 for consent-constrained check | Missing or no start/end |
| Evaluated at Step 4 (NOT Step 1) | Never causes Step 1 failure — HealthLake accepts any valid status and evaluates at Step 4 |
Note
The HRex Consent profile defines status with a fixed value of "active". HealthLake intentionally relaxes this constraint so that a non-active Consent receives a meaningful classification (ConsentConstrainedMembers) rather than a blanket validation rejection.
Matching behavior
Patient search (Step 2) — HealthLake searches using
name.family+name.given(exact, case-insensitive),birthDate(exact),gender(exact; Birth Sex used if gender absent), andidentifier(when present, optional).Coverage disambiguation (Step 3) — When multiple patient candidates are found,
CoverageToMatchis used to narrow to one. A coverage is "valid" when an active Coverage resource exists in the datastore matching onsubscriberIdoridentifier(MB type) ANDpayor.Consent evaluation (Step 4) — Only performed after a successful unique match. See the consent evaluation details section above.
Error handling
The operation handles the following error conditions:
400 Bad Request: Invalid request format, missing required parameters, or payload exceeds size limits (500 members or 5 MB).
422 Unprocessable Entity: Processing errors during job execution.
Individual member errors: When a specific member fails to process, the operation continues with remaining members and includes error details in the NonMatchedMembers Group with appropriate reason codes. For example, a
MemberBundlewith a Patient missing thebirthDateparameter will return the following error:"errors": [ { "memberIndex": 1, "jsonBlob": { "resourceType": "OperationOutcome", "issue": [ { "severity": "error", "code": "invalid", "diagnostics": "MemberPatient.birthDate is required" } ], "statusCode": 400 } } ]
Error details are available through the status polling endpoint and include:
numberOfMembersWithCustomerError: Count of members with validation or input errors.numberOfMembersWithServerError: Count of members with server-side processing errors.
Related operations
$member-match operation for HealthLake — Individual member matching operation.
FHIR R4 $davinci-data-export operation for HealthLake — Bulk data export using Group resources.
FHIR R4 $operations for HealthLake — Complete list of supported operations.