> For the complete documentation index, see [llms.txt](https://docs.bloodgpt.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bloodgpt.com/reference/widgets.md).

# Widgets SDK & API

Embed BloodGPT report widgets with the @bloodgpt/widgets React SDK — install, quickstart, and the session-token API

The technical reference for the `@bloodgpt/widgets` React SDK — install, components, and the session-token + read-endpoint API. For when to choose this approach over the other integration options, see the [Widgets integration guide](/integration-guides/integration-options/widgets.md).

You embed BloodGPT report views in your own web app, and the flow has two halves:

1. **Your backend** mints a short-lived, patient-scoped session token with your API key.
2. **The browser** uses that `wgt_` token (never your API key) to read report data.

```mermaid
sequenceDiagram
    participant Browser
    participant Your Backend
    participant BloodGPT API

    Browser->>Your Backend: request widget token (your auth)
    Your Backend->>BloodGPT API: POST /api/v1/widget-sessions (API key)
    BloodGPT API-->>Your Backend: 201 { session_token: "wgt_..." }
    Your Backend-->>Browser: session_token
    Browser->>BloodGPT API: GET /api/v1/widgets/... (Bearer wgt_...)
    BloodGPT API-->>Browser: report data
```

***

## Install

The widgets ship as a React package, `@bloodgpt/widgets`.

{% tabs %}
{% tab title="npm" %}

```bash
npm install @bloodgpt/widgets @tanstack/react-query next-intl
```

{% endtab %}

{% tab title="pnpm" %}

```bash
pnpm add @bloodgpt/widgets @tanstack/react-query next-intl
```

{% endtab %}

{% tab title="yarn" %}

```bash
yarn add @bloodgpt/widgets @tanstack/react-query next-intl
```

{% endtab %}
{% endtabs %}

**Peer dependencies:** `react` ≥ 18, `react-dom` ≥ 18, `@tanstack/react-query` ≥ 5, `next-intl` ≥ 4.

Import the stylesheet **once** in your app entry (e.g. `app/layout.tsx`):

```ts
import "@bloodgpt/widgets/styles.css";
```

{% hint style="info" %}
`@bloodgpt/widgets` is published on [npm](https://www.npmjs.com/package/@bloodgpt/widgets) and is currently **alpha** (`0.1.0-alpha.0`) — the API may still change. The [example app](#run-the-example-app) below is the quickest way to see it working.
{% endhint %}

## Run the Example App

The fastest way to see the widgets is the open-source example — a Next.js 15 (App Router) app that runs end-to-end with a **built-in mock backend, so it needs no credentials**:

```bash
git clone https://github.com/Realai-plus/bloodgpt-widgets-example
cd bloodgpt-widgets-example
pnpm install
pnpm dev        # → http://localhost:3100
```

It showcases all five widgets, the hook escape hatch (`useTestOverview`), and the real server-minted session-token flow. To switch from the mock to live data, set `NEXT_PUBLIC_BLOODGPT_API_URL` to a BloodGPT deployment and `BLOODGPT_API_KEY` to your API key — see the repo's `.env.example`.

**Repo:** [github.com/Realai-plus/bloodgpt-widgets-example](https://github.com/Realai-plus/bloodgpt-widgets-example)

## Quickstart

**1. Mint a session token on your backend** — this keeps your API key server-side:

```ts
// e.g. a Next.js route handler or an Express endpoint
const res = await fetch("https://sandbox.bloodgpt.com/api/v1/widget-sessions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.BLOODGPT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ external_patient_id: "your-patient-id", ttl_seconds: 900 }),
});
const { session_token } = await res.json();
```

**2. Render a widget on the frontend** — hand the token to `BloodGPTProvider`:

```tsx
import { BloodGPTProvider, TestReport } from "@bloodgpt/widgets";
import "@bloodgpt/widgets/styles.css";

export function PatientReport({ token, testId }: { token: string; testId: string }) {
  return (
    <BloodGPTProvider sessionToken={token}>
      <TestReport testId={testId} />
    </BloodGPTProvider>
  );
}
```

The provider calls the [widget read endpoints](#widget-read-endpoints) under the hood using the token — you don't call them directly.

{% hint style="warning" %}
**Alpha wiring.** In `0.1.0-alpha.0`, `BloodGPTProvider` needs a couple of extra props for a clean setup — a bound `fetch` wrapper, the `next-intl` message namespaces, and a complete stylesheet. The [example app](#run-the-example-app) ships the production-ready wiring; copy its `src/components/provider.tsx` and `src/app/globals.css` as a starting point.
{% endhint %}

## Components & Hooks

Each component has a matching hook, in case you want the raw data and your own rendering.

| Component                         | Hook                | Renders                                                                 |
| --------------------------------- | ------------------- | ----------------------------------------------------------------------- |
| `<ReportsList externalPatientId>` | `useReports`        | The patient's reports, newest first                                     |
| `<TestOverview testId>`           | `useTestOverview`   | Demographics, scan metrics, and a narrative summary                     |
| `<TestParameters testId>`         | `useTestParameters` | Biomarker panels with filtering, sparklines, and trend charts           |
| `<TestFollowUps testId>`          | `useTestFollowUps`  | Follow-up schedule and priority retests                                 |
| `<TestReport testId>`             | —                   | Composite of overview + parameters + follow-ups (the most common embed) |
| —                                 | `useTestTrends`     | Value-over-time series for a single parameter                           |

***

## Mint a Widget Session Token

**Endpoint:** `POST /api/v1/widget-sessions` **Auth:** your organization API key (`Authorization: Bearer YOUR_API_KEY`)

Mints a read-only token bound to a single external patient. Call this from your **backend** only.

### Request

```json
{
  "external_patient_id": "pat-123",
  "scopes": ["reports:read", "test:read"],
  "ttl_seconds": 900
}
```

| Field                 | Type      | Required | Description                                                    |
| --------------------- | --------- | -------- | -------------------------------------------------------------- |
| `external_patient_id` | string    | Yes      | Your patient identifier (1–255 chars) the token is bound to    |
| `scopes`              | string\[] | No       | Subset of `reports:read`, `test:read`. Defaults to all scopes. |
| `ttl_seconds`         | integer   | No       | Token lifetime in seconds (max 3600, default 900 = 15 min)     |

### cURL Example

```bash
curl -X POST https://sandbox.bloodgpt.com/api/v1/widget-sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_patient_id": "pat-123", "ttl_seconds": 900}'
```

### Response (201 Created)

```json
{
  "request_id": "req_abc123",
  "session_token": "wgt_eyJhbGci...",
  "expires_at": "2024-01-15T11:30:00Z",
  "external_patient_id": "pat-123",
  "scopes": ["reports:read", "test:read"]
}
```

Returns `422` with an `invalid_body` error if the request fails validation.

{% hint style="warning" %}
The `wgt_` token is safe to expose to the browser; your API key is not. Mint a fresh token per session and keep the TTL short.
{% endhint %}

***

## Widget Read Endpoints

**Auth:** the minted token, sent as `Authorization: Bearer wgt_<token>`.

These endpoints are consumed by the `@bloodgpt/widgets` SDK; the full per-parameter response shapes are owned by the SDK types. Each is scoped to the patient the token was minted for.

| Endpoint                                                      | Returns                                                                                                           |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GET /api/v1/widgets/patients/by-external/{externalId}/tests` | The patient's test reports with per-report counts (`total`/`normal`/`abnormal`/`critical`) and a headline summary |
| `GET /api/v1/widgets/tests/{testId}/overview`                 | Test metrics + AI overview                                                                                        |
| `GET /api/v1/widgets/tests/{testId}/parameters`               | Parameters grouped by panel, with trend data                                                                      |
| `GET /api/v1/widgets/tests/{testId}/follow-ups`               | Follow-up schedule and urgent tests                                                                               |
| `GET /api/v1/widgets/tests/{testId}/trends?parameter={name}`  | Value-over-time series for a single parameter                                                                     |

### Example

```bash
curl "https://sandbox.bloodgpt.com/api/v1/widgets/tests/clxyz789012/overview" \
  -H "Authorization: Bearer wgt_eyJhbGci..."
```

### Errors

| Status | `error`         | Meaning                                                                      |
| ------ | --------------- | ---------------------------------------------------------------------------- |
| `401`  | `invalid_token` | Missing `Authorization` header or a token that is not a `wgt_` session token |
| `403`  | —               | The token's scopes do not permit this endpoint                               |

A `401` is also returned when the token has expired — mint a new one via `POST /api/v1/widget-sessions`.
