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.

FilterWhat it does
nameFuzzy match. A nickname, a legal name or a typo still finds the patient.
email, phone_numberWiden the search. A request with both returns patients matching the email and patients matching the phone number.
date_of_birthExact match. Send YYYY-MM-DD.
foreign_idThe patient's ID in the health record system.
inactive, non_patientSet both to false when you plan to book. That drops records you cannot book for.
new_patienttrue 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_syncabletrue returns only patients whose file can take a form.
location_stricttrue returns only patients of this location. Without it, the search covers every location on the same server.
updated_sincePatients changed at or after the time you send.
sort, per_pageSort 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.

FieldWhat it tells you
foreign_id, foreign_id_typeThe 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_idThe ID the office sees on screen in some systems. It supplements or replaces foreign_id as the visible ID.
guarantor_idThe person responsible for the bill. Balances sit on the guarantor. See Payments configuration guide.
provider_idThe patient's primary provider.
inactive, bio.non_patient, bio.new_patientWhether you can book for the record.
last_sync_timeThe last time the record changed at the source.
hipaaWhether the patient has given HIPAA authorization, acknowledged the privacy notice and given consent, with dates.
preferred_languageISO 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_birth and bio.phone_number, plus provider.provider_id, the provider the patient is intaken with.
  • bio.gender defaults to Female when 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: true returns the existing patient with a 200 when one with the same date of birth, name and phone number already exists. Without it, the call fails with 400 and A 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.json
const { 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 wantEndpointNotes
A note the office seesPOST /patients/{id}/alertsOne 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 chartPOST /patients/{id}/documentsThe write is confirmed by the document_insertion webhook.
Insurance on fileGET /insurance_coveragesRead only.
Recalls dueGET /patient_recallsRead only.
Procedures, treatment plans, clinical notesGET /procedures, GET /treatment_plans, GET /clinical_notesRead 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_id ends in _np and bio.new_patient is true. A second appointment cannot be booked until the office converts them. On Dentrix you can still move the first appointment with PATCH /appointments/{id}.
  • Every system. When the office creates the real patient record and NexHealth reads it, NexHealth links the records and deletes the new_patient record. Subscribe to patient_updated so your copy follows the change.

When something goes wrong

See Troubleshooting patients. Every message the patient endpoints return is in the Errors reference.


Did this page help you?