# Patient summary (IPS) and SMART Health Links

> Read a patient's International Patient Summary with $summary, and open a SMART Health Link a person shares with your clinic: decode shlink:/, POST the manifest with a passcode and decrypt the JWE in Python.

Source: https://developers.anpheros.com/guides/share-patient-summary

Two open standards for moving a person's essential health data between systems. The International
Patient Summary (IPS) is one FHIR document with allergies, medications, problems, vaccinations and
recent results. A SMART Health Link is a `shlink:/…` link or QR code that a person gives to a
clinic or another app, which then opens an encrypted copy. This guide shows how to read a summary
with the API and how a receiving system opens a link a person shares with it.

**In short:**

| | IPS summary | SMART Health Link |
|---|---|---|
| What it is | a FHIR R4 document Bundle (IPS) | a link or QR code to an encrypted copy |
| Who creates it | your app, with `read` access to the record | the person, in their Anpheros app |
| How you get it | `GET /fhir/R4/Patient/{id}/$summary` | the person shows or sends you the link |
| How you read it | JSON | `POST` to the manifest, then decrypt the JWE (`dir` + `A256GCM`) |
| Expiry | none, it is generated on each call | 1 hour, 1 day, 7 days or 30 days, chosen by the person (an emergency card can have none) |

<!-- visual:seq-shl -->

## 1. Read a patient's IPS summary

```bash
curl "https://platform.anpheros.com/fhir/R4/Patient/$PID/\$summary" \
     -H "Authorization: Bearer $ANPHEROS_KEY" -o ips.json
python3 -c "import json; b = json.load(open('ips.json')); print(b['type'], [e['resource']['resourceType'] for e in b['entry']][:8])"
```

The answer is a FHIR `Bundle` of type `document` whose first entry is a `Composition` with the IPS
sections. Add `?lang=de` (or another language) for section titles in that language. Two response
headers tell you what was left out and why: `Anpheros-Summary-Excluded` and
`Anpheros-Summary-Detail`. More on the sections, codes and validation:
[International Patient Summary (IPS) in FHIR](https://developers.anpheros.com/guides/international-patient-summary-fhir).

## 2. How a person shares a link

The person creates the link in their Anpheros app: a full summary, an emergency subset or a
selection, an expiry (1 hour, 1 day, 7 days or 30 days) and, for a summary, a passcode they tell
you separately. Your app cannot create a link with an API key: sharing is the person's decision.
You receive either the QR code or the link itself:

```text
shlink:/eyJ1cmwiOiJodHRwczovL3BsYXRmb3JtLmFucGhlcm9zLmNvbS9zaGwv…
```

## 3. Open the link in your system

The part after `shlink:/` is base64url JSON with the manifest address (`url`), the decryption key
(`key`), flags (`P` = passcode needed, `L` = live, the copy is updated), `exp` and `label`.

```python
import base64, json, requests
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

def b64d(s): return base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))

link = "shlink:/eyJ1cmwiOi…"                       # from the QR code
payload = json.loads(b64d(link.split("shlink:/", 1)[1]))
body = {"recipient": "Clinica Example, reception"}  # who is opening it; shown to the person
if "P" in payload.get("flag", ""):
    body["passcode"] = input("Passcode: ")
manifest = requests.post(payload["url"], json=body)
manifest.raise_for_status()

for f in manifest.json()["files"]:
    jwe = f.get("embedded") or requests.get(f["location"]).text
    header, _, iv, ct, tag = jwe.split(".")
    data = AESGCM(b64d(payload["key"])).decrypt(b64d(iv), b64d(ct) + b64d(tag), header.encode())
    print(f["contentType"], json.loads(data)["resourceType"])
```

The manifest answer has `status` (`can-change` for a live link, `finalized` for a fixed copy) and
`files`, each either `embedded` (the encrypted content in the answer) or a `location` valid for one
hour. The content is a FHIR Bundle (`application/fhir+json`), the same standard your own FHIR code
already reads. Any SMART Health Links viewer that follows the specification can open these links;
Anpheros also has a web viewer.

## 4. Passcodes and expiry

- A wrong passcode returns `401` with `remainingAttempts`; after 5 wrong tries the link locks.
- After the expiry the link stops working. The person can also turn it off at any time.
- Every opening is shown to the person, with the `recipient` you sent, so write a name they will
  recognise.

## Common errors

| You get | Fix |
|---|---|
| `401` with `remainingAttempts` | Wrong passcode: ask the person again; it is not in the link |
| The link no longer opens | It expired or the person turned it off: ask for a new one |
| Decryption fails | Use the `key` from the same link, base64url-decoded, and the protected header as AAD, as above |

## Frequently asked questions

### Can my app generate a SMART Health Link for a patient?
Not with an API key. The person creates links in their Anpheros app, because sharing is their decision. Your app can read the IPS summary of records it has access to with `$summary`.

### Is the IPS from Anpheros valid?
`Patient/$summary` returns a FHIR R4 IPS document; the validation results are in the [IPS guide](https://developers.anpheros.com/guides/international-patient-summary-fhir).

### Does the receiving system need an Anpheros account?
No. Anyone with the link (and the passcode, if there is one) can open it, which is why the person decides who gets it and for how long.
