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.
Returns a paginated list of patients matching the filter criteria.
⚠️ Rate Limited: This endpoint is rate limited. See the rate limiting guide for details.
| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
Organization-ID | header | Yes | string | The organization ID |
filter | query | No | string | Filter criteria (see filter keys below) |
responseFields | query | No | string | URL-encoded comma-delimited list of fields to include, or ALL |
page | query | No | string | Page number for pagination (deprecated) |
pageSize | query | No | string | Number of records per page (max 500) |
lastId | query | No | string | Largest patientId from the previous page — used for cursor-based pagination |
| Filter Key | Operators | Example |
|---|---|---|
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 |
NEW, ACTIVE, NON-PATIENT, INACTIVE, DUPLICATE, DISMISSED, DECEASED
M, F, O
Use a specific location id, a list via ->, or all locations with ALL, *, ->["ALL"], or ->["*"].
⚠️ Upcoming Breaking Change:
preferredLocation.id(as a list of IDs or["ALL"]) will become a required filter. OmittingpreferredLocation.idcurrently 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, andupdatedDate.
⚠️ Narrow your query: A very wide
lastModifiedrange with no other scoping can return 408 (timeout) on large tenants. Combine name, chart, contact, or location filters with a date bound, or uselastIdpagination.
⚠️ Not Recommended: Do not paginate large data sets using the
pageparameter. UselastIdcursor pagination instead.
Empty results: 200 with
data: []means no patients matched; it does not indicate invalid filter values.
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:00ZlastId 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.
GET /api/v1/patients?pageSize=500&filter=lastModified>=2025-01-01T00:00:00Z,preferredLocation.id->[64000000000004]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]Continue until data is empty.
For how meta.pagination.total behaves (including when it is 0 or omitted), see Bulk GET — Total Count Behavior.
Returns a single patient by patientId.
| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
patientId | path | Yes | integer | The patient ID |
Organization-ID | header | Yes | string | The organization ID |
responseFields | query | No | string | Comma-delimited list of fields to include, or ALL |
GET /api/v1/patients/12000006542062?responseFields=firstName,lastName,chartNumberCreates a new patient. Returns 201 with the created resource in data.
| Field | Type | Description |
|---|---|---|
firstName | string | 1–25 characters |
lastName | string | 1–25 characters |
contactMethod | string | Call me, Text me, or Email me |
languageType | string | See schema enum (e.g. English, Spanish) |
patientStatus | string | See patientStatus values |
gender | string | M, F, or O |
dateOfBirth | string | ISO date YYYY-MM-DD |
preferredLocation | object | Linked model reference to a location |
address1 | string | 1–50 characters |
city | string | 1–30 characters |
state | string | US state/territory code |
postalCode | string | 5–10 characters |
| Condition | Required |
|---|---|
discountType: BASIC | discountPlan |
discountType: SLIDING_FEES | income and familySize |
{
"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
}
]
}Updates an existing patient. Only fields included in the request body are changed; omitted fields are unchanged.
| Parameter | In | Required | Type | Description |
|---|---|---|---|---|
patientId | path | Yes | integer | The patient ID |
Organization-ID | header | Yes | string | The organization ID |
responseFields | query | No | string | Comma-delimited list of fields to include in the response, or ALL |
PUT /api/v1/patients/12000006542062{
"patientStatus": "INACTIVE",
"emailAddress": "jane.smith.new@example.com"
}GET /api/v1/patients/calculated returns computed fields per patient row.
Both filter and responseFields are required.
| Filter Key | Operators | Example |
|---|---|---|
patient.id | ==, -> | ?filter=patient.id==12000006542062 |
| Field | Description |
|---|---|
lastVisitDate | Most recent visit date derived from appointments and patient procedures |
GET /api/v1/patients/calculated?filter=patient.id->[12000006542062]&responseFields=lastVisitDateGET /api/v1/patients/totalcountUses 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.
GET /api/v1/patients/totalcount?filter=lastModified>=2025-01-01,preferredLocation.id==64000000000004Patient billing types are managed on a nested path under the patient:
GET /api/v1/patients/{patientId}/billingtypes— list billing types for a patientPOST /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.
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.
A successful 200/201 response returns:
| Field | Type | Description |
|---|---|---|
data | object or array | Patient record(s) |
warnings | array | Non-fatal warnings |
errors | array | Field-level or structural errors |
For bulk GET, data is an array and includes meta.pagination.
| Field | Type | Description |
|---|---|---|
limit | integer | Page size used for this request |
offset | integer | Record offset applied |
total | integer | Present only on plain bulk GET without lastId or page — typically 0 (see Bulk GET — Total Count Behavior) |
| Field | Access | Description |
|---|---|---|
id | read-only | Unique patient identifier |
firstName, lastName, preferredName | read/write | Name fields |
gender, dateOfBirth, patientStatus | read/write | Demographics and status |
chartNumber, emailAddress | read/write | Chart and contact |
phones | read/write | Array of phone records |
preferredLocation | read/write | Linked location |
primaryGuarantor, secondaryGuarantor | read/write | Linked patient guarantors |
primaryContact, secondaryContact | read/write | Linked patient contacts |
address1, address2, city, state, postalCode | read/write | Address |
firstVisitDate, lastModified | read-only | Timestamps |
referredByPatient, referredByReferral | read/write | Referral links |
referredPatients | read-only | Patients referred by this patient |
For the authoritative field list, enums, and validation rules, use the
PatientV1andPatientUpdateV1schemas in the official API reference.
HTTP Status Code Errors — click to expand
These apply to all Patients V1 endpoints.
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.
| Message | Authentication is required and has failed or has not been provided. |
| Fix | Provide a valid Bearer token in the Authorization header. |
| Message | The request is understood, but it has been refused or access is not allowed. |
| Why | Token lacks model:read (GET) or model:write (POST/PUT) scope. |
| Fix | Request the appropriate scope for the operation. |
| Message | The requested resource is either missing or does not exist. |
| Why | No patient exists for the given patientId in your organization, or the id is invalid. |
| Fix | Confirm the patientId and organization context. |
| Message | The server timed out while processing the request. |
| Why | Query too broad — often a wide date range without location or name scoping. |
| Fix | Retry with a narrower filter or smaller pageSize. |
| Message | Rate limit exceeded. |
| Fix | Implement exponential backoff. See the rate limiting guide. |
| Message | An unexpected error occurred. |
| Fix | Retry. 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, orlanguageTypeenum values discountType: BASICwithoutdiscountPlan, ordiscountType: SLIDING_FEESwithoutincome/familySize- Duplicate phone
sequencevalues within the samephoneType - Invalid
dateOfBirthformat (must beYYYY-MM-DD)