Patients configuration guide
Search the right way, create records the health record system accepts, read the record back and keep your copy current.
This guide covers everything the patient endpoints do. If you have not created a patient yet, start with Patients quickstart.
The samples use the values from the Quickstart: NEX_TOKEN, NEX_SUBDOMAIN, NEX_LOCATION_ID, NEX_PATIENT_ID and NEX_PROVIDER_ID, plus NEX_WEBHOOK_ID from Setting up webhooks. Node samples need Node 18 or later, saved as .mjs files. Python samples need the requests package.
1. Search with every filter you have
GET /patients always runs for one location, so subdomain and location_id are required. The filters stack.
| Filter | What it does |
|---|---|
name | Fuzzy match. A nickname, a legal name or a typo still finds the patient. |
email, phone_number | Widen the search. A request with both returns patients matching the email and patients matching the phone number. |
date_of_birth | Exact match. Send YYYY-MM-DD. |
foreign_id | The patient's ID in the health record system. |
inactive, non_patient | Set both to false when you plan to book. That drops records you cannot book for. |
new_patient | true returns only new patients with an upcoming appointment. These do not fully exist in the health record system yet and cannot be booked again. |
forms_syncable | true returns only patients whose file can take a form. |
location_strict | true returns only patients of this location. Without it, the search covers every location on the same server. |
updated_since | Patients changed at or after the time you send. |
sort, per_page | Sort by id or updated_at, with a leading dash for descending. Up to 1,000 per page. |
curl -s --request GET \
--url "https://nexhealth.info/patients?subdomain=$NEX_SUBDOMAIN&location_id=$NEX_LOCATION_ID&phone_number=5165550100&email=ada%40example.com&date_of_birth=1990-12-10&inactive=false&non_patient=false&location_strict=true" \
--header "Nex-Api-Version: v3.0.0" \
--header "Authorization: Bearer $NEX_TOKEN"const { NEX_TOKEN, NEX_SUBDOMAIN, NEX_LOCATION_ID } = process.env;
const url = `https://nexhealth.info/patients?subdomain=${NEX_SUBDOMAIN}&location_id=${NEX_LOCATION_ID}&phone_number=5165550100&email=ada%40example.com&date_of_birth=1990-12-10&inactive=false&non_patient=false&location_strict=true`;
const res = await fetch(url, {
headers: {
"Nex-Api-Version": "v3.0.0",
Authorization: `Bearer ${NEX_TOKEN}`,
},
});
console.log(await res.json());import os
import requests
res = requests.get(
"https://nexhealth.info/patients",
params={
"subdomain": os.environ["NEX_SUBDOMAIN"],
"location_id": os.environ["NEX_LOCATION_ID"],
"phone_number": "5165550100",
"email": "[email protected]",
"date_of_birth": "1990-12-10",
"inactive": "false",
"non_patient": "false",
"location_strict": "true",
},
headers={
"Nex-Api-Version": "v3.0.0",
"Authorization": "Bearer " + os.environ["NEX_TOKEN"],
},
)
print(res.json())Send as much contact information as you have. A patient who gave the office a different email or phone number is still found by name and date of birth.
2. Read one patient
GET /patients/{id} returns the full record. location_id is not a parameter here, so leave it off. The response has location_ids instead, the locations the patient belongs to.
curl -s --request GET \
--url "https://nexhealth.info/patients/$NEX_PATIENT_ID?subdomain=$NEX_SUBDOMAIN" \
--header "Nex-Api-Version: v3.0.0" \
--header "Authorization: Bearer $NEX_TOKEN"const { NEX_TOKEN, NEX_SUBDOMAIN, NEX_PATIENT_ID } = process.env;
const url = `https://nexhealth.info/patients/${NEX_PATIENT_ID}?subdomain=${NEX_SUBDOMAIN}`;
const res = await fetch(url, {
headers: {
"Nex-Api-Version": "v3.0.0",
Authorization: `Bearer ${NEX_TOKEN}`,
},
});
console.log(await res.json());import os
import requests
res = requests.get(
"https://nexhealth.info/patients/" + os.environ["NEX_PATIENT_ID"],
params={
"subdomain": os.environ["NEX_SUBDOMAIN"],
},
headers={
"Nex-Api-Version": "v3.0.0",
"Authorization": "Bearer " + os.environ["NEX_TOKEN"],
},
)
print(res.json())The fields that matter when you build on the record.
| Field | What it tells you |
|---|---|
foreign_id, foreign_id_type | The patient's ID in the health record system and the system it came from. Treat foreign_id as an opaque string. A foreign_id_type of nex means the patient exists only in NexHealth so far. |
chart_id | The ID the office sees on screen in some systems. It supplements or replaces foreign_id as the visible ID. |
guarantor_id | The person responsible for the bill. Balances sit on the guarantor. See Payments configuration guide. |
provider_id | The patient's primary provider. |
inactive, bio.non_patient, bio.new_patient | Whether you can book for the record. |
last_sync_time | The last time the record changed at the source. |
hipaa | Whether the patient has given HIPAA authorization, acknowledged the privacy notice and given consent, with dates. |
preferred_language | ISO 639-1 code, when the office recorded it. |
The bio object holds what the office saved. Fields differ by system, so do not assume any one of them is always present.
3. Create a record the health record system accepts
POST /patients needs a provider and a patient.
- Required:
first_name,last_name,email,bio.date_of_birthandbio.phone_number, plusprovider.provider_id, the provider the patient is intaken with. bio.genderdefaults toFemalewhen you leave it out, so that every health record system accepts the record. Send it when you have it. The office updates it at the first visit.- Keep first names to 15 characters and last names to 20. Some systems cut longer names.
- The date of birth cannot be in the future or more than 130 years in the past.
return_existing_if_match: truereturns the existing patient with a200when one with the same date of birth, name and phone number already exists. Without it, the call fails with400andA patient with that information already exists -id=123.
cat > patient.json <<EOF
{
"provider": { "provider_id": $NEX_PROVIDER_ID },
"patient": {
"first_name": "Ada",
"last_name": "Lovelace",
"email": "[email protected]",
"bio": {
"date_of_birth": "1990-12-10",
"phone_number": "5165550100",
"gender": "Female",
"address_line_1": "333 Bush Street",
"city": "San Francisco",
"state": "CA",
"zip_code": "94104"
}
},
"return_existing_if_match": true
}
EOF
curl -s --request POST \
--url "https://nexhealth.info/patients?subdomain=$NEX_SUBDOMAIN&location_id=$NEX_LOCATION_ID" \
--header "Nex-Api-Version: v3.0.0" \
--header "Authorization: Bearer $NEX_TOKEN" \
--header "Content-Type: application/json" \
--data @patient.jsonconst { NEX_TOKEN, NEX_SUBDOMAIN, NEX_LOCATION_ID, NEX_PROVIDER_ID } = process.env;
const url = `https://nexhealth.info/patients?subdomain=${NEX_SUBDOMAIN}&location_id=${NEX_LOCATION_ID}`;
const res = await fetch(url, {
method: "POST",
headers: {
"Nex-Api-Version": "v3.0.0",
Authorization: `Bearer ${NEX_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
provider: {
provider_id: Number(NEX_PROVIDER_ID),
},
patient: {
first_name: "Ada",
last_name: "Lovelace",
email: "[email protected]",
bio: {
date_of_birth: "1990-12-10",
phone_number: "5165550100",
gender: "Female",
address_line_1: "333 Bush Street",
city: "San Francisco",
state: "CA",
zip_code: "94104",
},
},
return_existing_if_match: true,
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://nexhealth.info/patients",
params={
"subdomain": os.environ["NEX_SUBDOMAIN"],
"location_id": os.environ["NEX_LOCATION_ID"],
},
headers={
"Nex-Api-Version": "v3.0.0",
"Authorization": "Bearer " + os.environ["NEX_TOKEN"],
},
json={
"provider": {
"provider_id": int(os.environ["NEX_PROVIDER_ID"]),
},
"patient": {
"first_name": "Ada",
"last_name": "Lovelace",
"email": "[email protected]",
"bio": {
"date_of_birth": "1990-12-10",
"phone_number": "5165550100",
"gender": "Female",
"address_line_1": "333 Bush Street",
"city": "San Francisco",
"state": "CA",
"zip_code": "94104",
},
},
"return_existing_if_match": True,
},
)
print(res.json())The patient comes back in data.user. A 201 means a new record. A 200 means an existing one. Either way, confirm the match with the patient or the office before you book.
A new patient lives in NexHealth only until their first appointment is written into the health record system. Until then foreign_id is null, foreign_id_type is nex, and alerts and payments for the patient fail. See How a write reaches the health record system.
4. Add to the patient's file
| What you want | Endpoint | Notes |
|---|---|---|
| A note the office sees | POST /patients/{id}/alerts | One required field, note. The patient has to exist in the health record system, or the call fails with Data source must exist. |
| A document in the chart | POST /patients/{id}/documents | The write is confirmed by the document_insertion webhook. |
| Insurance on file | GET /insurance_coverages | Read only. |
| Recalls due | GET /patient_recalls | Read only. |
| Procedures, treatment plans, clinical notes | GET /procedures, GET /treatment_plans, GET /clinical_notes | Read only. Under Clinical records in the reference. |
curl -s --request POST \
--url "https://nexhealth.info/patients/$NEX_PATIENT_ID/alerts?subdomain=$NEX_SUBDOMAIN" \
--header "Nex-Api-Version: v3.0.0" \
--header "accept: application/vnd.Nexhealth+json;version=2" \
--header "Authorization: Bearer $NEX_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "patient_alert": { "note": "Balance due. Collect at check-in." } }'const { NEX_TOKEN, NEX_SUBDOMAIN, NEX_PATIENT_ID } = process.env;
const url = `https://nexhealth.info/patients/${NEX_PATIENT_ID}/alerts?subdomain=${NEX_SUBDOMAIN}`;
const res = await fetch(url, {
method: "POST",
headers: {
"Nex-Api-Version": "v3.0.0",
accept: "application/vnd.Nexhealth+json;version=2",
Authorization: `Bearer ${NEX_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
patient_alert: {
note: "Balance due. Collect at check-in.",
},
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://nexhealth.info/patients/" + os.environ["NEX_PATIENT_ID"] + "/alerts",
params={
"subdomain": os.environ["NEX_SUBDOMAIN"],
},
headers={
"Nex-Api-Version": "v3.0.0",
"accept": "application/vnd.Nexhealth+json;version=2",
"Authorization": "Bearer " + os.environ["NEX_TOKEN"],
},
json={
"patient_alert": {
"note": "Balance due. Collect at check-in.",
},
},
)
print(res.json())5. Keep your copy current
Subscribe to patient_created and patient_updated. patient_created fires when NexHealth detects a new patient in the health record system. patient_updated fires on any change to the record.
curl -s --request POST \
--url "https://nexhealth.info/webhook_endpoints/$NEX_WEBHOOK_ID/webhook_subscriptions?subdomain=$NEX_SUBDOMAIN" \
--header "Nex-Api-Version: v3.0.0" \
--header "Authorization: Bearer $NEX_TOKEN" \
--header "Content-Type: application/json" \
--data '{ "resource_type": "Patient", "event": "patient_updated" }'const { NEX_TOKEN, NEX_SUBDOMAIN, NEX_WEBHOOK_ID } = process.env;
const url = `https://nexhealth.info/webhook_endpoints/${NEX_WEBHOOK_ID}/webhook_subscriptions?subdomain=${NEX_SUBDOMAIN}`;
const res = await fetch(url, {
method: "POST",
headers: {
"Nex-Api-Version": "v3.0.0",
Authorization: `Bearer ${NEX_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
resource_type: "Patient",
event: "patient_updated",
}),
});
console.log(await res.json());import os
import requests
res = requests.post(
"https://nexhealth.info/webhook_endpoints/" + os.environ["NEX_WEBHOOK_ID"] + "/webhook_subscriptions",
params={
"subdomain": os.environ["NEX_SUBDOMAIN"],
},
headers={
"Nex-Api-Version": "v3.0.0",
"Authorization": "Bearer " + os.environ["NEX_TOKEN"],
},
json={
"resource_type": "Patient",
"event": "patient_updated",
},
)
print(res.json())If you poll instead, call GET /patients with updated_since and sort=updated_at. See Data pipelines quickstart.
6. Differences by health record system
- Dentrix and Denticon. A patient created through the API exists only on their first appointment until the office makes them a patient of record. Their
foreign_idends in_npandbio.new_patientistrue. A second appointment cannot be booked until the office converts them. On Dentrix you can still move the first appointment withPATCH /appointments/{id}. - Every system. When the office creates the real patient record and NexHealth reads it, NexHealth links the records and deletes the
new_patientrecord. Subscribe topatient_updatedso your copy follows the change.
When something goes wrong
See Troubleshooting patients. Every message the patient endpoints return is in the Errors reference.
Updated about 1 hour ago