Reference
Patients
Create a patient, or link a person who is already known to your clinic. Contact and shipping are the only things you can change.
One person, one record
A person is one patient record across the platform. If the person is already known, the same record is linked to your clinic rather than duplicated, and the response is identical to a first-time create. You will never be told whether the person was already known.Creating a patient#
POST /api/partner/v1/patients{
"external_id": "caelo-1042",
"first_name": "Jane",
"last_name": "Doe",
"date_of_birth": "1990-04-12",
"email": "jane@example.com",
"phone": "+15551234567",
"address": {
"line1": "120 Peachtree St",
"line2": "Apt 4",
"city": "Atlanta",
"state": "GA",
"postal_code": "30303",
"country": "US"
}
}| Field | Required | Notes |
|---|---|---|
external_id | Yes | Your stable id for the person. 1 to 128 characters. It is the idempotency key. |
first_name, last_name | Yes | 1 to 100 characters. |
date_of_birth | Yes | YYYY-MM-DD, not in the future. |
email, phone | One of the two | Email is lowercased. Phone is normalised to E.164. |
address | No | If present, line1, city, state and postal_code are required. |
clinic_id | No | Only for a key holding more than one clinic. It must be one of your grants. |
The response is always 200:
{
"ok": true,
"request_id": "...",
"patient": { "id": "…", "external_id": "caelo-1042", "status": "accepted" }
}Store the returned id. It is what the update endpoint takes. The status is always accepted: it describes the request, not the patient's lifecycle, so it cannot tell you whether the person was new.
Matching and retries#
- An exact email match links the existing person to your clinic.
- Otherwise an exact phone plus matching first name, last name and date of birth links.
- Otherwise a new patient is created.
A phone alone never links, because households share numbers.
external_id is the idempotency key. A repeat with the same body returns the same patient. A repeat with a different body is 409 idempotency_conflict. The compared body is the normalised one, so reordered JSON keys, email casing and phone formatting do not count as a change.
Updating a patient#
PATCH /api/partner/v1/patients/{id}{
"phone": "+15559876543",
"address": {
"line1": "120 Peachtree St",
"city": "Atlanta",
"state": "GA",
"postal_code": "30303"
}
}Send only what changed. null clears a field. Allowed fields: phone and address. Email, name, clinic and every clinical field are refused with 400 validation_failed.
The patient is resolved through your clinic's link. A patient outside your clinic is 404 not_found, the same answer as an id that does not exist, so the endpoint cannot be used to test whether a patient id belongs to someone else.
Errors#
| Status | Code | When |
|---|---|---|
| 400 | validation_failed | The body, a field or the id is not valid. No field-level detail is returned. |
| 401 | unknown_key | The key did not verify. |
| 403 | forbidden_scope | The key does not carry patients:write, or the clinic is not in its grants. |
| 404 | not_found | The update target is not linked to your clinic. |
| 409 | idempotency_conflict | Same external_id, different body. |
| 429 | rate_limited | A ceiling was reached. |
These endpoints are live in the sandbox with a patients:write key. Production follows the signed agreement.