Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →
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.
Step 2 of 5. You can read the sandbox (see Your 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} |
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:
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:
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.
categoryis the FHIR observation category:laboratoryfor lab results,vital-signsfor blood pressure, heart rate, weight and the like.author_typesays who wrote it:patient,practitioner,device,importorai. If you leave it out the platform recordsimport, neverpatientby assumption. A write byaicarries 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:
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"
}'
{"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/…"}
systemdefaults to ICD-10 (http://hl7.org/fhir/sid/icd-10); onlycodeis required.clinical_statusis one ofactive,recurrence,relapse,inactive,remission,resolved.onsetandabatementare 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
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"])
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.
4. Check what you wrote
# 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 |
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.
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.
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.
Ask your AI
Paste this into Claude, ChatGPT, Cursor or Claude Code. Your assistant reads this guide and adapts it to your code.
Read https://developers.anpheros.com/guides/first-write.md and help me write my first Observation and Condition to a sandbox patient. Use the Anpheros sandbox (base URL https://platform.anpheros.com) with my API key from the ANPHEROS_KEY environment variable, never written in the code. Give me complete, runnable code, the response I should expect, and how to fix the errors in https://developers.anpheros.com/guides/troubleshooting.md if a call fails.Open in ClaudeOpen in ChatGPTThis guide as MarkdownBuild the whole integration with an AI coding tool
Try it free in the sandbox · first FHIR call in 5 minutesFree · 30 synthetic patients · no cardYour first API call in 2 minutes →