# 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](/signup) if you don't have one.
- An application in the console under [Applications](/console/applications), with `medications` and `labs` among its data categories.
- A [sandbox](/docs/resources/glossary#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:

```bash
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:

   ```bash
   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:

   ```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:

   ```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.

   ```json
   {
     "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:

   ```bash
   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:

   ```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:

   ```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:

   ```bash
   curl "https://api.finchnode.com/api/v1/connect/sessions/$SESSION_ID" \
     -H "Authorization: Bearer $FINCHNODE_API_KEY"
   ```
   Node.js:

   ```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:

   ```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'`.

   ```json
   {
     "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:

   ```bash
   curl "https://api.finchnode.com/api/v1/users/$SUBJECT/records/medications?limit=25" \
     -H "Authorization: Bearer $FINCHNODE_API_KEY"
   ```
   Node.js:

   ```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:

   ```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):

   ```json
   {
     "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

- [After the patient connects](/docs/connect/after-the-patient-connects): tie a finished session to your own user.
- [Record model and categories](/docs/records/record-model): every field and category.
- [Scenarios](/docs/testing/scenarios): try failures, partial consent, and revoked access.
