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

FinchNode

Patient-authorized EHR integration

Read and paginate

Import a category by following its cursor until hasMore is false, then keep it current with the change feed.

Page through a category

curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records/labs?limit=100" \
  -H "Authorization: Bearer $FINCHNODE_API_KEY"

Each page has data, hasMore, nextCursor, and meta. Send nextCursor back as cursor until hasMore is false. Keep the first page's meta.changeCursor: you'll need it for the change feed.

Node.js
async function importCategory(subject, category) {
  const records = [];
  let cursor = null;
  let first;
  for (;;) {
    const url = new URL(`https://api.finchnode.com/api/v1/users/${subject}/records/${category}`);
    url.searchParams.set('limit', '100');
    if (cursor) url.searchParams.set('cursor', cursor);
    const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.FINCHNODE_API_KEY}` } });
    if (!res.ok) throw new Error(`FinchNode returned ${res.status}`);
    const page = await res.json();
    first ??= page;
    records.push(...page.data);
    if (!page.hasMore) break;
    cursor = page.nextCursor;
  }
  return { records, changeCursor: first.meta.changeCursor, meta: first.meta };
}
Python
import os
import requests

def import_category(subject, category):
    records, cursor, first = [], None, None
    while True:
        params = {"limit": 100}
        if cursor:
            params["cursor"] = cursor
        res = requests.get(
            f"https://api.finchnode.com/api/v1/users/{subject}/records/{category}",
            headers={"Authorization": f"Bearer {os.environ['FINCHNODE_API_KEY']}"},
            params=params,
        )
        if not res.ok:
            raise RuntimeError(f"FinchNode returned {res.status_code}")
        page = res.json()
        first = first or page
        records.extend(page["data"])
        if not page["hasMore"]:
            return {"records": records, "change_cursor": first["meta"]["changeCursor"], "meta": first["meta"]}
        cursor = page["nextCursor"]

The Python example raises its own error instead of raise_for_status(), because that message includes the URL, and the URL includes the subject.

Check the import is complete

Before you mark a category imported, read meta:

  • syncStatus is complete, not partial or not_started.
  • The category is in availableCategories and not in missingCategories.

If either fails, store what you got, mark it partial, and read again later. See Freshness, partial and stale records.

Limits

  • limit is 1 to 100. It defaults to 25.
  • Use a cursor only with the endpoint, subject, and category that returned it. FinchNode doesn't always catch a mismatch, and a reused cursor can skip records.
  • Don't build or edit cursors. They're opaque.

Delegated credentials

A delegated agent credential gets meta.changeCursor: null on snapshots. Use meta.changeCursors[category], or the changeCursor from a category page.

List your patients

GET /users returns a page of subjects who currently share with your app, with the same limit and cursor. Use it for admin views and reconciliation, not to find the patient behind a session you just completed. Use cursor; offset remains only for older integrations.