# Medication data for apps and AI agents

> Patient medication lists vs drug knowledge: FHIR resources, RxNorm, ATC, openFDA and EMA sources, and a tested join of a FHIR record with RxNorm and FDA labels.

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

**Healthcare software needs two different kinds of medication data: the patient's medication list — what this person takes, since when, at what dose, who recorded it — and drug knowledge — what a product contains, its official label, warnings and interactions.** The first belongs in the patient's record; the second comes from authoritative drug databases. Mixing them up is how AI agents end up stating drug facts from model memory. This page explains both, the FHIR R4 resources and code systems involved, which public sources exist in the US and the EU, and a working example that joins a patient's list from a FHIR record with RxNorm and the FDA label.

## Two kinds of medication data

| | Patient medication list | Drug knowledge |
|---|---|---|
| Answers | What does Ana take, since when, how often? | What is metformin 500 mg, what does its label warn about? |
| Changes | When the patient or a clinician updates it | When a regulator or publisher updates it |
| Lives in | The patient's record (EHR, PHR, FHIR server) | Drug databases and terminologies |
| FHIR | `MedicationStatement`, `MedicationRequest`, `MedicationAdministration`, `Medication` | Mostly outside the record; product information can itself be FHIR |
| Who may read it | Only with the patient's consent | Public or licensed |

An app or agent that answers "can I take ibuprofen with my blood pressure pill?" needs both: the patient's list to know which pill, and a drug knowledge source to know what is known about the combination — plus a clinician or pharmacist for the answer.

## FHIR resources for the medication list

- **MedicationStatement:** the person is taking, has taken or stopped a medicine (patient-reported or recorded by a clinician). The usual resource for a PHR or a reconciliation list.
- **MedicationRequest:** a prescription or order.
- **MedicationAdministration:** one dose given or taken (or skipped), useful for adherence.
- **Medication:** the product itself, when more detail than a code is needed (ingredients, form, batch).

The medicine is coded: in the US with **RxNorm** (from the National Library of Medicine) and NDC product codes; internationally with the WHO **ATC** classification; in Europe also with national product codes and, increasingly, the EMA's FHIR-based electronic product information. A statement can carry several codings for the same medicine.

## Drug knowledge sources

**United States**

- [RxNorm API](https://lhncbc.nlm.nih.gov/RxNav/APIs/RxNormAPIs.html) (NLM): normalised names and identifiers (RXCUI) for clinical drugs; free, and with one exception no licence is needed.
- [openFDA drug label API](https://open.fda.gov/apis/drug/label/) (FDA): the structured product labels — indications, boxed warnings, contraindications, drug interactions sections — searchable by RXCUI.
- DailyMed (NLM): the same labels for people to read.

**European Union**

- The European Medicines Agency publishes product information for centrally authorised medicines, and its electronic product information (ePI) is built on FHIR. Nationally authorised products are in national registers.
- ATC codes and defined daily doses are maintained by the WHO Collaborating Centre for Drug Statistics Methodology.

**Interactions** are the hardest part. NLM retired its drug-drug interaction API around January 2024, and comprehensive interaction checking today relies on licensed clinical knowledge bases. A label's interactions section is text written for clinicians, not a checker.

## Joining the record with drug knowledge

The example reads a patient's active medications from Anpheros, resolves each name to an RxNorm concept and fetches the FDA label. It was run against a local instance of the platform and the live RxNorm and openFDA APIs.

```ts
import { Anpheros, apiKey } from '@anpheros/sdk';

const anpheros = new Anpheros({ auth: apiKey(process.env.ANPHEROS_KEY!) });

// 1. What the person takes: from their record (ATC codes, names as they were entered)
const { data: meds } = await anpheros.medications.list(patientId, { status: 'active' });

// 2. What each medicine is: RxNorm (US National Library of Medicine), free, no key
async function rxcui(name: string): Promise<string | null> {
  const r = await fetch(`https://rxnav.nlm.nih.gov/REST/approximateTerm.json?term=${encodeURIComponent(name)}&maxEntries=1`);
  const j: any = await r.json();
  const id = j.approximateGroup?.candidate?.[0]?.rxcui;
  if (!id) return null;
  // An approximate match can be an outdated concept: keep it only if RxNorm still describes it as a drug
  const props: any = await (await fetch(`https://rxnav.nlm.nih.gov/REST/rxcui/${id}/properties.json`)).json();
  return ['SCD', 'SBD', 'GPCK', 'BPCK'].includes(props.properties?.tty) ? id : null;
}

// 3. What the official label says: openFDA drug label API (US FDA), free
async function label(id: string) {
  const r = await fetch(`https://api.fda.gov/drug/label.json?search=openfda.rxcui:%22${id}%22&limit=1`);
  if (!r.ok) return null;
  const doc: any = (await r.json()).results?.[0];
  return doc && { set_id: doc.set_id, boxed_warning: doc.boxed_warning?.[0]?.slice(0, 120), has_interactions_section: !!doc.drug_interactions };
}

for (const m of meds) {
  const id = await rxcui(m.display);
  console.log(JSON.stringify({ record: { display: m.display, atc: m.code, dosage: m.dosage }, rxnorm: id, fda_label: id ? await label(id) : null }));
}
```

Output for a patient who reported metformin and enalapril:

```
{"record":{"display":"Metformin 500 mg tablet","atc":"A10BA02","dosage":"500 mg twice a day"},"rxnorm":"861007",
 "fda_label":{"set_id":"011de1a5-…","boxed_warning":"BOXED WARNING WARNING: LACTIC ACIDOSIS …","has_interactions_section":true}}
{"record":{"display":"Enalapril maleate 10 mg oral tablet","atc":"C09AA02","dosage":"10 mg once a day"},"rxnorm":"858817",
 "fda_label":{"set_id":"074fe718-…","boxed_warning":"WARNING: FETAL TOXICITY …","has_interactions_section":true}}
```

**What we learned running it.** With the name written as "Enalapril 10 mg tablet", RxNorm's approximate match returned RXCUI 328290, a concept RxNorm no longer describes, with no label behind it. With the full clinical name, "enalapril maleate 10 mg oral tablet", it returned 858817, a current clinical drug with a label. Names typed by people are approximate; check what a match resolves to, store the code once it is confirmed, and show the person what was matched.

## Rules for AI agents

1. **Never let the model supply drug facts from memory.** Give it the patient's list from the record and the relevant text from an authoritative source, and ask it to cite them.
2. **Keep the two sources visible.** "You take metformin (your record, entered by you on 3 March)" and "the FDA label warns about lactic acidosis (label set 011de1a5…)" are different claims with different owners.
3. **Narrow scopes.** An agent that only needs the medication list should get read access to `MedicationStatement`, not the whole record. With Anpheros that is a consent scope or the MCP server's read-only tools.
4. **Do not answer what needs a clinician.** Interactions, dose changes and stopping a medicine go to a pharmacist or doctor; the agent can prepare the question.

## Where Anpheros fits

Anpheros stores the medication list: `MedicationStatement` and `MedicationAdministration` with ATC or other codes, who recorded each item, history, and consent for other apps; the [context API](https://developers.anpheros.com/guides/ai-healthcare) includes active medications in the summary it prepares for a model, and the [MCP server](https://developers.anpheros.com/guides/mcp) exposes them to agents read-only. It bundles a small ATC subset (about 50 common medicines) to name codes automatically; any other code is accepted, without an automatic name.

It is not a drug database: it does not provide labels, interactions, dosing or contraindications, and it does not support `MedicationRequest` or `Medication` resources today.

## Frequently asked questions

### Which FHIR resource should a medication list use?
`MedicationStatement` for what a person takes or took, including patient-reported medicines. Prescriptions are `MedicationRequest`; individual doses are `MedicationAdministration`.

### Is there a free drug database API?
In the US, RxNorm (NLM) for drug identifiers and names, and openFDA for official product labels, are free. In Europe, EMA and national agencies publish product information. Comprehensive interaction checking generally needs a licensed knowledge base.

### How should an AI agent get medication information?
From two tools: one that reads the patient's medication list from their record with consent, and one that looks up an authoritative drug source. The agent should cite both and leave clinical decisions to a professional.

### Does Anpheros check drug interactions?
No. It stores what a person takes, with provenance and consent. Interaction checking belongs to a drug knowledge source and a clinician.

## Related

- [AI agents and medical data](https://developers.anpheros.com/guides/ai-agents-medical-data)
- [How to give an AI agent access to medical records](https://developers.anpheros.com/guides/ai-agent-access-medical-records)
- [FHIR MCP server for AI agents](https://developers.anpheros.com/guides/mcp)
- [FHIR code systems](https://developers.anpheros.com/guides/fhir-code-systems)
- [FHIR R4 MedicationStatement example](https://developers.anpheros.com/tools/fhir-examples/medication-statement)
