> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/developer-tools/example-apps/terra-panel.md).

# Terra Panel

A clinician-facing dashboard that joins lab results to wearable data. Upload a lab report PDF and the [Lab Reports API](https://docs.tryterra.co/lab-reports/lab-reports) returns standardized biomarkers with ranges, flags, and provenance, which the app charts against the same patient's sleep, resting heart rate, and training history.

Without credentials it runs in demo mode on bundled synthetic data: four patients, three years of lab history for one of them, and 18 months of wearable data.

```bash
npm create tryterra-app -- --template lab-reports-web-app
cd my-app && npm install && npm run dev
```

## What it demonstrates

* [Upload and standardization](https://docs.tryterra.co/lab-reports/quickstart): PDF to canonical biomarkers with ranges, flags, and provenance
* Cross-report trends matched by [`biomarker.key`](https://docs.tryterra.co/lab-reports/biomarker-reference), so a marker charts across labs, languages, and formats
* Joining labs to wearable data by `reference_id`, the only key they share
* A timeline plotting lab draws over resting heart rate, HRV, sleep, and training lanes
* Pre-draw context: recent exertion and short sleep sit next to each report, since both skew results
* Deterministic domain scoring with an explainable breakdown, kept separate from the AI layer
* Chronicity assessed against each patient's own baseline
* AI insights with schema-validated structured output, cached by input hash

## Run it yourself

{% hint style="info" %}
**Prerequisites:** [Node.js](https://nodejs.org/) v20+. Demo mode needs nothing else. For live data you need a Terra **Dev ID** and **API key** with **Lab Reports** enabled, plus the wearable data types you want.
{% endhint %}

{% stepper %}
{% step %}

#### Start in demo mode

```bash
npm create tryterra-app -- --template lab-reports-web-app
cd my-app
npm install
npm run dev
```

Open <http://localhost:5173>. Mutating actions are disabled behind a banner. This is the same read path as live mode, served from a local cache.
{% endstep %}

{% step %}

#### Go live

Copy `.env.example` to `.env`, fill in `TERRA_DEV_ID` and `TERRA_API_KEY`, then run `npm run seed` to create four patients and print their `reference_id`s.
{% endstep %}

{% step %}

#### Upload reports

Upload the PDFs in `sample-reports/`, oldest first so trends build up, or your own. Results appear grouped by panel with range bars, flags, and deltas against the previous report.
{% endstep %}

{% step %}

#### Add wearable data

Use the in-app **Connect wearable** button, or seed synthetic data with `npm run seed:wearables -- <referenceId>`. Lab draws then appear as markers over daily metrics.
{% endstep %}
{% endstepper %}

Add an `ANTHROPIC_API_KEY` for the Intelligence Brief, chat, and wearable-signal insights on flagged values. Without it the rest of the app still works.

## How the integration works

Lab sessions are immutable once terminal, so the app caches terminal payloads write-through and refetches anything still in flight.

{% code title="src/server/lib/lab-reports.ts" %}

```typescript
export async function getSession(sessionId: string): Promise<LabReportSession> {
  const [hit] = await db
    .select()
    .from(schema.labSessionCache)
    .where(eq(schema.labSessionCache.sessionId, sessionId));

  // Terminal sessions never change, so a cached payload can't go stale
  if (hit && TERMINAL_LAB_STATUSES.has(hit.status)) {
    return withComputedFlags(JSON.parse(hit.payload) as LabReportSession);
  }

  const session = await client.get(`/lab-reports/${sessionId}`);
  await cacheIfTerminal(session);
  return withComputedFlags(session);
}
```

{% endcode %}

Some standardizations return `normal` for in-range rows but `null` for out-of-range ones. `withComputedFlags` fills that gap on the read path when the value and its applied range are both present, marking what it derived as `source: "computed"`. The cache keeps Terra's payload verbatim.

## Explore the code

<a href="https://github.com/tryterra/terra-examples/tree/main/packages/cli/templates/lab-reports-web-app" class="button primary" data-icon="github">View the source</a>

`AGENTS.md` maps the integration code worth lifting.

| Path                            | What it holds                                             |
| ------------------------------- | --------------------------------------------------------- |
| `src/server/lib/lab-reports.ts` | Session access, cache, computed flags; start here         |
| `src/server/lib/terra/`         | API client, typed responses, error handling               |
| `src/server/lib/wearables.ts`   | Per-user, per-resource, per-day caching                   |
| `src/server/lib/analysis/`      | Declarative registry of domains, weights, guideline bands |
| `src/server/lib/ai.ts`          | Structured-output insights, cached by input hash          |
| `scripts/`                      | Sample report generation and wearable seeding             |
