Skip to content

Patients V1 — Developer Guide

Reference: Patients (V1) Endpoint Documentation


Patient records are the core identity for scheduling, clinical, and billing integrations. This guide covers the published Patients V1 operations: bulk list, get by id, create, update, calculated fields, and total count.

For GET /v1/patients/async, see Async Endpoints V1 — Developer Guide. Async returns the first 10 results immediately and streams the full result set to the Streaming API. Results on the async endpoint are ordered by id descending (newest first), unlike the synchronous bulk GET which pages by ascending id.


GET — Bulk List Patients

Returns a paginated list of patients matching the filter criteria.

⚠️ Rate Limited: This endpoint is rate limited. See the rate limiting guide for details.

Parameters

ParameterInRequiredTypeDescription
Organization-IDheaderYesstringThe organization ID
filterqueryNostringFilter criteria (see filter keys below)
responseFieldsqueryNostringURL-encoded comma-delimited list of fields to include, or ALL
pagequeryNostringPage number for pagination (deprecated)
pageSizequeryNostringNumber of records per page (max 500)
lastIdqueryNostringLargest patientId from the previous page — used for cursor-based pagination

Filter Keys

Filter KeyOperatorsExample
id->?filter=id->[12000006542062,12000006542063]
firstName==, !=, ~=, <>=?filter=firstName~=Smi
lastName==, !=, ~=, <>=?filter=lastName==Doe
gender==, !=?filter=gender==M
preferredName==, !=, ~=, <>=?filter=preferredName~=John
dateOfBirth==, !=?filter=dateOfBirth==1985-08-25
patientStatus==, !=?filter=patientStatus==ACTIVE
phones.number==, !=, ->?filter=phones.number==5551234567
emailAddress==?filter=emailAddress==john.doe@example.com
chartNumber==, .absent, .present?filter=chartNumber==CH10023456
preferredLocation.id==, !=, ->?filter=preferredLocation.id->[64000000000004]
primaryGuarantor.id==?filter=primaryGuarantor.id==12000006542062
duplicateOfPatient.id==?filter=duplicateOfPatient.id==12000006542062
primaryContact.id==?filter=primaryContact.id==12000006542062
firstVisitDate==, >, >=, <, <=?filter=firstVisitDate>=2024-01-01
lastModified>, >=, <, <=?filter=lastModified>=2025-01-01T00:00:00Z
referredByPatient.id==, !=, ->?filter=referredByPatient.id==12000006542062
referredByReferral.id==, !=, ->?filter=referredByReferral.id==500

patientStatus Values

NEW, ACTIVE, NON-PATIENT, INACTIVE, DUPLICATE, DISMISSED, DECEASED

gender Values

M, F, O

preferredLocation.id — All Locations

Use a specific location id, a list via ->, or all locations with ALL, *, ->["ALL"], or ->["*"].

Important Notes

⚠️ Upcoming Breaking Change: preferredLocation.id (as a list of IDs or ["ALL"]) will become a required filter. Omitting preferredLocation.id currently defaults to all locations during a transitional period.

⚠️ Date-limiting filters: If you do not supply a date-limiting filter, one may be applied automatically using lastModified. Supply your own date bound for predictable results. The following filter keys count as date-limiting: lastModified, createdDate, and updatedDate.

⚠️ Narrow your query: A very wide lastModified range with no other scoping can return 408 (timeout) on large tenants. Combine name, chart, contact, or location filters with a date bound, or use lastId pagination.

⚠️ Not Recommended: Do not paginate large data sets using the page parameter. Use lastId cursor pagination instead.

Empty results: 200 with data: [] means no patients matched; it does not indicate invalid filter values.

Example Requests

Fetch patients modified after a date at specific locations:

GET /api/v1/patients?pageSize=500&filter=lastModified>=2025-01-01T00:00:00Z,preferredLocation.id->[64000000000004]

Fetch patients for a primary guarantor:

GET /api/v1/patients?filter=primaryGuarantor.id==12000006542062,lastModified>=2025-01-01T00:00:00Z

Paginating with lastId

lastId uses the patient's id as a cursor. When filtering by lastModified, firstVisitDate, or dateOfBirth, combine those filters with lastId for stable pagination. Results are ordered by id for paging.

Step 1 — Initial request

GET /api/v1/patients?pageSize=500&filter=lastModified>=2025-01-01T00:00:00Z,preferredLocation.id->[64000000000004]

Step 2 — Subsequent requests

Pass the id of the last record in data as lastId:

GET /api/v1/patients?lastId=22000012345678&pageSize=500&filter=lastModified>=2025-01-01T00:00:00Z,preferredLocation.id->[64000000000004]

Step 3 — Repeat

Continue until data is empty.

For how meta.pagination.total behaves (including when it is 0 or omitted), see Bulk GET — Total Count Behavior.


GET — Patient by ID

Returns a single patient by patientId.

Parameters

ParameterInRequiredTypeDescription
patientIdpathYesintegerThe patient ID
Organization-IDheaderYesstringThe organization ID
responseFieldsqueryNostringComma-delimited list of fields to include, or ALL

Example Request

GET /api/v1/patients/12000006542062?responseFields=firstName,lastName,chartNumber

POST — Create Patient

Creates a new patient. Returns 201 with the created resource in data.

Required Fields

FieldTypeDescription
firstNamestring1–25 characters
lastNamestring1–25 characters
contactMethodstringCall me, Text me, or Email me
languageTypestringSee schema enum (e.g. English, Spanish)
patientStatusstringSee patientStatus values
genderstringM, F, or O
dateOfBirthstringISO date YYYY-MM-DD
preferredLocationobjectLinked model reference to a location
address1string1–50 characters
citystring1–30 characters
statestringUS state/territory code
postalCodestring5–10 characters

Conditional Fields

ConditionRequired
discountType: BASICdiscountPlan
discountType: SLIDING_FEESincome and familySize

Example Request Body

{
  "firstName": "Jane",
  "lastName": "Smith",
  "contactMethod": "Email me",
  "languageType": "English",
  "patientStatus": "ACTIVE",
  "gender": "F",
  "dateOfBirth": "1990-03-15",
  "preferredLocation": { "id": "64000000000004" },
  "address1": "123 Maple Street",
  "city": "Anytown",
  "state": "CA",
  "postalCode": "90210",
  "emailAddress": "jane.smith@example.com",
  "phones": [
    {
      "phoneType": "MOBILE",
      "number": "5551234567",
      "sequence": 1
    }
  ]
}

PUT — Update Patient

Updates an existing patient. Only fields included in the request body are changed; omitted fields are unchanged.

Parameters

ParameterInRequiredTypeDescription
patientIdpathYesintegerThe patient ID
Organization-IDheaderYesstringThe organization ID
responseFieldsqueryNostringComma-delimited list of fields to include in the response, or ALL

Example Request

PUT /api/v1/patients/12000006542062
{
  "patientStatus": "INACTIVE",
  "emailAddress": "jane.smith.new@example.com"
}

GET — Calculated Fields

GET /api/v1/patients/calculated returns computed fields per patient row.

Both filter and responseFields are required.

Filter Keys

Filter KeyOperatorsExample
patient.id==, ->?filter=patient.id==12000006542062

Supported Calculated Fields

FieldDescription
lastVisitDateMost recent visit date derived from appointments and patient procedures

Example Request

GET /api/v1/patients/calculated?filter=patient.id->[12000006542062]&responseFields=lastVisitDate

GET — Total Count

GET /api/v1/patients/totalcount

Uses the same filter syntax and filter keys as bulk GET. See Bulk GET — Total Count Behavior for how pagination.total behaves on bulk GET responses.

Example Request

GET /api/v1/patients/totalcount?filter=lastModified>=2025-01-01,preferredLocation.id==64000000000004

Patient Billing Types

Patient billing types are managed on a nested path under the patient:

  • GET /api/v1/patients/{patientId}/billingtypes — list billing types for a patient
  • POST /api/v1/patients/{patientId}/billingtypes — replace all billing types for the patient (existing types are removed and replaced by the request body)

See the official API reference under Patient Billing Types for schema details.


Streaming API

For real-time patient creates and updates, use the Streaming API instead of polling bulk GET on a fixed schedule. Setup is covered in Creating and Setting Up Webhooks for the StreamAPI.

Patients are streamable under domain type PatientV1.


Response Structure

A successful 200/201 response returns:

FieldTypeDescription
dataobject or arrayPatient record(s)
warningsarrayNon-fatal warnings
errorsarrayField-level or structural errors

For bulk GET, data is an array and includes meta.pagination.

meta.pagination

FieldTypeDescription
limitintegerPage size used for this request
offsetintegerRecord offset applied
totalintegerPresent only on plain bulk GET without lastId or page — typically 0 (see Bulk GET — Total Count Behavior)

PatientV1 Object (summary)

FieldAccessDescription
idread-onlyUnique patient identifier
firstName, lastName, preferredNameread/writeName fields
gender, dateOfBirth, patientStatusread/writeDemographics and status
chartNumber, emailAddressread/writeChart and contact
phonesread/writeArray of phone records
preferredLocationread/writeLinked location
primaryGuarantor, secondaryGuarantorread/writeLinked patient guarantors
primaryContact, secondaryContactread/writeLinked patient contacts
address1, address2, city, state, postalCoderead/writeAddress
firstVisitDate, lastModifiedread-onlyTimestamps
referredByPatient, referredByReferralread/writeReferral links
referredPatientsread-onlyPatients referred by this patient

For the authoritative field list, enums, and validation rules, use the PatientV1 and PatientUpdateV1 schemas in the official API reference.


Errors & Warnings Reference

HTTP Status Code Errors — click to expand

These apply to all Patients V1 endpoints.


400 — Bad Request

The request body or query parameters are invalid or cannot be processed. Common causes include invalid filter syntax, missing required fields on POST, or missing filter / responseFields on /calculated.


401 — Unauthorized

MessageAuthentication is required and has failed or has not been provided.
FixProvide a valid Bearer token in the Authorization header.

403 — Forbidden

MessageThe request is understood, but it has been refused or access is not allowed.
WhyToken lacks model:read (GET) or model:write (POST/PUT) scope.
FixRequest the appropriate scope for the operation.

404 — Not Found

MessageThe requested resource is either missing or does not exist.
WhyNo patient exists for the given patientId in your organization, or the id is invalid.
FixConfirm the patientId and organization context.

408 — Request Timeout

MessageThe server timed out while processing the request.
WhyQuery too broad — often a wide date range without location or name scoping.
FixRetry with a narrower filter or smaller pageSize.

429 — Too Many Requests

MessageRate limit exceeded.
FixImplement exponential backoff. See the rate limiting guide.

500 — Internal Server Error

MessageAn unexpected error occurred.
FixRetry. If the issue persists, contact support.

Field-Level Validation — click to expand

Validation errors are returned in the errors array on 400 responses. Common cases on create/update include:

  • Missing required fields (firstName, lastName, gender, dateOfBirth, address fields, etc.)
  • Invalid patientStatus, gender, contactMethod, or languageType enum values
  • discountType: BASIC without discountPlan, or discountType: SLIDING_FEES without income / familySize
  • Duplicate phone sequence values within the same phoneType
  • Invalid dateOfBirth format (must be YYYY-MM-DD)