Developers

Learn · PatientNow Pro

Recipes — PatientNow Pro

The reference documents one endpoint at a time. Real features chain several together. Here are three end-to-end workflows for the PatientNow Pro API — showing how one call's output feeds the next.

ℹ️ PNP conventions used throughout

These recipes use the PNP base URL (https://backend-production.patientnow.net/backend/patientnowpro), token auth (Authorization: YOUR_TOKEN), practice-scoped paths (/practices/{practiceId}/…), and integer IDs. If you're on PNE or VISH, see the PNE & VISH recipes → instead.

The setup these recipes share

Every snippet below assumes an authenticated client from the PNP quickstart — a session with the Authorization header baked in and the practice ID stored, so we don't repeat auth on every call:

# Each curl below omits these for brevity — add them to every request:
#   -H "Authorization: YOUR_TOKEN"
# And substitute YOUR_PRACTICE_ID throughout.
const base = "https://backend-production.patientnow.net/backend/patientnowpro";
const practiceId = "YOUR_PRACTICE_ID";
const authHeader = { Authorization: "YOUR_TOKEN" };
import requests

base = "https://backend-production.patientnow.net/backend/patientnowpro"
practice_id = "YOUR_PRACTICE_ID"

s = requests.Session()
s.headers["Authorization"] = "YOUR_TOKEN"   # sent on every request
using System.Net.Http;

var http = new HttpClient {
  BaseAddress = new Uri("https://backend-production.patientnow.net/backend/patientnowpro/")
};
http.DefaultRequestHeaders.Add("Authorization", "YOUR_TOKEN");
var practiceId = "YOUR_PRACTICE_ID";
⚠️ Recipes 2 and 3 write to live data

Creating appointments and waitlist entries changes real practice data immediately — there's no sandbox. Run writes against records you can clean up afterward.

Look up a patient and read their appointments

Find a patient by name, then pull their upcoming appointments using the patient's integer ID as the thread between calls.

  1. Search for the patientGET /practices/{practiceId}/patients
  2. Read their appointmentsGET /practices/{practiceId}/patients/{patientId}/appointments

1 · Search for the patient

Filter by LastName, FirstName, DOB, or CellPhone. The response includes a patientId integer you carry into subsequent calls.

curl "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/patients?LastName=Smith&FirstName=Jane&Limit=5" \
  -H "Authorization: YOUR_TOKEN"
# → { "data": [ { "patientId": 12345, "firstName": "Jane", "lastName": "Smith", ... } ], ... }
const res = await fetch(`${base}/practices/${practiceId}/patients?LastName=Smith&FirstName=Jane&Limit=5`, { headers: authHeader });
const { data } = await res.json();
const patientId = data[0].patientId;
result = s.get(
    f"{base}/practices/{practice_id}/patients",
    params={"LastName": "Smith", "FirstName": "Jane", "Limit": 5},
).json()
patient = result["data"][0]
patient_id = patient["patientId"]   # integer, e.g. 12345
var res = await http.GetAsync($"practices/{practiceId}/patients?LastName=Smith&FirstName=Jane&Limit=5");
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
var patientId = body.GetProperty("data").EnumerateArray().First().GetProperty("patientId").GetInt32();

2 · Read their appointments

Pass the patientId in the path. Narrow the window with Page and Limit as needed.

curl "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/patients/12345/appointments?Limit=10" \
  -H "Authorization: YOUR_TOKEN"
const res = await fetch(`${base}/practices/${practiceId}/patients/${patientId}/appointments?Limit=10`, { headers: authHeader });
const { data: appts } = await res.json();
appts.forEach(a => console.log(a.appointmentId, a.apptDate));
appts = s.get(
    f"{base}/practices/{practice_id}/patients/{patient_id}/appointments",
    params={"Limit": 10},
).json()
for a in appts["data"]:
    print(a["appointmentId"], a["apptDate"])
var res = await http.GetAsync($"practices/{practiceId}/patients/{patientId}/appointments?Limit=10");
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
foreach (var a in body.GetProperty("data").EnumerateArray())
    Console.WriteLine($"{a.GetProperty("appointmentId")} {a.GetProperty("apptDate")}");

Full field lists: PNP API reference →

Book from an availability slot

Turn "this patient wants this event type with this provider" into a confirmed appointment. You query the scheduling availability endpoints to find open slots, then write the appointment into one.

  1. Find providers for an event typeGET /practices/{practiceId}/scheduling/availability/providers
  2. Get open slotsGET /practices/{practiceId}/scheduling/availability/slots
  3. Book the appointmentPOST /practices/{practiceId}/appointments

1 · Find available providers for an event type

You need an eventTypeId (from GET /practices/{practiceId}/event-types) and optionally a serviceFacilityId to scope to a location. The response lists providers who offer that event type.

curl "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/scheduling/availability/providers?EventTypeId=EVENT_TYPE_ID" \
  -H "Authorization: YOUR_TOKEN"
const etRes = await fetch(`${base}/practices/${practiceId}/event-types`, { headers: authHeader });
const { data: eventTypes } = await etRes.json();
const eventTypeId = eventTypes[0].eventTypeId;
const pvRes = await fetch(`${base}/practices/${practiceId}/scheduling/availability/providers?EventTypeId=${eventTypeId}`, { headers: authHeader });
const { data: providers } = await pvRes.json();
const providerId = providers[0].provider.providerId;
# First, pick an event type:
event_types = s.get(f"{base}/practices/{practice_id}/event-types").json()
event_type_id = event_types["data"][0]["eventTypeId"]

# Then find providers who offer it:
providers = s.get(
    f"{base}/practices/{practice_id}/scheduling/availability/providers",
    params={"EventTypeId": event_type_id},
).json()
provider_id = providers["data"][0]["provider"]["providerId"]
var etRes = await http.GetAsync($"practices/{practiceId}/event-types");
var etBody = await etRes.Content.ReadFromJsonAsync<JsonElement>();
var eventTypeId = etBody.GetProperty("data").EnumerateArray().First().GetProperty("eventTypeId").GetInt32();
var pvRes = await http.GetAsync($"practices/{practiceId}/scheduling/availability/providers?EventTypeId={eventTypeId}");
var pvBody = await pvRes.Content.ReadFromJsonAsync<JsonElement>();
var providerId = pvBody.GetProperty("data").EnumerateArray().First().GetProperty("provider").GetProperty("providerId").GetInt32();

2 · Query open slots

Pass EventTypeId, ProviderId, and a Start/ End date range. The response groups open slots by provider.

curl "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/scheduling/availability/slots?EventTypeId=EVENT_TYPE_ID&ProviderId=PROVIDER_ID&Start=2026-08-01&End=2026-08-07" \
  -H "Authorization: YOUR_TOKEN"
const qs = new URLSearchParams({ EventTypeId: eventTypeId, ProviderId: providerId, Start: "2026-08-01", End: "2026-08-07" });
const res = await fetch(`${base}/practices/${practiceId}/scheduling/availability/slots?${qs}`, { headers: authHeader });
const { data: slotsData } = await res.json();
const firstSlot = slotsData[0].slots[0];
slots_result = s.get(
    f"{base}/practices/{practice_id}/scheduling/availability/slots",
    params={
        "EventTypeId": event_type_id,
        "ProviderId": provider_id,
        "Start": "2026-08-01",
        "End": "2026-08-07",
    },
).json()
first_slot = slots_result["data"][0]["slots"][0]   # a datetime string
var res = await http.GetAsync($"practices/{practiceId}/scheduling/availability/slots?EventTypeId={eventTypeId}&ProviderId={providerId}&Start=2026-08-01&End=2026-08-07");
var body = await res.Content.ReadFromJsonAsync<JsonElement>();
var firstSlot = body.GetProperty("data").EnumerateArray().First().GetProperty("slots").EnumerateArray().First().GetString();

3 · Create the appointment

POST /practices/{practiceId}/appointments with the patient, event type, provider, and the chosen slot datetime. A 201 Created returns the new appointmentId.

curl -X POST \
  "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/appointments" \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "patientId": 12345,
        "eventTypeId": EVENT_TYPE_ID,
        "providerId": PROVIDER_ID,
        "apptDate": "2026-08-04T10:00:00"
      }'
const res = await fetch(`${base}/practices/${practiceId}/appointments`, {
  method: "POST",
  headers: { ...authHeader, "Content-Type": "application/json" },
  body: JSON.stringify({
    patientId: patientId, eventTypeId: eventTypeId,
    providerId: providerId, apptDate: firstSlot,
  }),
});
const { appointmentId } = await res.json();
console.log("new appointment:", appointmentId);
booked = s.post(
    f"{base}/practices/{practice_id}/appointments",
    json={
        "patientId": patient_id,
        "eventTypeId": event_type_id,
        "providerId": provider_id,
        "apptDate": first_slot,
    },
).json()
print("new appointment:", booked["appointmentId"])
var body = JsonSerializer.Serialize(new {
    patientId, eventTypeId, providerId, apptDate = firstSlot
});
var res = await http.PostAsync(
    $"practices/{practiceId}/appointments",
    new StringContent(body, Encoding.UTF8, "application/json"));
var result = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"new appointment: {result.GetProperty("appointmentId")}");

Add a patient to the waitlist

When no slot is available right now, add the patient to the waitlist with their preferred days, times, and event type. If a matching slot opens, the practice can offer it to waitlisted patients.

  1. Create the waitlist entryPOST /practices/{practiceId}/waitlist
  2. Confirm it landedGET /practices/{practiceId}/waitlist/{waitlistId}

1 · Create the waitlist entry

Specify the patient, event type, preferred days/times, and an optional date range. daysOfWeek and timesOfDay are enum strings — see the PNP reference for valid values.

curl -X POST \
  "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/waitlist" \
  -H "Authorization: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "patientId": 12345,
        "eventTypeId": EVENT_TYPE_ID,
        "providerIds": [PROVIDER_ID],
        "daysOfWeek": "Monday,Wednesday,Friday",
        "timesOfDay": "Morning",
        "dateRangeStart": "2026-08-01",
        "dateRangeEnd": "2026-09-30",
        "comments": "Prefers mornings, any provider"
      }'
const res = await fetch(`${base}/practices/${practiceId}/waitlist`, {
  method: "POST",
  headers: { ...authHeader, "Content-Type": "application/json" },
  body: JSON.stringify({
    patientId: patientId, eventTypeId: eventTypeId,
    providerIds: [providerId],
    daysOfWeek: "Monday,Wednesday,Friday", timesOfDay: "Morning",
    dateRangeStart: "2026-08-01", dateRangeEnd: "2026-09-30",
    comments: "Prefers mornings, any provider",
  }),
});
const { waitlistId } = await res.json();
console.log("waitlist entry:", waitlistId);
entry = s.post(
    f"{base}/practices/{practice_id}/waitlist",
    json={
        "patientId": patient_id,
        "eventTypeId": event_type_id,
        "providerIds": [provider_id],
        "daysOfWeek": "Monday,Wednesday,Friday",
        "timesOfDay": "Morning",
        "dateRangeStart": "2026-08-01",
        "dateRangeEnd": "2026-09-30",
        "comments": "Prefers mornings, any provider",
    },
).json()
waitlist_id = entry["waitlistId"]
print("waitlist entry:", waitlist_id)
var body = JsonSerializer.Serialize(new {
    patientId, eventTypeId,
    providerIds = new[] { providerId },
    daysOfWeek = "Monday,Wednesday,Friday", timesOfDay = "Morning",
    dateRangeStart = "2026-08-01", dateRangeEnd = "2026-09-30",
    comments = "Prefers mornings, any provider",
});
var res = await http.PostAsync(
    $"practices/{practiceId}/waitlist",
    new StringContent(body, Encoding.UTF8, "application/json"));
var result = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"waitlist entry: {result.GetProperty("waitlistId")}");

2 · Confirm the entry

Read it back by waitlistId to confirm the details landed correctly.

curl "https://backend-production.patientnow.net/backend/patientnowpro/practices/YOUR_PRACTICE_ID/waitlist/WAITLIST_ID" \
  -H "Authorization: YOUR_TOKEN"
const res = await fetch(`${base}/practices/${practiceId}/waitlist/${waitlistId}`, { headers: authHeader });
const entry = await res.json();
console.log(entry.patient.firstName, "→", entry.eventType.name);
entry = s.get(f"{base}/practices/{practice_id}/waitlist/{waitlist_id}").json()
print(entry["patient"]["firstName"], "→", entry["eventType"]["name"],
      entry["dateFrom"], "–", entry["dateTo"])
var res = await http.GetAsync($"practices/{practiceId}/waitlist/{waitlistId}");
var entry = await res.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"{entry.GetProperty("patient").GetProperty("firstName")} → {entry.GetProperty("eventType").GetProperty("name")}");
📎 Waitlist slots vs. waitlist entries

The PNP API distinguishes between a waitlist entry (/waitlist — the patient's standing request) and waitlist slots (/waitlist-slots — specific openings matched against waitlisted patients). Use GET /practices/{practiceId}/waitlist-slots to see slots that have been offered, and PUT …/close to mark one as filled or declined.

ℹ️ Patterns these recipes rely on

All three use the same conventions: integer IDs passed verbatim, Page/Limit pagination on list calls, and status-code-first error handling. See Guides & concepts for the full picture, and the PNP auth page for token setup.