Flexpa
Developer PortalGet SandboxTry it yourself

All docs

Introduction

    Setup

    • Create a data store
    • IAM permissions
    • AWS credentials
    • Environment variables

    How it works

    • Everything operation
    • Transaction bundle
    • HealthLake-specific considerations
    • Working with multiple consents
    • Complete implementation
    • Handling updates

    Additional resources

      AWS HealthLake integration

      #Introduction

      Working with healthcare data often requires both retrieving and storing patient health records in a FHIR-compliant way. Let's walk through how to use AWS HealthLake as a FHIR data repository for health records from Flexpa.

      While Flexpa helps you retrieve clinical, financial, and coverage records from multiple sources, you may need a secure and compliant place to store this information. AWS HealthLake provides a fully-managed, HIPAA-eligible FHIR R4 data store with built-in search, analytics, and machine learning capabilities that make it well-suited for storing and working with health data at scale.

      When using AWS HealthLake with Flexpa you can build things like:

      • A longitudinal patient record aggregated from multiple payers
      • Patient onboarding with claims history from prior insurance
      • Population health analytics across members

      This guide will walk you through how to set up AWS HealthLake to store data from Flexpa, and then show you how to implement the integration.

      AWS HealthLake endpoint

      # HealthLake FHIR endpoint format
      https://healthlake.{region}.amazonaws.com/datastore/{datastoreId}/r4/
      

      Make sure you've completed the Flexpa Quickstart setup before following along.

      #Setup

      #Create a data store

      Start by creating a HealthLake data store in the AWS Console or CLI.

      Using the AWS Console:

      • Open the AWS HealthLake console
      • Choose Create Data Store
      • Enter a name for your data store (e.g. flexpa-fhir-store)
      • Select R4 as the FHIR version
      • Choose your preferred authorization strategy (AWS SigV4 is the default)
      • Click Create Data Store

      Once created, note the Data Store ID and Data Store Endpoint from the console. You will need these values to configure your integration.

      Data store creation can take several minutes. Wait until the status shows Active before proceeding. Make sure to use a newly created data store — the $everything operation and _tag search parameter require data stores created after February 2024.

      Using the AWS CLI:

      Create data store via CLI

      aws healthlake create-fhir-datastore \
        --datastore-type-version R4 \
        --datastore-name "flexpa-fhir-store"
      

      Response

      {
        "datastoreId": "abc123def456",
        "datastoreArn": "arn:aws:healthlake:us-east-1:123456789:datastore/fhir/abc123def456",
        "datastoreStatus": "CREATING",
        "datastoreEndpoint": "https://healthlake.us-east-1.amazonaws.com/datastore/abc123def456/r4/"
      }
      

      #IAM permissions

      Your application needs IAM permissions to interact with the HealthLake data store.

      Attach the following policy to your application's IAM role or user. Replace the ARN with your data store's ARN.

      At minimum, your application needs permissions for:

      • healthlake:CreateResource - to create FHIR resources
      • healthlake:UpdateResource - to update existing resources
      • healthlake:ReadResource - to read resources
      • healthlake:SearchWithGet - to search for resources

      IAM policy

      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Effect": "Allow",
            "Action": [
              "healthlake:CreateResource",
              "healthlake:UpdateResource",
              "healthlake:ReadResource",
              "healthlake:SearchWithGet",
              "healthlake:SearchWithPost"
            ],
            "Resource": "arn:aws:healthlake:<region>:<account-id>:datastore/fhir/<datastore-id>"
          }
        ]
      }
      

      #AWS credentials

      Next, we need to get API keys.

      HealthLake uses AWS Signature Version 4 to authenticate requests. You need AWS access keys for the IAM user or role that has the HealthLake permissions from the previous step.

      To create access keys for an IAM user:

      • Open the IAM console
      • Navigate to Users and select the user with HealthLake permissions
      • Go to the Security credentials tab
      • Under Access keys, click Create access key
      • Select Application running outside AWS as the use case
      • Copy the Access key ID and Secret access key

      The secret access key is only shown once at creation. Store it securely. For production workloads, consider using IAM roles with the AWS SDK credential provider chain instead of long-lived access keys.

      We will use these values in the .env file in the next step.

      Verify credentials via CLI

      # Verify your credentials have HealthLake access
      aws healthlake list-fhir-datastores
      
      # Expected output includes your data store
      {
        "DatastorePropertiesList": [
          {
            "DatastoreId": "abc123def456",
            "DatastoreName": "flexpa-fhir-store",
            "DatastoreStatus": "ACTIVE",
            ...
          }
        ]
      }
      

      #Environment variables

      Open .env from your project in a text editor.

      Add the Data Store Endpoint, AWS Region, and your AWS credentials from the previous steps.

      The endpoint should be the full URL shown in the HealthLake console or returned from the CLI.

      Update the .env file

      # Open the .env file and fill in the HealthLake configuration
      HEALTHLAKE_ENDPOINT=https://healthlake.us-east-1.amazonaws.com/datastore/abc123def456/r4
      AWS_REGION=us-east-1
      AWS_ACCESS_KEY_ID=
      AWS_SECRET_ACCESS_KEY=
      

      #How it works

      The HealthLake integration has two main steps:

      1. Everything operation - Records are retrieved from Flexpa using the $everything operation
      2. Transaction Bundle - Records are created in HealthLake using a FHIR transaction bundle

      #Everything operation

      The Patient $everything operation, available at /fhir/Patient/$PATIENT_ID/$everything, is a FHIR standard operation that retrieves all available resources that are related to a specific patient.

      When used with Flexpa, this operation returns all available health data for the authenticated patient, including clinical records, financial information, and coverage details.

      We use the Node SDK to make this call but you can make it with any HTTP client.

      src/app/api/sync/route.ts

      const client = FlexpaClient.fromBearerToken(session.accessToken);
      const everything = await client.$everything();
      

      #Transaction bundle

      A transaction bundle is a FHIR operation that allows us to create or update multiple resources in a single atomic request. HealthLake supports both batch and transaction bundle types. We use transaction to ensure all resources are committed together.

      The transformation process involves:

      1. Converting the bundle type from searchset to transaction
      2. Adding a request object to each entry with the PUT method to leverage update-as-create
      3. Preserving Flexpa's resource IDs so that subsequent syncs can update existing resources in place

      Before the data reaches your application, Flexpa performs extensive processing to ensure data integrity:

      • Each consent's records are assigned globally unique identifiers
      • Resource references are properly mapped and maintained
      • Identifier collisions are eliminated, even when different members pull data from different payers
      • References between resources are validated and normalized

      This preprocessing means you can safely store data from multiple payers and members in your HealthLake data store without worrying about identifier conflicts or reference integrity issues.

      HealthLake supports update-as-create via PUT requests. When a resource with a given ID does not already exist, HealthLake creates it. This means you can use PUT with Flexpa's resource IDs to both create and update resources without additional logic.

      #HealthLake-specific considerations

      There are several important behaviors specific to HealthLake that differ from other FHIR servers:

      Update-as-create with PUT

      HealthLake supports update-as-create: a PUT request creates a new resource if no resource with the given ID exists, or updates it if it does. This is ideal for syncing Flexpa data because Flexpa preserves resource IDs between syncs.

      Preserving meta tags

      Flexpa tags resources with metadata like ConsentId and PatientAuthorizationId in meta.tag. HealthLake preserves meta on resources, so you can search by these tags later to scope queries per consent.

      Searching by _tag

      You can find all resources for a specific consent using the _tag search parameter:

      GET /Patient?_tag=https://fhir.flexpa.com/identifiers/ConsentId|{CONSENT_ID}
      

      Patient $everything

      HealthLake supports $everything on Patient resources to retrieve all related data. Note that HealthLake scopes $everything to a single Patient resource, so if a consent produces multiple Patient resources, you need to call $everything for each.

      Bundle size limits

      HealthLake enforces a 15 MB payload limit per API call, including transaction bundles. If a patient's $everything response exceeds this limit, you may need to split the entries across multiple bundles. Consider using batch instead of transaction when atomicity is not required, as transaction bundles consume 2x write capacity.

      Validation levels

      HealthLake validates resources on creation. If you encounter validation errors, you can set a less strict validation level using the x-amzn-healthlake-fhir-validation-level header with values: strict (default), structure-only, or minimal.

      Search by Flexpa consent tag

      # Find all Patient resources for a specific consent
      GET /Patient?_tag=https://fhir.flexpa.com/identifiers/ConsentId|abc-123
      
      # Find all resources for a patient
      GET /Patient/{patientId}/$everything
      
      # Find all resources for a patient, filtered by type
      GET /Patient/{patientId}/$everything?_type=ExplanationOfBenefit,Coverage
      

      #Working with multiple consents

      When a user connects multiple payers through Flexpa (e.g. switching insurance providers), you will receive multiple consents and potentially multiple Patient resources.

      To query across all of a user's data in HealthLake:

      1. Search for Patient resources by consent tag for each consent
      2. Collect the resulting Patient resource IDs
      3. Call $everything for each Patient ID

      You should associate multiple consent IDs with a single user in your application database so you can retrieve all of their data across payers.

      Query across multiple consents

      async function getAllPatientData(
        consentIds: string[],
        healthlakeEndpoint: string
      ) {
        const allResources = [];
      
        for (const consentId of consentIds) {
          // Find Patient resources for this consent
          const searchResponse = await fetch(
            `${healthlakeEndpoint}/Patient?_tag=${encodeURIComponent(
              `https://fhir.flexpa.com/identifiers/ConsentId|${consentId}`
            )}`,
            { headers: await getAuthHeaders() }
          );
          const searchBundle = await searchResponse.json();
      
          // Call $everything for each Patient
          for (const entry of searchBundle.entry || []) {
            const patientId = entry.resource.id;
            const everythingResponse = await fetch(
              `${healthlakeEndpoint}/Patient/${patientId}/$everything`,
              { headers: await getAuthHeaders() }
            );
            const everythingBundle = await everythingResponse.json();
            allResources.push(...(everythingBundle.entry || []));
          }
        }
      
        return allResources;
      }
      

      #Complete implementation

      Below is the complete implementation showing how to fetch data from Flexpa using the $everything operation and store it in AWS HealthLake using a transaction bundle. This code handles authentication with both services, performs the data fetch, transforms the response, and executes the transaction.

      import FlexpaClient from '@flexpa/node-sdk';
      import { SignatureV4 } from '@smithy/signature-v4';
      import { Sha256 } from '@aws-crypto/sha256-js';
      import { defaultProvider } from '@aws-sdk/credential-provider-node';
      import { HttpRequest } from '@smithy/protocol-http';
      import { Bundle, FhirResource } from 'fhir/r4';
      
      const HEALTHLAKE_ENDPOINT = process.env.HEALTHLAKE_ENDPOINT!;
      const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
      
      async function signedFetch(url: string, options: RequestInit = {}) {
        const parsedUrl = new URL(url);
      
        const request = new HttpRequest({
          method: (options.method || 'GET') as string,
          hostname: parsedUrl.hostname,
          path: parsedUrl.pathname + parsedUrl.search,
          headers: {
            'Content-Type': 'application/fhir+json',
            host: parsedUrl.hostname,
          },
          body: options.body as string | undefined,
        });
      
        const signer = new SignatureV4({
          credentials: defaultProvider(),
          region: AWS_REGION,
          service: 'healthlake',
          sha256: Sha256,
        });
      
        const signed = await signer.sign(request);
      
        return fetch(url, {
          method: signed.method,
          headers: signed.headers as Record<string, string>,
          body: options.body,
        });
      }
      
      async function syncToHealthLake(accessToken: string) {
        const client = FlexpaClient.fromBearerToken(accessToken);
        const everything = await client.$everything();
      
        const transaction: Bundle = {
          resourceType: 'Bundle',
          type: 'transaction',
          entry: everything.entry?.map((entry) => {
            const resource = entry.resource as FhirResource;
            // HealthLake does not support the non-standard `meta.project` field
            if (resource.meta) {
              const { project, ...meta } = resource.meta as Record<string, unknown>;
              resource.meta = meta;
            }
            return {
              resource,
              request: {
                method: 'PUT' as const,
                url: `${resource.resourceType}/${resource.id}`,
              },
            };
          }),
        };
      
        const response = await signedFetch(HEALTHLAKE_ENDPOINT, {
          method: 'POST',
          body: JSON.stringify(transaction),
        });
      
        return response.json();
      }
      

      If you encounter validation errors during the transaction, you can add the x-amzn-healthlake-fhir-validation-level header set to minimal in the signedFetch function. This relaxes validation while still ensuring basic FHIR R4 compliance.

      #Handling updates

      When Flexpa sends updated data (e.g. new claims, updated coverage), you can re-run the same sync process. Because we use PUT with the original resource IDs, HealthLake will update existing resources in place rather than creating duplicates.

      Flexpa preserves resource IDs between syncs, so no additional reconciliation logic is needed. The same resource will always have the same ID, regardless of when it was retrieved.

      Sync is idempotent

      // First sync: creates resources
      // PUT /Patient/flexpa-patient-123 → 201 Created
      // PUT /Coverage/flexpa-coverage-456 → 201 Created
      
      // Second sync: updates existing resources
      // PUT /Patient/flexpa-patient-123 → 200 OK
      // PUT /Coverage/flexpa-coverage-456 → 200 OK
      

      #Additional resources

      • AWS HealthLake Documentation
      • HealthLake FHIR Operations
      • HealthLake Bundling Resources
      • Flexpa API reference
      • FHIR resources guide
      • Medplum Integration Guide - an alternative FHIR server option
      Status TwitterGitHub

      © 2026 Flexpa. All rights reserved.