Quickstart
#Introduction
Build a working Flexpa integration using the Quickstart app.
You'll need API keys from the Developer Portal. Flexpa operates in Test and Live modes—we'll start in Test mode.
#API Keys
A client-side key that is okay to share
A server-side key that you must keep private
The OAuth PKCE flow used in this quickstart only requires your Publishable Key.
#Modes
Test credentials and synthetic data
Launch your integration with real patient data
#Quickstart setup
Let's start by cloning the Quickstart repository from GitHub.
The repository includes a .env.example file. You will copy this template file and use it as a starting point for your own .env file.
Clone and copy template
# Clone the repository
git clone https://github.com/flexpa/quickstart.git
cd quickstart
# Copy the environment variables template
cp .env.example .env
#Environment variables
Open .env in a text editor.
Add your Publishable Key for Test mode from the Developer Portal → API Keys.
The redirect URI is where users return after authorization. For local development, use http://localhost:3000/callback. Register this URI in the Developer Portal.
You'll also need to generate a random secret for session signing. Your own app will have its own authentication.
Update the .env file
NEXT_PUBLIC_FLEXPA_PUBLISHABLE_KEY=pk_test_...
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/callback
Generate a random session key
# Generate a random secret key and add it to the .env file
echo "SESSION_SECRET=$(openssl rand -base64 32)" >> .env
#Running
The Flexpa Quickstart is a Node.js app built with Next.js.
To get it started, install dependencies with npm install and start the app with npm run dev.
The application will be running at http://localhost:3000.

Install dependencies
# Install dependencies
npm install
# Start the development server
npm run dev
# Go to http://localhost:3000
#Your first consent
A Patient Authorization represents consent to share healthcare records. Each authorization links a person, your application, and their healthcare records.
Flexpa supports a 3-in-1 network for patient access to health insurance payers, healthcare providers, and national TEFCA networks.
Now that you have Quickstart running, click Launch Consent to start the OAuth flow. You'll be redirected to Flexpa where you can select a health plan.
Select Foo Medical and log in with the test credentials:
Test credentials
# Username
patient10@flexpa.com
# Password
examplepatient
Foo Medical is a fake sandbox we use to test the integration and it provides synthetic records. Learn more about test mode.
After completing consent, you'll be redirected back to Quickstart.
You have created your first Patient Authorization! You can now make API calls by using the buttons in Quickstart.
In the next section, we'll explain what actually happened and how Quickstart works.
#How it works
Quickstart demonstrates the OAuth 2.0 PKCE flow:
- Authorization - Redirect users to Flexpa for consent
- Code Exchange - Exchange the authorization code for an access token
- API Calls - Use the access token to query FHIR resources
#Consent flow
The OAuth 2.0 PKCE (Proof Key for Code Exchange) flow securely authorizes your application without exposing secrets on the client.
The Quickstart uses a Next.js server action to handle PKCE generation server-side:
Building the authorization URL
'use server'
import FlexpaClient from '@flexpa/node-sdk'
import { setCodeVerifier } from './session'
export async function startOAuthFlow() {
// Generate PKCE credentials on the server
const codeVerifier = FlexpaClient.generateCodeVerifier()
const codeChallenge = FlexpaClient.generateCodeChallenge(codeVerifier)
// Store codeVerifier securely in server-side session
await setCodeVerifier(codeVerifier)
// Build the authorization URL
const authUrl = FlexpaClient.buildAuthorizationUrl({
publishableKey: process.env.NEXT_PUBLIC_FLEXPA_PUBLISHABLE_KEY,
redirectUri: process.env.NEXT_PUBLIC_REDIRECT_URI,
codeChallenge,
externalId: crypto.randomUUID(),
})
return authUrl
}
After a patient completes their consent on Flexpa, they are redirected back to your redirectUri with an authorization code parameter.
Your callback handler should:
- Extract the
code from the URL query parameters
- Retrieve the stored
code_verifier
- Exchange them for an access token
Exchanging the code for an access token
import FlexpaClient from '@flexpa/node-sdk'
// Get the authorization code from the callback URL
const code = new URL(request.url).searchParams.get('code')
// Retrieve the code verifier you stored earlier
const codeVerifier = await getStoredCodeVerifier()
// Exchange for access token
const client = await FlexpaClient.fromAuthorizationCode(
code,
codeVerifier,
process.env.NEXT_PUBLIC_REDIRECT_URI,
process.env.NEXT_PUBLIC_FLEXPA_PUBLISHABLE_KEY
)
// Store the access token securely
const accessToken = client.getAccessToken()
The access_token grants access to a Patient Authorization's records. Store it securely—you'll need it for all FHIR API requests.
#Retrieving records
Now that we've covered the OAuth flow, let's explore how to make API calls.
As an example, let's take a look at some of the requests made on the dashboard at http://localhost:3000/dashboard.
The first request, $everything, is to /fhir/Patient/$PATIENT_ID/$everything - which retrieves all of the available data for a patient.
The request uses the access_token like all consented records requests.
In this code we see how the Quickstart makes this call using Node SDK but you can make it with any HTTP client:
src/app/api/fhir/Patient/$everything/route.ts
import { NextResponse } from 'next/server'
import FlexpaClient from '@flexpa/node-sdk'
import { getSession } from '@/lib/session';
export async function GET() {
const session = await getSession();
if (!session?.accessToken) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
)
}
const client = FlexpaClient.fromBearerToken(session.accessToken);
const everything = await client.$everything();
return NextResponse.json(everything)
}
Flexpa API supports reading and searching a variety of FHIR Resources. The Quickstart demonstrates a few common requests:
When making FHIR API requests, you may encounter 429 status codes while Flexpa syncs data from the payer. This happens during the initial sync period, which typically lasts less than 1 minute. Implement retry logic to handle these, or use the Node SDK which retries automatically.

#Advanced
The FHIR responses above contain rich healthcare data, but they're deeply nested and complex to parse. For most applications, you want specific fields—not raw FHIR structures.
The ViewDefinition/$run endpoint lets you define exactly what data you need and receive it as simple rows and columns. No FHIR parsing required.
Here's an example that extracts a summary from ExplanationOfBenefit resources (claims data):
src/app/api/fhir/claims-summary/route.ts
import { NextResponse } from 'next/server'
import { getSession } from '@/app/lib/session';
const claimsView = {
resourceType: 'ViewDefinition',
name: 'claims_summary',
status: 'active',
resource: 'ExplanationOfBenefit',
select: [{
column: [
{ name: 'claim_id', path: 'id' },
{ name: 'type', path: "type.coding.where(system='http://terminology.hl7.org/CodeSystem/claim-type').code.first()" },
{ name: 'service_date', path: 'billablePeriod.start' },
{ name: 'provider', path: 'provider.display' },
{ name: 'total_billed', path: "total.where(category.coding.code='submitted').amount.value.first()" },
{ name: 'you_paid', path: "total.where(category.coding.code='memberliability').amount.value.first()" },
{ name: 'diagnosis_codes', path: 'diagnosis.diagnosisCodeableConcept.coding.code.distinct()', collection: true },
]
}]
};
export async function GET() {
const session = await getSession();
if (!session?.accessToken) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
}
const response = await fetch('https://api.flexpa.com/fhir/ViewDefinition/$run', {
method: 'POST',
headers: {
'Authorization': `Bearer ${session.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
resourceType: 'Parameters',
parameter: [{ name: 'viewResource', resource: claimsView }]
})
});
return NextResponse.json(await response.json());
}
Instead of parsing nested FHIR structures, you get clean, flat data:
Response
{
"rows": [
{
"claim_id": "eob-12345",
"type": "professional",
"service_date": "2024-03-15",
"provider": "Dr. Sarah Chen",
"total_billed": 250.00,
"you_paid": 35.00,
"diagnosis_codes": ["Z00.00", "J06.9"]
}
],
"resourceCount": 1
}
Need help building ViewDefinitions? We're FHIR experts. Tell us what data your application needs, and we can help you craft a ViewDefinition that extracts exactly that. Reach out to your Flexpa contact or our support team.
Learn more about ViewDefinitions in our parsing guide or see the API reference.
#Next steps
Want to see an example of how to integrate Flexpa into an existing application? Check out our integration tutorial!
Congratulations, you have completed the Flexpa Quickstart!
There are a few directions you can go in now: