Create Lead¶
Raises a lead — for an existing patient, or for a new one created in the same call — and answers as soon as its case exists.
Endpoint¶
Request Body¶
| Parameter | Type | Required | Description |
|---|---|---|---|
patient_id |
string | No | An existing patient to raise the lead for. Leave it out to create the patient instead, with the fields below. |
first_name |
string | Conditional | New patient only — required without patient_id. |
last_name |
string | Conditional | New patient only — required without patient_id. |
date_of_birth |
string (date) | Conditional | New patient only — required without patient_id. YYYY-MM-DD. |
clinic_ids |
array of string | Conditional | New patient only — required without patient_id. Only the first is used: it becomes the patient's clinic, and the lead's. |
email |
string | No | New patient only. |
mobile |
string | No | New patient only. |
middle_name |
string | No | New patient only. |
suffix |
string | No | New patient only. |
gender |
string | No | New patient only — as on Create Patient. One of MALE, FEMALE, OTHER. |
address |
object | No | New patient only. See Address. |
preferred_language |
string | No | New patient only — as on Create Patient. |
clinic_id |
string | No | With patient_id only — rejected without it. The clinic the lead belongs to. Defaults to the patient's clinic; required for a token limited to specific clinics. |
title |
string | No | With patient_id only — rejected without it. A title for the case. Generated when omitted. |
speciality |
string | No | With patient_id only — rejected without it. The case's speciality. Often not known yet when a lead is raised. |
complaint |
string | No | The patient's presenting complaint, in their words — with either shape. |
lead_source |
string | No | Where the lead came from. MANUAL when omitted. One of MANUAL, REFERRAL, WEBSITE, PHONE, FAX, PATIENT_PORTAL. |
Address¶
| Parameter | Type | Required | Description |
|---|---|---|---|
line1 |
string or null | No | |
line2 |
string or null | No | |
city |
string or null | No | |
state |
string or null | No | |
postal_code |
string or null | No | |
country |
string or null | No |
Request¶
For an existing patient:
curl --location '{base_url}/v2/leads' \
--header 'Authorization: Bearer JWT_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"patient_id": "63120",
"clinic_id": "5831",
"title": "L Knee Post-Op",
"complaint": "Pain in the left knee since surgery"
}'
For a new patient — the body of Create Patient, plus complaint and lead_source:
curl --location '{base_url}/v2/leads' \
--header 'Authorization: Bearer JWT_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"first_name": "Morgan",
"last_name": "Delgado",
"date_of_birth": "1984-02-19",
"clinic_ids": [
"5831"
],
"email": "m.delgado@example.com",
"mobile": "+1-555-0143",
"complaint": "Pain in the left knee since surgery",
"lead_source": "WEBSITE"
}'
Response¶
Success Response¶
Code: 202 Accepted
Response Fields¶
| Field | Type | Description |
|---|---|---|
patient_id |
string | The patient the lead is for — the one sent, or the one just created. |
case_id |
string or null | The case that was opened, and the lead's reference — a case has exactly one lead, so GET /v2/leads?case_id= returns it once written. null only if a new patient's case couldn't be read back yet. |
Error Response¶
Code: 400 Bad Request
{
"code": "bad_request",
"message": "Validation failed",
"errors": [
{
"field": "patient_id",
"message": "must be a string"
}
]
}
Every error has the same shape — a machine-readable code, a message, and an errors[] array that's empty when there's nothing field-specific to report. The HTTP status is authoritative.
| Status | code |
Meaning |
|---|---|---|
400 Bad Request |
bad_request |
The request body or parameters failed validation. errors[] names the offending fields. |
401 Unauthorized |
unauthorized |
The access token is missing, malformed or expired. |
403 Forbidden |
forbidden |
The request references a resource outside the token's organisation or clinic scope. |
404 Not Found |
not_found |
No such resource, or it's outside your scope. |
409 Conflict |
conflict |
The record conflicts with existing state — see the endpoint's notes. |
429 Too Many Requests |
rate_limited |
Rate limit exceeded. Back off and retry. |
500 Internal Server Error |
internal_error |
Unexpected server error. |
502 Bad Gateway |
upstream_error |
Upstream service failed or timed out. |
Notes¶
- A lead is raised by opening a case. Send
patient_idand a case is opened for that patient. Leave it out and send a new patient instead — the body of Create Patient — and the patient is created and their case opened in one step.complaintgoes with either. - If a new patient's case can't be opened the request fails (
502) and the patient is rolled back — though by then they may already show in patient search and have sentpatient.created. - A new patient that matches an existing one is a
409, and nothing is created. The match is named inerrors[0].message. Check their leads withGET /v2/leads?patient_id=before raising one withpatient_id— a retry of a request that timed out lands here too, against the patient it created. The duplicate check matches within the first clinic on the name with the mobile, the date of birth or both, so it isn't done silently. - A new patient's lead belongs to the first of its
clinic_ids.clinic_id,titleandspecialityneedpatient_id. - The new lead opens an intake request for the patient and, unless your organisation completes registration in person, sends them a link to the forms — as a lead raised in Spry does.
202as soon as the case exists, withpatient_idandcase_id— it doesn't wait for the lead. The lead is written a moment later, and a case has exactly one lead, socase_idis its reference:GET /v2/leads?case_id=(theLocationheader) returns it — an empty page means it hasn't landed yet, so read again shortly — andlead.createdannounces it.Locationfollows?patient_id=instead, withcase_idnull, in the rare case a new patient's case couldn't be read back yet.- Not idempotent. A create that times out or fails with a
5xxmay still have gone through, and retrying one withpatient_idopens a second case and lead — checkGET /v2/leads?patient_id=first. For a new patient, retrying is the check: if the first attempt went through, the retry is a409naming the patient it created. - With
patient_id, a token limited to specific clinics must sendclinic_id: without it the lead takes the patient's clinic, which that token may not be able to see. - To attach a referring physician, update the case once the lead exists —
referring_physician_idisn't accepted here. - Emits the
lead.createdwebhook,case.createdfor the case it opens and, for a new patient,patient.created.