Change Lead Status
Moves the lead to another status, and answers the lead together with the status it moved from.
Endpoint
PUT /v2/leads/{lead_id}/status
Path Parameters
| Parameter |
Type |
Required |
Description |
lead_id |
string |
Yes |
The lead. |
Request Body
| Parameter |
Type |
Required |
Description |
status_id |
string |
Yes |
The status to move the lead to — an id from List Lead Status Definitions. Choosing the DROPPED-stage status drops the lead, as Drop Lead does. |
reason_id |
string |
No |
A reason belonging to the status you're moving to. A reason marked needs_follow_up makes follow_up_date mandatory, except under the Dropped status. |
subreason_id |
string |
Conditional |
A sub-reason belonging to the reason. Requires reason_id. |
follow_up_date |
string (date) |
No |
YYYY-MM-DD. Required when the reason is marked needs_follow_up; ignored when the status is the DROPPED-stage one. |
patient_type |
string |
No |
Corrects whether this is a new or a returning patient. One of NEW, RETURNING. |
notes |
string |
No |
Free text recorded with the change. |
Request
curl --location '{base_url}/v2/leads/3f8b21c4-5d9e-4a17-b6c2-0e7154a9d833/status' \
--request PUT \
--header 'Authorization: Bearer JWT_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"status_id": "c4a91f37-0b6e-4d28-8f15-6e3a2b9d7c42",
"reason_id": "5e9a2c71-d84b-4f06-a3c9-0b7e1f6d2a55",
"subreason_id": "a7d3f5b9-2c18-4e6a-9b40-8f1c5e2d7b63",
"follow_up_date": "2026-10-02",
"patient_type": "NEW",
"notes": "Patient asked to be called back after 5pm."
}'
Response
Success Response
Code: 200 OK
{
"lead": {
"id": "3f8b21c4-5d9e-4a17-b6c2-0e7154a9d833",
"organisation_id": "7412",
"clinic_id": "5831",
"patient_id": "63120",
"case_id": "SPRY_CASE_6aabbdec586d1e1ac56a37fd",
"case_title": "L Knee Post-Op",
"patient_name": "Morgan Delgado",
"patient_type": "NEW",
"phone": "+1-555-0143",
"email": "m.delgado@example.com",
"date_of_birth": "1991-07-23",
"status": {
"stage": "IN_PROGRESS",
"status_id": "c4a91f37-0b6e-4d28-8f15-6e3a2b9d7c42",
"status_label": "Contacted",
"reason_id": "5e9a2c71-d84b-4f06-a3c9-0b7e1f6d2a55",
"reason_label": "Spoke with patient",
"subreason_id": "a7d3f5b9-2c18-4e6a-9b40-8f1c5e2d7b63",
"subreason_label": "Interested, deciding",
"notes": "Patient asked to be called back after 5pm.",
"follow_up_date": "2026-10-02",
"changed_at": "2026-09-21T14:31:37Z",
"drop_reason": null,
"dropped_at": null
},
"lead_source": "MANUAL",
"payment_mode": "INSURANCE",
"complaint": "Pain in the left knee since surgery",
"referring_physician_id": "3377",
"intake_status": "INCOMPLETE",
"eligibility_status": "PENDING",
"authorization_status": "UNDETERMINED",
"assigned_to_user_id": "2094",
"appointment_id": null,
"created_at": "2026-09-18T09:12:04Z",
"updated_at": "2026-09-21T14:31:37Z"
},
"previous_status": {
"stage": "IN_PROGRESS",
"status_id": "8d2c5e0a-41f7-4b3e-9a6d-2f1b7c4e9a01",
"status_label": "New Lead",
"reason_id": null,
"reason_label": null,
"subreason_id": null,
"subreason_label": null,
"notes": null,
"follow_up_date": null,
"changed_at": null,
"drop_reason": null,
"dropped_at": null
}
}
Response Fields
| Field |
Type |
Description |
lead |
object |
The updated lead. See Lead. |
previous_status |
object |
The status the lead moved from — so a change can be read without keeping your own copy of every lead. See Previous Status. |
Lead
| Field |
Type |
Description |
id |
string |
The lead id. |
organisation_id |
string or null |
The organisation this record belongs to. Derived from the access token on writes and never trusted from the body. |
clinic_id |
string or null |
The clinic the lead belongs to: the one its case was opened for, else the patient's. null when neither names one. |
patient_id |
string or null |
The patient. Correlate on this — it's a stable id, and there's no external-reference field. |
case_id |
string or null |
The lead's case — a spry_case_id. A case has exactly one lead, ever. |
case_title |
string or null |
The title of the lead's case. |
patient_name |
string or null |
The patient's first and last name. |
patient_type |
string or null |
Whether this is a new or a returning patient. One of NEW, RETURNING. |
phone |
string or null |
|
email |
string or null |
|
date_of_birth |
string (date) or null |
YYYY-MM-DD. |
status |
object |
The lead's position in the pipeline: its stage, your organisation's status, the reason behind it, any follow-up date, and — once dropped — why and when. See Status. |
lead_source |
string or null |
Where the lead came from: the source it was created with, MANUAL when none was given. |
payment_mode |
string or null |
How this episode of care is paid for. UNKNOWN until the case or the patient's registration says. One of INSURANCE, SELF_PAY, UNKNOWN. |
complaint |
string or null |
The patient's presenting complaint, in their words. |
referring_physician_id |
string or null |
The case's primary referring physician. |
intake_status |
string or null |
Progress through the intake forms the lead sent: N/A (none sent), PENDING (sent, not started), INCOMPLETE, COMPLETE. |
eligibility_status |
string or null |
Where the insurance eligibility check for the lead's case stands. |
authorization_status |
string or null |
Where the insurance authorization for the lead's case stands. |
assigned_to_user_id |
string or null |
The staff member the lead is assigned to. |
appointment_id |
string or null |
The appointment that converted the lead. null until it converts. |
created_at |
string (date-time) or null |
When the lead was raised. |
updated_at |
string (date-time) or null |
When the lead last changed. |
Status
| Field |
Type |
Description |
stage |
string |
Where the lead is in the pipeline. Never set directly, and only moves forward: to IN_PROGRESS when the patient starts intake or the lead is given an in-progress status, CONVERTED when an appointment is booked against its case, DROPPED when it's dropped. It can run ahead of the status — a lead whose patient has started intake is IN_PROGRESS while its status is still the one it was raised on. One of NEW, IN_PROGRESS, CONVERTED, DROPPED. |
status_id |
string or null |
Your organisation's own status, by its id in List Lead Status Definitions. The id stays the same when the status is renamed. |
status_label |
string or null |
The status's name, as your organisation configured it. |
reason_id |
string or null |
The reason behind the current status, by its id in your organisation's catalogue. On a dropped lead, the reason it was dropped for — one of the Dropped status's reasons — or null. |
reason_label |
string or null |
The reason's name. |
subreason_id |
string or null |
The sub-reason behind the current status, by its id. |
subreason_label |
string or null |
The sub-reason's name. |
notes |
string or null |
Free text recorded with the change. |
follow_up_date |
string (date) or null |
When the lead is next due a follow-up. Cleared when it's dropped. YYYY-MM-DD. |
changed_at |
string (date-time) or null |
When the status last changed. |
drop_reason |
string or null |
Why the lead was dropped, as free text: the reason sent to Drop Lead (MANUAL when none was, including a drop that chose its reason by id), or STALE when it was dropped automatically, 31 days after it was raised. null unless it was dropped. |
dropped_at |
string (date-time) or null |
When the lead was dropped. null unless it was. |
Previous Status
| Field |
Type |
Description |
stage |
string |
Where the lead is in the pipeline. Never set directly, and only moves forward: to IN_PROGRESS when the patient starts intake or the lead is given an in-progress status, CONVERTED when an appointment is booked against its case, DROPPED when it's dropped. It can run ahead of the status — a lead whose patient has started intake is IN_PROGRESS while its status is still the one it was raised on. One of NEW, IN_PROGRESS, CONVERTED, DROPPED. |
status_id |
string or null |
Your organisation's own status, by its id in List Lead Status Definitions. The id stays the same when the status is renamed. |
status_label |
string or null |
The status's name, as your organisation configured it. |
reason_id |
string or null |
The reason behind the current status, by its id in your organisation's catalogue. On a dropped lead, the reason it was dropped for — one of the Dropped status's reasons — or null. |
reason_label |
string or null |
The reason's name. |
subreason_id |
string or null |
The sub-reason behind the current status, by its id. |
subreason_label |
string or null |
The sub-reason's name. |
notes |
string or null |
Free text recorded with the change. |
follow_up_date |
string (date) or null |
When the lead is next due a follow-up. Cleared when it's dropped. YYYY-MM-DD. |
changed_at |
string (date-time) or null |
When the status last changed. |
drop_reason |
string or null |
Why the lead was dropped, as free text: the reason sent to Drop Lead (MANUAL when none was, including a drop that chose its reason by id), or STALE when it was dropped automatically, 31 days after it was raised. null unless it was dropped. |
dropped_at |
string (date-time) or null |
When the lead was dropped. null unless it was. |
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
- Statuses are defined per organisation and named by
id, which stays the same when a status is renamed — read List Lead Status Definitions rather than hard-coding a set.
- Send the status's full detail each time:
reason_id, subreason_id, notes and follow_up_date left out are cleared, not kept.
previous_status is what lets you interpret the change without keeping your own copy of every lead.
400 when the reason needs a follow-up date and none was sent, or when the reason doesn't belong to the status. 409 once the lead is converted or dropped — both are terminal.
- Choosing the
DROPPED-stage status drops the lead, as Drop Lead does: the reason and sub-reason chosen under it are recorded, drop_reason is MANUAL, and a follow-up date is ignored. The CONVERTED-stage status can't be chosen here (400); booking an appointment against the lead's case puts the lead on it.
- Emits the
lead.updated webhook — lead.dropped when the status chosen is the DROPPED-stage one.