FlexpaFlexpa
Developer PortalGet a DemoTry it yourself

All docs

Consent

  • How it works
  • What you need

Getting Started

  • Generate PKCE
  • Build authorization URL
  • Scopes
  • Redirect
  • Handle callback
  • Error codes
  • Exchange
  • Fetch records

OAuth API

  • GETAuthorization
  • POSTToken
  • GETIntrospect
  • POSTRevoke

REST API

  • GETConsent
  • GETList consents

Discovery API

  • GETOAuth Authorization Server Metadata
  • GETOpenID Configuration
  • GETSMART Configuration
  • GETJWKS

Mobile applications

  • iOS
  • Android
  • React Native

Workflow Ideas

  • Onboarding
  • Integration
  • Campaigns
  • Incentivization

Best practices

  • Explain the benefit
  • Set expectations
  • Present as default
  • Security
  • Polish your flow
  • Allow for multiple links

Migration from v1

  • Key changes

Troubleshooting

    Next steps

      OAuth Reference

      #Consent

      Flexpa Consent enables secure patient authorization for Records retrieval using SMART on FHIR with OAuth 2.0 PKCE.

      Consent can be used in web and mobile apps.

      #How it works

      1. Your app redirects users to Flexpa's authorization endpoint
      2. Users connect their healthcare records from our 3-in-1 network
      3. Flexpa redirects back to your app with an authorization code
      4. You exchange the authorization code for an access token and start making Records requests

      #What you need

      • A pair of API Keys from the Flexpa Portal
      • A redirect URI registered in the Flexpa Portal. This is where users will be redirected after authorization.
      • Optional: The @flexpa/node-sdk package installed in your project
      Flexpa Consent

      #Looking for a demo?

      Try it yourself

      Go through a Patient Access API flow yourself! See exactly what patients will see

      Try it yourself →

      #Getting Started

      The SMART on FHIR authorization flow involves these main steps:

      1. Generate PKCE - Create a code verifier and challenge
      2. Build authorization URL - Construct the URL with your parameters
      3. Redirect - Send the user to Flexpa for authorization
      4. Handle callback - Process the authorization code on redirect
      5. Exchange - Exchange the code for access tokens
      6. Fetch records - Use the access token to retrieve patient data

      Let's detail each of these steps below.

      The examples below show raw HTTP/JavaScript implementations. For convenience helpers, install the Node SDK:

      Install

      npm install @flexpa/node-sdk
      

      #Generate PKCE

      PKCE (Proof Key for Code Exchange, pronounced "pixie") is an extension to OAuth 2.0 (RFC 7636) that prevents authorization code interception attacks. It ensures the application that starts the authorization flow is the same one that finishes it.

      You'll generate two values:

      • Code Verifier: A cryptographically random string (43-128 characters) that you keep secret and store until the callback
      • Code Challenge: A SHA-256 hash of the verifier, base64url-encoded, sent with the authorization request

      The security works because hashing is one-way: even if an attacker intercepts the authorization code and challenge, they cannot reverse the hash to obtain the verifier needed to exchange the code for tokens.

      For web apps, store the verifier in sessionStorage—it survives redirects within the same tab and is automatically cleared when the tab closes.

      Generate PKCE

      # Generate code verifier (43-128 character base64url string)
      CODE_VERIFIER=$(openssl rand -base64 32 | tr -d '=' | tr '/+' '_-')
      
      # Generate code challenge: BASE64URL(SHA256(verifier))
      CODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | \
        openssl sha256 -binary | base64 | tr -d '=' | tr '/+' '_-')
      
      echo "Code Verifier: $CODE_VERIFIER"
      echo "Code Challenge: $CODE_CHALLENGE"
      

      #Build authorization URL

      Construct the authorization URL with your OAuth parameters.

      Parameters

      client_idstringRequired

      Your publishable key from the Flexpa Portal.

      redirect_uristringRequired

      The URL where users will be redirected after authorization. Must be registered in the Flexpa Portal.

      response_typestringRequired

      Must be code for the authorization code flow.

      code_challengestringRequired

      The PKCE code challenge generated from the code verifier (43 base64url characters).

      code_challenge_methodstringRequired

      Must be S256 (SHA-256).

      scopestringRequired

      Space-separated OAuth scopes. Must include launch/patient. Add offline_access for refresh tokens enabling multiple usage.

      flexpa_external_idstringRequired

      Your application's user identifier for this patient. Used for tracking and correlating authorizations across your system. If omitted, the authorization request is rejected with error=invalid_request and error_description of flexpa_external_id is required.

      statestring

      An opaque value used to maintain state between the request and callback. This value will be returned in the redirect. Recommended for CSRF protection.

      flexpa_endpoint_idstring

      Pre-select a specific endpoint (health plan) for the user. Skips the endpoint selection screen. Get endpoint IDs from the Directory. Cannot be set at the same time as flexpa_ial2_mode.

      flexpa_ial2_modestring

      The only accepted value is "true". When set, starts the IAL2 identity verification flow — patients verify their identity through CLEAR, ID.me, or Persona, and Flexpa searches TEFCA networks automatically. At any point during the flow, the patient can also add endpoints manually via search. Cannot be set at the same time as flexpa_search_mode or flexpa_endpoint_id.

      flexpa_search_modestring

      The only accepted value is "true". Pass this unless you specifically want the IAL2 identity flow. When set, starts directly in the Consent 2.1 search flow, skipping IAL2 identity verification. Patients search by provider, facility, or health plan and can connect multiple sources in a single session. Cannot be set at the same time as flexpa_ial2_mode.

      flexpa_resumestring

      Set to true when starting an authorization for a user who previously exited the consent flow mid-way (for example, after closing an identity-verification tab) so they can pick up where they left off instead of restarting. Omit the parameter to always start a new authorization.

      Resume is backed by a session cookie set when the user enters the consent flow. If that cookie is no longer present, or any request parameter differs from the original (scope, redirect_uri, flexpa_endpoint_id, etc.), Flexpa silently starts a new authorization — there is no error path to handle.

      Demographic hints (search mode only)

      flexpa_hint_first_namestring

      First name hint for the patient. Requires flexpa_search_mode=true.

      flexpa_hint_last_namestring

      Last name hint for the patient. Requires flexpa_search_mode=true.

      flexpa_hint_dobstring

      Date of birth hint for the patient (YYYY-MM-DD format). Requires flexpa_search_mode=true.

      Build URL

      https://api.flexpa.com/oauth/authorize
        ?client_id=pk_test_...
        &redirect_uri=https://example.com/callback
        &response_type=code
        &code_challenge=$CODE_CHALLENGE
        &code_challenge_method=S256
        &scope=launch/patient
        &flexpa_external_id=usr_1234
      

      #Scopes

      OAuth scopes control what data your application can access and for how long.

      #Supported scopes

      launch/patientstringRequired

      Required scope that indicates patient context will be provided. This is necessary for all Flexpa integrations.

      offline_accessstring

      Optional scope that requests MULTIPLE usage — Flexpa maintains the authorization with the endpoint and syncs fresh patient data on your application's configured sync frequency, without requiring the user to re-authorize.

      A refresh_token is returned in the token response regardless of this scope. What the scope changes is whether Flexpa continues to sync fresh patient data from the endpoint.

      Not all health plans support offline_access. Where an endpoint can't maintain the authorization, the authorization still succeeds and you still receive a refresh_token, but Flexpa won't sync fresh patient data from that endpoint. The data from the initial sync is available for 24 hours; after that, the user re-authorizes for new data. Refreshability is listed per endpoint in the directory.

      #Redirect

      Once you have the authorization URL, redirect the user to begin the consent flow. Users will:

      1. Connect their health records
      2. Consent to share their data with your application
      3. Be redirected back to your redirect_uri with an authorization code

      #Handle callback

      After authorization, Flexpa redirects back to your redirect_uri with query parameters.

      Parse the code parameter from the URL, retrieve the stored code verifier, and exchange them for tokens.

      Success parameters

      codestring

      The authorization code to exchange for an access token. Valid for 30 minutes.

      statestring

      The state parameter you provided (if any). Verify this matches your stored state for CSRF protection.

      Error parameters

      errorstring

      One of the error codes: access_denied or server_error

      error_descriptionstring

      Human-readable error description providing additional detail

      error_uristring

      Link to error code documentation. Included only on terminal/server error redirects (e.g. server_error, or an abandoned/expired session). It is not present when the patient cancels the flow or no matching records are found.

      statestring

      The state parameter you provided (if any). Always returned when present in the original request.

      Always validate the state parameter matches what you stored to prevent CSRF attacks.

      Success

      https://example.com/callback?code=abc123&state=xyz789
      

      Error

      https://example.com/callback
        ?error=access_denied
        &error_description=User+abandoned+consent
        &error_uri=https://flexpa.com/docs/consent%23error-codes
        &state=xyz789
      

      #Error codes

      When consent fails, Flexpa redirects back to your redirect_uri with standardized error parameters aligned with RFC 6749 where applicable.

      Error codes

      access_deniedstring

      The user did not complete authorization. This includes abandoning the flow, closing the modal, navigating away, or the session timing out. The error_description parameter explains the nature of the error:

      • User cancelled the consent flow
      • No records found for this patient — identity was verified but no matching records were found
      server_errorstring

      An internal error prevented authorization. This includes initialization failures, no facilities authorized, or authorization code creation failures.

      All error redirects include error and, when available, error_description (human-readable detail) and state (echoed back when provided in the original request). The error_uri parameter (a link to this section) is included only on server-driven terminal redirects (when the consent workflow reaches an abandoned or expired state, e.g. server_error or a session timeout). Redirects triggered by the patient explicitly leaving the flow — including access_denied with User cancelled the consent flow and the No records found for this patient case — include error and error_description but may omit error_uri.

      The state parameter is always returned on error redirects when it was provided in the original request. Use this to correlate the error with the user's session and for CSRF validation.

      User cancelled

      https://example.com/callback
        ?error=access_denied
        &error_description=User+cancelled+the+consent+flow
        &state=xyz789
      

      No records found

      https://example.com/callback
        ?error=access_denied
        &error_description=No+records+found+for+this+patient
        &state=xyz789
      

      Server error

      https://example.com/callback
        ?error=server_error
        &error_description=No+facilities+...
        &error_uri=...
        &state=xyz789
      

      #Exchange

      Exchange the authorization code for an access token using the token endpoint.

      This request:

      • Validates the authorization code with Flexpa
      • Verifies the PKCE code verifier
      • Returns access and refresh tokens

      See the Token endpoint for full details on request and response fields.

      Exchange

      POST
      /oauth/token
      curl -X POST https://api.flexpa.com/oauth/token \
        -H "Content-Type: application/json" \
        -d '{
          "grant_type": "authorization_code",
          "client_id": "pk_test_...",
          "code": "the-authorization-code",
          "redirect_uri": "https://example.com/callback",
          "code_verifier": "the-code-verifier"
        }'
      

      #Fetch records

      Use your access token to fetch patient data from the FHIR API.

      Use the Patient Access Token returned from the exchange step.

      The $everything operation returns a Bundle containing all available resources for the patient—claims, coverage, conditions, medications, and more.

      See the Records documentation for the full list of supported FHIR resources and operations.

      Request

      GET
      /fhir/Patient/$everything
      curl 'https://api.flexpa.com/fhir/Patient/$everything' \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      

      #OAuth API

      The OAuth API provides endpoints for authorization, token management, and introspection.


      GEThttps://api.flexpa.com/oauth/authorize

      #Authorization

      The authorization endpoint initiates the OAuth 2.0 PKCE flow. See Build authorization URL for implementation details.


      POSThttps://api.flexpa.com/oauth/token

      #Token

      The token endpoint supports three grant types:

      1. authorization_code - For exchanging an authorization code for access tokens (OAuth PKCE flow)
      2. refresh_token - For refreshing Patient Access Tokens
      3. client_credentials - For obtaining Application Access Tokens

      Request headers

      Authorizationstring

      Client identity can be provided in one of two ways:

      • client_id in the request body — for public clients (e.g. mobile apps) that cannot store a secret key. Supported for authorization_code and refresh_token grants.
      • Authorization: Basic header — for confidential clients with a secret key. Required for client_credentials grant, also supported for refresh_token grant. Concatenate your publishable key and secret key (from the Flexpa Portal) with a colon (:), then base64-encode the result.

      You cannot provide both an Authorization header and client_id in the same request.

      Request body

      grant_typestringRequired

      The type of grant used to obtain the access token.

      Valid values:

      • authorization_code - For exchanging an authorization code (PKCE)
      • client_credentials - For Application Access Tokens
      • refresh_token - For refreshing Patient Access Tokens
      codestring

      Required when grant_type is authorization_code.

      The authorization code from the callback.

      redirect_uristring

      Required when grant_type is authorization_code.

      Must match the redirect URI used in the authorization request.

      code_verifierstring

      Required when grant_type is authorization_code.

      The PKCE code verifier used to generate the code challenge.

      client_idstring

      Required when grant_type is authorization_code. Optional for refresh_token as an alternative to Basic Auth for public clients that cannot store a secret key.

      Your publishable key.

      refresh_tokenstring

      Required when grant_type is refresh_token.

      The refresh_token obtained from the exchange step. Once you have used this token to refresh the access_token, it is no longer valid.

      A new refresh_token will be issued upon successful token refresh.

      When using grant_type=refresh_token, this endpoint accepts any patient authorization whose refresh_token is still valid (within refresh_expires_in, default 90 days) and whose consent has not been revoked. This applies to both ONE_TIME and MULTIPLE usage authorizations — refreshability is determined by the validity of the refresh_token, not by the usage value. Request the offline_access scope to create a MULTIPLE usage authorization. See patient authorization usage for more information.

      When using grant_type=client_credentials, you must use live mode API keys. Application access tokens cannot be created using test mode keys.

      Response fields

      access_tokenstring

      The access_token to be used to make Flexpa API requests.

      For Patient Access Tokens, this is associated with a specific patient. For Application Access Tokens, this is associated with your application only.

      expires_innumber

      expires_in is the time (in seconds) for which the access_token is valid.

      For Patient Access Tokens: 86400 seconds (24 hours). For Application Access Tokens: 1800 seconds (30 minutes).

      token_typestring

      The type of token, always Bearer.

      refresh_tokenstring

      The refresh_token to be used to refresh the Patient Access Token.

      Returned on every successful authorization_code exchange and on every successful grant_type=refresh_token refresh, regardless of the requested scopes or the usage of the authorization. Refreshing returns a new access_token valid for another 24 hours; it does not itself sync fresh patient data from the endpoint — that requires MULTIPLE usage via the offline_access scope.

      refresh_expires_innumber

      Returned alongside refresh_token for the authorization_code and refresh_token grants.

      The time period for which the refresh_token is valid. You must call this route to refresh the Patient Access Token before this time period elapses otherwise, the patient will need to re-authorize.

      Defaults to 7776000 seconds (90 days).

      Refresh Token Grant Request (Confidential Client)

      POST
      /oauth/token
      PUBLIC_KEY=pk_test...
      SECRET_KEY=sk_test...
      REFRESH_TOKEN=flexpa-refresh-token
      
      # Base64 encode the credentials
      CREDENTIALS=$(echo -n "${PUBLIC_KEY}:${SECRET_KEY}" | base64)
      
      curl -X POST https://api.flexpa.com/oauth/token \
        -H "Authorization: Basic ${CREDENTIALS}" \
        -H "Content-Type: application/json" \
        -d '{
          "grant_type": "refresh_token",
          "refresh_token": "'"${REFRESH_TOKEN}"'"
        }'
      

      Refresh Token Grant Request (Public Client)

      POST
      /oauth/token
      PUBLIC_KEY=pk_test...
      REFRESH_TOKEN=flexpa-refresh-token
      
      curl -X POST https://api.flexpa.com/oauth/token \
        -H "Content-Type: application/json" \
        -d '{
          "grant_type": "refresh_token",
          "refresh_token": "'"${REFRESH_TOKEN}"'",
          "client_id": "'"${PUBLIC_KEY}"'"
        }'
      

      Client Credentials Grant Request

      POST
      /oauth/token
      PUBLIC_KEY=pk_live...
      SECRET_KEY=sk_live...
      
      # Base64 encode the credentials
      CREDENTIALS=$(echo -n "${PUBLIC_KEY}:${SECRET_KEY}" | base64)
      
      curl -X POST https://api.flexpa.com/oauth/token \
        -H "Authorization: Basic ${CREDENTIALS}" \
        -H "Content-Type: application/json" \
        -d '{
          "grant_type": "client_credentials"
        }'
      

      GEThttps://api.flexpa.com/oauth/introspect

      #Introspect

      Returns information about an access token, including the consent and endpoint details.

      Request headers

      AuthorizationstringRequired

      An Authorization: Bearer header value must be presented with a Patient Access Token

      Response fields

      jtistring

      A random nonce value that uniquely identifies the access token.

      iatnumber

      When the access token was issued in Unix time (in seconds).

      expnumber

      When the access token expires in Unix time (in seconds).

      activeboolean

      A boolean that states whether the access token can be used.

      substring

      A unique identifier for the consent backing this access token.

      patientstring

      The Patient ID connected to the authorization (formatted as Patient/<patient_id>).

      endpointobject

      The Endpoint to which the patient authorized access, can be used to determine which health plan the patient connected.

      usageenum

      Either ONE_TIME or MULTIPLE, indicating the data access pattern.

      statestring

      The authorization lifecycle state of the consent. One of CREATED, AUTHORIZING, AUTHORIZED, EXCHANGED, ERRORED, ABANDONED, REVOKED, BOUNCED, or EXPIRED.

      client_idstring

      The identifier of the application (client) that the access token was issued to.

      userobject

      An object describing the end user, containing externalId — the external user identifier you supplied when launching the flow (if any).

      syncobject

      Data synchronization status, containing resourceTypes (a map of FHIR resource type to the number of records loaded), lastSyncedAt (when the most recent sync completed, in Unix seconds, or null), and state (the state of the most recent sync job).

      timeUntilMaxAuthnumber

      The number of seconds remaining until the consent reaches its maximum authorization window, or the string unsupported when the endpoint does not enforce a maximum authorization period.

      Request

      GET
      /oauth/introspect
      ACCESS_TOKEN=your-access-token
      
      curl https://api.flexpa.com/oauth/introspect \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      

      Response

      {
        "jti": "4e99f5ae-eb40-4161-9506-23b119e7136f",
        "iat": 1671116375,
        "exp": 1671202775,
        "active": true,
        "iss": "https://api.flexpa.com/",
        "aud": "https://api.flexpa.com/",
        "sub": "329034ef-5fa8-4a08-99d5-c389e22a2533",
        "client_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
        "patient": "Patient/33512af4-5ab2-4be2-90f5-3945cff01c1a",
        "user": {
          "externalId": "your-external-user-id"
        },
        "state": "EXCHANGED",
        "usage": "MULTIPLE",
        "endpoint": {
          "id": "d39433b7-0fbd-4bc2-bdae-fb276799979f",
          "label": ["Humana"],
          "name": "humana-sandbox",
          "refreshable": true
        },
        "sync": {
          "resourceTypes": { "ExplanationOfBenefit": 42, "Coverage": 1 },
          "lastSyncedAt": 1671116300,
          "state": "COMPLETED"
        },
        "timeUntilMaxAuth": "unsupported"
      }
      

      POSThttps://api.flexpa.com/oauth/revoke

      #Revoke

      Revokes a consent and expunges the patient's data from Flexpa's cache. This invalidates both the access_token and refresh_token.

      Request headers

      AuthorizationstringRequired

      An Authorization: Bearer header value must be presented with a Patient Access Token

      Request fields

      secret_keystringRequired

      Your Flexpa API secret key

      Response fields

      countnumber

      The number of patient authorizations revoked on the consent

      Request

      POST
      /oauth/revoke
      ACCESS_TOKEN=your-access-token
      
      curl -X POST https://api.flexpa.com/oauth/revoke \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -d '{
          "secret_key": "sk_test..."
        }'
      

      Response

      {
        "count": 1
      }
      

      #REST API


      GEThttps://api.flexpa.com/rest/consent

      #Consent

      Retrieve information about a consent record and its associated patient authorizations, including sync status and SMART scope information.

      Endpoints:

      • GET /rest/consent - Returns the consent associated with the authenticated token
      • GET /rest/consent/:consentId - Returns a specific consent by ID

      Request headers

      AuthorizationstringRequired

      An Authorization: Bearer header value must be presented. The accepted token type depends on the endpoint:

      • GET /rest/consent (no consentId) accepts only a Patient Access Token. Presenting an Application Access Token returns 401 Unauthorized with the message A Consent access token is required for this endpoint.
      • GET /rest/consent/:consentId accepts either a Patient Access Token or an Application Access Token.

      Query parameters

      skipResourceCountsboolean

      When set to true, omits sync.resourceTypes from each patient authorization in the response and skips the FHIR resource count lookup. Useful when you only need consent state, sync status, or scope information and want a faster response. Defaults to false.

      Response fields

      idstring

      The unique identifier for the consent record.

      statestring

      The consent state: CREATED, AUTHORIZED, EXCHANGED, ABANDONED, or REVOKED.

      modestring

      The operational mode: TEST or LIVE.

      createdAtstring

      When the consent was created, in ISO 8601 format.

      updatedAtstring | null

      When the consent was last updated, in ISO 8601 format.

      userobject | null

      The application user associated with this consent, set via flexpa_external_id during authorization.

      externalIdstring

      Your application's user identifier, provided as flexpa_external_id during authorization.

      patientAuthorizationsarray

      Patient authorizations associated with this consent.

      idstring

      Unique identifier for the patient authorization.

      typestring

      How the patient connected. OAUTH for SMART on FHIR authorization (health insurers and medical record systems), CREDENTIALS for patient portal authorization, IAL2 for identity verification via nationwide exchanges (TEFCA IAS), or IAL1 for TEFCA search-flow authorizations using self-attested demographics. See network types.

      statestring

      Authorization state: CREATED, AUTHORIZED, EXCHANGED, ERRORED, ABANDONED, REVOKED, AUTHORIZING, BOUNCED, or EXPIRED.

      usagestring

      Data access pattern: ONE_TIME or MULTIPLE.

      createdAtstring

      When the authorization was created (ISO 8601).

      authorizedAtstring | null

      When the patient completed authorization (ISO 8601).

      exchangedAtstring | null

      When the authorization code was exchanged for tokens (ISO 8601).

      revokedAtstring | null

      When the authorization was revoked (ISO 8601).

      expiredAtstring | null

      When the authorization expired (ISO 8601).

      patientstring | null

      FHIR Patient reference (formatted as Patient/<id>).

      identityProviderstring | null

      Identity verification provider used for IAL2 authorizations: CLEAR, IDME, or PERSONA. Only present for IAL2 types.

      endpointobject | null

      The health plan endpoint the patient connected to.

      idstring

      Unique identifier for the endpoint.

      namestring

      Machine-readable endpoint name.

      labelstring[]

      Human-readable display labels for the endpoint.

      refreshableboolean

      Whether this endpoint supports token refresh for MULTIPLE usage.

      activeboolean

      Whether the authorization is currently usable (exchanged, not revoked or expired).

      timeUntilMaxAuthnumber | string

      Seconds remaining until the max authorization period expires, for refreshable OAUTH/IAL1 authorizations (may be negative once past the window). Otherwise one of: "unsupported" (e.g. IAL2/CREDENTIALS types or non-refreshable endpoints), "unknown" (endpoint max auth period not known), or "indefinite" (no expiry).

      syncobject

      Current data synchronization status.

      resourceTypesobject

      Map of FHIR resource type names to the count of resources synced (e.g. {"ExplanationOfBenefit": 42}). Omitted when skipResourceCounts=true.

      lastSyncedAtnumber | null

      Unix timestamp (seconds) of the last completed sync.

      statestring

      Sync job state: CREATED, WAITING, ACTIVE, COMPLETED, or FAILED. Omitted when no sync job has run yet.

      scopeobject

      SMART on FHIR scope information. Present when scope data is available.

      requestedstring[]

      Scopes requested during authorization.

      grantedstring[]

      Scopes granted by the endpoint.

      rejectedstring[]

      Scopes that were requested but not granted.

      Request

      GET
      /rest/consent
      ACCESS_TOKEN=your-access-token
      
      curl https://api.flexpa.com/rest/consent \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      

      Response

      {
        "id": "01234567-89ab-cdef-0123-456789abcdef",
        "state": "EXCHANGED",
        "mode": "LIVE",
        "createdAt": "2024-01-15T10:30:00.000Z",
        "updatedAt": null,
        "user": { "externalId": "user-123" },
        "patientAuthorizations": [
          {
            "id": "98765432-10fe-dcba-9876-543210fedcba",
            "type": "IAL2",
            "state": "EXCHANGED",
            "usage": "ONE_TIME",
            "createdAt": "2024-01-15T10:31:00.000Z",
            "authorizedAt": "2024-01-15T10:31:30.000Z",
            "exchangedAt": "2024-01-15T10:32:00.000Z",
            "revokedAt": null,
            "expiredAt": null,
            "patient": "Patient/33512af4-5ab2-4be2-90f5-3945cff01c1a",
            "identityProvider": "CLEAR",
            "endpoint": null,
            "active": true,
            "timeUntilMaxAuth": "unsupported",
            "sync": {
              "resourceTypes": { "Condition": 12, "Patient": 1 },
              "lastSyncedAt": 1705312200,
              "state": "COMPLETED"
            }
          },
          {
            "id": "abcdef01-2345-6789-abcd-ef0123456789",
            "type": "OAUTH",
            "state": "EXCHANGED",
            "usage": "ONE_TIME",
            "createdAt": "2024-01-15T10:33:00.000Z",
            "authorizedAt": "2024-01-15T10:33:30.000Z",
            "exchangedAt": "2024-01-15T10:34:00.000Z",
            "revokedAt": null,
            "expiredAt": null,
            "patient": "Patient/33512af4-5ab2-4be2-90f5-3945cff01c1a",
            "endpoint": {
              "id": "d39433b7-0fbd-4bc2-bdae-fb276799979f",
              "name": "epic-mychart",
              "label": ["Epic MyChart"],
              "refreshable": true
            },
            "active": true,
            "timeUntilMaxAuth": 86400,
            "sync": {
              "resourceTypes": { "ExplanationOfBenefit": 42, "Patient": 1 },
              "lastSyncedAt": 1705312200,
              "state": "COMPLETED"
            },
            "scope": {
              "requested": ["patient/*.read", "offline_access"],
              "granted": ["patient/Patient.read", "patient/Coverage.read"],
              "rejected": ["patient/Observation.read"]
            }
          }
        ]
      }
      

      GEThttps://api.flexpa.com/rest/consents

      #List consents

      List all consents for your application with cursor-based pagination. Requires an Application Access Token.

      Each consent in the list includes summary patient authorization data (state, endpoint, active status) but omits the detailed sync and scope fields returned by Consent. To get full details for a specific consent, use GET /rest/consent/:consentId.

      Request headers

      AuthorizationstringRequired

      An Authorization: Bearer header value must be presented with an Application Access Token.

      Query parameters

      limitnumber

      Maximum number of consents per page. Defaults to 20, maximum 100.

      cursorstring

      Pagination cursor from a previous response's meta.nextCursor. Omit for the first page.

      statestring

      Filter by consent state: CREATED, AUTHORIZED, EXCHANGED, ABANDONED, or REVOKED.

      modestring

      Filter by operational mode: TEST or LIVE.

      Response fields

      consentsarray

      An array of consent objects. Unlike Consent, the patientAuthorizations here are summaries — sync and scope are omitted. Use GET /rest/consent/:consentId for full details.

      idstring

      The unique identifier for the consent record.

      statestring

      The consent state: CREATED, AUTHORIZED, EXCHANGED, ABANDONED, or REVOKED.

      modestring

      The operational mode: TEST or LIVE.

      createdAtstring

      When the consent was created, in ISO 8601 format.

      updatedAtstring | null

      When the consent was last updated, in ISO 8601 format.

      userobject | null

      The application user associated with this consent.

      externalIdstring

      Your application's user identifier, provided as flexpa_external_id during authorization.

      patientAuthorizationsarray

      Summary patient authorizations (no sync or scope).

      idstring

      Unique identifier for the patient authorization.

      typestring

      How the patient connected. OAUTH for SMART on FHIR authorization (health insurers and medical record systems), CREDENTIALS for patient portal authorization, IAL2 for identity verification via nationwide exchanges (TEFCA IAS), or IAL1 for TEFCA search-flow authorizations using self-attested demographics. See network types.

      statestring

      Authorization state: CREATED, AUTHORIZED, EXCHANGED, ERRORED, ABANDONED, REVOKED, AUTHORIZING, BOUNCED, or EXPIRED.

      usagestring

      Data access pattern: ONE_TIME or MULTIPLE.

      createdAtstring

      When the authorization was created (ISO 8601).

      authorizedAtstring | null

      When the patient completed authorization (ISO 8601).

      exchangedAtstring | null

      When the authorization code was exchanged for tokens (ISO 8601).

      revokedAtstring | null

      When the authorization was revoked (ISO 8601).

      expiredAtstring | null

      When the authorization expired (ISO 8601).

      patientstring | null

      FHIR Patient reference (formatted as Patient/<id>).

      identityProviderstring | null

      Identity verification provider. Only present for IAL2 authorizations: CLEAR, IDME, or PERSONA.

      endpointobject | null

      The health plan endpoint the patient connected to.

      idstring

      Unique identifier for the endpoint.

      namestring

      Machine-readable endpoint name.

      labelstring[]

      Human-readable display labels for the endpoint.

      refreshableboolean

      Whether this endpoint supports token refresh for MULTIPLE usage.

      activeboolean

      Whether the authorization is currently usable (exchanged, not revoked or expired).

      timeUntilMaxAuthnumber | string

      Seconds remaining until the max authorization period expires, for refreshable OAUTH/IAL1 authorizations (may be negative once past the window). Otherwise one of: "unsupported" (e.g. IAL2/CREDENTIALS types or non-refreshable endpoints), "unknown" (endpoint max auth period not known), or "indefinite" (no expiry).

      metaobject

      Pagination metadata.

      hasMoreboolean

      Whether more results are available beyond this page.

      nextCursorstring | null

      Cursor to pass as the cursor query parameter for the next page. Null when there are no more results.

      Request

      GET
      /rest/consents
      ACCESS_TOKEN=your-application-access-token
      
      # List all consents
      curl "https://api.flexpa.com/rest/consents?limit=20" \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      
      # Filter by state and mode
      curl "https://api.flexpa.com/rest/consents?state=EXCHANGED&mode=LIVE" \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      

      Response

      {
        "consents": [
          {
            "id": "01234567-89ab-cdef-0123-456789abcdef",
            "state": "EXCHANGED",
            "mode": "LIVE",
            "createdAt": "2024-01-15T10:30:00.000Z",
            "updatedAt": null,
                "user": { "externalId": "user-123" },
            "patientAuthorizations": [
              {
                "id": "98765432-10fe-dcba-9876-543210fedcba",
                "type": "OAUTH",
                "state": "EXCHANGED",
                "usage": "ONE_TIME",
                "createdAt": "2024-01-15T10:31:00.000Z",
                "authorizedAt": "2024-01-15T10:31:30.000Z",
                "exchangedAt": "2024-01-15T10:32:00.000Z",
                "revokedAt": null,
                "expiredAt": null,
                "patient": "Patient/33512af4-5ab2-4be2-90f5-3945cff01c1a",
                "endpoint": {
                  "id": "d39433b7-0fbd-4bc2-bdae-fb276799979f",
                  "name": "humana-sandbox",
                  "label": ["Humana"],
                  "refreshable": true
                },
                "active": true,
                "timeUntilMaxAuth": 86400
              }
            ]
          }
        ],
        "meta": {
          "hasMore": true,
          "nextCursor": "eyJjcmVhdGVkQXQiOi..."
        }
      }
      

      #Discovery API

      Standard discovery endpoints for OAuth 2.0 and SMART on FHIR clients to automatically discover authorization server capabilities.

      For automated clients, prefer the discovery metadata for the endpoints and auth methods you use. These values are authoritative and may differ from examples elsewhere in the docs.


      GEThttps://api.flexpa.com/.well-known/oauth-authorization-server

      #OAuth Authorization Server Metadata

      Returns OAuth 2.0 Authorization Server Metadata per RFC 8414. Use this endpoint for standard OAuth 2.0 clients and MCP integrations.

      Response fields

      issuerstring

      The authorization server's issuer identifier.

      authorization_endpointstring

      URL of the authorization endpoint.

      token_endpointstring

      URL of the token endpoint.

      registration_endpointstring

      URL of the dynamic client registration endpoint.

      jwks_uristring

      URL of the JSON Web Key Set.

      response_modes_supportedstring[]

      Supported response modes: query.

      grant_types_supportedstring[]

      Supported grant types: authorization_code, refresh_token, client_credentials.

      code_challenge_methods_supportedstring[]

      Supported PKCE methods: S256.

      token_endpoint_auth_methods_supportedstring[]

      Supported token endpoint authentication methods: client_secret_basic, none.

      scopes_supportedstring[]

      Supported scopes: launch/patient, offline_access.

      Request

      GET
      /.well-known/oauth-authorization-server
      curl https://api.flexpa.com/.well-known/oauth-authorization-server
      

      Response

      {
        "issuer": "https://api.flexpa.com",
        "authorization_endpoint": "https://api.flexpa.com/oauth/authorize",
        "token_endpoint": "https://api.flexpa.com/oauth/token",
        "registration_endpoint": "https://api.flexpa.com/oauth/register",
        "jwks_uri": "https://api.flexpa.com/.well-known/jwks.json",
        "response_types_supported": ["code"],
        "response_modes_supported": ["query"],
        "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
        "code_challenge_methods_supported": ["S256"],
        "token_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
        "scopes_supported": ["launch/patient", "offline_access"]
      }
      

      GEThttps://api.flexpa.com/.well-known/openid-configuration

      #OpenID Configuration

      Returns OpenID Connect Discovery metadata per OpenID Connect Core 1.0. Use this endpoint for OIDC-compatible clients.

      Response fields

      issuerstring

      The authorization server's issuer identifier.

      authorization_endpointstring

      URL of the authorization endpoint.

      token_endpointstring

      URL of the token endpoint.

      registration_endpointstring

      URL of the dynamic client registration endpoint.

      jwks_uristring

      URL of the JSON Web Key Set.

      response_types_supportedstring[]

      Supported response types: code.

      response_modes_supportedstring[]

      Supported response modes: query.

      grant_types_supportedstring[]

      Supported grant types: authorization_code, refresh_token, client_credentials.

      code_challenge_methods_supportedstring[]

      Supported PKCE methods: S256.

      token_endpoint_auth_methods_supportedstring[]

      Supported authentication methods: client_secret_basic, none.

      scopes_supportedstring[]

      Supported scopes: launch/patient, offline_access.

      subject_types_supportedstring[]

      Supported subject types: public.

      id_token_signing_alg_values_supportedstring[]

      Supported ID token signing algorithms: RS256.

      service_documentationstring

      URL to service documentation.

      Request

      GET
      /.well-known/openid-configuration
      curl https://api.flexpa.com/.well-known/openid-configuration
      

      Response

      {
        "issuer": "https://api.flexpa.com",
        "authorization_endpoint": "https://api.flexpa.com/oauth/authorize",
        "token_endpoint": "https://api.flexpa.com/oauth/token",
        "registration_endpoint": "https://api.flexpa.com/oauth/register",
        "jwks_uri": "https://api.flexpa.com/.well-known/jwks.json",
        "response_types_supported": ["code"],
        "response_modes_supported": ["query"],
        "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
        "code_challenge_methods_supported": ["S256"],
        "token_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
        "scopes_supported": ["launch/patient", "offline_access"],
        "subject_types_supported": ["public"],
        "id_token_signing_alg_values_supported": ["RS256"],
        "service_documentation": "https://flexpa.com/docs"
      }
      

      GEThttps://api.flexpa.com/.well-known/smart-configuration

      #SMART Configuration

      Returns SMART App Launch configuration per the SMART App Launch specification. Use this endpoint for SMART on FHIR applications.

      Response fields

      issuerstring

      The authorization server's issuer identifier.

      authorization_endpointstring

      URL of the authorization endpoint.

      token_endpointstring

      URL of the token endpoint.

      jwks_uristring

      URL of the JSON Web Key Set.

      grant_types_supportedstring[]

      Supported grant types: authorization_code, refresh_token, client_credentials.

      response_types_supportedstring[]

      Supported response types: code.

      scopes_supportedstring[]

      Supported scopes: launch/patient, offline_access.

      code_challenge_methods_supportedstring[]

      Supported PKCE methods: S256.

      token_endpoint_auth_methods_supportedstring[]

      Supported authentication methods: client_secret_basic, none.

      capabilitiesstring[]

      SMART capabilities: launch-standalone, client-public, client-confidential-symmetric, context-standalone-patient, permission-offline, permission-patient.

      Request

      GET
      /.well-known/smart-configuration
      curl https://api.flexpa.com/.well-known/smart-configuration
      

      Response

      {
        "issuer": "https://api.flexpa.com",
        "authorization_endpoint": "https://api.flexpa.com/link/auth",
        "token_endpoint": "https://api.flexpa.com/link/token",
        "jwks_uri": "https://api.flexpa.com/.well-known/jwks.json",
        "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
        "response_types_supported": ["code"],
        "scopes_supported": ["launch/patient", "offline_access"],
        "code_challenge_methods_supported": ["S256"],
        "token_endpoint_auth_methods_supported": ["client_secret_basic", "none"],
        "capabilities": [
          "launch-standalone",
          "client-public",
          "client-confidential-symmetric",
          "context-standalone-patient",
          "permission-offline",
          "permission-patient"
        ]
      }
      

      GEThttps://api.flexpa.com/.well-known/jwks.json

      #JWKS

      Returns the JSON Web Key Set per RFC 7517. Use this endpoint to verify token signatures.

      Request

      GET
      /.well-known/jwks.json
      curl https://api.flexpa.com/.well-known/jwks.json
      

      #Mobile applications

      Consent is a standard OAuth 2.0 PKCE flow, so it works out of the box with the native authentication session APIs that iOS, Android, and React Native already ship: ASWebAuthenticationSession on iOS and Chrome Custom Tabs on Android. This is the mechanism Apple and Google define for authenticating with a third party, and it is what patients already recognize from other apps they log into.

      Building on the OS-native session, rather than rendering the flow inside your app, is what makes the integration strong:

      • Works out of the box. Consent is built to the OS standards for authentication, so you call the API the platform already gives you. There is no Flexpa SDK to install, no versions to keep in step with our releases, no native module to configure, and no WebView shim to maintain.
      • Password manager autofill. The session runs in the system browser context, so the patient's saved credentials, passkeys, and biometrics work when they sign in with their health plan or provider. Payer login pages are the highest-friction step in the flow, and autofill removes most of that friction.
      • Shared browser session. If the patient is already signed in to their payer in Safari or Chrome, they may not have to sign in again.
      • Credentials stay out of your app. Your app never sees the patient's payer username and password, which keeps you out of scope for handling them.
      • Native return path. The OS hands the authorization code back to your app through your registered URL scheme and closes the session automatically.

      Never render the consent flow in an embedded WebView (WKWebView on iOS or WebView on Android). Embedded WebViews let the host app read the credentials the patient types, break password manager autofill, and are prohibited by OAuth 2.0 security best practices. Many payer login pages also block them.

      The examples below all send flexpa_resume=true. Patients on a phone are the most likely to leave mid-flow, whether they switch to their email app for a verification code or dismiss the session by accident. Resume lets them pick up where they left off on the next attempt instead of starting over. It relies on the consent session cookie, which the native session keeps because it shares cookie storage with the system browser.

      #iOS

      Use ASWebAuthenticationSession (iOS 12+), Apple's native authentication session API. It:

      • Opens a secure system browser context
      • Shares authentication state and autofill with Safari
      • Automatically handles the redirect callback and dismisses itself
      • Prevents credential interception by the host app

      Setup:

      1. Register your redirect URI scheme in Info.plist and in the Flexpa Portal
      2. Generate PKCE parameters before starting the session
      3. Store the code verifier in Keychain or memory
      4. Exchange the authorization code for tokens

      Leave prefersEphemeralWebBrowserSession set to false so the session can reuse the patient's existing Safari cookies and offer saved credentials.

      iOS Implementation

      import AuthenticationServices
      import CryptoKit
      
      class FlexpaAuth: NSObject, ASWebAuthenticationPresentationContextProviding {
        private var codeVerifier: String?
      
        func authenticate() {
          // Generate PKCE parameters
          codeVerifier = generateCodeVerifier()
          let codeChallenge = generateCodeChallenge(codeVerifier!)
      
          // Build authorization URL
          var components = URLComponents(string: "https://api.flexpa.com/oauth/authorize")!
          components.queryItems = [
            URLQueryItem(name: "client_id", value: "pk_test_..."),
            URLQueryItem(name: "redirect_uri", value: "yourapp://callback"),
            URLQueryItem(name: "response_type", value: "code"),
            URLQueryItem(name: "code_challenge", value: codeChallenge),
            URLQueryItem(name: "code_challenge_method", value: "S256"),
            URLQueryItem(name: "scope", value: "launch/patient offline_access"),
            URLQueryItem(name: "flexpa_external_id", value: "usr_1234"),
            URLQueryItem(name: "flexpa_search_mode", value: "true"),
            URLQueryItem(name: "flexpa_resume", value: "true"),
            URLQueryItem(name: "state", value: UUID().uuidString)
          ]
      
          let session = ASWebAuthenticationSession(
            url: components.url!,
            callbackURLScheme: "yourapp"
          ) { [weak self] callbackURL, error in
            guard let url = callbackURL,
                  let code = URLComponents(url: url, resolvingAgainstBaseURL: false)?
                    .queryItems?.first(where: { $0.name == "code" })?.value
            else { return }
      
            self?.exchangeCode(code)
          }
      
          session.presentationContextProvider = self
          session.prefersEphemeralWebBrowserSession = false
          session.start()
        }
      
        func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
          UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first { $0.isKeyWindow }!
        }
      }
      

      #Android

      Use Chrome Custom Tabs, Android's native in-app browser session. It:

      • Opens a Chrome tab within your app's task
      • Shares cookies, saved passwords, and autofill with Chrome
      • Provides a native-feeling UI
      • Prevents credential interception

      Setup:

      1. Add an intent filter for your redirect URI in AndroidManifest.xml, and register the same URI in the Flexpa Portal
      2. Generate PKCE parameters before launching Custom Tabs
      3. Store the code verifier in EncryptedSharedPreferences
      4. Handle the redirect in your callback Activity

      AppAuth for Android wraps this pattern, including PKCE generation and the token exchange. Point its authorizationEndpoint and tokenEndpoint at Flexpa and pass the Flexpa parameters as additional query parameters.

      Android Implementation

      <activity
        android:name=".OAuthCallbackActivity"
        android:exported="true"
        android:launchMode="singleTask">
        <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data
            android:scheme="yourapp"
            android:host="callback" />
        </intent-filter>
      </activity>
      

      #React Native

      React Native reaches the same native authentication sessions as the platforms above. expo-auth-session calls ASWebAuthenticationSession on iOS and Chrome Custom Tabs on Android, generates the PKCE verifier and challenge, and returns the authorization code to your component. You point it at the Flexpa endpoints the way you would any other OAuth provider, and the patient gets the same password manager autofill and shared browser session a fully native app would give them.

      Setup:

      1. Install expo-auth-session and expo-web-browser
      2. Set a scheme in app.config.js (for example flexpaexample) and register the resulting redirect URI in the Flexpa Portal
      3. Call WebBrowser.maybeCompleteAuthSession() at module scope so the session closes when the app is reopened by the deep link
      4. Exchange the authorization code for tokens, sending client_id in the body since a mobile app is a public client

      Bare React Native apps can use react-native-app-auth instead. It wraps the same two native APIs and takes the same parameters, under the names redirectUrl and additionalParameters.

      makeRedirectUri returns a custom-scheme URI built from the scheme in your app config, such as flexpaexample://. That is the value to register in the Portal, and it is available at runtime as request.redirectUri.

      Store the access and refresh tokens in the Keychain or Keystore, for example with expo-secure-store. Never put them in AsyncStorage, which is unencrypted on disk.

      React Native Implementation

      import * as WebBrowser from 'expo-web-browser';
      import { makeRedirectUri, useAuthRequest, CodeChallengeMethod } from 'expo-auth-session';
      
      // Closes the auth session when the app is reopened by the redirect
      WebBrowser.maybeCompleteAuthSession();
      
      const discovery = {
        authorizationEndpoint: 'https://api.flexpa.com/oauth/authorize',
        tokenEndpoint: 'https://api.flexpa.com/oauth/token',
      };
      
      export function useFlexpaAuth(externalId: string) {
        const redirectUri = makeRedirectUri({ scheme: 'flexpaexample' });
      
        const [request, response, promptAsync] = useAuthRequest(
          {
            clientId: 'pk_test_...',
            responseType: 'code',
            redirectUri,
            scopes: ['launch/patient', 'offline_access'],
            usePKCE: true,
            codeChallengeMethod: CodeChallengeMethod.S256,
            extraParams: {
              flexpa_external_id: externalId,
              flexpa_search_mode: 'true',
              flexpa_resume: 'true',
            },
          },
          discovery
        );
      
        return {
          request,
          response,
          promptAsync,
          redirectUri,
          codeVerifier: request?.codeVerifier,
          isReady: !!request,
        };
      }
      

      #Workflow Ideas

      Here are different techniques for embedding Consent into your application to ensure participation of new users and activate existing users:

      #Onboarding

      Incorporate the consent flow directly into your new user onboarding experience. New users have higher engagement as completion of enrollment is a compelling event to complete the authorization.

      As users sign up and learn to navigate your application, prompt them to link their payer account through Flexpa. This could be presented as an initial setup step, or as a highlighted feature in a tutorial. Make sure to explain the benefits and reassure the user about the security of the process.

      #Integration

      Embed the consent trigger as an activity or option within your existing patient interface. This could be as a button, a banner, or a new tab in a user's profile settings. Explain the value of linking their payer account and gently prompt users to do so.

      #Personalization

      On the user's profile or dashboard, you can include a personalized status or progress bar indicating the completion of their profile setup, including whether they've linked their payer account. This visual cue can motivate users to complete their setup.

      #In-app notifications

      Use notifications in your application to remind users to link their payer accounts. These can be triggered based on certain user behaviors, like logging in or navigating to certain sections. Make sure these reminders are not too intrusive and clearly communicate the value.

      #Campaigns

      Send targeted marketing campaigns to encourage users to link their payer accounts:

      • Email: Craft an informative email explaining the benefits and including a direct link
      • SMS: Send a short, compelling message with a link to your application

      #Incentivization

      Consider offering small incentives or rewards for users who link their payer account. This could be in the form of discounts, access to premium features, or other value-added benefits.


      #Best practices

      Below are some principles we recommend to optimize user conversion and ensure users complete the authorization flow.

      #Explain the benefit

      Your UI should tell the user why they want to use Flexpa and the value they get from linking their payer. For example, linking their payer account might save them time inputting medication data manually or enable more relevant recommendations.

      #Set expectations

      Before starting the flow, explain to the user that they'll be redirected to authorize with their health plan. Explain that they'll need to input their username and password, but also that they can create an account with their payer if they do not have one. Explain what data your app collects and why it's needed.

      #Present as default

      Rather than presenting Flexpa and manual flows as equal alternatives, encourage your customers to use Flexpa through the size, positioning, and color of the consent entry point. You can also use labels such as "Recommended" or "Preferred".

      #Security

      Let customers know that the consent flow is secure and uses industry-standard OAuth 2.0 with PKCE. Explain how patients can control their data and remove connectivity through Connections.

      #Polish your flow

      A consent hosting flow that is aesthetically engaging, polished, and reflects your brand conveys the legitimacy and importance of linking an account.

      #Allow for multiple links

      Patients sometimes have multiple payer accounts (primary and secondary insurance, or current and prior insurance). To capture all of this information, allow patients to link multiple accounts to your app.


      #Migration from v1

      If you are migrating from the legacy FlexpaLink SDK, see the v1 documentation for the previous implementation details and migration guide.

      #Key changes

      FlexpaLink (v1)OAuth PKCE
      <script> tag installationnpm package or raw HTTP
      FlexpaLink.create() + open()Build URL and redirect
      public_token via callbackcode via redirect URL
      POST /link/exchangePOST /oauth/token with PKCE
      iframe/popupFull page redirect

      #Troubleshooting

      Invalid code_challenge

      The code_challenge must be a base64url-encoded SHA256 hash (43 characters, no padding). Verify you're using SHA256 hashing and correct base64url encoding.

      Invalid redirect_uri

      The redirect_uri must exactly match a URI registered in the Flexpa Portal including protocol, domain, and path. Check for typos and ensure the URI is registered.

      Token exchange fails

      Common causes:

      • Authorization code expired (30 minute lifetime)
      • Code already used (single-use only)
      • redirect_uri doesn't match the authorization request
      • client_id (publishable key) doesn't match

      For additional help, contact support@flexpa.com with the X-Request-Id response header from the failed request.


      #Next steps

      Quickstart

      Get started with a cloneable quickstart project

      Try it out →

      Flexpa API

      Use Flexpa API as a unified API to access Explanation of Benefits and more

      Build with Flexpa API →
      Status TwitterGitHub

      © 2026 Flexpa. All rights reserved.

      FHIR® is the registered trademark of Health Level Seven International and its use does not constitute endorsement by HL7.