# AI agents and medical data

> Controlled access for AI agents to patient records: OAuth scopes, the tools an agent needs, audit and provenance of what it reads and writes, and event-driven agents.

Source: https://developers.anpheros.com/guides/ai-agents-medical-data

**An AI agent is a program in which a language model decides, step by step, which actions to take — which data to read, which tool to call, what to write — to complete a task.** When the task involves a patient's medical record, the agent needs controlled access: it should reach only the records it is allowed to, only the parts of them it needs, leave a trace of what it read and mark what it wrote as AI-generated. Anpheros provides that controlled access through its API; the agent framework and the model are yours.

Anpheros has no built-in agent runtime, no Model Context Protocol (MCP) server and no official integration with any agent framework or model provider. Agents use it the way any application does: through the REST and FHIR API or the SDKs.

## The architecture

```
Patient ── grants access (OAuth scopes, duration) ────────────────┐
                                                                  ▼
Agent runtime (your code, any framework) ── tools ──► Anpheros API ──► the patient's FHIR record
      │   ▲                                   │
      │   └── context, search results ◄───────┘      every call: scope checks + access log
      ▼
The model you choose (hosted or local) ── plans the next step
```

The model proposes actions; your runtime executes them with the credential it holds. The model itself never holds an Anpheros key or token.

## Controlled access

- **Whose records.** With an API key the agent sees the patients your project created. To act on someone's existing record it needs an OAuth grant from that person — for one patient, for 30 to 365 days.
- **Which data.** Scopes limit resource types and actions, optionally to a category: `patient/Observation.rs?category=laboratory` lets an agent read and search lab results and nothing else. To build a context, the grant must allow reading observations, conditions, medications and allergies (for example `patient/*.rs`).
- **Read-only by default.** Grant write scopes only when the agent is meant to record something.
- **Revocable.** The person can revoke the grant at any time; the agent's next call is refused.

## Tools an agent typically needs

| Tool | Anpheros call | Notes |
|---|---|---|
| Get context for a task | `POST /v1/context` | the first call for most tasks; budgeted and source-labelled ([LLM applications and healthcare data](https://developers.anpheros.com/guides/llm-healthcare-data)) |
| List recent results | `GET /v1/patients/{id}/observations?category=laboratory&from=…` | filters: `category`, `code`, `from`, `to` |
| Active medications | `GET /v1/patients/{id}/medications?status=active` | |
| What happened when | `GET /v1/patients/{id}/timeline?from=…` | one chronological list across types |
| Full FHIR detail | `GET /fhir/R4/{type}?patient=…` | when a summary is not enough |
| A standard summary | `GET /fhir/R4/Patient/{id}/$summary` | International Patient Summary bundle |
| Look up a code | `GET /v1/terminology/loinc?q=…` | bundled LOINC and ATC subsets |
| Record a note | `POST /v1/patients/{id}/observations` with `author_type: "ai"` | only with a write scope |

The OpenAPI specification (`https://developers.anpheros.com/openapi.json`) describes every endpoint and can be turned into tool definitions; the TypeScript and Dart SDKs can be wrapped as tools directly.

## Example: a visit-preparation agent with the patient's consent

A patient who already keeps a record in Anpheros asks a third-party assistant to prepare questions for a cardiology visit. The agent needs read access to the record for a limited time — nothing more.

**1. Consent (OAuth 2.1 + PKCE, SMART v2 scopes).** The assistant sends the person to the consent page with the narrowest scopes that still allow a context (observations, conditions, medications and allergies):

```ts
import { Anpheros, OAuthAuth, OAuthFlow, generatePkce } from '@anpheros/sdk';

const flow = new OAuthFlow({ baseUrl: 'https://platform.anpheros.com', clientId: CLIENT_ID, redirectUri: REDIRECT_URI });
const pkce = await generatePkce();
const url = flow.authorizeUrl({
  scopes: ['patient/Observation.rs', 'patient/Condition.rs', 'patient/MedicationStatement.rs',
           'patient/AllergyIntolerance.rs', 'offline_access'],
  state, pkce, purpose: 'treatment',
});
// … the person chooses the record and the duration (30–365 days) and allows; on the callback:
const tokens = await flow.exchange(code, pkce);
const anpheros = new Anpheros({ auth: new OAuthAuth({ tokens, onRefresh: flow.refresh }) });
const patient = tokens.patient!;   // this application's own (pairwise) id for the person
```

**2. Tools.** The agent runtime exposes a few functions to the model; each one is a real API call made with the access token:

```ts
const tools = {
  // POST /v1/context — relevant sections, within a budget, labelled with author type and source
  get_context: (a: { question: string }) =>
    anpheros.context.build({ patient, task: 'prepare a cardiology visit', question: a.question, budget_tokens: 2000, format: 'text' }),
  // GET /v1/patients/{id}/observations?category=laboratory&code=…&from=…
  list_lab_results: (a: { code?: string; from?: string }) =>
    anpheros.observations.list(patient, { category: 'laboratory', code: a.code, from: a.from, limit: 20 }),
  // GET /v1/patients/{id}/timeline?from=…
  get_timeline: (a: { from: string }) => anpheros.timeline.list(patient, { from: a.from, limit: 50 }),
};
```

Describe them to the model in whatever tool or function-calling format your model API uses; the model proposes a call, your runtime runs it and returns the JSON. The model never sees the token.

**3. What the platform enforces.** The token reaches only that person's record and only the scoped types; anything else answers `403` (a type outside the scopes) or `404` (another patient). Every call — including each context request, with its `manifest_id` — appears in the person's access log for this application. If the person revokes access, the next call answers `401` and the agent must stop; the refresh token cannot bring it back.

**4. Output.** The agent returns questions for the doctor with the sources it used. It writes nothing back, because it was not given a write scope.

## Audit and provenance

- Every call the agent makes is an access to the record and appears in the patient's access log for your application — including each context request, which also stores a manifest of the sections and sources used.
- Everything the agent writes is recorded as AI-authored in `Provenance`. When a context is built later, AI-written values are listed under `ai_notes`, apart from clinical facts, so an agent does not end up citing its own earlier output as evidence.

## Starting agents from events

Webhooks let an agent run when something changes — a new lab result (`resource.created`), a new consent (`consent.granted`) — instead of polling. Events carry ids only; the agent then reads what its scopes allow. [Webhooks](https://developers.anpheros.com/guides/webhooks)

## Design rules worth keeping

1. Give the agent the narrowest scopes that complete the task.
2. Start from a context, not from raw bulk reads; respect `omitted` and `warnings`.
3. Show the patient's data sources in the agent's answer.
4. Never let the agent present a diagnosis or change a treatment on its own; route such outputs to a clinician.
5. Log the `manifest_id` of each context next to the agent's answer, so a person can later see what the answer was based on.

## Related

- [FHIR MCP server for AI agents](https://developers.anpheros.com/guides/mcp)
- [AI healthcare applications](https://developers.anpheros.com/guides/ai-healthcare)
- [LLM applications and healthcare data](https://developers.anpheros.com/guides/llm-healthcare-data)
- [AI medical assistant](https://developers.anpheros.com/guides/ai-medical-assistant)
- [Consent and access model](https://developers.anpheros.com/guides/consent)
- [Authentication and OAuth](https://developers.anpheros.com/guides/authentication)
