---
title: Fetching a patient's full record
description: Pull a patient's whole clinical record — encounters, observations, notes, documents, medications, and orders — in one Patient/$everything Bundle, scope-filtered, with date and type narrowing.
nav: Recipes
order: 55
version: v1
source: handwritten
updated: 2026-06-26
---

# Fetching a patient's full record

Pull everything you are allowed to see about one patient in a single read. The
`Patient/$everything` operation aggregates the patient's clinical record — `Encounter`,
`Observation`, `Composition`, `DocumentReference`, `MedicationRequest`, and `ServiceRequest`,
plus the `Patient` itself — into one `searchset` `Bundle`. It is **read-only** and
**scope-filtered**: each resource type appears only if your token carries read scope for it, and
the Bundle tells you, in-band, which types it withheld.

This is the fastest way to hydrate a record without orchestrating six separate searches. The
trade-off to understand up front: the result is shaped by your scopes. A token that can read
encounters but not documents gets the encounters and a machine-readable note that documents were
held back — never a silent omission.

## Audience

You build a record-sync, a care-summary view, or a migration that ingests a patient's full chart.
You read a `Bundle` without a viewer, you know what a FHIR reference is, and you want one call that
returns as much of a patient's record as your token is entitled to.

## You'll need

- A bearer token from HuliPractice (**Practice Settings → Integrations → API Keys**), or a
  SMART Backend Services access token. See [Bearer Tokens](/v1/auth/bearer) and
  [`POST /auth/token`](/v1/auth).
- <Scope>system/Patient.rs</Scope> at minimum — the operation gates on patient read. Then add a
  read scope for **each** type you want included:
  - <Scope name="system/Encounter.rs" /> and <Scope name="system/Observation.rs" /> (the
    BAA-gated **Clinical information** card), plus <Scope name="system/MedicationRequest.rs" />
    and <Scope name="system/ServiceRequest.rs" />.
  - <Scope name="system/Composition.rs" /> and <Scope name="system/DocumentReference.rs" /> (the
    BAA-gated **Clinical information** card).
    A type whose read scope is absent is **withheld**, not an error — the call still succeeds.
- The `id` of the patient. Resolve it with
  [a Patient search](/v1/recipes/getting-started-patient-search) if you only hold a name.
- `curl`, or Node, Python, Java, or Go.

<Callout variant="info">
`$everything` only ever **reads**. A search-only token (`.s` without `.r`) does not satisfy the
per-type instance-read gate — disclosing a type's instances in the aggregate requires an instance
read grant (`.r`/`.rs`/`.cru`/`.crud`), so a type you can only search is reported as withheld.
</Callout>

## End state

You hold a `200 OK` whose body is a `searchset` `Bundle`. The patient is the first `match` entry;
each clinical resource you are scoped for follows as further `match` entries. If any type was
withheld for lack of scope, the Bundle carries a `meta.tag` of `scope-filtered` and an
`OperationOutcome` entry naming the withheld types.

## Steps

### 1. Export the token and the patient id

```bash
export HULI_TOKEN="<paste your bearer token here>"
export PATIENT_ID="01965e2a-8c4d-7000-9001-0000000000a2"
```

### 2. Call $everything

`GET` the operation on the patient instance. Optional parameters narrow the result:

- `start` / `end` — bound the clinical resources to a date window (`YYYY-MM-DD`).
- `_type` — a comma-separated list to include only specific types (e.g.
  `_type=Encounter,Observation`). Omit it to include every type you are scoped for.
- `_count` — the per-type page cap (default 50). When a type has more rows than the cap, the
  Bundle flags it as truncated.

<Endpoint method="GET" path="/fhir/R4/Patient/01965e2a-8c4d-7000-9001-0000000000a2/$everything?start=2026-01-01&_count=50" />

:::CodeGroup

```bash
curl "https://api.huli.ai/fhir/R4/Patient/01965e2a-8c4d-7000-9001-0000000000a2/\$everything?start=2026-01-01&_count=50" \
  -H "Authorization: Bearer $HULI_TOKEN" \
  -H "Accept: application/fhir+json"
```

```typescript
const id = process.env.PATIENT_ID!;
const params = new URLSearchParams({ start: '2026-01-01', _count: '50' });

const resp = await fetch(`https://api.huli.ai/fhir/R4/Patient/${id}/$everything?${params}`, {
  headers: {
    Authorization: `Bearer ${process.env.HULI_TOKEN}`,
    Accept: 'application/fhir+json',
  },
});

const bundle = (await resp.json()) as {
  entry?: { resource: { resourceType: string }; search?: { mode: string } }[];
  meta?: { tag?: { code: string }[] };
};

// match entries are the record; outcome entries carry withheld/truncation notices.
const matches = bundle.entry?.filter((e) => e.search?.mode === 'match') ?? [];
const notices = bundle.entry?.filter((e) => e.search?.mode === 'outcome') ?? [];
console.log(matches.map((e) => e.resource.resourceType));
if (bundle.meta?.tag?.some((t) => t.code === 'scope-filtered')) {
  console.log('some types withheld:', notices);
}
```

```python
import os
import requests

patient_id = os.environ["PATIENT_ID"]
resp = requests.get(
    f"https://api.huli.ai/fhir/R4/Patient/{patient_id}/$everything",
    params={"start": "2026-01-01", "_count": 50},
    headers={
        "Authorization": f"Bearer {os.environ['HULI_TOKEN']}",
        "Accept": "application/fhir+json",
    },
    timeout=30,
)
bundle = resp.json()
matches = [e for e in bundle.get("entry", []) if e.get("search", {}).get("mode") == "match"]
notices = [e for e in bundle.get("entry", []) if e.get("search", {}).get("mode") == "outcome"]
print([e["resource"]["resourceType"] for e in matches])
if any(t.get("code") == "scope-filtered" for t in bundle.get("meta", {}).get("tag", [])):
    print("some types withheld", notices)
```

```java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class PatientEverything {
    public static void main(String[] args) throws Exception {
        String id = System.getenv("PATIENT_ID");
        String url = "https://api.huli.ai/fhir/R4/Patient/" + id
            + "/$everything?start=2026-01-01&_count=50";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", "Bearer " + System.getenv("HULI_TOKEN"))
            .header("Accept", "application/fhir+json")
            .GET()
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());

        // Parse with a JSON library: match entries are the record, outcome entries
        // (search.mode=outcome) carry the withheld/truncation notices.
        System.out.println(response.statusCode());
        System.out.println(response.body());
    }
}
```

```go
package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	id := os.Getenv("PATIENT_ID")
	url := "https://api.huli.ai/fhir/R4/Patient/" + id +
		"/$everything?start=2026-01-01&_count=50"

	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer "+os.Getenv("HULI_TOKEN"))
	req.Header.Set("Accept", "application/fhir+json")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	// entry[].search.mode is "match" (the record) or "outcome" (withheld/truncation notices).
	fmt.Printf("%d\n%s\n", resp.StatusCode, body)
}
```

:::

First, resolve a real patient id in your sandbox:

<RunnableRequest method="GET" path="/fhir/R4/Patient?_count=1" recipe="fetching-a-patient-record" captures='[{"name":"patientId","path":"entry.0.resource.id"}]' />

Then run `$everything` against it:

<RunnableRequest method="GET" path="/fhir/R4/Patient/{{patientId}}/$everything?_count=50" recipe="fetching-a-patient-record" />

### 3. Read the Bundle — matches, and the in-band notices

Every record resource is an `entry` with `search.mode: "match"`; the `total` counts only those.
Any notice — withheld types, truncation — rides as an extra `entry` with
`search.mode: "outcome"` carrying an `OperationOutcome`, and does **not** count toward `total`.

```json
{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 3,
  "meta": {
    "tag": [
      { "system": "https://fhir.huli.ai/r4/CodeSystem/bundle-tags", "code": "scope-filtered" }
    ]
  },
  "link": [
    {
      "relation": "self",
      "url": "https://api.huli.ai/fhir/R4/Patient/01965e2a-8c4d-7000-9001-0000000000a2/$everything?start=2026-01-01&_count=50"
    }
  ],
  "entry": [
    {
      "fullUrl": "https://api.huli.ai/fhir/R4/Patient/01965e2a-8c4d-7000-9001-0000000000a2",
      "resource": { "resourceType": "Patient", "id": "01965e2a-8c4d-7000-9001-0000000000a2" },
      "search": { "mode": "match" }
    },
    {
      "fullUrl": "https://api.huli.ai/fhir/R4/Encounter/01965e2a-8c4d-7000-9060-0000000000e9",
      "resource": { "resourceType": "Encounter", "id": "01965e2a-8c4d-7000-9060-0000000000e9" },
      "search": { "mode": "match" }
    },
    {
      "fullUrl": "https://api.huli.ai/fhir/R4/Observation/01965e2a-8c4d-7000-9080-0000000000a7",
      "resource": { "resourceType": "Observation", "id": "01965e2a-8c4d-7000-9080-0000000000a7" },
      "search": { "mode": "match" }
    },
    {
      "resource": {
        "resourceType": "OperationOutcome",
        "issue": [
          {
            "severity": "information",
            "code": "informational",
            "diagnostics": "The following resource types were withheld because the access token lacks read scope for them: DocumentReference, MedicationRequest."
          }
        ]
      },
      "search": { "mode": "outcome" }
    }
  ]
}
```

Three things to lift from the Bundle:

- **The `meta.tag` of `scope-filtered`** is a fast machine signal that the result is incomplete by
  scope — check it before treating the Bundle as the whole record.
- **The `information` outcome** names the **withheld** types (type names only — never PHI). Add
  the missing read scopes to the key if you need them.
- **A `warning` outcome** (not shown above) names **truncated** types — a type had more rows than
  `_count`. Narrow `start`/`end` or raise `_count`, then re-read for the rest. A separate
  `warning` flags any type that failed to read (partial results), so a single type's outage never
  fails the whole call.

## What to verify

- HTTP status is `200`. <StatusBadge code="200" /> `resourceType` is `Bundle`, `type` is
  `searchset`.
- The first `match` entry is the `Patient`, and `total` equals the number of `match` entries.
- Every type you hold read scope for is present (within your date window and `_count`).
- If `meta.tag` is `scope-filtered`, the `information` outcome lists exactly the types you did not
  scope for.

## What can go wrong

All errors return a FHIR `OperationOutcome` — `{severity, code, diagnostics}`, no `details`
object. Branch on the HTTP status and `issue[0].code`; the Huli code is the prefix of
`issue[0].diagnostics`, split on `": "`.

<StatusBadge code="404" /> `HPB-00102` — **patient not found.** The id does not name a patient in
your organization (a patient outside your organization is invisible, not forbidden). Confirm the id and
the token's organization.

<StatusBadge code="403" /> `HPB-00104` — **insufficient scope.** The token lacks the baseline
<Scope>system/Patient.rs</Scope> the operation gates on. (Lacking a _per-type_ read scope does not
cause a `403` — that type is withheld in-band instead.)

<StatusBadge code="400" /> `HPB-00101` — **validation error.** The patient id is malformed, or a
parameter is invalid. Use a well-formed UUID and `YYYY-MM-DD` dates.

<Callout variant="note">
Withheld and truncated types are **not** errors — the call returns `200` with the partial record
and the in-band notices. Treat the `scope-filtered` tag and the `outcome` entries as the contract
for "what is missing and why", rather than inferring completeness from the absence of an error.
</Callout>

## Next recipes

- **[Writing and amending a clinical note](/v1/recipes/writing-a-clinical-note)** — read or amend
  the `Composition` notes that appear in the aggregate.
- **[Uploading a document](/v1/recipes/uploading-a-document)** — add the `DocumentReference`
  attachments the aggregate surfaces.
- **[Wiring a read-only partner](/v1/recipes/wiring-a-read-only-partner)** — the per-resource
  search alternative when you want to page one type at a time instead of one aggregate read.
