# Getting started

> From an API key to reading and writing a patient's FHIR R4 record in the sandbox: keys, first calls, writes with provenance, documents, AI context, IPS, consent, webhooks and lab reports.

Source: https://developers.anpheros.com/guides/getting-started

Anpheros Platform is a health-data backend: a FHIR R4 record per patient, project isolation,
provenance on every write, and a simple REST API on top of the same data. This guide takes you
from nothing to reading and writing a patient's record in the sandbox.

Base URL (sandbox and production share it; the key decides the data plane):

```
https://platform.anpheros.com
```

Interactive reference: `/docs` (OpenAPI) and `/fhir/R4/metadata` (CapabilityStatement).

## 1. Get a key (1 minute)

The sandbox is self-service. Sign in to the dashboard (`https://platform.anpheros.com/dashboard/`)
with Google and press **Get a sandbox key**: you get an organization, a sandbox project with its own
synthetic patients, and a key — shown once. The same through the Admin API, with your Firebase user token:

```bash
curl -X POST $BASE/admin/sandbox/quickstart -H "Authorization: Bearer $FIREBASE_ID_TOKEN"
# {"organization": {…}, "project": {"id": "proj_…", "environment": "sandbox", …}, "key": {"key": "sk_test_…", …}, "sandbox": {…}}
```

Or step by step:

```bash
# 1. an organization (you become its owner)
curl -X POST $BASE/admin/organizations -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
     -H 'content-type: application/json' -d '{"name":"CardioAI SRL","kind":"developer","country":"RO"}'
# 2. a sandbox project (it comes with its own synthetic patients)
curl -X POST $BASE/admin/organizations/org_…/projects -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
     -H 'content-type: application/json' -d '{"name":"CardioAI dev","environment":"sandbox"}'
# 3. a key — the plain key is returned exactly once
curl -X POST $BASE/admin/projects/proj_…/keys -H "Authorization: Bearer $FIREBASE_ID_TOKEN" \
     -H 'content-type: application/json' -d '{"name":"local dev","scopes":["read","write"]}'
```

You get `sk_test_…`. Sandbox keys only ever see sandbox data. Production keys (`sk_live_…`) are
issued to verified organizations with a signed DPA — write to platform@anpheros.com. The sandbox is free;
production starts at €49 a month and the first month is free (see [Pricing](https://developers.anpheros.com/guides/pricing)).

## 2. First call (1 minute)

Your sandbox project already has its own copy of 30 synthetic patients, with six months of history
up to the day the project was created: adults, children and older people from across the EU, with
conditions (ICD-10), medications (ATC), allergies, lab results and vital signs (LOINC). The copy
belongs to your project: change or delete anything — no other project sees it. **Reset test
patients** in the dashboard (or `POST /admin/projects/{id}/sandbox/reset`) brings them back as they
were, with new ids; the patients you created yourself are not touched. Each synthetic patient keeps a
stable identifier across resets: `Patient?identifier=https://anpheros.com/fhir/sid/sandbox-persona|p02`
is always Elena Ionescu.

```bash
export KEY=sk_test_…
curl $BASE/v1/patients -H "Authorization: Bearer $KEY"
```

```json
{"data":[{"id":"…","given":"Elena","family":"Ionescu","birth_date":"1985-09-03","gender":"female",…,"fhir":"Patient/…"}, …],"total":30,"next_cursor":null}
```

Sandbox limits are generous but finite: 10 000 resource writes a day per project, 500 patients of
your own besides the synthetic ones, 250 MB of documents (see [limits](https://developers.anpheros.com/guides/limits)).

## 3. Read a record

```bash
# lab results, newest first
curl "$BASE/v1/patients/$PID/observations?category=laboratory&limit=10" -H "Authorization: Bearer $KEY"
# active medications
curl "$BASE/v1/patients/$PID/medications?status=active" -H "Authorization: Bearer $KEY"
# everything on one timeline
curl "$BASE/v1/patients/$PID/timeline?from=2026-06-01" -H "Authorization: Bearer $KEY"
# the same data, strict FHIR
curl "$BASE/fhir/R4/Observation?patient=$PID&code=http://loinc.org|4548-4&_sort=-date" -H "Authorization: Bearer $KEY"
```

Ids are identical in `/v1` and `/fhir/R4`; every v1 object carries its `fhir` reference.

## 4. Write

Say who the author is. `author_type` is `patient`, `practitioner`, `device`, `import` or `ai`;
if you omit it the platform records `import`, never `patient` by assumption.

```bash
curl -X POST "$BASE/v1/patients/$PID/observations" -H "Authorization: Bearer $KEY" \
     -H 'content-type: application/json' -H "Idempotency-Key: $(uuidgen)" -d '{
  "code": "2339-0", "display": "Glucose", "category": "laboratory",
  "value": 112, "unit": "mg/dL", "effective_at": "2026-09-19T08:10:00Z",
  "author_type": "device"
}'
```

The response is the stored observation with `id`, `fhir` and `updated_at`. Repeating the exact
same request with the same `Idempotency-Key` returns the same response and creates nothing.

## 5. Documents

```bash
# 1. create the document → upload target
curl -X POST "$BASE/v1/patients/$PID/documents" -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
     -d '{"title":"Analize 2026-09-14","kind":"lab_report","content_type":"application/pdf","date":"2026-09-14"}'
# 2. upload the bytes to the returned url (direct to storage, or through the platform)
curl -X PUT "$BASE/v1/documents/$DOC/content" -H "Authorization: Bearer $KEY" -H 'content-type: application/pdf' --data-binary @analize.pdf
```

The original is never modified after that; `sha256` and `size` are recorded, and any values you
later extract point back to the document via `derived_from`.

## 6. Provenance

```bash
curl "$BASE/v1/patients/$PID/provenance/Observation/$OBS" -H "Authorization: Bearer $KEY"
```

One entry per version: who (author type and project), when, from which source system.

## Rules worth knowing

- **Isolation**: a project sees the patients it created (plus the shared sandbox patients). Anyone
  else's patient does not exist for you: `404`, never `403`.
- **Pairwise ids**: the patient id you see is yours. Another project sees the same person under a
  different id. Store our ids freely; never try to correlate them.
- **Provenance**: every write leaves a Provenance resource. Nothing is silently overwritten;
  history is available at `/fhir/R4/{Type}/{id}/_history`.
- **Errors**: FHIR endpoints return `OperationOutcome`; `/v1` and `/admin` return
  `{"error": {"type", "message", "field"}}`. Every response carries `Anpheros-Request-Id`; quote it
  when you write to us.
- **Limits**: 600 requests/minute per key by default, 200 items per page, 25 MB per document.

## 7. Context for an AI model

Instead of pulling raw records and deciding what fits a prompt, ask the platform for a context:

```bash
curl -X POST "$BASE/v1/context" -H "Authorization: Bearer $KEY" -H 'content-type: application/json' -d '{
  "patient": "'$PID'",
  "task": "medication review before a cardiology visit",
  "question": "How did blood pressure evolve over the last 3 months?",
  "budget_tokens": 1500,
  "format": "text"
}'
```

You get sections (summary card, conditions, medications, allergies, labs, vitals with weekly
trends and before/after treatment markers, symptoms, timeline, documents), each item labelled
with `author_type` and `source`, a list of what was omitted to fit the budget, warnings, and a
`manifest_id` that ties the call to the audit log. Values produced by AI are never mixed with
facts: they appear only under `ai_notes`. Explicit needs are also accepted:
`"needs": ["labs:4548-4", "vitals:trend:85354-9", "timeline:180d"]`.

## 8. Standards

- `GET /fhir/R4/Patient/{id}/$summary` returns an International Patient Summary document Bundle.
- `POST /fhir/R4/Observation/$validate?profile=eu-lab` reports how a lab result measures up to
  the European laboratory report profile (errors, warnings, information).
- `GET /v1/terminology/loinc?q=hba1c` and `/v1/terminology/atc?q=metformin` search the bundled
  code subsets; observations created without a display name get the LOINC name automatically.

## 9. Patient consent (OAuth 2.1 + SMART on FHIR)

API keys see the patients your project created. To reach a person's existing record, ask them:

1. Register an application: `POST /admin/projects/{id}/applications` with your redirect URI
   (`public` for mobile/web apps with PKCE, `confidential` for servers; the secret is shown once).
2. Send the person to the consent page:
   `GET /oauth/authorize?response_type=code&client_id=…&redirect_uri=…&state=…&code_challenge=…&code_challenge_method=S256&scope=patient/Observation.rs?category=laboratory patient/MedicationStatement.r offline_access&purpose=treatment&lang=ro`
   They sign in to Anpheros, see what you ask for in plain language, choose the record (their own,
   a dependent's, or the one your app created for them) and how long the access lasts.
3. Exchange the code: `POST /oauth/token` (`authorization_code` + `code_verifier`). You receive an
   access token (30 min), a refresh token (30 days, rotated on use) and the patient id.
4. Call the API with `Authorization: Bearer at_…`. You see only that patient, only the resource
   types and actions in the scopes; out-of-scope reads answer `403`, searches are filtered.

The person can revoke at any time from Anpheros; the next call answers `401`. Every grant is
mirrored as a FHIR `Consent` resource, and every access under it is logged and visible to the
person. Discovery: `/.well-known/smart-configuration`.

## 10. Webhooks

Let the platform call you instead of polling:

```bash
curl -X POST $BASE/v1/webhooks -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://your.app/anpheros/hook", "events": ["*"]}'
```

Keep the `secret` from the response and verify `Anpheros-Signature` on every delivery
(`verifyWebhookSignature` in both SDKs). Events carry ids and versions, not clinical data.
Details, retries and examples: [webhooks.md](https://developers.anpheros.com/guides/webhooks).

## 11. Organisations and practitioners

Clinics, labs and the people who work there are first-class resources: `Organization`,
`Practitioner`, `PractitionerRole`. They are **directory** resources: no patient, readable by
every project in the same environment, changeable only by the project that created them.

```bash
curl -X POST $BASE/fhir/R4/Practitioner -H "Authorization: Bearer $KEY" -H "Content-Type: application/fhir+json" \
  -d '{"resourceType":"Practitioner","active":true,"name":[{"family":"Popescu","given":["Ana"],"prefix":["Dr."]}]}'
```

Name the practitioner behind a write with `X-Anpheros-Author-Ref: Practitioner/<id>` (or a
`PractitionerRole`, `Organization`, `Device`): the server-generated Provenance then carries
`agent.who.reference`, and `GET /fhir/R4/Provenance?agent=Practitioner/<id>` lists everything
that person recorded. Search: `Organization?name=`, `?identifier=`, `?type=`, `Practitioner?name=`,
`?family=`, `?given=`, `PractitionerRole?practitioner=`, `?organization=`, `?role=`, `?specialty=`, `?active=`.

Who is working behind your app? Send `X-Anpheros-Acting-As: PractitionerRole/<id>` (or
`Practitioner/<id>`) on any `/v1` or `/fhir/R4` request made with a project key. The person must
exist in the directory and be active. The reference is written to the access log, becomes the
author of that request's writes (unless you set `X-Anpheros-Author-Ref`) and is shown to the
patient by name in their connections ("Dr. Ana Popescu · Clinica Dente read your lab results").

## 12. Lab reports in one call

Send a whole lab report (CSV, HL7 v2 ORU^R01 or JSON) and get laboratory Observations with LOINC
and reference ranges; re-sending is safe. Details: [lab-connector.md](https://developers.anpheros.com/guides/lab-connector).

```bash
curl -X POST "$BASE/v1/patients/$PATIENT/labs/import" -H "Authorization: Bearer $KEY" -H "Content-Type: text/csv" --data-binary @report.csv
```



## Conformance feedback on writes

Every `POST`/`PUT` of a clinical resource is checked against the IPS profile of its section and, for
laboratory observations, the HL7 Europe laboratory report rules. The write is never rejected for
profile reasons (structural FHIR errors still are); when something is missing the response carries

```
Anpheros-Conformance: warnings=2; IPS Condition: Condition without code | ...
```

Fix what it names and the resource will be included in the patient's `$summary`. Resource types added
on 22 September 2026: `Immunization`, `MedicationAdministration` (search `statement=MedicationStatement/{id}`),
`RelatedPerson`, `QuestionnaireResponse`, `Basic`. Writes made by other projects into a record you can
see arrive as webhook events with `data.external = true` — see [`docs/webhooks.md`](https://developers.anpheros.com/guides/webhooks).
