Production access is free and self-serve with an account. · Synthetic demo · no account needed

FinchNode

Records

Records API: normalized, consent-filtered health records with a change feed.

Consent-filtered, normalized health records with stable IDs, cursor pages, and a change feed.

Read a patient-authorized health_record snapshot, page one category at a time with stable record IDs, and consume incremental upserts and deletion tombstones after later syncs. Every read evaluates the app’s current allowlist and active consent again, and fails closed.

What it is

Built for: Backend teams that ingest patient-authorized records and keep them current after later syncs.

Base URL: https://api.finchnode.com/api/v1

Authentication: Send an app-scoped key as a Bearer token from your backend: Authorization: Bearer ck_test_... for sandbox keys, which read only sandbox connections, or Authorization: Bearer ck_live_... for live keys, which read only production connections and which eligible applications create in the self-service console. Keys are hashed at rest, revocable, and must never be embedded in browser or mobile code.

Create a free account to get sandbox keys: https://finchnode.com/signup

At a glance

Status
Available
Auth
Sandbox or live API key
Contract
OpenAPI 3.1
Authenticated MCP endpoint (API key)
https://api.finchnode.com/api/mcp

Documents for this product

Try it in 30 seconds

Set FINCHNODE_API_KEY once in the shell that runs these commands or starts your agent client (replace ck_test_... with your sandbox key; it is the only key value to substitute)

export FINCHNODE_API_KEY=ck_test_...

Read a consent-filtered snapshot

curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records?categories=medications,labs" -H "Authorization: Bearer $FINCHNODE_API_KEY"

SUBJECT is the app-scoped subject a completed Connect session exposes, such as u_7f3aa1c2d9e4b801. GET /users lists the app’s subjects in cursor pages; follow nextCursor while hasMore is true. Categories are demographics, medications, conditions, labs, vitals, allergies, immunizations, encounters, documents, and claims.

Expected response: 200 with a health_record: id, categories, consent, sources, data by category, and meta with syncStatus, dataAsOf, lastSuccessfulSyncAt, availableCategories, missingCategories, warnings, and changeCursor. Responses carry Cache-Control: private, no-store.

For coding agents

Three ways in for a coding agent: connect the MCP endpoint below, load the OpenAPI document (https://finchnode.com/openapi.yaml), or fetch this page as markdown (https://finchnode.com/products/records-api.md). https://finchnode.com/llms.txt lists every product’s markdown twin.

Authenticated MCP endpoint (API key)

MCP endpoint
https://api.finchnode.com/api/mcp
Transport
Streamable HTTP (JSON-RPC over POST)
Auth
Authorization: Bearer ck_test_... (sandbox keys) or Authorization: Bearer ck_live_... (live keys)
Clients
Server-side and desktop MCP clients only; the endpoint is not CORS-enabled
MCP Registry name
com.finchnode/health-records

Tools at https://api.finchnode.com/api/mcp

  • list_users
  • get_health_record
  • get_category_records
  • get_record_changes

Claude Code

claude mcp add --scope project --transport http finchnode https://api.finchnode.com/api/mcp --header 'Authorization: Bearer ${FINCHNODE_API_KEY}'

Claude Code writes this server to the project's .mcp.json, which is meant to be checked into version control and shared with the project, and expands ${FINCHNODE_API_KEY} when it connects, so the file never holds the key: set FINCHNODE_API_KEY in the shell that runs Claude Code.

Codex CLI

codex mcp add finchnode --url https://api.finchnode.com/api/mcp --bearer-token-env-var FINCHNODE_API_KEY

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "finchnode": {
      "url": "https://api.finchnode.com/api/mcp",
      "headers": {
        "Authorization": "Bearer ${env:FINCHNODE_API_KEY}"
      }
    }
  }
}

Machine-readable resources

How it works

  1. Get a sandbox subject

    Create a Connect session with a sandbox key (POST /connect/sessions, with the body Hosted Connect documents; its id is SESSION_ID). A sandbox session needs no real patient: either open its hosted url yourself and choose one of the FinchNode Scenario Sandbox organizations listed above the search, which connects a synthetic source without a hospital sign-in (the account and consent steps are unchanged), or POST /connect/sessions/{sessionId}/simulate with a scenario and poll the session until simulation.state is completed. Either way the completed session exposes the subject (its id is SUBJECT), and GET /users lists the app’s subjects.

    curl -X POST https://api.finchnode.com/api/v1/connect/sessions/$SESSION_ID/simulate -H "Authorization: Bearer $FINCHNODE_API_KEY" -H "Content-Type: application/json" -d '{"scenario":"baseline-adult"}'
  2. Read a snapshot

    The snapshot is filtered by active consent. dataAsOf is the oldest relevant source watermark, lastSuccessfulSyncAt the most recent completed sync, and warnings keep source- and category-level partial failures. An empty category array means the source returned no current records; a category in missingCategories means FinchNode could not establish a complete read. These are not equivalent.

    curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records?categories=labs,medications" -H "Authorization: Bearer $FINCHNODE_API_KEY"
  3. Page one category

    For bounded ingestion, read one category with limit from 1 to 100 (default 25) and follow nextCursor while hasMore is true. The first page pins meta.changeCursor, and every later page in that cursor chain returns the same checkpoint.

    curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records/labs?limit=100" -H "Authorization: Bearer $FINCHNODE_API_KEY"
  4. Consume changes from the checkpoint

    CHANGE_CURSOR is the meta.changeCursor you saved after a complete read, and after that the last nextCursor the feed returned. Apply rows in ascending sequence order and persist every returned nextCursor, even when hasMore is false. changeType upsert identifies a new or content-changed source record, and its record holds the current normalized representation when the record still exists. An older upsert can have record null if a later deletion happened before the historical page was read: keep advancing the cursor through the feed, where the later tombstone remains authoritative. changeType delete is a tombstone whose record is always null, so delete the local object matching recordId.

    curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records/labs/changes?cursor=$CHANGE_CURSOR&limit=100" -H "Authorization: Bearer $FINCHNODE_API_KEY"
  5. Stop when consent says stop

    403 app_scope_exceeded means the category is outside the app’s current allowlist. 403 consent_scope_exceeded means this user did not authorize it. 410 consent_inactive means all relevant share consent is revoked or expired; it is returned on every read after revocation, including category and change-feed routes. Stop reading and apply your deletion and retention policy.

    curl -i "https://api.finchnode.com/api/v1/users/$SUBJECT/records" -H "Authorization: Bearer $FINCHNODE_API_KEY"   # 410 consent_inactive once all relevant share consent is inactive

Reference

Endpoints
MethodPathOpenAPI operationId
GEThttps://api.finchnode.com/api/v1/userslistUsers
GEThttps://api.finchnode.com/api/v1/users/{subject}/recordsgetUserRecord
GEThttps://api.finchnode.com/api/v1/users/{subject}/records/{category}listCategoryRecords
GEThttps://api.finchnode.com/api/v1/users/{subject}/records/{category}/changeslistRecordChanges

What it does not do

  • It never returns data without active consent: once all relevant share consent is revoked or expired, reads fail closed with 410 consent_inactive, including category and change-feed routes.
  • It does not interpret a failed source query as deletion: FinchNode reconciles only resource searches that succeeded and were authorized.
  • It does not compute an exact total for user lists: total is null, so follow nextCursor while hasMore is true.
  • It is read-only. It does not provide EHR write-back, scheduling, provider-side bulk access, or a general-purpose HL7 interface.
  • Subject IDs are stable only within one app: the same person gets a different ID for every unrelated app.

Frequently asked questions

What health record data is available through the API?

The API supports demographics, medications, conditions, allergies, vital signs, laboratory results, immunizations, and source-specific coverage and claims data, subject to patient consent and source availability.

Is the FinchNode EHR API based on FHIR?

Yes. FinchNode connects to supported sources using SMART on FHIR and FHIR R4 where available, then presents stable, normalized resources through its developer API.

What do 403 and 410 responses mean on a read?

403 app_scope_exceeded: the category is outside the app’s current allowlist. 403 consent_scope_exceeded: the app may request the category, but this user did not authorize it. 410 consent_inactive: all relevant share consent is revoked or expired; stop reading and apply your deletion and retention policy.

How should my client retry?

Retry 429 after Retry-After, with jitter. Retry 5xx and network failures with exponential backoff. Do not blindly retry other 4xx errors; use the stable error.code to change the request or user flow. RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset describe the key budget, and error.requestId identifies a request for support.

Is there a free EHR API for real patient data in production?

Yes. With a FinchNode account, developers get free, self-serve production access to real, patient-authorized records. Free ($0/month) includes one production application, a synthetic sandbox included with every account, hosted consent and patient controls, scheduled sync, and signed webhooks. Pro ($99/month) includes up to 5 production applications, email support, and everything in Free. Usage-based pricing is not in effect. Owners enable production when application settings are complete; FinchNode can suspend access. Live reads require production credentials and patient authorization and consent; availability varies by organization and scopes.