Join Now

Connection API Reference

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 guide covers the Connection API. For the Partner API, see its documentation.

Every response on this page is the server’s actual reply from production, with per-client values replaced by placeholders. Values in angle brackets are placeholders for secrets or per-client values.

0. Instructions for your coding agent

Paste this into the agent that writes your integration. Sections 1 to 8 and 9a are live in production and the sections further down this page show each call’s actual response. Section 9b (the Consumer APIs on a user’s access token) is not live yet and is marked as such, so your agent knows what is coming without building against it. Partners reach the Consumer APIs today with Partner API credentials (see the Partner API).

Text
Add AllClear to this app so it can read a user's records with their consent.Sections 1 to 8 and 9a are live in production today. Section 9b (the Consumer APIs on auser's access token) is not live yet: read it for context, but do not build against it.Partners reach the Consumer APIs today with Partner API credentials (see /partners).Do not invent endpoints, scopes or claims.IF THERE IS NO CODEBASE TO BUILD INTOIf the user has no codebase (they pasted this into a chat app), don't ask for a repo, zip orstack. Say in one plain sentence that connecting AllClear lets you read their own healthrecords with their permission, ask what they'd like to do with their records in everydaywords, and keep your replies non-technical.Check what you can do first. If you can add an MCP connection yourself, do. If you can't, givethe user these manual steps. In Claude: Settings → Connectors → Add custom connector, URLhttps://mcp.app.allclearid.com/mcp (note the /mcp), then Connect and approve in the AllClearapp; added this way, Claude shows as verified. In other apps: add AllClear as a custom MCPconnector with the URL https://mcp.app.allclearid.com/mcp in your app's connector or MCPsettings, then approve in the AllClear app. Hand-written OAuth (steps 1 to 8) is for agentsbuilding an app, not for chat users.LIVE IN PRODUCTION1. Register this app once as a public OAuth client (no secret):   POST https://oauth.app.allclearid.com/register   { "client_name": "<app name>",     "redirect_uris": ["<https redirect uri>"],     "token_endpoint_auth_method": "none",     "grant_types": ["authorization_code", "refresh_token"],     "response_types": ["code"] }   Keep the returned client_id. There is no secret, and no approval step or API key to   request: registration is open and immediate.2. To connect a user, send them to https://oauth.app.allclearid.com/authorize with   response_type=code, client_id, redirect_uri, scope="records:read offline_access",   code_challenge (S256), code_challenge_method=S256, state. On return, check state and iss   before using the code. To also sign the user in, add openid to scope and send a nonce   (see 9a).   Don't set resource yourself; MCP client libraries add it, and the server doesn't need it otherwise.3. Exchange the code: POST https://oauth.app.allclearid.com/token (form-encoded)   grant_type=authorization_code, code, redirect_uri, client_id, code_verifier.   Store access_token (1 hour) and refresh_token (30 days). Renew with   grant_type=refresh_token. Every refresh returns a new refresh_token and the old one stops   working, so store the new one each time and never refresh one connection in parallel:   presenting a used refresh_token revokes the connection. The 30 days is sliding: each   refresh issues a fresh 30-day refresh_token. Treat both tokens as opaque: only the   id_token is meant to be read, after verifying it against the JWKS (see 9a). Never log them.4. Read records over MCP: POST https://mcp.app.allclearid.com/mcp   Authorization: Bearer <access_token>   Accept: application/json, text/event-stream   Use a standard MCP client library if you can; it does the handshake for you. By hand: POST   initialize (protocolVersion 2025-06-18), then the notifications/initialized notification,   and send MCP-Protocol-Version: 2025-06-18 on every later request, plus Mcp-Session-Id if   the initialize response returned one. Then call tools/list, then tools/call with get_medications, get_conditions, get_allergies,   get_lab_results, get_vital_signs, get_immunizations, get_encounters, get_procedures,   get_care_plans, get_patient_insurance, list_patient_documents, search_medical_data.   Records come back as the source holds them: structured (fhir) plus document excerpts.   Health records are live today; further record types follow.5. On a 401, refresh the access token and retry once. If the refresh is refused too, drop   both tokens, clear anything cached, and send the user through authorize again. A refresh   token left unused for more than 30 days expires and the refresh returns invalid_grant:   that is an expiry, not the user disconnecting.6. Disconnect, from your side:   POST https://oauth.app.allclearid.com/revoke (form-encoded)   token=<refresh_token>&client_id=<client_id>   The user can also disconnect in the AllClear app, under Connections. Either way your   next MCP call returns 401 with a WWW-Authenticate: Bearer challenge.7. Discovery, if you need it:   https://oauth.app.allclearid.com/.well-known/oauth-authorization-server   https://oauth.app.allclearid.com/.well-known/openid-configuration   https://mcp.app.allclearid.com/.well-known/oauth-protected-resource   https://oauth.app.allclearid.com/.well-known/jwks.json8. The scopes you can ask for in step 2:   records:read     the permission to read the user's records. One permission, two                    surfaces: live over MCP today, and it gates the Consumer APIs on a                    user's token in section 9b when that ships.   offline_access   a standing connection: you receive a refresh token and access                    continues until the user disconnects or the refresh token goes 30                    days unused. Without it the share is one-time and ends when the                    access token expires. Live.   records:add      add records to the user's account.   messages:send    send the user a message.   account:assist   assisted actions on the user's behalf.                    The user can grant records:add, messages:send and account:assist on                    the consent screen today, but this brief does not cover calling them.                    Do not request them unless you need them; do not guess endpoints.   openid, profile, email, address, phone                    identity (OpenID Connect). Live: see section 9a.   These instructions cover the read path. Ask only for the scopes you actually use: the user   reviews what you asked for before approving, and can end the connection at any time.9a. Identity (OpenID Connect). Live in production. The discovery document is    https://oauth.app.allclearid.com/.well-known/openid-configuration. It advertises    userinfo_endpoint https://oauth.app.allclearid.com/userinfo, the scopes openid, profile,    email, address and phone, RS256 id_token signing and pairwise subjects. Add openid to the    same authorize call in step 2, and the token response carries an id_token (a JWT) alongside    the access token. Send a nonce on the authorize call whenever you request openid, and    check it matches in the id_token. Verify signature (RS256), iss =    https://oauth.app.allclearid.com, aud = your client_id, and exp, against the JWKS in    section 7.    The id_token carries registered claims only: iss, sub, aud, azp, auth_time,    at_hash, iat, exp, and nonce if you sent one. It carries no personal    data and no verification claim. sub is per client_id by default; when several clients are    linked to one organization it can be configured to be the same across them. Key users on    iss + sub.    Personal details come from GET /userinfo on the access token, released by scope:    profile (name, given_name, family_name, birthdate, gender), email (email),    phone (phone_number), address (address). Without a Bearer token /userinfo answers    401 with WWW-Authenticate: Bearer.NOT LIVE YET. DO NOT BUILD AGAINST THIS.9b. Consumer APIs on a user's OAuth token. Not live yet (coming). Today the Consumer APIs    are available to partners through a partner agreement, with Partner API credentials    issued by AllClear's integration team: partners use the Partner API documentation    (/partners). When calling them with a user's access token ships: a user's source    documents in their original form, fetched on the user's access token; and an event stream addressed    to your app. The event notification is thin and signed (an event id and type, with    your client_id as the audience, and no user in it by design). You fetch the event    itself with the client_credentials grant on your app's own credential, not a user    token, and the event names the patient it is about.

1. Discovery

The MCP server tells a client where to get authorized. Verbatim:

HTTP
GET https://mcp.app.allclearid.com/.well-known/oauth-protected-resource
JSON
{  "resource": "https://mcp.app.allclearid.com",  "authorization_servers": ["https://oauth.app.allclearid.com"],  "bearer_methods_supported": ["header"]}

The authorization server describes itself. Verbatim:

HTTP
GET https://oauth.app.allclearid.com/.well-known/oauth-authorization-server
JSON
{  "issuer": "https://oauth.app.allclearid.com",  "authorization_endpoint": "https://oauth.app.allclearid.com/authorize",  "token_endpoint": "https://oauth.app.allclearid.com/token",  "revocation_endpoint": "https://oauth.app.allclearid.com/revoke",  "revocation_endpoint_auth_methods_supported": ["none"],  "registration_endpoint": "https://oauth.app.allclearid.com/register",  "jwks_uri": "https://oauth.app.allclearid.com/.well-known/jwks.json",  "response_types_supported": ["code"],  "grant_types_supported": ["authorization_code", "refresh_token"],  "code_challenge_methods_supported": ["S256"],  "token_endpoint_auth_methods_supported": ["none"],  "authorization_response_iss_parameter_supported": true}

What it says in words: public clients, PKCE with S256, authorization code and refresh grants, self-registration, revocation. No client secrets anywhere.

2. The 401 that starts the flow

An unauthenticated MCP call. Verbatim:

HTTP
POST https://mcp.app.allclearid.com/mcpContent-Type: application/jsonAccept: application/json, text/event-stream{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"your-agent","version":"1.0"}}}
HTTP
HTTP/2 401WWW-Authenticate: Bearer realm="oauth", resource_metadata="https://mcp.app.allclearid.com/.well-known/oauth-protected-resource"

A spec-conformant MCP client follows that header on its own. This is the whole reason “point your client at one server” is true.

3. Point an MCP client at it

JSON
{  "mcpServers": {    "allclear": {      "url": "https://mcp.app.allclearid.com/mcp"    }  }}

No auth block is needed. The client discovers the authorization server from the 401.

4. Register a client

Public client, RFC 7591. Request:

HTTP
POST https://oauth.app.allclearid.com/registerContent-Type: application/json{  "client_name": "Your Agent",  "redirect_uris": ["https://yourapp.example/callback"],  "token_endpoint_auth_method": "none",  "grant_types": ["authorization_code", "refresh_token"],  "response_types": ["code"]}

Response (values illustrative, shape exact):

JSON
{  "client_id": "mcp_…",  "client_name": "Your Agent",  "redirect_uris": ["https://yourapp.example/callback"],  "grant_types": ["authorization_code", "refresh_token"],  "application_type": "web",  "response_types": ["code"],  "token_endpoint_auth_method": "none",  "client_id_issued_at": 1788854719}
HTTP
HTTP/2 201Access-Control-Allow-Origin: *

5. Ask for permission

Real scopes. Two are enough for an agent that reads records and keeps a standing connection:

Scope

What the user is asked

What it does

records:read

Share my records with Your Agent

Read access over MCP

records:add

Receive my records from Your Agent

Your app can contribute records

account:assist

Authorize Your Agent to assist with sharing and ordering my records

Act on the account’s behalf

messages:send

Allow Your Agent to send me messages and notifications

Notifications to the user

offline_access

(standing connection)

Refresh token

openid profile email address phone

Identity

Identity claims (OpenID Connect)

Authorize URL, built by the client:

Text
https://oauth.app.allclearid.com/authorize  ?response_type=code  &client_id=mcp_…  &redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback  &scope=records%3Aread%20offline_access  &code_challenge=<S256 of code_verifier>  &code_challenge_method=S256  &state=<random>

The user signs in to AllClear, sees the request in plain words, and taps Authorize. The redirect back carries code, state, and iss (the server supports the iss response parameter, so the client can check who answered).

Node, no SDK needed:

JavaScript
const verifier = crypto.randomBytes(32).toString("base64url");const challenge = crypto.createHash("sha256").update(verifier).digest("base64url");​const url = new URL("https://oauth.app.allclearid.com/authorize");url.search = new URLSearchParams({  response_type: "code",  client_id: CLIENT_ID,  redirect_uri: "https://yourapp.example/callback",  scope: "records:read offline_access",  code_challenge: challenge,  code_challenge_method: "S256",  state,});
Which scopes for which flow

The scope string you request decides what the user approves and what you get back. Four common flows:

Flow

Scopes to request

What it gives you

Identity

openid profile email

A signed id_token that identifies a verified person, plus their details from /userinfo. No record access.

One-time data share

records:read

Read access to the user’s records over MCP. Omit offline_access when you only need to read once.

Standing connection

records:read offline_access

Read access plus a standing connection you can refresh without the user, until they disconnect or the refresh token goes 30 days unused.

Login and standing records

openid records:read offline_access

Identity and ongoing record access in one grant: the shape a full connector uses.

  • openid turns on OpenID Connect: you get an id_token, which is Identity. The token identifies the person and nothing more: registered claims only (iss, sub, aud, azp, auth_time, at_hash, iat, exp, plus nonce when you send one). sub is per client by default; it can be configured to be the same across clients linked to one organization. Key users on iss + sub. Their details are not in it. Add profile email address phone for the claims you need and read them from GET /userinfo with the access token.

  • records:read is read access to the user’s records over MCP. On its own it is a one-time share. The connection panel at the top of the home page requests it together with openid profile, so it can greet the visitor by name before it reads their records.

  • offline_access is the standing connection: include it when you want to keep access and refresh it without the user (see sections 6 and 8), and leave it out when a single read is all you need.

  • Add records:add, account:assist, or messages:send on top of any of these when your integration also contributes records, acts on the account, or notifies the user. The user approves each as its own line on the consent screen (section 10) and can decline any one of them.

  • openid turns on OpenID Connect: you get an id_token, which is Identity. The token identifies the person and nothing more: registered claims only (iss, sub, aud, azp, auth_time, at_hash, iat, exp, plus nonce when you send one). sub is per client by default; it can be configured to be the same across clients linked to one organization. Key users on iss + sub. Their details are not in it. Add profile email address phone for the claims you need and read them from GET /userinfo with the access token.

  • records:read is read access to the user’s records over MCP. On its own it is a one-time share. The connection panel at the top of the home page requests it together with openid profile, so it can greet the visitor by name before it reads their records.

  • offline_access is the standing connection: include it when you want to keep access and refresh it without the user (see sections 6 and 8), and leave it out when a single read is all you need.

  • Add records:add, account:assist, or messages:send on top of any of these when your integration also contributes records, acts on the account, or notifies the user. The user approves each as its own line on the consent screen (section 10) and can decline any one of them.

6. Exchange the code

HTTP
POST https://oauth.app.allclearid.com/tokenContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=<code>&redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback&client_id=mcp_…&code_verifier=<verifier>

Response shape, from the server code (field names exact, values illustrative):

JSON
{  "token_type": "Bearer",  "access_token": "<token, 1 hour>",  "expires_in": 3600,  "refresh_token": "<token, 30 days>",  "refresh_token_expires_in": 2592000}

Treat both tokens as opaque; the format is the server’s business and may change. What a client needs to know: the access token is bound to the grant the user gave, to your client, and to the MCP server it was issued for; it lasts one hour; the refresh token renews it under the same grant without the user, and each refresh issues a fresh 30-day refresh token; when the user disconnects, both stop working at once, and a refresh token left unused for 30 days expires.

7. Call a tool

HTTP
POST https://mcp.app.allclearid.com/mcpAuthorization: Bearer <access_token>Content-Type: application/jsonAccept: application/json, text/event-stream{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_medications","arguments":{"status":"active"}}}

Tools the server advertised on 2026-09-08 (the live answer is tools/list): get_patient_basic_info, get_medications, get_conditions, get_allergies, get_lab_results, get_vital_signs, get_immunizations, get_encounters, get_procedures, get_care_plans, get_patient_insurance, list_patient_documents, summarize_document, search_medical_data, search_faqs, search_faqs, list_connections, connect_provider.

Response envelope from an account with no active medications (structure is the point; a populated account fills fhir_records and retrieved_context):

XML
<tool_output>  <fhir_output>    <fhir_records source="fhir" format="columnar-json" />  </fhir_output>  <document_output>    <retrieved_context />  </document_output>  <pagination>    <page>1</page>    <page_size>20</page_size>    <has_more>false</has_more>  </pagination></tool_output>

list_connections (values illustrative, shape exact):

JSON
{  "status": "ok",  "connections": [    {      "connection_id": "…",      "provider_name": "Example Health System",      "status": "CONNECTED",      "npi": "1234567893",      "provider_type": "ORGANIZATION",      "connection_type": "IN_NETWORK_CONNECTION",      "share_provider_to_patient": true,      "fasten_connected": false,      "can_order_records": true,      "can_order_electronically": false    }  ]}

Two notes for the page: records come back as they are, structured where the source had structure (the fhir half) and as the clinical documents themselves where it did not (the document half), which keeps the context a conversion would lose; and there is no per-record source or consent id in the output today.

8. Refresh

HTTP
POST https://oauth.app.allclearid.com/tokenContent-Type: application/x-www-form-urlencodedgrant_type=refresh_token&refresh_token=<refresh_token>

Same response shape as section 6. Access tokens live one hour; the standing connection is the refresh token, which the user can end at any time from the app. client_id is optional on the refresh grant (the token identifies the grant); if you do send it, it must match your registered client.

9. Revoke

From your side:

HTTP
POST https://oauth.app.allclearid.com/revokeContent-Type: application/x-www-form-urlencodedtoken=<refresh_token>&client_id=mcp_…

From the user’s side: they open Connections in the AllClear app and disconnect. Either way, the next MCP call answers 401 and the flow in section 2 starts again. No webhook exists; the page shows the 401 instead of a webhook.

10. What the user sees

Real screens from the consent flow, in order:

  1. Header “Connection Request”, title “Choose Your Settings”, card “You’re connecting with Your Agent”. Verified clients show a shield and their domain; unverified ones show “Not verified by Health Bank One”.

  2. Four toggles, worded as the user reads them: “Share my records with Your Agent”, “Receive my records from Your Agent”, “Authorize Your Agent to assist with sharing and ordering my records”, “Allow Your Agent to send me messages and notifications”.

  3. “Confirm Your Settings” with the same lines read back, then Authorize or Cancel.