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

FinchNode

Patient-authorized EHR integration

Quickstart

Get a synthetic patient's records into your terminal with a sandbox key, no hospital sign-in needed.

You'll create a Connect session, let the sandbox complete it with a synthetic patient, and read that patient's medications. Every record here is synthetic.

Before you start

  • A FinchNode developer account. Sign up if you don't have one.
  • An application in the console under Applications, with medications and labs among its data categories.
  • A sandbox key for it, starting with ck_test_. The console shows it once, so copy it then.
  • curl, Node.js 18 or later, or Python 3 with requests (pip install requests).

Put the key in your shell, not in code or URLs:

export FINCHNODE_API_KEY='PASTE_YOUR_CK_TEST_KEY'

Read a synthetic patient's records

  1. Create a Connect session. Both fields are optional; categories must be on your application's list.

    cURL
    curl -X POST "https://api.finchnode.com/api/v1/connect/sessions" \
      -H "Authorization: Bearer $FINCHNODE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"externalId": "user_123", "categories": ["medications", "labs"]}'
    Node.js
    const response = await fetch('https://api.finchnode.com/api/v1/connect/sessions', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.FINCHNODE_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ externalId: 'user_123', categories: ['medications', 'labs'] }),
    });
    if (!response.ok) throw new Error(`FinchNode returned ${response.status}`);
    const session = await response.json();
    Python
    import os
    import requests
    
    response = requests.post(
        "https://api.finchnode.com/api/v1/connect/sessions",
        headers={"Authorization": f"Bearer {os.environ['FINCHNODE_API_KEY']}"},
        json={"externalId": "user_123", "categories": ["medications", "labs"]},
    )
    response.raise_for_status()
    session = response.json()

    You get a 201 with the session (abbreviated here). Its link is for one patient and expires in 24 hours.

    {
      "id": "cs_4b2e9d0c7a1f5e3d8c6b",
      "object": "connect_session",
      "status": "pending",
      "categories": ["medications", "labs"],
      "subject": null,
      "environment": "sandbox",
      "expiresAt": "2026-10-01T18:00:00.000Z",
      "url": "https://finchnode.com/connect/cs_4b2e9d0c7a1f5e3d8c6b"
    }
    

    In a shell, save the ID with export SESSION_ID='PASTE_THE_ID'.

  2. Complete it with a synthetic patient. In the sandbox you can skip the patient's browser; baseline-adult is a fictional adult with diabetes and hypertension.

    cURL
    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"}'
    Node.js
    const simulated = await fetch(`https://api.finchnode.com/api/v1/connect/sessions/${session.id}/simulate`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.FINCHNODE_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ scenario: 'baseline-adult' }),
    });
    if (simulated.status !== 202) throw new Error(`FinchNode returned ${simulated.status}`);
    Python
    simulated = requests.post(
        f"https://api.finchnode.com/api/v1/connect/sessions/{session['id']}/simulate",
        headers={"Authorization": f"Bearer {os.environ['FINCHNODE_API_KEY']}"},
        json={"scenario": "baseline-adult"},
    )
    simulated.raise_for_status()

    You get a 202, and the simulation runs in the background.

  3. Wait for it to finish. Poll until simulation.state is completed, and stop if it's failed.

    cURL
    curl "https://api.finchnode.com/api/v1/connect/sessions/$SESSION_ID" \
      -H "Authorization: Bearer $FINCHNODE_API_KEY"
    Node.js
    let current;
    for (let attempt = 0; attempt < 60; attempt += 1) {
      await new Promise((resolve) => setTimeout(resolve, 2000));
      const res = await fetch(`https://api.finchnode.com/api/v1/connect/sessions/${session.id}`, {
        headers: { Authorization: `Bearer ${process.env.FINCHNODE_API_KEY}` },
      });
      if (!res.ok) throw new Error(`FinchNode returned ${res.status}`);
      current = await res.json();
      if (['completed', 'failed'].includes(current.simulation.state)) break;
    }
    if (current.simulation.state !== 'completed') {
      throw new Error(`Simulation did not complete: ${current.simulation.failureCode ?? 'timed out'}`);
    }
    Python
    import time
    
    for attempt in range(60):
        time.sleep(2)
        polled = requests.get(
            f"https://api.finchnode.com/api/v1/connect/sessions/{session['id']}",
            headers={"Authorization": f"Bearer {os.environ['FINCHNODE_API_KEY']}"},
        )
        if not polled.ok:
            raise RuntimeError(f"FinchNode returned {polled.status_code}")
        current = polled.json()
        if current["simulation"]["state"] in ("completed", "failed"):
            break
    if current["simulation"]["state"] != "completed":
        raise RuntimeError(f"Simulation did not complete: {current['simulation'].get('failureCode', 'timed out')}")

    When it completes, subject holds the patient's ID for your app. In a shell, export SUBJECT='PASTE_THE_SUBJECT'.

    {
      "id": "cs_4b2e9d0c7a1f5e3d8c6b",
      "status": "completed",
      "subject": "u_4f3a9c1e2b7d6a05",
      "simulation": { "scenario": "baseline-adult", "state": "completed" }
    }
    
  4. Read the records. Use the subject from this session, never one looked up from GET /users.

    cURL
    curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records/medications?limit=25" \
      -H "Authorization: Bearer $FINCHNODE_API_KEY"
    Node.js
    const recordResponse = await fetch(
      `https://api.finchnode.com/api/v1/users/${current.subject}/records/medications?limit=25`,
      { headers: { Authorization: `Bearer ${process.env.FINCHNODE_API_KEY}` } },
    );
    if (!recordResponse.ok) throw new Error(`FinchNode returned ${recordResponse.status}`);
    const records = await recordResponse.json();
    Python
    record_response = requests.get(
        f"https://api.finchnode.com/api/v1/users/{current['subject']}/records/medications",
        headers={"Authorization": f"Bearer {os.environ['FINCHNODE_API_KEY']}"},
        params={"limit": 25},
    )
    if not record_response.ok:
        raise RuntimeError(f"FinchNode returned {record_response.status_code}")
    records = record_response.json()

    You get one page of records in FinchNode's normalized format (abbreviated, synthetic):

    {
      "object": "list",
      "category": "medications",
      "data": [
        {
          "id": "rec_a81e05c4c049bf549c1e4fc5",
          "resourceType": "MedicationRequest",
          "name": "Metformin 500 mg tablet",
          "status": "active",
          "startDate": "2026-07-18",
          "source": "northstar-health",
          "sourceName": "Northstar Health System (Synthetic)",
          "codes": [{ "system": "http://www.nlm.nih.gov/research/umls/rxnorm", "code": "861007", "display": "Metformin 500 MG Oral Tablet" }]
        }
      ],
      "hasMore": false,
      "nextCursor": null
    }
    

Why poll simulation.state and not status? For multi-source-overlap, source-unavailable, and consent-revoked, status reads completed before the simulation finishes.

Sandbox limits

An application holds up to 25 active synthetic patients by default. Each becomes eligible for purge 7 days after it's created. Past the limit, simulate returns 409 sandbox_limit_reached.

Connect through the browser instead

Open the session's url instead of calling simulate. With a sandbox key, the health-system search starts with a list of synthetic health systems. Pick a scenario's source, such as Northstar Health System (Synthetic), and finish the two consent screens. No hospital sign-in is needed.

You'll also go through the patient's account step. If you aren't already signed in to a FinchNode account in that browser, it asks you to create one and verify the email.

What's next