# Your first write: an Observation and a Condition

> Step 2 of the developer path: write a LOINC-coded Observation and an ICD-10 Condition to a sandbox patient with an author and an Idempotency-Key, in curl, Python and JavaScript, then check the timeline, FHIR and provenance.

Source: https://developers.anpheros.com/guides/first-write

Step 2 of 5. You can read the sandbox (see [Your first API call](https://developers.anpheros.com/guides/first-api-call)); now write
two of the most common things a health app stores: a measurement (an `Observation`, coded with
LOINC) and a diagnosis already made by a clinician (a `Condition`, coded with ICD-10). Every write
records who the author is and can be retried safely.

**In short:**

| | Observation | Condition |
|---|---|---|
| What it is | a measurement or a lab result | a diagnosis or problem on record |
| Code system | LOINC (for example `2339-0` glucose) | ICD-10 (for example `E11.9`) |
| Endpoint | `POST /v1/patients/{id}/observations` | `POST /v1/patients/{id}/conditions` |
| Minimal body | `code`, `category`, `effective_at`, `value` + `unit` | `code` |
| Same data in FHIR | `Observation/{id}` | `Condition/{id}` |

<!-- visual:seq-first-write -->

## Before you start

You need a sandbox key with the `write` scope (the key from **Get a sandbox key** has `read` and
`write`) and the `id` of a sandbox patient:

```bash
export ANPHEROS_KEY=sk_test_your_key_here
export PID=$(curl -s https://platform.anpheros.com/v1/patients -H "Authorization: Bearer $ANPHEROS_KEY" \
  | python3 -c "import sys, json; print(json.load(sys.stdin)['data'][0]['id'])")
echo $PID
```

## 1. Write an Observation

A fasting glucose of 112 mg/dL, measured by a device:

```bash
curl -X POST "https://platform.anpheros.com/v1/patients/$PID/observations" \
     -H "Authorization: Bearer $ANPHEROS_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 answer is the stored observation, with its `id`, its `fhir` reference (`Observation/…`) and
`updated_at`.

- `category` is the FHIR observation category: `laboratory` for lab results, `vital-signs` for
  blood pressure, heart rate, weight and the like.
- `author_type` says who wrote it: `patient`, `practitioner`, `device`, `import` or `ai`. If you
  leave it out the platform records `import`, never `patient` by assumption. A write by `ai`
  carries the HL7 *AI asserted* label.
- `Idempotency-Key`: send the same request with the same key again (after a timeout, for example)
  and you get the same answer without a second observation.

## 2. Write a Condition

A type 2 diabetes diagnosis, active since March 2021, entered by a clinician:

```bash
curl -X POST "https://platform.anpheros.com/v1/patients/$PID/conditions" \
     -H "Authorization: Bearer $ANPHEROS_KEY" -H 'content-type: application/json' \
     -H "Idempotency-Key: $(uuidgen)" -d '{
  "code": "E11.9", "display": "Type 2 diabetes mellitus without complications",
  "clinical_status": "active", "onset": "2021-03-01", "author_type": "practitioner"
}'
```

```json
{"id":"…","patient":"…","code":"E11.9","system":"http://hl7.org/fhir/sid/icd-10","display":"Type 2 diabetes mellitus without complications","clinical_status":"active","onset":"2021-03-01","abatement":null,"note":null,"updated_at":"…","fhir":"Condition/…"}
```

- `system` defaults to ICD-10 (`http://hl7.org/fhir/sid/icd-10`); only `code` is required.
- `clinical_status` is one of `active`, `recurrence`, `relapse`, `inactive`, `remission`,
  `resolved`. `onset` and `abatement` are dates (`YYYY-MM-DD`).
- Store a diagnosis that a clinician made. An app that suggests diagnoses itself may be a medical
  device under EU and US rules; Anpheros stores what you send and does not diagnose.

## 3. The same writes in Python and JavaScript

```python
import os, uuid, requests

BASE = "https://platform.anpheros.com"
H = {"Authorization": f"Bearer {os.environ['ANPHEROS_KEY']}"}
pid = requests.get(f"{BASE}/v1/patients", headers=H).json()["data"][0]["id"]

obs = requests.post(f"{BASE}/v1/patients/{pid}/observations",
                    headers={**H, "Idempotency-Key": str(uuid.uuid4())},
                    json={"code": "2339-0", "display": "Glucose", "category": "laboratory", "value": 112,
                          "unit": "mg/dL", "effective_at": "2026-09-19T08:10:00Z", "author_type": "device"})
cond = requests.post(f"{BASE}/v1/patients/{pid}/conditions",
                     headers={**H, "Idempotency-Key": str(uuid.uuid4())},
                     json={"code": "E11.9", "clinical_status": "active", "onset": "2021-03-01",
                           "author_type": "practitioner"})
print(obs.status_code, obs.json()["fhir"], cond.status_code, cond.json()["fhir"])
```

```js
const BASE = 'https://platform.anpheros.com';
const H = { Authorization: `Bearer ${process.env.ANPHEROS_KEY}`, 'content-type': 'application/json' };
const { data } = await (await fetch(`${BASE}/v1/patients`, { headers: H })).json();
const post = (path, body) => fetch(`${BASE}${path}`, {
  method: 'POST', headers: { ...H, 'Idempotency-Key': crypto.randomUUID() }, body: JSON.stringify(body),
}).then(r => r.json());
const obs = await post(`/v1/patients/${data[0].id}/observations`, { code: '2339-0', display: 'Glucose',
  category: 'laboratory', value: 112, unit: 'mg/dL', effective_at: '2026-09-19T08:10:00Z', author_type: 'device' });
const cond = await post(`/v1/patients/${data[0].id}/conditions`, { code: 'E11.9', clinical_status: 'active',
  onset: '2021-03-01', author_type: 'practitioner' });
console.log(obs.fhir, cond.fhir);
```

The SDKs add the `Idempotency-Key` for you: `anpheros.observations.create(...)` in TypeScript and
Dart. See [SDKs](https://developers.anpheros.com/guides/sdks).

## 4. Check what you wrote

```bash
# the record, newest first
curl "https://platform.anpheros.com/v1/patients/$PID/timeline" -H "Authorization: Bearer $ANPHEROS_KEY"
# the same Condition as strict FHIR R4
curl "https://platform.anpheros.com/fhir/R4/Condition?patient=$PID&code=http://hl7.org/fhir/sid/icd-10|E11.9" \
     -H "Authorization: Bearer $ANPHEROS_KEY"
# who wrote it, and when
curl "https://platform.anpheros.com/v1/patients/$PID/provenance/Observation/<observation id>" \
     -H "Authorization: Bearer $ANPHEROS_KEY"
```

Nothing is overwritten silently: an update creates a new version, and the history stays at
`/fhir/R4/{Type}/{id}/_history`.

## Common errors

| You get | Fix |
|---|---|
| `403` `This credential does not have the 'write' scope.` | Use a key with `write`; see [Troubleshooting](https://developers.anpheros.com/guides/troubleshooting) |
| `422` `The referenced Patient does not exist.` | The patient id is from another project or from before a sandbox reset: read it again |
| `422` `Idempotency-Key was already used with a different request.` | Use a new key for each new write |
| `400` with a field name | A required field is missing or a value is not allowed, for example a `clinical_status` outside the list above |

## Next step

Step 3: when a real person uses your app, they sign in and approve access themselves:
[Create an OAuth app and the consent screen](https://developers.anpheros.com/guides/oauth-app).

## Frequently asked questions

### Do I have to use LOINC and ICD-10?
They are the defaults: LOINC for observations and ICD-10 for conditions. Codes from other systems can be sent with their `system`; see [FHIR code systems](https://developers.anpheros.com/guides/fhir-code-systems).

### Can I write with FHIR instead of /v1?
Yes. `POST /fhir/R4/Observation` and `POST /fhir/R4/Condition` take FHIR R4 resources and store them in the same record, with the same ids.

### What happens if I send the same write twice?
With the same `Idempotency-Key`, the second request returns the first answer and nothing is duplicated. Without one, you get two resources.
