@flexpa/node-sdk
v2.1.0
Published
The NodeSDK simplifies the process of integrating Flexpa by providing a strongly-typed client for calling Flexpa APIs.
Readme
@flexpa/node-sdk
The NodeSDK simplifies the process of integrating Flexpa by providing a strongly-typed client for calling Flexpa APIs.
Installation
With yarn:
yarn add @flexpa/node-sdkWith npm:
npm install @flexpa/node-sdkWith pnpm:
pnpm install @flexpa/node-sdkUsage
Initialize FlexpaClient (from Link Exchange)
import FlexpaClient, { IntrospectResponse } from '@flexpa/node-sdk';
const flexpaClient = await FlexpaClient.fromExchange(myPublicToken, mySecretKey, 'https://api.flexpa.com');
const tokenData: IntrospectResponse = await flexpaClient.introspect();Initialize FlexpaClient (from Access Token)
import FlexpaClient from '@flexpa/node-sdk';
const flexpaClient = new FlexpaClient(myAccessToken);
// Or: const flexpaClient = new FlexpaClient(myAccessToken, "https://api.flexpa.com");Initialize FlexpaClient (from Client Credentials)
The Client Credentials flow provides application access tokens for server-to-server authentication without a patient context. This is useful for:
- Making API calls on behalf of your application rather than a specific patient
- Accessing non-patient-specific endpoints
- Running backend services that need Flexpa API access
import FlexpaClient from '@flexpa/node-sdk';
// Get an application access token using OAuth 2.0 Client Credentials Grant Flow
const flexpaClient = await FlexpaClient.fromClientCredentials(
myPublishableKey,
mySecretKey,
'https://api.flexpa.com', // optional
);
// The response includes:
// - access_token: JWT token for API requests
// - expires_in: 1800 seconds (30 minutes)
// - token_type: "Bearer"
// Application access tokens are valid for 30 minutes and use JWT format with ES256 signaturesReading a FHIR Resource
import * as r4 from 'fhir/r4';
import { FlexpaClient } from '@flexpa/node-sdk';
const flexpaClient = new FlexpaClient(myAccessToken);
const patient: r4.Patient = await flepxaClient.read('Patient', '$PATIENT_ID');Searching a FHIR Resource
import * as r4 from 'fhir/r4';
import { FlexpaClient } from '@flexpa/node-sdk';
const flexpaClient = new FlexpaClient(myAccessToken);
const bundle: r4.Bundle = await flepxaClient.search('Patient', { given: 'john', family: 'doe' });Refreshing a Token
import FlexpaClient from '@flexpa/node-sdk';
// For multiple usage patient authorizations with refresh tokens
const flexpaClient = await FlexpaClient.fromExchange(myPublicToken, mySecretKey);
// When the access token is about to expire, refresh it
const refreshResponse = await flexpaClient.tokenRefresh(myPublishableKey, mySecretKey);
// The client's access token is automatically updatedbuildAuthorizationUrl
Builds the OAuth 2.0 authorization URL for the PKCE flow. Open the returned URL in a browser to start patient authorization.
const authUrl = FlexpaClient.buildAuthorizationUrl({
publishableKey,
redirectUri,
codeChallenge,
externalId,
});Options
| Option | Type | Description |
| ---------------- | ------------------- | ------------------------------------------------------------------------------------ |
| publishableKey | string | Your Flexpa publishable key (pk_test_… / pk_live_…). |
| redirectUri | string | The URI to redirect to after the user completes authorization. |
| codeChallenge | string | Base64url-encoded SHA256 of the code verifier (see generateCodeChallenge). |
| externalId | string | Stable identifier for the user in your system. |
| scope | string[] | OAuth scopes. Defaults to ['launch/patient']. |
| state | string | Opaque CSRF token returned on the callback. |
| endpointId | string | Pre-select a specific payer/provider endpoint by ID. |
| apiBaseUrl | string | Override the API base URL. Defaults to https://api.flexpa.com. |
| resume | boolean | Set to true to resume an in-progress consent workflow. Emits flexpa_resume=true. |
| flow | AuthorizationFlow | Opt into a non-default flow. See Flow below. |
Flow
The flow option models non-default authorization flows as a discriminated
union. Omit flow for the standard payer/provider flow.
TEFCA / IAL2 identity-verified flow
FlexpaClient.buildAuthorizationUrl({
publishableKey,
redirectUri,
codeChallenge,
externalId,
flow: { type: 'ial2' },
});Search-mode patient match (with optional demographic hints)
FlexpaClient.buildAuthorizationUrl({
publishableKey,
redirectUri,
codeChallenge,
externalId,
flow: {
type: 'search',
hints: { firstName: 'Jane', lastName: 'Doe', dob: '1985-04-12' },
},
});The discriminated union enforces the server-side constraints at compile time: the two modes are mutually exclusive, and hints only apply to the search flow.
SMART Health Links
Available in @flexpa/node-sdk 2.1.0. Initialize an application-token client on
your server with test or live keys; the API scopes every request to that
application and mode.
import FlexpaClient from '@flexpa/node-sdk';
const client = await FlexpaClient.fromClientCredentials(publishableKey, secretKey);
const link = await client.createSmartHealthLink({
files: [
{
contentType: 'application/fhir+json',
content: { resourceType: 'Bundle', type: 'collection', entry: [] },
},
],
user: { externalId: 'user-123' },
ttl: 3600,
});
const page = await client.listSmartHealthLinks({ limit: 20 });
if (page.meta.nextCursor) {
const nextPage = await client.listSmartHealthLinks({ limit: 20, cursor: page.meta.nextCursor });
}
const filters = { status: 'SUCCEEDED', type: 'MANIFEST', limit: 20 } as const;
const audit = await client.listSmartHealthLinkAccesses(link.id, filters);
const nextUrl = audit.link?.find((entry) => entry.relation === 'next')?.url;
const cursor = nextUrl ? new URL(nextUrl).searchParams.get('cursor') : null;
if (cursor) {
const nextAudit = await client.listSmartHealthLinkAccesses(link.id, { ...filters, cursor });
}
await client.revokeSmartHealthLink(link.id);ttl is in seconds and required unless longTerm: true. Direct transfer requires
exactly one file and cannot use a passcode. Application tokens and legacy secret
keys require user.externalId; patient tokens derive the user from their authorization.
listSmartHealthLinks returns metadata, including successful accessCount and
lastAccessedAt, plus meta.hasMore and meta.nextCursor. Its page size is 1–1000.
listSmartHealthLinkAccesses returns a FHIR Bundle of AuditEvents, including denied
attempts, and accepts page sizes of 1–100. Both default to 20. Keep the same audit
filters when requesting the next cursor. The SDK sends requests to its configured
API URL; it does not fetch URLs returned in a Bundle.
All four methods also accept existing patient tokens or legacy secret keys through
FlexpaClient.fromBearerToken(credential). Patient tokens can manage only their own
user's links. New server integrations should use application tokens.
These methods use the existing HttpError and retry 429 responses up to ten
times, respecting Retry-After. Other failures are returned immediately.
Creation and access statistics (unreleased)
getSmartHealthLinkStats retrieves aggregate statistics for links created during
a rolling reporting period. lookbackDays defaults to 30 and must be an integer
from 1 to 365. The API applies the same application, test/live mode, and patient
ownership restrictions as listSmartHealthLinks.
const stats = await client.getSmartHealthLinkStats({ lookbackDays: 30 });
const { linksCreated, linksAccessed, successfulAccesses } = stats.data;data contains linksCreated, linksAccessed, and successfulAccesses.
Secondary counts usersWithLinks and usersWithAccesses represent
distinct link owners, not recipients. Repeated successful
accesses increase successfulAccesses without inflating distinct links or users.
Denied attempts are excluded; revoked and expired links remain in the cohort.
A successful manifest access does not establish that its files were downloaded.
Earlier access activity was not backfilled, so metrics reflect recorded events.
timeSeries groups counts by the link's UTC creation date (YYYY-MM-DD), including
zero-count dates. Daily distinct user counts cannot be summed into period totals.
period.start and period.end are ISO 8601 UTC timestamps. This method uses the
same credentials, HttpError, and rate-limit retry behavior as the other SHL methods.
Publishing to NPM
- Update
packages/node-sdk/package.json'sversion(or runyarn workspace @flexpa/node-sdk version patch | minor | major). - Add a corresponding entry to
packages/node-sdk/CHANGELOG.md. - Open a PR to merge the version + changelog changes into the
masterbranch. - Once merged, run the Publish @flexpa/node-sdk github workflow to publish the new version to NPM.
- The NPM package will be available at https://www.npmjs.com/package/@flexpa/node-sdk
FAQ
| Topic | Answer | Comments |
| ----------- | ------------ | ------------ |
| Runtime | Node |
| Build | Through yarn | yarn build |
