- Plain JSON is the default. It is the API’s own, simple shape. If you do not ask for anything else, this is what you get.
- FHIR is a health-care industry standard. You get it only when you ask for it, and only on the resources listed below.
Plain JSON
Single records and lists
Some endpoints return one record. Some return a list. The shapes differ a little, and each endpoint’s reference page shows exactly which it uses. A list is wrapped in an object with the records underdata:
Use
limit (default 25, maximum 100) to choose the page size. See Pagination for how to read to the end.
A few list-like endpoints are different on purpose. A search that is capped (for example pharmacy search)
returns only data, and the change feed uses events and next_after.
A single record is wrapped as { "data": { … } } on most endpoints. A few return the record directly, with no
wrapper: GET /v1/me, one inventory item, and a pharmacy’s listing profile
are examples. Check the endpoint before you write code that unwraps it.
Ids, dates and times
- Ids are UUIDs, written as text, such as
5b0c6f0e-1111-4a2b-9c3d-0123456789ab. Treat them as opaque strings: store and send them back unchanged. - Timestamps are RFC 3339 date-times such as
2026-10-01T09:00:00Z. Send them in the same form to filters likeupdated_since. - Dates with no time of day, such as a date of birth or an onset date, are plain
2019-05-01(year, month, day).
Money
There are two shapes today, and each endpoint’s page says which it uses:- A money object, where the amount is text so no digit is ever lost:
{ "amount": "1200.00", "currency": "NGN" }.currencyis the three-letter ISO 4217 code of the clinic’s own currency. Readamountas a decimal, never as a floating-point number. - A plain number, for example an appointment’s payment amount or an insurance co-pay. The currency is not repeated beside it: it is the clinic’s own.
null,
never 0.
Fields that can be null
A field that has no value is returned as null. It is not left out. So "address_line2": null means “this has
no value”, and you can always rely on the key being there. Do not treat null as zero, “no”, or an empty string. An empty list
([]) is different again: it means the clinic has recorded none, and not that a patient has none. For allergies
in particular, see Allergies.
Errors
A failed request always returnsapplication/problem+json, whatever endpoint you called:
code, which is stable, not on title or detail, which may be reworded. errors, when present,
lists each field that was wrong. pointer says where in your request, and message says why. Quote request_id if you
contact support. Every code is listed on Errors.
FHIR
What FHIR is
FHIR (say “fire”) is a standard, published by the health-care standards body HL7, for how health records are shaped when systems exchange them. Hospitals, labs, insurers and health apps around the world use it, so a record in FHIR means the same thing to all of them. This API speaks FHIR version R4. Three ideas are enough to start:- A resource is one kind of health record, written as a JSON object. Every resource has a
resourceTypethat names its kind:Patient,Appointment,AllergyIntolerance,Condition, and so on, plus anid. - A resource points at another with a reference, a short text such as
"Patient/9d2e7a10-2222-4b3c-8d4e-123456789abc": the kind, a slash, and the id. - A Bundle is a resource that holds a list of other resources. When you ask for a list as FHIR, you get a
Bundleoftype: "searchset", which is FHIR’s word for “the results of a search”.
system (whose vocabulary) and a code
(the entry in it). That is how active or a medicine’s RxNorm number means the same thing everywhere.
How to ask for FHIR
Add one header to a request:Accept: application/fhir+json.
- The header is the only way to ask. There is no query-string switch and no separate
/fhiraddress. A query parameter such as_formatis not recognised and is refused with400. - Leave the header off and you get plain JSON.
- If an endpoint has no FHIR form, the header is ignored. You get plain JSON rather than an error. This applies to every endpoint that is not in the list below, such as listings, events and outside care providers.
- A FHIR answer has the content type
application/fhir+json. - Everything else is the same as plain JSON: the same URL, key, permission, query parameters (under our own names, such as
patient_id),limitandstarting_after, rate limits and audit trail. - A list is a
Bundle. It has alinkofrelation: "self"and, only when another page exists, alinkofrelation: "next"that already carries yourstarting_after. Eachentryhas afullUrland theresource. There is nototal, because the total is never counted. - A single read is the bare resource, with no
datawrapper.
Content-Type: application/fhir+json on a request body is accepted for Patient, Appointment,
DocumentReference (a draft note) and Condition. Every other resource is read-only as FHIR. A write is
answered in plain JSON, even when you sent FHIR. The full rules, with what is and is not accepted, are on FHIR R4.
Which resources can answer in FHIR
Each supports both a list (a search) and a single read. You can confirm the list any time.
GET /v1/metadata
returns a FHIR CapabilityStatement that names exactly what is supported, and it needs no key:
The same record, both ways
One allergy, read as plain JSON, then as FHIR. Plain JSON (GET /v1/clinics/YOUR_CLINIC_ID/allergies/ALLERGY_ID):
Accept: application/fhir+json):
meta.tag marks which clinic the record belongs to. A field that is null in plain JSON is simply left out in FHIR.
A list of the same allergies as FHIR is a Bundle. Here, with its resources shortened to …:
Errors in FHIR
Most errors are the sameapplication/problem+json described above, even if you sent Accept: application/fhir+json:
a missing or wrong key, a missing permission, a record not found, a rate limit. Branch on code as usual.
The exception is a refused FHIR request body. If you send FHIR to write something and it is refused (not valid JSON,
the wrong resourceType, an element that has nowhere to go, or FHIR sent to an endpoint that is read-only as FHIR), the
answer is 400 with the content type application/fhir+json and an OperationOutcome: FHIR’s own error resource.
issue[].severity and issue[].code (FHIR’s own words, such as invalid, value, structure or not-supported),
and diagnostics for the explanation. See Errors for what the same refusals look like in plain JSON.
What FHIR does not do here
- No
_includeor_revinclude, and no FHIR-standard search names. Searches use our own parameters, such aspatient_id. They are listed on FHIR R4. - No separate
/fhiraddress and no resource types beyond the list above. - No exact stock quantities. A medicine’s availability is a coarse band, as in plain JSON.
Which should I use?
See also
- FHIR R4: the full rules for reading, writing and importing
- Pagination
- Errors
- Allergies