Quickstart
Flexpa Link can be used in an app to authorize access to health plans, claims,
and clinical data. Then, Flexpa API can be used to query electronic medical
records via FHIR Resources.
See this in action by building and running Flexpa's quickstart application hosted on GitHub.
After that, continue reading to learn how the application works so you can modify this example for your own uses.
#Quickstart requires access to test mode
Test mode access requires a signed Flexpa Service Agreement.
#Quickstart setup
Let's start by getting the quickstart project running locally on your machine.
This section walks through cloning the quickstart repo, setting your API keys, running the project, and testing it.
#Clone repo
First, clone the quickstart repository:
Clone
# Clone the quickstart repository
git clone https://github.com/flexpa/quickstart.git
#Set API keys
Flexpa integrations run in one of two modes, determined by the keys used. Test mode functions like live mode, except the data returned is not real patient data. When you've finished testing, simply swap in your live mode keys!
Each mode uses a pair of API keys: a publishable key, that can be used publicly, and a secret key, that must be kept private.
Create an account in the Flexpa Portal to get test mode keys and contact us for live mode access.
A client-side key that uniquely identifies your app
A server-side key that uniquely identifies your integration's back end
You must use the test mode publishable key with the test mode secret key and the live mode publishable key with the live mode secret key.
You'll need to add your publishable and secret API keys to the .env
files in the client and server directories.
Rename the .env.template
file as .env
in both directories.
cd quickstart
cp client/.env.template client/.env
cp server/.env.template server/.env
Open both client/.env
and server/.env
and add your test mode API keys.
client/.env
VITE_FLEXPA_PUBLISHABLE_KEY=
server/.env
FLEXPA_API_SECRET_KEY=
#Run project
The quickstart code includes two components:
- Frontend client for creating the Flexpa Link connection to a health plan
- Backend server that swaps a public token for an access token
Run both components simultaneously to test the application.
From the root of the project, run the frontend client:
# Navigate into the client directory
cd client
# Set the correct node version
nvm use
# Install the dependencies
npm install
# Run the frontend server
npm run dev
Next, from the root of the project in a new terminal, run the backend server:
# Navigate into the server directory
cd server
# Set the correct node version
nvm use
# Install the dependencies
npm install
# Compile the TypeScript to JavaScript code
npm run build
# Run the backend server
npm run dev
#Test application
To test the quickstart app:
- Open http://localhost:3000 in your browser.
- Click the "Connect your health plan with Flexpa Link" to open the Flexpa Link modal.
- Select any health plan payer from the list.
- When directed to the provider's site, authenticate using a test mode login.
- When Flexpa Link indicates success, click "Continue".
Upon successful authentication, you'll be redirected back to localhost. The server script automatically exchanges the public token for an access token, and then the client app performs a FHIR request and reports upon the result. (This may take a moment to complete.)
#How it works
Now that you've seen the application in action, let's examine some of the main sections.
#Flexpa Link configuration
Flexpa Link is included through a script tag inside the head of the HTML
document:
client/index.html
<html>
...
<head>
...
<script src="https://js.flexpa.com/v1/"></script>
...
</head>
...
</html>
Then, in the client-side JavaScript code,
FlexpaLink.create()
is called with the publishableKey
, a user
object, and an onSuccess
callback function to configure Flexpa Link:
client/src/main.ts
FlexpaLink.create({
publishableKey: import.meta.env.VITE_FLEXPA_PUBLISHABLE_KEY, // Defined in .env
user: {
externalId: 'usr_1234'
}, // A unique id for the user owned by the host application
onSuccess: async (publicToken: string) => {},
// ^ We'll talk about the onSuccess callback later
});
#Authentication flow
The Flexpa authentication flow helps you obtain authorization to access health plans, coverage, and clinical data directly from a patient.
Read more about which plans Flexpa supports in our Endpoints reference.
To start the authentication flow, FlexpaLink.open()
is called within the onclick
handler of a button.
client/src/main.ts
const linkButton = document.getElementById("flexpa-link-btn");
...
linkButton.addEventListener("click", () => {
FlexpaLink.open();
});
You must call FlexpaLink.create(...)
before calling FlexpaLink.open()
.
Once clicked, the button presents the user with a modal to connect their health plan payer:
#Obtain access token
After a user completes the payer login flow, the onSuccess
callback is executed, with a publicToken
as the argument.
To query patient data, you must exchange the public token for an access token using your secret API key (i.e., from the server).
Here's an updated Flexpa Link configuration with that callback definition:
client/src/main.ts
FlexpaLink.create({
publishableKey: import.meta.env.VITE_FLEXPA_PUBLISHABLE_KEY,
onSuccess: async (publicToken: string) => {
/* Make a request to the `POST /flexpa-access-token` endpoint
defined in `server`.
Include the `publicToken` in the body. */
const resp = await fetch(
`${import.meta.env.VITE_SERVER_URL}/flexpa-access-token`,
{
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ publicToken }),
}
);
},
});
After the server swaps the public token for an access token you can use the access token to query Flexpa API for patient data. The flow looks like:
To exchange the public token for an access token, you must:
- Pass the
publicToken
to your server.
- Have your server make a
POST
request to https://api.flexpa.com/link/exchange
, passing the public_token
and your secret_key
within the JSON body.
- Parse the response JSON body of
https://api.flexpa.com/link/exchange
to extract the access_token
.
Here is a minimal endpoint that performs the token exchange:
server/src/routes/flexpa_access_token.ts
/**
* POST /flexpa-access-token
* Exchanges your `publicToken` for an `access_token`
*/
router.post("/flexpa-access-token", async (req: Request, res: Response) => {
const { publicToken } = req.body as FlexpaAccessTokenBody;
...
// Call Flexpa API's exchange endpoint to exchange
// your `publicToken` for an `access_token`
try {
const resp = await fetch(`${process.env.FLEXPA_PUBLIC_API_BASE_URL}/link/exchange`, {
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
public_token: publicToken,
secret_key: process.env.FLEXPA_API_SECRET_KEY,
}),
});
const { access_token: accessToken, expires_in: expiresIn } = await resp.json() as LinkExchangeResponse;
res.send({ accessToken, expiresIn });
}
catch (err) {
return res.status(500).send(`Error exchanging token: ${err}`);
}
});
#Wait for the initial sync
Immediately after the token exchange step above, an initial sync process is automatically initiated in the background to asynchronously retrieve all the authorized patient's data on behalf of your application.
Your application must wait for the initial sync to complete before making FHIR requests.
The initial sync period typically lasts less than 1 minute.
The duration of the initial sync period is affected by the volume of the patient's data records, the responsiveness of the payer FHIR server, and the existing load on Flexpa's servers.
If your application attempts to make a FHIR request during this time, it will receive a response with a 429 status code.
Please ensure that your application implements retry logic upon receiving a 429 status code for FHIR requests.
Here is an example function that performs fetch with retry logic:
server/src/routes/fhir.ts
async function fetchWithRetry(
url: string,
authorization: string,
maxRetries = 10,
) {
let retries = 0;
let delay = 1;
while (retries < maxRetries) {
try {
console.log(`Fetching ${url}, retries: ${retries}`);
const response = await fetch(url, {
method: "GET",
headers: {
"content-type": "application/json",
Authorization: authorization,
},
});
if (response.status !== 429) {
console.log(`Received ${response.status} from ${url}`);
return response;
}
const retryAfter = response.headers.get("Retry-After") || delay;
await new Promise((resolve) =>
setTimeout(resolve, Number(retryAfter) * 1000),
);
retries++;
delay *= 2; // Double the delay for exponential backoff
} catch (err) {
console.log(`Error fetching ${url}: ${err}`);
throw err;
}
}
throw new Error("Max retries reached.");
}
#Make FHIR requests
After the initial sync is complete, your application can make FHIR requests from your application's backend server by targetting the URL https://api.flexpa.com/fhir
.
All FHIR requests must include the access token within the Authorization
HTTP header.
server/src/routes/fhir.ts
router.get("*", async (req: Request, res: Response) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).send("All requests must be authenticated.");
}
const { href } = new URL(
`fhir${req.path}`,
process.env.FLEXPA_PUBLIC_API_BASE_URL, // https://api.flexpa.com/fhir
);
try {
const fhirResp = await fetchWithRetry(href, authorization);
const fhirBody: Bundle = await fhirResp.json();
res.send(fhirBody);
} catch (err) {
console.log(`Error retrieving FHIR: ${err}`);
return res.status(500).send(`Error retrieving FHIR: ${err}`);
}
});
Flexpa API supports reading and searching a variety of FHIR Resources.
The quickstart example retrieves the Coverage FHIR Resource by instructing the frontend client to request FHIR resources via the backend server.
client/src/main.ts
let fhirCoverageResp;
let fhirCoverageBody: Bundle;
try {
fhirCoverageResp = await fetch(
`${
import.meta.env.VITE_SERVER_URL // URL of your backend server
}/fhir/Coverage?patient=$PATIENT_ID`,
{
method: "GET",
headers: {
authorization: `Bearer ${accessToken}`,
},
},
);
// Parse the Coverage response body
fhirCoverageBody = await fhirCoverageResp.json();
} catch (err) {
console.log("Coverage error: ", err);
return;
}
if (!fhirCoverageResp.ok) {
console.error(`Fetch failed with status: ${fhirCoverageResp.status}`);
return;
}
For FHIR endpoints that need a patient ID, Flexpa API automatically pulls the patient ID from the access token when you use the wildcard parameter $PATIENT_ID
.
Attempting to make requests to https://api.flexpa.com
directly from your frontend client may result in a CORS error.
#End to end flow
The following diagram displays the end to end authentication and data retrieval flow.
To retrieve FHIR data, you must:
- Pass the
publicToken
to your server.
- Have your server make a
POST
request to https://api.flexpa.com/link/exchange
, passing the public_token
and your secret_key
within the JSON body.
- Parse the response JSON body of
https://api.flexpa.com/link/exchange
to extract the access_token
.
- Wait for the initial sync to complete.
- Make a
GET
request to https://api.flexpa.com/fhir/[resourceType]
with the access_token
in the Authorization
HTTP header.
#Next steps