Join Now

AllClear Partner API Documentation

Which API should I use?

Connection API

Act for one user at a time, on the OAuth token they grant. Self-serve, no keys.

Partner API

Reach all your connections from your backend. Partner-signed tokens over mTLS. Available today through a partner agreement.

Compare them in detail

Who does it act for?

One user at a time

All of your connections

How do you call it?

MCP tools today; REST on the user’s token is coming

REST endpoints

What do you use to authenticate?

An OAuth token the user granted

A partner-signed token over mTLS

How do you get set up?

Self-serve: your app registers itself, no keys

A partner agreement, then a tenant set up with us: signing keys, source-IP ranges

What you can reach. “Coming” in the Connection API column means calling it with a user’s OAuth token. To reach document downloads and connection events today, use the Partner API.

Records

✓

✓

Document downloads

Coming

✓

Connection events

Coming

✓

Messaging

Coming

Coming

This page covers the Partner API. For the Connection API, see the developer docs.

Integration guide for the AllClear Partner APIs: real-time event notifications, patient data, and health record access. This is the canonical source; Confluence links here rather than duplicating content.

A machine-readable OpenAPI 3.1 spec for the request/response endpoints is in openapi.yaml.

Getting Started

All Partner APIs are served from a single base URL:

Text
https://partner.healthbankone.com

The integration follows one flow: you register a tenant with us (Configuration), we notify your endpoint when events occur (Events API), and you call back to fetch the full data (Patient API and Patient Records API). Every request you make to us carries a signed JWT (Authentication); production tenants must also use mTLS and call from allowlisted IPs (Security and mTLS).

Sandbox vs production

Production and sandbox are distinguished by your client ID, not by a separate host, both use the same base URL above.

  • A sandbox-* client is for testing: it is unrestricted (no mTLS or source-IP requirements) and may connect with or without a client certificate.

  • A prod-* client is your live tenant: mTLS public-key pinning and the source-IP allowlist are enforced.

Build against your sandbox client first, then switch to your production client for go-live.

How a connection is created

Everything you receive is scoped to a connection: a link between one AllClear user and you (the relying party), authorized by that user. A connection is created in one of two ways:

  • The user connects to you. The user finds you in AllClear and authorizes the connection.

  • You invite the user. You send an invitation and the user accepts it.

The user’s consent at connection time determines which events we send you and which scopes you hold (for example whether record.added events and the Patient Records API are available, see records:read). A user can withdraw consent at any time, which disconnects the connection and stops further events.

Quickstart

The shortest path to a working end-to-end integration:

  1. Configure your sandbox client. Submit your configuration (endpoint URL, mTLS/JWT public keys). We return your sandbox-* client ID.

  2. Stand up your notification endpoint. Accept POST, verify the notification JWT, durably store the notification, and only then return 200 within 5 seconds.

  3. Connect a test user to your sandbox client (see Testing in sandbox) so events are generated.

  4. Receive a notification and, asynchronously, call GET /events/{id} with a Client Access Token to fetch the full event.

  5. Follow the event. For a connection.connected with records:read, list the patient’s records with GET /patients/{id}/records and download one via GET /patients/{id}/records/{recordId}.

  6. Go live. Repeat against your prod-* client, this time over mTLS from an allowlisted IP (Security and mTLS).

Testing in sandbox
  • Patient and Records APIs return mock data for any ID in sandbox, so you can develop against them without a real connection.

  • Events are real for sandbox: to exercise the notification flow, connect a test user to your sandbox client (the same way a connection is created in production, see How a connection is created). Connecting, updating, or disconnecting that user, and records arriving for them, generate the corresponding events. Only events can return 404 in sandbox, if you request an event ID that was never issued.

Sandbox exposes no real data. Everything you receive in sandbox is synthetic. You will never see real PII, and no identifier in a sandbox payload maps back to a real patient. Real events are anonymized before they reach a sandbox client: the demographic fields are replaced with fixed placeholder values, and every real patient and record ID (including inside portal_link) is swapped for a synthetic one. The synthetic IDs are deterministic and easy to recognize:

Identifier

Sandbox value

Patient ID

00000000-0000-8000-face-000000000001

Record ID

00000000-0000-8000-f11e-0000000000NN (NN = 1-based record index)

Because they are fixed, do not treat a sandbox ID as unique per user; use sandbox only to validate your integration mechanics, not to model real data volumes.

Configuration

To start using the Partner APIs, provide your configuration details and cryptographic keys in a structured JSON format.

Required Parameters

General

Name

Type

Format

Required

Description

client_id

string

Max length: 256 chars

Always

Unique identifier for your tenant. Must start with prod- (production) or sandbox- (non-production).

Outbound, We Call You (Events API only)

Name

Type

Format

Required

Description

outbound.endpoints

array

—

Always

Publicly accessible HTTPS endpoints where we send event notifications.

outbound.endpoints[].url

string

uri, max length: 2048

Always

The HTTPS URL for event notifications.

Inbound, You Call Us

Name

Type

Format

Required

Description

inbound.jwt_sig_keys

object

JWKS

Always

Your JWT public keys in JWKS format. We use these to verify API requests you sign. RSA keys must be at least 3072 bits long.

inbound.allowed_cidrs

array

IPv4/IPv6 CIDRs

Production only

The CIDR ranges you send API requests from (a single host is /32 for IPv4, /128 for IPv6). We only accept requests from these ranges.

inbound.mtls_client_keys

array

base64 strings

Production only

Base64-encoded DER public key from your mTLS client certificate. Used for mutual TLS authentication. RSA keys must be at least 3072 bits long.

Public Keys

You need two distinct public keys:

Key

Required

Used For

mTLS Client Key

Production tenants

Authenticating your mTLS connections when you call our API.

JWT Signing Key

Always

Verifying JWTs you sign when making API requests.

Caution: Do NOT include private keys. Each key must be unique.

Generating Keys

Create a Private Key

Shell
openssl genpkey -algorithm RSA -out private.pem -pkeyopt rsa_keygen_bits:3072

Extract Your mTLS Client Certificate

Shell
openssl x509 -in your_mtls_client_cert.pem -pubkey -noout | openssl rsa -pubin -outform DER | base64

Use this in inbound.mtls_client_keys.

Convert JWT Private Key to JWKS

JavaScript
import { createPublicKey } from "crypto";import fs from "fs";​const pemKey = fs.readFileSync("privateKey.pem", "utf8");const publicKey = createPublicKey(pemKey);const jwk = publicKey.export({ format: "jwk" });​jwk.kid = '<clientid>-sig-rs256-20250820';jwk.alg = 'RS256';jwk.use = 'sig';​console.log(JSON.stringify({ keys: [jwk] }, null, 2));

Use this output in inbound.jwt_sig_keys.

Full Configuration Example

Once you have extracted all public keys, structure them as follows and send this payload to us:

With Events API

JSON
{  "client_id": "prod-acme",  "outbound": {    "endpoints": [      {        "url": "https://api.acme.example/webhook"      }    ]  },  "inbound": {    "allowed_cidrs": ["203.0.113.0/24", "198.51.100.42/32"],    "mtls_client_keys": [      "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0fNmR7hK8a...HrNs6"    ],    "jwt_sig_keys": {      "keys": [        {          "kty": "RSA",          "alg": "RS256",          "use": "sig",          "kid": "prod-acme-sig-rs256-20250212",          "n": "sXW2Lk5z3m9fCzNJ1X8vJX9d1F1oUtIpt9A5rzQmL4hPl6X4cWbNh...",          "e": "AQAB"        }      ]    }  }}

Without Events API

JSON
{  "client_id": "prod-acme",  "inbound": {    "allowed_cidrs": ["203.0.113.0/24", "198.51.100.42/32"],    "mtls_client_keys": [      "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0fNmR7hK8a...HrNs6"    ],    "jwt_sig_keys": {      "keys": [        {          "kty": "RSA",          "alg": "RS256",          "use": "sig",          "kid": "prod-acme-sig-rs256-20250212",          "n": "sXW2Lk5z3m9fCzNJ1X8vJX9d1F1oUtIpt9A5rzQmL4hPl6X4cWbNh...",          "e": "AQAB"        }      ]    }  }}
Checklist Before Submission
  • All keys are unique and correct:

    • mTLS Client Key -> inbound.mtls_client_keys

    • JWT Signing Key (JWKS) -> inbound.jwt_sig_keys

  • All fields adhere to max length constraints:

    • client_id -> 256 chars

    • outbound.endpoints[].url -> 2048 chars

  • Public keys are in the correct format:

    • Base64 DER for the mTLS client key

    • JWKS for the JWT key

Send the JSON file via Slack or email to our integration team. We’ll verify and confirm or provide guidance.

Authentication

All API requests from partners to AllClear must be authenticated using a client-signed JWT bearer access token (Client Access Token), provided in the Authorization header.

HTTP
Authorization: Bearer <Client Access Token>
Client Access Token Format

JWT Payload

A Client Access Token must include the following claims:

JSON
{  "htu": "5N38sVL2OOBRENFGU0hmti-m-H_Uxo8Brf1WYULMh3M",  "htm": "FOMM0WPHMpEuBIxMg34VxOkMBi67eVq5R9V3BuLRDdg",  "iss": "prod-acme",  "aud": "partner.healthbankone.com",  "iat": 1738262025,  "nbf": 1738262025,  "exp": 1738262325,  "jti": "155e9353-42f1-4813-a23a-267f43b18c4c"}

Field

Required

Type

Format

Description

htu

Yes

string

base64url (SHA-256)

Base64url-encoded SHA-256 hash of the full request URI you are about to call. Ensures the JWT is valid only for that specific URI.

htm

Yes

string

base64url (SHA-256)

Base64url-encoded SHA-256 hash of the HTTP method in uppercase (e.g., GET, POST). Ensures the JWT is valid only for that request method.

iss

Yes

string

Max length: 256 chars

Issuer: must be the client ID of the requesting partner (e.g., prod-acme).

aud

Yes

string

Fixed: "partner.healthbankone.com"

Audience: must always be "partner.healthbankone.com".

iat

Yes

integer

int64 (Unix timestamp)

“Issued at” timestamp in Unix time (seconds since epoch).

nbf

Yes

integer

int64 (Unix timestamp)

“Not before” timestamp. Minimum value: 5 min before iat. Ensure your system clock is synchronized.

exp

Yes

integer

int64 (Unix timestamp)

Expiration timestamp. Maximum value: 5 min after iat. Ensure your system clock is synchronized.

jti

Yes

string

uuid

Unique identifier for the JWT to prevent replay attacks.

Computing htu and htm

JavaScript
import crypto from "crypto";​const sha256Base64Url = (input) => {  return crypto.createHash("sha256")    .update(input, "utf8")    .digest("base64url");};​// Request URL (as-is, including query parameters)const requestUrl = "https://partner.healthbankone.com/events/550e8400-e29b-41d4-a716-446655440000";​// HTTP Method (uppercase)const httpMethod = "GET";​const htu = sha256Base64Url(requestUrl);const htm = sha256Base64Url(httpMethod);​console.log("htu:", htu);console.log("htm:", htm);

JWT Signature

The Client Access Token must be signed using RS256 (unless otherwise agreed). AllClear will verify the signature using the partner’s public key identified by the kid value in the token’s header.

JWT Header

JSON
{  "typ": "JWT",  "alg": "RS256",  "kid": "prod-acme-sig-rs256-20250130"}

Field

Type

Format

Description

typ

string

Fixed: "JWT"

Token type. Always "JWT".

alg

string

Signing algorithm. Default "RS256" unless agreed otherwise.

kid

string

Max length: 255 chars

ID of the key used for signature verification. Must match a key in your JWKS.

Security and mTLS

Production tenants require additional security measures beyond JWT authentication. These requirements apply to requests you make to our APIs. Receiving event notifications from us carries no additional requirements (see the last section).

mTLS (Mutual TLS) with Public-Key Pinning

When making requests to our APIs, you must authenticate using mutual TLS (mTLS). Both the client and server present a certificate during the TLS handshake. Your client certificate does not need to be issued by a public or trusted Certificate Authority. A self-signed certificate is fine. What matters is the public key it carries: we accept your requests only if the certificate presents the public key you registered with us. See public-key pinning below.

We enforce public-key pinning as part of mTLS, we will only accept requests whose client certificate contains a public key associated with your client ID. We pin the public key itself, not the certificate, so a renewed certificate carrying the same key is accepted without any coordination.

Public-Key Pinning Mechanics (your mTLS client certificate)
  • You can renew or replace your client certificate at any time without coordination, as long as its public key remains the same.

  • To rotate your key (change the public key itself), you must send us the new public key first so we can pin it alongside the existing one for a seamless transition, after which the old key is removed.

Static IPs

We only accept requests from your predefined static IP addresses. Any request from an unknown IP will be rejected. Send us the IP address ranges your requests will originate from before your production tenant is enabled.

Event Notification Delivery (outbound)

When we deliver event notifications to your endpoint there are no additional requirements on your side:

  • We connect over standard TLS to the endpoint hostname you provide: your endpoint’s certificate is validated against trusted CAs and its hostname is verified. We do not pin your endpoint’s certificate key and do not require a static IP on your endpoint: managed certificates (for example AWS ACM or Let’s Encrypt) and dynamic-IP hosting (for example AWS API Gateway) are fine.

  • Each notification is a JWT signed by AllClear containing only an event ID and event type: no patient data. Verify its signature against our published key to confirm it genuinely came from us, then fetch the event through the Events API.

Events API

The Events API delivers real-time event notifications by sending HTTP POST requests to your endpoints when designated events occur, connections, updates, disconnections, and new health records.

Notification Flow
  1. Notification received: Your public endpoint receives an HTTP POST containing only the event type and event ID.

  2. Verify the notification: The notification is a signed JWT. Verify its signature and claims. A notification that fails verification is not from AllClear, reject it and do not process it.

  3. Persist the notification to durable storage: Write the notification (its event type and event ID) to a queue or other persistent store. This is the step that makes the event recoverable, so it must succeed before you acknowledge. If it fails, do not acknowledge (see step 4).

  4. Acknowledge with 200 OK within 5 seconds: Return 200 OK only once the notification is safely stored. The 200 confirms receipt, not that you have finished processing, and it stops our retries: if you acknowledge before the notification is durably stored and then lose it, we will not redeliver it. If you cannot store it, do not return 200, let the delivery fail so we retry (see Retry Strategy).

  5. De-duplicate by event ID: We may deliver the same event more than once. Skip event IDs you have already stored or processed.

  6. Request full event data (asynchronously): Working off your stored queue, send an authenticated GET /events/{id} request with the event ID. AllClear responds with the full event payload as JSON.

  7. Process the event: Process according to your business logic. If your fetch or processing fails, retry on your side from the stored notification; the event is safe because you persisted it before acknowledging.

The order is persist, then acknowledge, then fetch and process asynchronously. Never fetch or process before you acknowledge, and never acknowledge before the notification is durably stored. The 5-second window is for the acknowledgement of a stored notification, not for processing.

AllClearYour endpoint
Acknowledge ≤ 5 sec
POSTnotification (signed JWT, event type + id)
Verify the JWT (RS256)
Persist to a durable queue
200within 5s, after the durable write
Acknowledged, not re-delivered
Process async
Then, asynchronously, off the queue
GET/events/{id}
200full event data (JSON)
De-duplicate, then process
altif not stored, or no 200 OK in 5s
RETRYafter 30s, then exponential backoff
Receiving Event Notifications

Your endpoint receives an HTTP POST with a signed JWT body:

HTTP
POST /your-notification-endpoint HTTP/1.1Host: your-server.comContent-Type: application/jwteyJhbGciOiJSUzI1NiIsImtpZCI6I...

Header

Value

Description

Content-Type

application/jwt

The request body is a JWT.

Notification JWT Format

JSON
{  "type": "event_notification",  "event": {    "type": "connection.connected",    "id": "b7b96067-691c-4e3a-b50f-762edd11f1b2"  },  "iss": "partner.healthbankone.com",  "aud": "prod-acme",  "iat": 1700000000,  "nbf": 1700000000,  "exp": 1700000300,  "jti": "93c8100c-f77e-436c-a003-60bcbc1b6ed8"}

Note: Event notifications contain only the event type and id. Full event data must be retrieved by calling GET /events/{id}.

Field

Type

Format

Description

type

string

Fixed: "event_notification"

Always "event_notification".

event.type

string

Max length: 255 chars

Event type, e.g., "connection.connected".

event.id

string

uuid

Unique identifier of the event. Use for de-duplication.

iss

string

Fixed: "partner.healthbankone.com"

Issuer. Always "partner.healthbankone.com".

aud

string

Max length: 255 chars

Intended recipient (your client ID).

iat

integer

int64 (Unix timestamp)

“Issued at” timestamp.

nbf

integer

int64 (Unix timestamp)

“Not before” timestamp.

exp

integer

int64 (Unix timestamp)

Expiration timestamp (5 minutes after iat).

jti

string

uuid

Unique JWT identifier for replay prevention.

Notification JWT Header

JSON
{  "typ": "JWT",  "alg": "RS256",  "kid": "healthbankone-sig-rs256-20250130"}

The JWT is signed using RS256 and can be verified using AllClear’s public key identified by kid.

Validating Event Notifications
  1. Validate the JWT header:

    • alg equals "RS256" (or a pre-agreed algorithm).

    • kid is the identifier of AllClear’s signing key.

  2. Verify the JWT signature using AllClear’s key identified by kid.

  3. Validate the JWT metadata:

    • iss is exactly "partner.healthbankone.com".

    • aud matches your client ID.

    • nbf is in the past (allow margin for clock skew).

    • exp is in the future (allow margin for clock skew).

Fetch our public signing keys from our JWKS endpoint and select the one whose kid matches the token header:

Text
https://partner.healthbankone.com/.well-known/jwks.json

This endpoint is public: it is not subject to mTLS or source-IP restrictions, so your webhook receiver can fetch it from any host, including sandbox.

We recommend the jose library’s createRemoteJWKSet, which fetches and caches the key set across warm invocations and refreshes automatically when we rotate keys, so you should not store or cache our keys yourself:

JavaScript
import { createRemoteJWKSet, jwtVerify } from "jose";​// Create once, at module scope (do not recreate per request).const JWKS = createRemoteJWKSet(  new URL("https://partner.healthbankone.com/.well-known/jwks.json"));​const { payload } = await jwtVerify(notificationJwt, JWKS, {  issuer: "partner.healthbankone.com",  audience: "<your client ID>",  algorithms: ["RS256"],});

If verification fails, do not process the notification: a notification that does not verify was not sent by AllClear. Do not fetch the event and do not acknowledge it as a genuine event.

Requesting Full Event Data
HTTP
GET /events/{event.id}Authorization: Bearer <Client Access Token>

See Authentication for how to build the Client Access Token.

Example Response

JSON
{  "type": "connection.connected",  "timestamp": "2025-01-24T10:30:00Z",  "id": "b7b96067-691c-4e3a-b50f-762edd11f1b2",  "patient_id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",  "patient": {    "id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",    "first_name": "Floyd",    "last_name": "Perkins",    "gender": "Male",    "date_of_birth": "1976-05-12",    "email": "pfloyd@email.com",    "mobile_phone": "+15551234567",    "address": {      "street1": "123 Elm St.",      "street2": "Unit 2A",      "city": "Springfield",      "state": "IL",      "zip": "12345"    }  },  "permissions": ["records:read", "records:add"],  "portal_link": "https://adminportal.app.healthbankone.com/portal-patients/connections/9a8b419b-816e-4ae6-82f4-802f7c206fdb/electronic-record/allergies",  "promo_code": "PROMO2025"}

Error Responses

Status

Description

401 Unauthorized

Invalid or missing Client Access Token (see Errors).

404 Not Found

No event with that ID for your client (in sandbox, also if the ID was never issued).

500 Internal Server Error

Unexpected server error.

See Errors for the response bodies.

Acknowledging Event Notifications

Respond with HTTP 200 OK within 5 seconds, but only after you have durably stored the notification. The acknowledgement confirms receipt only, it does not mean you have finished processing the event, and because it stops our retries the durable write is its precondition: verify the notification, write it (its event type and ID) to a queue or other persistent store, and only then return 200. Fetch the full event and process it asynchronously off that stored record (see Notification Flow). If you cannot store the notification, do not return 200, let the delivery fail so we retry.

The following scenarios constitute a delivery failure:

  • We cannot negotiate or validate your server’s SSL certificate.

  • Your domain resolves to a disallowed IP address range.

  • We receive any response other than HTTP 200 OK.

  • We wait longer than 5 seconds for a response.

Tip: The order is durable-store, then 200, then fetch and process on a background worker. Never fetch GET /events/{id} or run business logic before acknowledging, and never acknowledge before the notification is safely stored, so neither processing latency nor a crash mid-processing can cost you an event.

Retry Strategy

If a delivery fails, we retry up to 5 times total, with the delays growing between attempts: 30 seconds, then 1, 2, 4, and 8 minutes (about 15.5 minutes across all attempts). We may add jitter to prevent retry storms and throttle notifications during high failure rates.

Upon consistent failures, we may disable event delivery and will notify you ahead of time.

Event Ordering

We try to deliver related events in order, connection.connected before record.added for the same patient. Occasionally events may arrive out of order.

To maintain order on your side, persist events in order-preserving storage as you acknowledge them, and have your background worker process them in received order rather than concurrently. Because you acknowledge before processing, ordering is your responsibility once the event is persisted.

See Event Types for detailed payloads of each event.

Replay and Backfill

If you miss events (for example an outage on your side that outlasts our retry window), we can replay or backfill them. This is currently a manual process coordinated with our operations team: contact us with the affected connection and time range and we will re-deliver. Automated self-service replay is on the roadmap.

Because notifications may be redelivered, your handler must be idempotent: de-duplicate by event ID (see Notification Flow).

Event Types

Data Model

A Connection links you (the partner) to a Patient and carries the permissions that gate access; a Patient belongs to a User (one user can hold several patients). Connection events describe changes to the connection; record.added signals new Records.

Connection Events

Event

Description

connection.connected

A new connection between you and a patient is established.

connection.updated

Connection attributes (e.g., permissions) are updated.

connection.disconnected

The connection is fully terminated.

Record Events

Event

Description

record.added

New medical records are added for a connected patient.

connection.connected

Triggered when a new connection between you and a patient is established.

If records:read is not included in permissions, you will not have access to the patient’s records or receive record-related event notifications.

With records:read

JSON
{  "type": "connection.connected",  "timestamp": "2025-01-24T10:30:00Z",  "id": "b7b96067-691c-4e3a-b50f-762edd11f1b2",  "patient_id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",  "patient": {    "id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",    "first_name": "Floyd",    "last_name": "Perkins",    "gender": "Male",    "date_of_birth": "1976-05-12",    "email": "pfloyd@email.com",    "mobile_phone": "+15551234567",    "address": {      "street1": "123 Elm St.",      "street2": "Unit 2A",      "city": "Springfield",      "state": "IL",      "zip": "12345"    }  },  "permissions": ["records:read", "records:add"],  "portal_link": "https://adminportal.app.healthbankone.com/portal-patients/connections/9a8b419b-816e-4ae6-82f4-802f7c206fdb/electronic-record/allergies",  "promo_code": "PROMO2025"}

Without records:read

JSON
{  "type": "connection.connected",  "timestamp": "2025-01-24T10:30:00Z",  "id": "b7b96067-691c-4e3a-b50f-762edd11f1b2",  "patient_id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",  "permissions": ["records:add"],  "portal_link": "https://adminportal.app.healthbankone.com/portal-patients/connections/9a8b419b-816e-4ae6-82f4-802f7c206fdb/electronic-record/allergies",  "promo_code": "PROMO2025"}

Fields

Field

Type

Format

Description

type

string

Max length: 255 chars

connection.connected, a new connection has been established.

timestamp

string

date-time (ISO 8601, UTC)

When the event occurred. Example: "2025-01-24T10:30:00Z".

id

string

uuid

Unique identifier of the event.

patient_id

string

uuid

Unique identifier of the patient. Use it with the Patient API.

patient.id

string

uuid

Unique identifier of the patient (only present when records:read is granted).

patient.first_name

string

Max length: 100 chars

Patient’s first name.

patient.last_name

string

Max length: 100 chars

Patient’s last name.

patient.gender

string

enum: ("Male", "Female", "Other")

Patient’s gender.

patient.date_of_birth

string

date (ISO 8601), pattern: "^\d{4}-\d{2}-\d{2}$"

Patient’s birthdate (YYYY-MM-DD).

patient.email

string

email, max length: 200 chars

Patient’s email address.

patient.mobile_phone

string

E.164, pattern: ^\+?[1-9]\d{1,14}$, max length: 16

Patient’s mobile phone number in E.164 format.

patient.address

object

—

Patient’s address containing street1, street2, city, state, and zip.

patient.address.street1

string

Max length: 200 chars

First line of the patient’s address.

patient.address.street2

string

Max length: 100 chars

Second line of the patient’s address (optional).

patient.address.city

string

Max length: 100 chars

City of residence.

patient.address.state

string

pattern: "^[A-Z]{2}$", max length: 2

U.S. state abbreviation (e.g., CA, NY).

patient.address.zip

string

pattern: "^\d{5}$", max length: 5

ZIP code (U.S. format).

permissions

array

Items: string (enum: records:read, records:add)

Permissions the user granted. records:read allows retrieving records via the Patient Records API. records:add is a consent scope for records being added to the patient’s account on their behalf; it is not backed by a partner write endpoint (records are added by third parties through AllClear, see Patient Records API). If records:read is not included, the partner will not have access to records or receive related notifications.

portal_link

string

uri, max length: 2048

Direct URL to view this connection in the partner portal (within healthbankone.com).

promo_code

string

Max length: 32 chars

Reference data from connection creation (optional, may be absent). Contains the promo code if one was used.

connection.updated

Same structure as connection.connected (with and without records:read), but type is "connection.updated".

This fires whenever connection attributes change, including permissions being added or removed. If records:read is removed you lose access to the patient’s records and stop receiving record-related event notifications; if it is added, access begins.

connection.disconnected

Triggered when a connection is fully terminated (by either the patient or the partner). You lose access to the patient’s records and stop receiving any event notifications about this patient.

JSON
{  "type": "connection.disconnected",  "timestamp": "2025-01-24T10:30:00Z",  "id": "b7b96067-691c-4e3a-b50f-762edd11f1b2",  "patient_id": "dd27837a-ba7f-4e28-b042-ba0dc00addac",  "portal_link": "https://adminportal.app.healthbankone.com/portal-patients/connections/9a8b419b-816e-4ae6-82f4-802f7c206fdb/electronic-record/allergies",  "promo_code": "PROMO2025"}

Field

Type

Format

Description

type

string

Max length: 255 chars

connection.disconnected, the connection has been terminated.


string

date-time (ISO 8601, UTC)

When the event occurred. Example: "2025-01-24T10:30:00Z".

id

string

uuid

Unique identifier of the event.

patient_id

string

uuid

Unique identifier of the patient.

portal_link

string

uri, max length: 2048

Direct URL to view this connection in the partner portal.

promo_code

string

Max length: 32 chars

Reference data from connection creation (optional, may be absent).

record.added

Triggered when new medical records are added and available for a connected patient.

JSON
{  "type": "record.added",  "timestamp": "2025-01-24T10:30:00Z",  "id": "fa032c9c-6e4b-4090-a719-cb49de0a5ebc",  "patient_id": "9a8b419b-816e-4ae6-82f4-802f7c206fdb",  "records": [    {      "id": "fa032c9c-6e4b-4090-a719-cb49de0a5ebc",      "portal_link": "https://adminportal.app.healthbankone.com/portal-patients/connections/dd27837a-ba7f-4e28-b042-ba0dc00addac/medical-documents/fa032c9c-6e4b-4090-a719-cb49de0a5ebc"    }  ]}

Field

Type

Format

Description

type

string

Max length: 255 chars

record.added, a new record has been added to the patient’s file.

timestamp

string

date-time (ISO 8601, UTC)

When the event occurred. Example: "2025-01-24T10:30:00Z".

id

string

uuid

Unique identifier of the event.

patient_id

string

uuid

Unique identifier of the patient. Use it with the Patient API.

records

array

—

The new records.

records[].id

string

uuid

Record identifier. Use it with the Patient Records API to download the file.

records[].portal_link

string

uri, max length: 2048

Direct URL to view the record in the partner portal.

Patient API

The Patient API allows you to fetch demographic and insurance information about connected patients.

Request
HTTP
GET /patients/{id}Authorization: Bearer <Client Access Token>

See Authentication for how to build the Client Access Token.

Response
JSON
{  "id": "60389406-7863-428d-9831-cd5fa1e79e64",  "first_name": "Floyd",  "last_name": "Perkins",  "gender": "Male",  "date_of_birth": "1976-05-12",  "email": "pfloyd@email.com",  "mobile_phone": "+15551234567",  "address": {    "street1": "123 Elm St.",    "street2": "Unit 2A",    "city": "Springfield",    "state": "IL",    "zip": "12345"  },  "primary_insurance": {    "health_insurance_provider": "BlueCross BlueShield of Texas",    "member_id_number": "TC001001",    "group_number": "123456789",    "member_name": "Floyd Perkins",    "insurance_plans": ["Blue Edge PPO"]  },  "secondary_insurance": {    "health_insurance_provider": "BlueCross BlueShield of Texas",    "member_id_number": "TC001002",    "group_number": "123456789",    "member_name": "Floyd Perkins",    "insurance_plans": ["Blue Edge PPO"]  }}
Field Descriptions

Patient

Field

Type

Format

Description

id

string

uuid

Unique identifier of the patient.

first_name

string

Max length: 100 chars

Patient’s first name.

last_name

string

Max length: 100 chars

Patient’s last name.

gender

string

enum: "Male", "Female", "Other"

Patient’s gender.

date_of_birth

string

date (ISO 8601), pattern: "^\d{4}-\d{2}-\d{2}$"

Patient’s birthdate in ISO 8601 format (YYYY-MM-DD).

email

string

email, max length: 200 chars

Patient’s email address.

mobile_phone

string

E.164, pattern: ^\+?[1-9]\d{1,14}$, max length: 16

Patient’s mobile phone number in E.164 format. Example: "+15551234567".

Address

Field

Type

Format

Description

address.street1

string

Max length: 200 chars

First line of the address.

address.street2

string

Max length: 100 chars

Second line of the address (optional).

address.city

string

Max length: 100 chars

City of residence.

address.state

string

pattern: "^[A-Z]{2}$", max length: 2

U.S. state abbreviation (e.g., CA, NY).

address.zip

string

pattern: "^\d{5}$", max length: 5

ZIP code (U.S. format).

Insurance

Both primary_insurance and secondary_insurance are optional: they may be absent from the response.

Field

Type

Format

Description

health_insurance_provider

string

Max length: 200 chars

Name of the insurance provider.

member_id_number

string

Max length: 100 chars

Member ID number.

group_number

string

Max length: 100 chars

Group number (optional, may be absent).

member_name

string

Max length: 200 chars

Name of the member on the plan.

insurance_plans

array

Items: string

List of insurance plan names.

Patient Records API

The Patient Records API allows you to list and download health records for connected patients.

This API is read-only for partners: there is no endpoint to add or upload records. Records are added to a patient’s account when third parties provide them to AllClear; when a new record becomes available for a connected patient (and you hold records:read), you are notified with a record.added event.

List All Records
HTTP
GET /patients/{patient.id}/recordsAuthorization: Bearer <Client Access Token>

See Authentication for how to build the Client Access Token.

Returns all of the patient’s records as a flat JSON array in a single response. There is no pagination and there are no query parameters. A patient with no records returns 200 with an empty array ([]).

Response

JSON
[  {    "id": "b44a4e8e-dc25-4f1c-9bfc-c2c8f68cc34f",    "file_name": "AUSTIN REGIONAL CLINIC PA_09-08-2025_4.pdf",    "file_type": "pdf"  },  {    "id": "a12b3c4d-5678-9abc-def0-1234567890ab",    "file_name": "LAB_RESULTS_09-10-2025.pdf",    "file_type": "pdf"  }]

Field

Type

Format

Description

id

string

uuid

Unique identifier of the record. Use it to fetch the full record with download URL.

file_name

string

Max length: 100 chars

Name of the file.

file_type

string

enum

One of: ccda, image, pdf, dicom, zip, text, bin

Get a Specific Record
HTTP
GET /patients/{patient.id}/records/{record.id}Authorization: Bearer <Client Access Token>

Response

JSON
{  "id": "b44a4e8e-dc25-4f1c-9bfc-c2c8f68cc34f",  "file_name": "AUSTIN REGIONAL CLINIC PA_09-08-2025_4.pdf",  "file_type": "pdf",  "download": {    "url": "https://storage.googleapis.com/<bucket>/partner-access/acme/b44a4e8e-dc25-4f1c-9bfc-c2c8f68cc34f.pdf?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Expires=300&X-Goog-Signature=...",    "expires_at": "2025-09-08T13:38:25.419Z"  }}

Field

Type

Format

Description

id

string

uuid

Unique identifier of the record.

file_name

string

Max length: 100 chars

Name of the file.

file_type

string

enum

One of: ccda, image, pdf, dicom, zip, text, bin

download

object

—

Metadata for downloading the record.

download.url

string

Max length: 2048 chars

Pre-signed download URL. Already includes authorization: no extra auth needed, and no mTLS or source-IP restriction. Valid until download.expires_at (5 minutes).

download.expires_at

string

ISO 8601 datetime

When the download URL expires. Example: "2025-09-08T13:38:25.419Z".

Errors

Every error carries an HTTP status and a JSON body. There are two body shapes: authentication failures use a flat { status, message }, and all other errors use the standard error envelope below.

Authentication failures (401)

A request that fails authentication is rejected with HTTP 401 and a flat body:

JSON
{  "status": "UNAUTHENTICATED",  "message": "JWT expired"}

The message states the specific reason. Common causes:

Cause

What to check

JWT invalid or expired

nbf/exp are within 5 minutes of iat; htu/htm match the exact request URL and method; signed RS256 by a key in your JWKS (the header kid matches).

mTLS key not recognized (production)

Your client certificate must present the public key you registered in inbound.mtls_client_keys. A self-signed certificate is fine; we pin the key, not the certificate.

Source IP not allowlisted (production)

The request must originate from a CIDR listed in inbound.allowed_cidrs.

Sandbox clients skip the mTLS and source-IP checks.

Error envelope (403, 404, and other errors)

All other errors return the standard envelope:

JSON
{  "error": {    "code": "NOT_FOUND",    "message": "Record not found",    "requestId": "b1f2c3d4-5678-90ab-cdef-1234567890ab",    "correlationId": "9a8b7c6d-..."  }}

Field

Description

error.code

Machine-readable code, for example FORBIDDEN, NOT_FOUND.

error.message

Human-readable description.

error.requestId

Identifier for this request. Include it when contacting support.

error.correlationId

Present when the request crossed internal services; include it too if present.

error.details

Optional array with field-level specifics when applicable.

Status

code

When

403

FORBIDDEN

You are authenticated, but not permitted to access this resource (for example a patient or record outside your consent). message is "Access Denied".

404

NOT_FOUND

The event, patient, or record does not exist (or, in sandbox, the event ID was never issued).

There are currently no request rate limits on the Partner APIs; you will not receive 429. This may change in future, in which case it will be announced ahead of time and documented here.

Versioning and Deprecation

  • Additive changes are backward-compatible and ship without a version bump. We may add new fields to responses and new event type values at any time. Build tolerantly: ignore fields you do not recognize, and ignore event types you do not handle rather than failing on them.

  • Breaking changes are introduced as a new version and announced in advance. The previous version continues to work for a deprecation window of 6 months from the announcement, so you have time to migrate.

  • All changes are recorded in the Changelog and announced over Slack or email.

Onboarding and Support

We assign your client IDs. You do not choose them. On onboarding we issue a sandbox-* client ID and, for go-live, a prod-* client ID.

Configuration lifecycle:

  1. You submit your configuration JSON (endpoint URL, public keys) to the integration team.

  2. We validate it and provision your sandbox client.

  3. You build and test against sandbox.

  4. You submit your production configuration; we provision your prod client with mTLS and source-IP enforcement.

  5. You go live.

To change configuration later (rotate keys, add or remove allowed_cidrs, change your endpoint URL), submit an updated JSON to the integration team, the same way. We do not expose a self-service configuration endpoint today.

Proposed SLA. Support-response and delivery-availability targets are being finalized and are not yet contractual. Until then, treat delivery as best-effort with the retry and replay guarantees described above.

Need Help?

If you run into any issues, we’ll treat it as our top priority. Message us on Slack or email, we’ll jump in quickly to keep you moving.

We love hearing from partners, and we treat every request as a chance to make your integration smoother.

Changelog

Release Date

Changes

2026-09-16

Clarified the notification flow: acknowledge receipt with 200 within 5 seconds, then fetch and process the event asynchronously. Reworked the errors section (401 {status,message}, error envelope for 403/404) and removed rate-limit references (none are configured). Documented that /.well-known/jwks.json is public. Added Quickstart, connection creation, sandbox testing, event replay/backfill, versioning/deprecation, and onboarding/support sections. Added a machine-readable openapi.yaml.

2026-09-15

Renamed inbound.allowed_ips to inbound.allowed_cidrs. Added the 3072-bit RSA minimum to the mTLS client-key field (the JWT signing-key field already stated it). Clarified that mTLS uses public-key pinning: a self-signed client certificate is accepted, and the certificate does not need to be issued by a trusted CA.

2025-09-17

Updated event data format (no longer wrapped into JWE+JWS). Clarified payload structure and order guarantees. Added Patient API and Patient Records API.

2025-04-29

Defined encrypted (JWE) and signed (JWS) event wrapping. Added a sequence diagram and restructured to follow flow steps. Added htu/htm example code.

2025-02-10

Changed phone number format to E.164. Removed timestamp from event notifications. Specified delivery semantics, order, and retry nuances.

2025-01-31

Initial version.