Centaur Dental API (UAT) - Pre-Production Documentation Pre-production docs for the Centaur Software Dental API. Use developer.dental4web.com.au for production unless explicitly testing. ## Sections • [Introduction to Dental API](https://developer-uat.dental4web.com.au/introduction-to-dental-api.md): The Dental API provides secure, programmatic access to dental practice management data. Built for third-party integrators, healthcare platforms, and custom applications, it lets you integrate with practice data while maintaining strict security and privacy controls. Connectivity beyond the cloud While designed for Dental4Web (cloud) and MediaWeb PACS, this API is not limited to cloud deployments. Practices running Dental4Windows on-premise can also connect via secure data pipelines provided by Halo Connect . This gives you a unified API experience regardless of whether your practice data resides in the cloud or on local servers. What you can do The API provides read access to core practice data across five resource domains. Write access is available for three core domains. Resource domain Description Patient Records Read demographics, contact details, health fund information, and recall schedules. Create and update patient records. Appointments Read scheduled, confirmed, cancelled, and attended appointments with full status tracking. Create, update, cancel and reschedule appointments. Financial Transactions Invoices, payments, discounts, refunds, and health fund rebates Treatments & Clinical Read completed treatments, treatment plans, and clinical notes. Create clinical notes. Dental Imaging DICOM images via MediaWeb PACS integration (online deployments only) Key features Multi-organisation access Access data from multiple dental practices using a single API key. Each request specifies the target organisation via the org-id header, which ensures complete data isolation between organisations. FHIR R4 support Selected endpoints support HL7® FHIR® R4 output for healthcare interoperability. Add /fhir/ to supported endpoint paths to receive data formatted as FHIR resources. See FHIR Capabilities for details. Incremental sync Use the mode=1 parameter to filter by modification timestamp instead of business date. This enables efficient delta synchronisation—you fetch only records that have changed since your last sync. Flexible filtering All list endpoints support filtering by patient, practitioner, location, and date range. Use the include parameter to embed related resources and reduce API calls. API access Title Details Base URL https://api-uat.dental4web.com.au Protocol HTTPS only (TLS 1.2+) Format JSON (with optional FHIR R4 output) Authentication API key + organisation ID headers Rate Limit 100,000 requests per day per integrator Current capabilities Capability Status Read patient demographics & recalls ✅ Available Read patient extended (custom fields) ✅ Available Write access (patients, appointments and clinical notes) ✅ Available Read appointments (incl. cancelled) ✅ Available Find available appointment slots ✅ Available Read treatments, treatment plans, clinical notes ✅ Available Read financial transactions & receipts ✅ Available Read reference data (practitioners, locations, books, items, payment types, classes, cancel reasons, slot types, titles, users, recalls) ✅ Available Read DICOM images & metadata via MediaWeb PACS ✅ Available FHIR R4 reads (Patient, Appointment, Practitioner, Location, Observation, Procedure) ✅ Available Create / update Patient ✅ Available Create / update Appointment, Cancel, Reschedule ✅ Available Create Clinical Note ✅ Available OAuth 2.0 authentication and authorisation 🔜 Planned for future releases Webhook notifications 🔜 Planned for future releases Prerequisites Before integrating with the Dental API, you should have: Technical knowledge : Understanding of REST APIs, HTTP methods, and JSON format Healthcare context : Familiarity with dental practice workflows and healthcare data standards Credentials : A valid API key and organisation ID(s) from Centaur Software Getting help Integration support : Contact Centaur Software for API access and integration assistance Documentation feedback : Report issues or suggest improvements via dentalapi@centaursoftware.com Quick links I want to... Go to... Set up authentication Getting Started Understand the response format API Response Formats Find the right endpoint for my use case Find Your Use Case Learn about FHIR support FHIR Capabilities See all available endpoints Appointment Resources , Patient Resources , Financial Resources , Treatment Resources , Reference Data • [Getting Started](https://developer-uat.dental4web.com.au/getting-started.md): 1 Obtain credentials To gain API access, an integrator must contact Centaur Software to set up an integration account. This request should state the intended use case, the scope of required data, and the organisation(s) for which access is being sought. Once the account is created, the integrator will be provided with an API key and given access to Dental4Web/Dental4Windows organisations. E very request must carry an org-id header identifying the organisation . 2 Add authentication All HTTP requests must include the following headers: x-api-key This is the integrator’s API key. org-id The organisation ID identifies a specific organisation within Dental4Web/Dental4Windows. Note that an integrator's API key can be used to access multiple organisations, so both the organisation ID and the API key are required for all requests. The API token grants access only to the data of the mapped org-id. Any attempt to retrieve information for an organisation other than the current one will be rejected by the API. Accept: application/json Required. All requests must include this header to specify the expected response format. Requests sent without Accept: application/json are rejected. 3 Try it out Once you have all the required details, you can test your connection using the curl example below. Plain text curl -X GET "https://api-uat.dental4web.com.au/api/v1/practitioners" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" curl -X GET "https://api-uat.dental4web.com.au/api/v1/practitioners" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" Troubleshooting your first call Error Cause Fix HTTP 401 Missing or invalid x-api-key header Check the header is present and the key matches Sandbox HTTP 400 "Missing Organisation ID" Missing org-id header Add org-id: Sandbox HTTP 403 API key valid but not authorised for this organisation Contact Centaur Software to map your key to the org HTTP 429 Rate limit exceeded Wait the retry_after seconds and try again Timeout Slow connection or a temporary service slowdown Increase client timeout to 60 s and retry • [Authentication](https://developer-uat.dental4web.com.au/authentication.md): All requests to Dental API require authentication via API keys. Authentication credentials are provided when your integration account is created by Centaur Software. Required headers Every API request must include these three headers: Header Value Description x-api-key Your API key Authenticates the integrator. Provided by Centaur Software during account setup. org-id Organisation ID Identifies which Dental4Web/Dental4Windows organisation to access. A single API key can be mapped to multiple organisations. Accept application/json Specifies the expected response format. Multi-organisation model Dental API isolates each organisation’s data, so a single API key can be authorised for several organisations without their data ever mixing: One API key, multiple organisations : A single integrator API key can be authorised to access data from multiple dental practices. Organisation isolation : The org-id header in each request determines which organisation’s data is returned. The API strictly enforces organisation isolation; you cannot access data for an organisation your key isn’t mapped to. Request scoping : All data returned is scoped to the specified organisation only. Sandbox environment A sandbox environment is available for development and testing: Sandbox org-id : Use Sandbox as your org-id value to route requests to the sandbox environment Like-for-like : The sandbox environment mirrors production functionality Test data : Contains realistic sample patient, appointment, and treatment records Safe testing : Ideal for developing and testing integrations before switching to production credentials Example sandbox request Plain text curl -X GET "https://api-uat.dental4web.com.au/api/v1/practitioners" \ -H "org-id: Sandbox" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" curl -X GET "https://api-uat.dental4web.com.au/api/v1/practitioners" \ -H "org-id: Sandbox" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" Security best practices Keep credentials confidential : Never embed API keys in client-side code, mobile apps, or public repositories Use HTTPS only : All API traffic must use HTTPS. HTTP requests will be rejected Rotate keys periodically : Contact Centaur Software to rotate API keys if you suspect a compromise Minimal scope : Request access only to the organisations you need API key lifecycle Keys do not expire on a fixed schedule. They remain valid until revoked. Rotation : contact Centaur Software to issue a new key. The old key remains valid for 24 hours after rotation. Revocation : keys can be revoked immediately on request. Revoked keys return HTTP 401 on the next request. Storage : store keys in environment variables or a secrets manager. Never commit them to source control. Authentication errors Status code Meaning What to check 401 Unauthorized Your x-api-key is missing, invalid, or expired Verify the API key value is correct and has not been revoked 403 Forbidden Your API key does not have access to the specified org-id Confirm your key is mapped to this organisation. Contact Centaur Software if needed Example request Plain text curl -X GET "https://api-uat.dental4web.com.au/api/v1/patients?patient_id=12345" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" curl -X GET "https://api-uat.dental4web.com.au/api/v1/patients?patient_id=12345" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" • [Find Your Use Case](https://developer-uat.dental4web.com.au/find-your-use-case.md): Choose your primary integration scenario to jump to the most relevant sections. * **Patient Data Integration** — synchronise patient demographics, contact info, and medical history into your system. → [Patient Resources](/patient-resources) * **Appointment Synchronisation** — pull appointment schedules, status updates, and practitioner assignments for calendar apps. → [Appointment Resources](/appointment-resources) * **Billing & Payment Systems** — extract transaction data, payment records, and financial reports for accounting software. → [Financial Resources](/financial-resources) * **EHR/EMR Connectivity** — FHIR R4 endpoints for healthcare interoperability and clinical data exchange. → [FHIR Capabilities](/fhir-capabilities) * **Development & Testing** — quick authentication setup and endpoint testing for proof-of-concept builds. → [Getting Started](/getting-started) * **Booking Automation** — create appointments via online forms, voice agents, or chatbots. → [POST Appointment](/post-appointment), [POST Appointment Reschedule](/post-appointment-reschedule), [POST Appointment Cancel](/post-appointment-cancel) * **CRM-to-PMS Patient Sync** — push patient demographics and updates into Dental4Web. → [POST Patient](/post-patient), [PATCH Patient](/patch-patient) * **Clinical Note Capture** — record notes from dictation, telehealth, or device integrations. → [POST Clinical Notes](/post-clinical-notes) * **Imaging Integration** — embed DICOM or JPEG dental imagery in patient portals or AI diagnostic tools. → [MediaWeb PACS Resources](/mediaweb-pacs-resources) • [API Response Formats](https://developer-uat.dental4web.com.au/api-response-formats.md): Every endpoint in the Dental API returns JSON in a consistent envelope, so your integration can parse success and failure the same way across the whole surface. This page describes the request headers, the standard response envelope, pagination, and the FHIR response format. For the full list of HTTP status codes and support_code values, see Validation, Limits & Character Handling . Required headers Every request must include the following headers: Header Required Value Notes org-id Yes Your organisation identifier (for example Sandbox ) Identifies the practice database to route to. x-api-key Yes Your API key Issued per organisation. Keep it secret; never embed it in client-side code. Accept Yes application/json All responses are JSON. Send this header on every request. Standard response envelope Successful responses share a single shape. data is an array for non-FHIR endpoints. FHIR endpoints place a single FHIR Bundle object under data . JSON { "status": true, "message": "Success", "data": [ { "...": "one or more records" } ], "pagination": { "page": 1, "per_page": 100, "total_rows": 1, "total_pages": 1 } } status — true on success, false on error. message — a short human-readable summary ( "Success" on the happy path). data — an array of records. Empty ( [] ) when there is nothing to return. pagination — present on list endpoints (see below). Single-record and write responses may return an empty object ( {} ) or omit it. Pagination List endpoints accept page and per_page query parameters and echo a pagination block: Field Meaning page The page returned (1-based). per_page Records per page. total_rows Total matching records across all pages. total_pages Total number of pages available. per_page has an upper bound; values above the maximum are rejected with a validation error. See Validation, Limits & Character Handling for the exact limit. Error responses Errors use the same envelope with status: false and an empty data array. Where the platform can attribute the failure to a specific cause, it includes a stable support_code you can quote to Centaur Software support: JSON { "data": [], "status": false, "message": "postcode must be a valid Australian postcode (4 digits, range 0200-9999; got 'ABCD').", "support_code": "PW-6020" } message is human-readable and, where possible, field-specific. support_code is a stable identifier. Treat it as optional — many ordinary 4xx responses (authentication, organisation mismatch, not-found, request-schema validation) are message-only and carry no support_code . Always read message as the fallback. The complete reference — HTTP status code conventions, every support_code , field limits, character handling, and the message-only responses — lives on the Validation, Limits & Character Handling page. It is the single source of truth; this page does not duplicate it. Date and time Timezone: all date and datetime values are interpreted as Australian Eastern Time (AEST/AEDT, with daylight savings applied automatically). The rate-limit window boundaries are anchored on this timezone. Format for date values: YYYY-MM-DD (ISO 8601 date). Example: 2026-06-09 . Format for datetime values: YYYY-MM-DDTHH:MM:SS (ISO 8601). Example: 2026-06-09T14:30:00 . Inclusive bounds: date range filters are inclusive on both ends. Warnings Many responses carry a top-level warnings[] array. Each entry signals that the request succeeded but with a side-effect the caller should be aware of (capping, sanitisation, default-range injection, transformation). JSON { "status": true, "message": "Success", "data": [], "pagination": {}, "metadata": {}, "warnings": [ { "type": "patient_recency_window_injected", "message": "Auto-injected 90-day recency window because no narrowing filter was supplied", "details": { "window_days": 90, "from_date": "2026-03-11", "to_date": "2026-06-09" } } ] } Warning types currently emitted: Type Emitted by Meaning patient_recency_window_injected /patients Broad call had no narrowing filter; a 90-day window was auto-applied. patient_fanout_cap_applied /patients Result set was truncated to the cap (10,000 broad / 1,000 DOB). notes_html_stripped POST /appointments , PATCH /appointments , POST /clinical-notes HTML/script content was removed from a notes field before saving. notes_ascii_folded POST /patients , PATCH /patients , POST /appointments , etc. Non-ASCII characters were folded to ASCII equivalents before saving. gender_sanitised POST /patients , PATCH /patients An invalid gender_code was sanitised to NULL. warnings[] is omitted (or empty) when nothing notable happened. Metadata The /patients endpoint carries a top-level metadata object with informational fields callers can read to detect capping and window-injection: Field Type Meaning patients_default_window_injected boolean true when an auto 90-day recency window was injected. patients_default_window_days int | null 90 when injected, null otherwise. patient_fanout_cap_applied boolean true when either patient cap fired. patient_fanout_cap_value int | null 1000 (DOB cap) or 10000 (broad cap) when fired, null otherwise. parameter_fallback_applied boolean true when an optional parameter fell back to its default. Internal telemetry fields (request timing, routing, and similar) are not exposed in the public response. Idempotency The POST /patients and POST /appointments endpoints support idempotent retries via the Idempotency-Key header. Header: Idempotency-Key: <8–128 URL-safe characters> Lifetime: the key reserves a slot for 15 minutes (in-flight) and stores the result for 24 hours (replayable). Replay: within 24 hours, retrying the same key with the same body returns the original response. Body mismatch: retrying with a different body and the same key returns 400 RP-4221 . Concurrent reservation: two simultaneous requests with the same key return 400 RP-4091 to the later caller. Invalid format: keys outside the length / character set return 400 RP-4000 . The PATCH, Cancel, and Reschedule endpoints do NOT support idempotency keys — sending the header returns 400 RP-4003 . FHIR response format HL7 FHIR R4 output is served from dedicated endpoints under /api/v1/fhir/... — there is no fhir=true query parameter. Passing fhir=true to a normal read returns 400 with “Unexpected parameters: fhir, Please try again.”; on a write endpoint an unknown fhir parameter is simply ignored. Each FHIR endpoint returns the standard envelope with an HL7 FHIR R4 Bundle (type searchset ) inside data . The bundle’s entry[] contains the primary resource plus any referenced resources (for a Patient, the referenced Practitioner and Location are included). JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "type": "searchset", "total": 1, "entry": [ { "resource": { "resourceType": "Patient", "id": "72", "name": [ { "use": "official", "family": "Citizen", "given": ["Jane"], "prefix": ["Ms"] } ] } } ] }, "pagination": { "page": 1, "per_page": 100, "total_rows": 1, "total_pages": 1 } } Resource mapping: Endpoint FHIR resource GET Patients / Patient By ID Patient GET Appointments / Appointment By ID Appointment GET Practitioners Practitioner GET Locations Location GET Clinical Notes Observation GET Treatments Procedure FHIR is read-only; there are no FHIR write endpoints. Write endpoints always return the standard envelope, and any fhir parameter on a write is ignored. • [FHIR Capabilities](https://developer-uat.dental4web.com.au/fhir-capabilities.md): Dental API supports HL7® FHIR® R4 (Release 4.0.1) output for healthcare interoperability. FHIR (Fast Healthcare Interoperability Resources) is an international standard for exchanging healthcare information electronically. Scope: FHIR data format, not SMART on FHIR The Dental API provides FHIR R4 output formatting for selected reads. It does not implement the SMART on FHIR authorisation framework. Specifically: Authentication is via a static x-api-key header (see Authentication). There is no OAuth 2.0 authorisation server, no /authorize or /token endpoint, no PKCE/authorisation-code flow, and no per-user tokens. No SMART scopes are accepted ( patient/*.read , user/*.read , launch , etc.). Access is granted per integrator + organisation, not per end-user or per resource. No launch context. There is no standalone-launch or EHR-launch flow, no launch parameter, no patient/encounter context binding. No discovery documents. /.well-known/smart-configuration is not exposed, and there is no FHIR CapabilityStatement at /metadata . No OpenID Connect identity assertions or id_token . Media type. FHIR endpoints return Content-Type: application/json (not application/fhir+json ), and each FHIR Bundle is wrapped in the Dental API’s standard envelope ( {status, message, data, pagination} ) rather than returned as a bare FHIR resource. If your integration needs SMART on FHIR (patient-facing apps, EHR-launched clinical apps, end-user OAuth), the Dental API is not a drop-in target. If you only need FHIR R4 data shapes for a server-to-server backend integration, the /api/v1/fhir/ endpoints plus the standard x-api-key flow cover that case. What is FHIR? FHIR is a standard developed by HL7 International that defines how healthcare information can be exchanged between systems. It uses modern web technologies (REST, JSON, XML) and provides a set of standardised “resources” representing clinical concepts like patients, appointments, and observations. Key benefits of FHIR: Interoperability : Exchange data with EHR/EMR systems, health information exchanges, and healthcare applications Standardisation : Consistent data structures across different healthcare systems Compliance : Meet healthcare data exchange requirements and regulations Ecosystem : Wide adoption across healthcare technology vendors FHIR-enabled endpoints The following endpoints support FHIR R4 output. Use the dedicated /api/v1/fhir/ endpoints to receive FHIR R4-formatted responses. Passing ?fhir=true to a non-FHIR read path returns 400 with Unexpected parameters: fhir . Standard Endpoint FHIR Endpoint FHIR Resource Type GET /api/v1/patients GET /api/v1/fhir/patients Patient GET /api/v1/patients/{id} GET /api/v1/fhir/patients/{id} Patient GET /api/v1/appointments GET /api/v1/fhir/appointments Appointment GET /api/v1/appointments/{id} GET /api/v1/fhir/appointments/{id} Appointment GET /api/v1/clinical-notes GET /api/v1/fhir/clinical-notes Observation GET /api/v1/practitioners GET /api/v1/fhir/practitioners Practitioner GET /api/v1/locations GET /api/v1/fhir/locations Location GET /api/v1/treatments GET /api/v1/fhir/treatments Procedure Endpoints not supporting FHIR The following endpoints do not support FHIR output: Endpoint Reason GET /patients_extended Extended fields are practice-specific and don't map to FHIR GET /patients_extended/{id} Extended fields are practice-specific and don't map to FHIR GET /financials Financial data uses practice-specific structures GET /receipts/{id} Payment details don't have direct FHIR equivalents GET /treatment-plans Treatment plans use practice-specific formats GET /appointments/cancelled Cancellations are returned in native format only All MWP (imaging) endpoints Image metadata is returned in native format Reference data Only practitioners and locations have FHIR equivalents. FHIR response format When using a FHIR endpoint, responses are returned as a FHIR Bundle of type searchset : Plain text { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "type": "searchset", "total": 1, "entry": [ { "resource": { "resourceType": "Patient", "id": "12345", ... } } ] }, "pagination": { "page": 1, "per_page": 10, "total_rows": 1, "total_pages": 1 } } { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "type": "searchset", "total": 1, "entry": [ { "resource": { "resourceType": "Patient", "id": "12345", ... } } ] }, "pagination": { "page": 1, "per_page": 10, "total_rows": 1, "total_pages": 1 } } Bundle structure Field Description resourceType Always "Bundle" for list responses type Always "searchset" indicating a search result total Total number of matching resources entry Array of resources matching the query entry[].resource The actual FHIR resource (Patient, Appointment, etc.) FHIR resource mappings Patient resource Practice Management System (PMS) patient data maps to FHIR Patient as follows: Source field FHIR Path Notes patient_id Patient.id Primary identifier patient_card Patient.identifier[0].value Secondary identifier patient_status Patient.active true if status is "Active" title Patient.name[0].prefix e.g., "Mr", "Mrs", "Dr" first_name Patient.name[0].given[0] Given name middle_name Patient.name[0].given[1] Middle name if present surname Patient.name[0].family Family name preferred_name Patient.name[1].given[0] Nickname (use: "usual") gender Patient.gender "male", "female", "other", "unknown" birthdate Patient.birthDate ISO 8601 date phone Patient.telecom[].value system: "phone", use: "home" mobile Patient.telecom[].value system: "phone", use: "mobile" email Patient.telecom[].value system: "email" address1 , address2 Patient.address[0].line[] Street address lines suburb Patient.address[0].city City/suburb state Patient.address[0].state State/territory postcode Patient.address[0].postalCode Postal code practitioner_id Patient.generalPractitioner[0].reference Reference to Practitioner Appointment resource Source field FHIR Path Notes appointment_id Appointment.id Primary identifier appointment_status Appointment.status Mapped to FHIR status codes appointment_date + start_time Appointment.start ISO 8601 datetime appointment_date + end_time Appointment.end ISO 8601 datetime duration Appointment.minutesDuration Duration in minutes appointment_type Appointment.appointmentType Type of appointment patient_id Appointment.participant[].actor Reference to Patient practitioner_id Appointment.participant[].actor Reference to Practitioner location_id Appointment.participant[].actor Reference to Location notes Appointment.comment Appointment notes Clinical notes (Observation resource) Source field FHIR Path Notes note_id Observation.id Primary identifier note_date Observation.effectiveDateTime When the note was recorded note_text Observation.valueString The clinical note content patient_id Observation.subject.reference Reference to Patient practitioner_id Observation.performer[0].reference Reference to Practitioner Treatments (Procedure resource) PMS treatment data maps to FHIR Procedure as follows: Source field FHIR Path Notes treatment_id Procedure.id Primary identifier treatment_date Procedure.performedDateTime When the procedure was performed item_code Procedure.code.coding[0].code Procedure/item code (item description) Procedure.code.coding[0].display Procedure description (always "completed") Procedure.status All returned treatments are completed patient_id Procedure.subject.reference Reference to Patient practitioner_id Procedure.performer[0].actor.reference Reference to Practitioner location_id Procedure.location.reference Reference to Location tooth + surface Procedure.bodySite[0].text Tooth number and surface (e.g., "18, MOD") item_qty Procedure.extension Quantity if > 1 updated_date Procedure.meta.lastUpdated Last modification timestamp Note: The Procedure.code.coding[0].system uses a practice-specific namespace (e.g., http://your-practice.com/item-codes ). ADA codes may be included as an additional coding entry when available. Practitioner resource Source field FHIR Path Notes practitioner_id Practitioner.id Primary identifier provider_number Practitioner.identifier[0].value Provider number title Practitioner.name[0].prefix Title first_name Practitioner.name[0].given[0] Given name surname Practitioner.name[0].family Family name is_active Practitioner.active Active status Location resource Source field FHIR Path Notes location_id Location.id Primary identifier location_name Location.name Location name address Location.address Full address phone Location.telecom[0].value Phone number is_active Location.status "active" or "inactive" Using FHIR endpoints Request example Plain text curl -X GET "https://api-uat.dental4web.com.au/api/v1/fhir/patients?patient_id=12345" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" curl -X GET "https://api-uat.dental4web.com.au/api/v1/fhir/patients?patient_id=12345" \ -H "org-id: YOUR_ORG_ID" \ -H "x-api-key: YOUR_API_KEY" \ -H "Accept: application/json" Inline references FHIR responses include inline references to related resources. For example, an Appointment resource will include references to the Patient, Practitioner, and Location: JSON { "participant": [ { "actor": { "reference": "Patient/12345", "display": "Jane Smith" }, "status": "accepted" }, { "actor": { "reference": "Practitioner/42", "display": "Dr David Wilson" }, "status": "accepted" }, { "actor": { "reference": "Location/1", "display": "Sydney CBD Dental" }, "status": "accepted" } ] } FHIR specification references FHIR R4 Specification : https://www.hl7.org/fhir/ Bundle Resource : https://www.hl7.org/fhir/bundle.html Patient Resource : https://www.hl7.org/fhir/patient.html Appointment Resource : https://www.hl7.org/fhir/appointment.html Observation Resource : https://www.hl7.org/fhir/observation.html Practitioner Resource : https://www.hl7.org/fhir/practitioner.html Location Resource : https://www.hl7.org/fhir/location.html • [Rate Limits](https://developer-uat.dental4web.com.au/rate-limits.md): To ensure fair usage and system stability, Dental API enforces rate limits on all requests. Daily limits In addition to the per-minute rate limits documented below, each integrator account is subject to a daily aggregate cap of 10,000 requests across all endpoints (24-hour rolling window). Exceeding the daily cap returns HTTP 429 with support_code: "RL-002" . Current limits Limit Type Threshold Window Per Integrator 10,000 requests Per day (24 hours, rolling) Per Integrator 100 calls/min Per minute (rolling) Per Organisation 60 calls/min Peak: 8:00 AM - 8:00 PM AEST(rolling) Per Organisation 120 calls/min Off-peak: 8:00 PM - 8:00 AM AEST(rolling) Rate limit behaviour When you exceed the rate limit: The API returns a 429 Too Many Requests response The response includes a Retry-After header indicating seconds to wait Requests resume normally after the rate limit window resets What counts as a request Every successful (2xx) response counts as one request. HTTP 4xx responses (including validation errors and RL-* rate-limit rejections) count as one request — they consume quota. HTTP 5xx responses do NOT count toward your quota. Retries triggered by 429 responses count as separate requests; back off with exponential delay. Daylight savings The peak / off-peak windows shift with Australian Eastern Time daylight savings. During AEDT (October–April), the peak window runs 8:00 AM – 8:00 PM AEDT; during AEST (April–October) it runs 8:00 AM – 8:00 PM AEST. Configure clients to interpret times in Australia/Sydney rather than fixed UTC offsets. Rate limit response example JSON { "status": false, "message": "Rate limit exceeded. Please retry after the indicated time.", "retry_after": 60, "support_code": "RL-001", "data": [] } Best practices to avoid rate limits Cache reference data Reference data (practitioners, locations, procedure codes, appointment books, payment types) changes infrequently. Cache these locally and refresh periodically (e.g., daily) rather than fetching on every request. Use pagination wisely Default per_page is 10, maximum is 500 For large data sets, use reasonable page sizes (10-50 records) Avoid repeatedly fetching the same pages Implement exponential backoff When receiving a 429 response, implement exponential backoff: Plain text Wait 1 second, retry If still 429, wait 2 seconds, retry If still 429, wait 4 seconds, retry ...continue doubling up to 32 seconds Wait 1 second, retry If still 429, wait 2 seconds, retry If still 429, wait 4 seconds, retry ...continue doubling up to 32 seconds Use date range filters Always specify from_date and to_date to limit result sets. Smaller, targeted queries are more efficient than fetching all data. Leverage the mode parameter for sync operations Use mode=1 with date filters to fetch only records modified since your last sync, rather than fetching all records repeatedly. • [Validation, Limits & Character Handling](https://developer-uat.dental4web.com.au/validation-limits-and-character-handling.md): This page is the single source of truth for how the Dental API validates input, the limits it enforces, how it handles special characters, and every support_code you may encounter. It applies across all read and write endpoints. Error envelope Error responses use the standard envelope with status: false and an empty data array. Where the cause can be attributed, a stable support_code is included: JSON { "data": [], "status": false, "message": "postcode must be a valid Australian postcode (4 digits, range 0200-9999; got 'ABCD').", "support_code": "PW-6020" } message is human-readable and field-specific where possible. support_code is a stable identifier you can quote to support. It is optional — many ordinary 4xx responses are message-only (see Responses with no support_code ). Always fall back to message . HTTP status code conventions Status Used When 200 Success. 400 Business-rule or input validation failure (for example invalid postcode, non-positive duration, missing required parameter , organisation not associated). 403 Authentication failure (missing or invalid x-api-key ), or a privilege restriction. 404 The requested record does not exist, or a referenced ID (patient, book, practitioner, class) was not found. 409 Conflict — for example a time slot already taken, a locked day, or an idempotency collision. 422 Request-schema validation failure (wrong type, value out of the schema's range) 429 Rate limit exceeded. Honour the Retry-After header / retry_after field. 500 Unexpected internal error. Safe to retry after a short delay. 502 / 503 / 504 Gateway, service-unavailable, or timeout condition. Retryable. Field-level validation Validation errors are returned per field with a descriptive message. Some endpoints enforce required parameters in business logic rather than the request schema, returning 400 RP-4000 (for example POST /api/v1/clinical-notes without patient_id ). Common request-schema ( 422 ) examples: Required field missing: JSON { "data": [], "status": false, "message": "'surname': field required; 'date_of_birth': field required" } Wrong type: JSON { "data": [], "status": false, "message": "'force_create': value is not a valid integer" } Value too long: JSON { "data": [], "status": false, "message": "'firstname': ensure this value has at most 30 characters" } Input limits Patient fields Field Limit firstname 1–30 characters (Latin-1) surname 1–30 characters (Latin-1) middle_name up to 15 characters title up to 6 characters email up to 60 characters phone_home / phone_mobile / phone_work up to 20 characters occupation up to 30 characters company up to 100 characters address_line1 / address_line1_work up to 300 characters address_line2 / address_line2_work up to 40 characters suburb / suburb_work up to 80 characters state / state_work up to 5 characters (AU state code) postcode / postcode_work 4 digits, range 0200–9999 date_of_birth YYYY-MM-DD , between 1900-01-01 and today gender_code exactly 1 character Appointment fields Field Limit appointment_date / to_appointment_date YYYY-MM-DD ; not in the past, no more than 3650 days (~10 years) ahead start_time / finish_time HH:MM (24-hour); finish_time strictly after start_time ; within the book's operating hours appt_book_id / to_appt_book_id integer ≥ 1 class_id valid appointment class ID is_copy 0 or 1 notes up to 1000 characters Pagination and range limits Title Value per_page maximum 500 Appointment-slot search range 90 days (single book) / 14 days (all books) Character handling Some practice-management storage cannot represent characters outside a narrow Latin set. The API normalises input so your data is stored cleanly and predictably: Accent folding (writes). Accented characters in write string fields are transliterated to their plain-letter equivalent before storage — for example Zoë becomes Zoe , François becomes Francois . Each transformation is disclosed in a warnings[] entry on the (successful) response. Unsupported characters. Characters with no accepted plain-letter equivalent (CJK, emoji, Cyrillic) are rejected with 400 and a precise code ( PW-6025 / AW-6025 ). Length after folding. If folding pushes a value past its column limit, it is rejected with 400 ( PW-6026 / AW-6026 ). HTML and script content. HTML/script-shaped content in text fields is removed before saving; the request still succeeds ( 200 ) and the change is recorded in warnings[] . Payloads that are clearly script/markup in shape are rejected ( PW-6023 / AW-6023 ). Postcode. Must be a 4-digit Australian postcode in the range 0200–9999 ( PW-6020 ). Parameter type validation Before a request reaches the database, parameter values are checked against their declared type and rejected at the edge with a precise 400 code rather than an opaque error: Code Meaning TYP-INT Integer parameter received a non-integer value TYP-VARCHAR String parameter failed validation (for example over length) TYP-DATE Date parameter is not a valid date TYP-DATETIME Datetime parameter is not a valid datetime TYP-TIME Time parameter is not a valid time TYP-BIT Boolean/bit parameter is not 0 / 1 Reliability and error classification Retryable ( 429 , 500 , 502 , 503 , 504 ): transient. Back off and retry, honouring Retry-After where present. Not retryable ( 400 , 403 , 404 , 409 , 422 ): fix the request before resending. Retrying without changes will fail the same way. Support code catalogue Codes are grouped by family. Every code below can appear in a real response to an API consumer. Codes are stable; quote them to Centaur Software support. RP — platform dispatcher (reads and writes) Cross-cutting conditions from the request dispatcher. May appear on any /api/v1/* operation. Code HTTP Meaning RP-4000 400 User-input validation failure (for example a required filter is missing) RP-4001 400 System-originated bad request (request shape rejected) RP-4002 400 FHIR not supported on writes RP-4003 400 Idempotency-Key not supported on this endpoint RP-4031 403 Forbidden (operation not permitted for the org/key) RP-4091 409 Idempotency key still in progress RP-4221 422 Idempotency body mismatch (same key, different body) RP-5001 500 Unexpected internal error RP-5021 502 Bad gateway (storage/gateway failure) RP-5031 503 Service unavailable (route offline / site unavailable) RP-5041 504 Timeout RP-5042 504 Data-pipeline queue timeout (on-premise database slow to respond) SLT — appointment slot search From GET /api/v1/appointment-slots pre-execute validation. All 400 . Code HTTP Meaning SLT-001 400 Slot range cap exceeded (> 14 days all-books, or > 90 days single-book) SLT-002 400 End of requested window is in the past SLT-003 400 Intraday start_time is at or after end_time TYP — parameter type validation See Parameter type validation above. All 400 . Codes: TYP-INT , TYP-VARCHAR , TYP-DATE , TYP-DATETIME , TYP-TIME , TYP-BIT . RL / HST — rate limiting and host load Code HTTP Meaning RL-001 429 Rate limit exceeded (per-organisation cap); response carries retry_after and a Retry-After header HST-001 503 Host overloaded — retry shortly; same retry_after shape FAN — patient fan-out / extended detail Code HTTP Meaning FAN-EXT-PID 400 Returned when GET /patients_extended/{patient_id} is called without a real positive-integer patient_id . Use the detail-fetch path ( /patients_extended/{patient_id} ), or query /patients first for broad search. PW — patient writes From POST /api/v1/patients and PATCH /api/v1/patients/{id} . Code HTTP Meaning PW-6001 409 Duplicate patient (name + date of birth match). Pass force_create: 1 to allow. PW-6002 400 A required patient field is missing PW-6003 400 Invalid gender_code (normally sanitised automatically; rarely surfaces) PW-6004 404 title not found PW-6005 500 Patient could not be created PW-6006 200 Patient created, but address insert failed (success + warnings[] ) PW-6007 500 Create reported no row PW-6010 400 date_of_birth outside 1900-01-01 to today PW-6011 400 gender_code length not 1 PW-6012 404 practitioner_id not found PW-6013 404 patient_id not found PW-6014 500 Patient could not be updated PW-6015 200 Patient updated, but address update failed (success + warnings[] ) PW-6020 400 postcode failed Australian validation PW-6021 400 Unsafe text (control/unicode) on a narrow-character field PW-6023 400 Script/HTML-shaped payload rejected PW-6025 400 Field contains characters with no accepted plain-letter equivalent PW-6026 400 Field exceeds its maximum length after character folding PW-6097 400 Internal error caught at the edge PW-6098 500 Unrecognised database error on a patient write PW-6099 500 Unrecognised failure on a patient write AW — appointment writes From POST /api/v1/appointments , …/{id}/reschedule , …/{id}/cancel , and PATCH …/{id} . Code HTTP Meaning AW-6001 403 Access denied AW-6002 400 Required parameters null AW-6003 400 Incorrect start/finish AW-6004 422 Date out of range AW-6005 422 Time outside the book's operating hours AW-6010 404 Appointment book not found AW-6011 404 Practitioner not found (create/reschedule) AW-6012 404 Patient not found AW-6013 404 Source appointment not found (or cancelled) AW-6014 404 Destination appointment book not found (reschedule) AW-6020 409 Target day or source appointment is locked AW-6021 409 Time slot is no longer available AW-6023 400 Script/HTML-shaped payload rejected AW-6025 400 notes contain characters with no accepted plain-letter equivalent AW-6026 400 notes exceed the column width after character folding AW-6040 422 appointment_date / to_appointment_date in the past or more than 3650 days ahead AW-6041 422 is_copy not 0 / 1 AW-6042 400 Duration not positive ( start_time ≥ finish_time ) AW-6043 422 Appointment book ID below 1 AW-6050 404 Appointment not found AW-6051 404 Appointment class not found AW-6052 404 Practitioner not found (update) AW-6053 400 Nothing to change (no updatable fields supplied) AW-6054 400 Incorrect finish time (at or before start) AW-6055 500 Confirm status not found (internal state) AW-6056 409 No place to confirm status AW-6057 409 Update failed and was rolled back AW-6058 409 Appointment already attended (cannot cancel) AW-6059 409 Ambiguous appointment ID AW-6097 500 Fatal internal error AW-6098 500 Unrecognised failure on an appointment write AW-6099 500 Database (SQL) exception MWP — MediaWeb PACS imaging From the imaging endpoints ( GET /api/v1/mwp/images , …/{image_id} , …/patient/{patient_id} ). Code HTTP Meaning MWP-4000 400 User-input validation failure MWP-4001 400 System-originated bad request MWP-4031 403 Forbidden MWP-4041 404 Image or object not found MWP-5001 500 Unexpected internal error MWP-5021 502 Bad gateway (storage failure) MWP-5031 503 Service unavailable MWP-5041 504 Timeout MWP-5042 504 Data-pipeline queue timeout Responses with no support_code Several common responses are deliberately message-only. Clients that key off support_code must tolerate its absence and fall back to message : HTTP Example message Cause 400 "Given organisation <ORG> is not associated with provided token." org-id not associated with the supplied key 400 "Request rejected by security policy. Check your input does not contain HTML or script content." Request blocked by the security policy 403 { "message": "Forbidden" } (bare, not the standard envelope) Missing or invalid x-api-key 404 "No records found matching your search criteria." A read returned no matching rows 422 "'query -> per_page': ensure this value is less than or equal to 500" Request-schema (query/path) validation 502 "Service temporarily unreachable. Please try again." The platform could not reach the database service • [Filtering & Date Ranges](https://developer-uat.dental4web.com.au/filtering-and-date-ranges.md): This page explains how date filtering and the mode parameter work across all Dental API endpoints. Understanding these concepts is essential for building efficient sync workflows and delta queries. The mode parameter The mode parameter controls how date filters are interpreted. It appears on most list endpoints. Value Name Behaviour 0 Business Date Mode Filters by the business/transaction date (e.g., appointment date, treatment date). This is the default. 1 Modification Timestamp Mode Filters by when records were created or last modified . Use this for sync/delta queries to capture all changes since your last sync. When to use each mode Mode 0 (Business Date) — Use when: Generating reports for a specific date range Displaying appointments for a calendar view Fetching treatments performed during a period Mode 1 (Modification Timestamp) — Use when: Syncing data to an external system Detecting changes since your last API call Building incremental backup workflows Example: business date vs modification timestamp Plain text # Mode 0: Get appointments scheduled for January 2026 curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-01-01&to_date=2026-01-31&mode=0" # Mode 1: Get all appointments created or modified since January 1st curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-01-01&to_date=2026-01-31&mode=1" # Mode 0: Get appointments scheduled for January 2026 curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-01-01&to_date=2026-01-31&mode=0" # Mode 1: Get all appointments created or modified since January 1st curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-01-01&to_date=2026-01-31&mode=1" The first request returns appointments scheduled in January. The second returns appointments that were created or updated in January—including future appointments booked during that period. Automatic date range defaults When querying list endpoints without a specific patient_id , the API applies automatic date range defaults to prevent unbounded queries. Each endpoint has its own default range optimised for typical use cases. Default ranges by endpoint Endpoint Condition Total Range Past Days Future Days GET /appointments When patient_id AND appointment_id are NULL 90 days 30 60 GET /appointments/cancelled When patient_id is NULL 60 days 60 0 GET /financials When patient_id is NULL 365 days 365 0 GET /treatment-plans When patient_id is NULL 180 days 180 0 GET /treatments When patient_id is NULL 90 days 90 0 GET /recalls When patient_id is NULL 210 days 30 180 GET /clinical-notes When patient_id is NULL 90 days 90 0 GET /mwp/images When start_date AND end_date are NULL 365 days 365 0 GET /patients When NO patient_id / patient_card AND no complete from_date + to_date window 90 days 90 0 How defaults are applied The API handles three scenarios when dates are omitted: Plain text Scenario 1 (both NULL): from_date = today − [Past Days] to_date = today + [Future Days] Scenario 2 (only from_date NULL): from_date = to_date − [Total Range Days] Scenario 3 (only to_date NULL): to_date = from_date + [Total Range Days] Scenario 1 (both NULL): from_date = today − [Past Days] to_date = today + [Future Days] Scenario 2 (only from_date NULL): from_date = to_date − [Total Range Days] Scenario 3 (only to_date NULL): to_date = from_date + [Total Range Days] Examples Plain text # Appointments: No dates provided → defaults to 30 days past, 60 days future # If today is 2026-02-13: # from_date = 2026-01-14 (30 days ago) # to_date = 2026-04-14 (60 days ahead) curl "https://api-uat.dental4web.com.au/api/v1/appointments" # Financials: No dates provided → defaults to 365 days past only # If today is 2026-02-13: # from_date = 2025-02-13 (365 days ago) # to_date = 2026-02-13 (today) curl "https://api-uat.dental4web.com.au/api/v1/financials" # Recalls: No dates provided → defaults to 30 days past, 180 days future # If today is 2026-02-13: # from_date = 2026-01-14 (30 days ago) # to_date = 2026-08-12 (180 days ahead) curl "https://api-uat.dental4web.com.au/api/v1/recalls" # Appointments: No dates provided → defaults to 30 days past, 60 days future # If today is 2026-02-13: # from_date = 2026-01-14 (30 days ago) # to_date = 2026-04-14 (60 days ahead) curl "https://api-uat.dental4web.com.au/api/v1/appointments" # Financials: No dates provided → defaults to 365 days past only # If today is 2026-02-13: # from_date = 2025-02-13 (365 days ago) # to_date = 2026-02-13 (today) curl "https://api-uat.dental4web.com.au/api/v1/financials" # Recalls: No dates provided → defaults to 30 days past, 180 days future # If today is 2026-02-13: # from_date = 2026-01-14 (30 days ago) # to_date = 2026-08-12 (180 days ahead) curl "https://api-uat.dental4web.com.au/api/v1/recalls" Patient endpoint The /patients endpoint behaves differently to other list endpoints. If your request has no patient_id or patient_card AND no complete from_date / to_date window, the API auto-injects a 90-day recency window before executing the search. The injection is surfaced in metadata.patients_default_window_injected: true and a warnings[] entry — see ‘Patient endpoint row caps’ below. When date parameters are used without a patient_id , the following rules apply: Title Description Scenario Behaviour Both from_date and to_date are NULL (but another filter parameter is provided) A 90-day recency window is auto-injected. metadata.patients_default_window_injected: true , metadata.patient_fanout_cap_applied: true , metadata.patient_fanout_cap_value: 10000 , plus a warnings[] entry. Only from_date is NULL (i.e. to_date supplied) from_date = to_date − 90 days . Counts as an injected window; the 10,000 cap fires. Only to_date is NULL (i.e. from_date supplied) to_date = today (the system clamps at today rather than projecting forward). Counts as an injected window — metadata.patients_default_window_injected: true and the 10,000 cap fires. Both from_date AND to_date supplied, valid ISO dates, span ≤ 370 days No injection, no cap. The API respects your window verbatim. This is the only shape that bypasses the 10,000 cap on broad calls. Partial-bound calls (only one date supplied) and truly-no-filter calls take the SAME code path as recency-window injection — the missing end is auto-filled and the response is treated as bounded. The 10,000 broad-fanout cap fires in both cases. The ONLY shape that bypasses both the recency injection and the row cap is supplying both ends of a valid from_date + to_date window ≤ 370 days. Patient endpoint row caps In addition to the recency window injection, patient read endpoints have two layered row caps that protect against unbounded fan-out: Title Description Broad-fanout cap TOP 10,000 Any call NOT bounded by patient_id , patient_card , date_of_birth , OR a valid both-bounds from_date + to_date window (≤ 370 days). DOB-fanout cap TOP 1,000 date_of_birth supplied without patient_id . Identity-verification semantics — DOB alone is not unique. Both caps are bypassed by a patient_id / patient_card lookup (point lookups). The broad cap is also bypassed by a valid both-bounds from_date + to_date window ≤ 370 days (the trusted sync-delta path). A single-ended date filter (only from_date OR only to_date ) does NOT bypass the cap; the API auto-fills the missing end and treats it as an injected window. Garbage values ( patient_id='abc' , patient_card='ABCDEFGHIJKL' , malformed dates) do NOT count as bounding — the cap still applies. When a cap fires, the response includes metadata.patient_fanout_cap_applied: true and metadata.patient_fanout_cap_value: 10000 (broad) or 1000 (DOB), plus a warnings[] entry describing the action. Detecting injection or capping in the response When the window injector or either cap fires, the response carries these informational fields so callers can programmatically detect the bounded outcome: Field Type When present metadata.patients_default_window_injected boolean Always present; true when the recency window was injected. metadata.patients_default_window_days int 90 when injected, null otherwise metadata.patient_fanout_cap_applied boolean Always present; true when either row cap fired. metadata.patient_fanout_cap_value int 1000 (DOB cap) or 10000 (broad cap) when fired, null otherwise. warnings[] entry object Present when window or cap fires, with a human-readable description and the resolved date values. Treat these as informational. The response data is correct but bounded; supply a wider explicit window or a unique identifier to bypass. Plain text # Partial-bound on /patients (only from_date supplied) # If today is 2026-06-05: # from_date stays as supplied # to_date auto-filled to today (2026-06-05) # Response metadata: # patients_default_window_injected: true # patients_default_window_days: 90 # patient_fanout_cap_applied: true # patient_fanout_cap_value: 10000 curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-04-01" # Partial-bound on /patients (only to_date supplied) # from_date auto-filled to to_date - 90 days; counts as an injected window curl "https://api-uat.dental4web.com.au/api/v1/patients?to_date=2026-06-05" # Cap-bypass: both ends supplied, span ≤ 370 days # No window injection, no row cap. Full result set paginated. curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-01-01&to_date=2026-06-05" # Partial-bound on /patients (only from_date supplied) # If today is 2026-06-05: # from_date stays as supplied # to_date auto-filled to today (2026-06-05) # Response metadata: # patients_default_window_injected: true # patients_default_window_days: 90 # patient_fanout_cap_applied: true # patient_fanout_cap_value: 10000 curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-04-01" # Partial-bound on /patients (only to_date supplied) # from_date auto-filled to to_date - 90 days; counts as an injected window curl "https://api-uat.dental4web.com.au/api/v1/patients?to_date=2026-06-05" # Cap-bypass: both ends supplied, span ≤ 370 days # No window injection, no row cap. Full result set paginated. curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-01-01&to_date=2026-06-05" Why defaults exist Without defaults, a query like GET /appointments would attempt to return the entire appointment history—potentially millions of records. Defaults protect both API performance and your application from timeout errors. Overriding defaults Simply provide explicit date parameters to override: Plain text # Uses defaults (30 days past, 60 days future) curl "https://api-uat.dental4web.com.au/api/v1/appointments" # Explicit range (full year) curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2025-01-01&to_date=2025-12-31" # Patient-specific (no date limits) curl "https://api-uat.dental4web.com.au/api/v1/appointments?patient_id=12345" # Uses defaults (30 days past, 60 days future) curl "https://api-uat.dental4web.com.au/api/v1/appointments" # Explicit range (full year) curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2025-01-01&to_date=2025-12-31" # Patient-specific (no date limits) curl "https://api-uat.dental4web.com.au/api/v1/appointments?patient_id=12345" MWP image search defaults The MediaWeb PACS image search endpoint ( GET /mwp/images ) applies a 365-day default range: Scenario Resulting Dates Both start_date and end_date omitted start_date = today − 365 days, end_date = today Only start_date provided end_date = start_date + 365 days Only end_date provided start_date = end_date − 365 days Cancelled appointments: special behaviour The /appointments/cancelled endpoint has unique mode behaviour: Mode Filters By 0 Original appointment date (when it was scheduled for) 1 Cancellation date (when it was cancelled, not modification date) This is intentional: when syncing cancellations, you typically want to know which appointments were cancelled during a period, not when they were originally scheduled. Plain text # Get appointments originally scheduled for January that were cancelled curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2026-01-01&to_date=2026-01-31&mode=0" # Get appointments cancelled during January (regardless of original date) curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2026-01-01&to_date=2026-01-31&mode=1" # Get appointments originally scheduled for January that were cancelled curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2026-01-01&to_date=2026-01-31&mode=0" # Get appointments cancelled during January (regardless of original date) curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2026-01-01&to_date=2026-01-31&mode=1" Endpoints supporting the mode parameter Endpoint Mode 0 Filters By Mode 1 Filters By GET /appointments Appointment date Modification timestamp GET /appointments/cancelled Original appointment date Cancellation date GET /treatments Treatment date Modification timestamp GET /treatment-plans Plan date Modification timestamp GET /clinical-notes Note date Modification timestamp Note: GET /patients does not require (or accept) the mode parameter. If date values are provided in the request, the endpoint operates on the record’s last-modified timestamp without needing explicit declaration of mode=1 . Sync workflow best practices The sync pattern differs between patient endpoints and other list endpoints. Patient reads have row caps (10,000 broad / 1,000 DOB) and filter on the record’s last-modified timestamp implicitly. Other list endpoints have no row caps and use the mode parameter to switch between business-date and modification-timestamp filtering. When a cap fires or a recency window is auto-injected, the response carries a warnings[] entry and informational metadata.* fields — check these on every sync call. Initial sync — non-patient endpoints No row cap applies. Use a wide explicit window with mode=0 (or omit mode ) to walk full history by business date: Plain text # Sync all appointments for the past 2 years curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2024-06-05&to_date=2026-06-05" # Sync all financial transactions for the past 5 years curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2021-06-05&to_date=2026-06-05" # Sync all treatments performed in the past 3 years curl "https://api-uat.dental4web.com.au/api/v1/treatments?from_date=2023-06-05&to_date=2026-06-05" # Sync all appointments for the past 2 years curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2024-06-05&to_date=2026-06-05" # Sync all financial transactions for the past 5 years curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2021-06-05&to_date=2026-06-05" # Sync all treatments performed in the past 3 years curl "https://api-uat.dental4web.com.au/api/v1/treatments?from_date=2023-06-05&to_date=2026-06-05" Initial sync — patient endpoints The 10,000 broad-fanout cap requires sync windows of ≤ 370 days. Walk back in 1-year increments until you reach your practice start date. Each call respects the cap-bypass condition (both bounds supplied AND span ≤ 370 days), so each window returns the complete patient set modified inside it. Plain text # Walk patients back in 1-year windows — each call returns the complete bounded set. curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2025-06-05&to_date=2026-06-05" curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2024-06-05&to_date=2025-06-05" curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2023-06-05&to_date=2024-06-05" # ...continue back in 1-year increments until you reach your practice start date. # Walk patients back in 1-year windows — each call returns the complete bounded set. curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2025-06-05&to_date=2026-06-05" curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2024-06-05&to_date=2025-06-05" curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2023-06-05&to_date=2024-06-05" # ...continue back in 1-year increments until you reach your practice start date. Note: /patients automatically filters on the record’s last-modified timestamp whenever date parameters are supplied — no mode parameter is needed (or accepted). If metadata.patient_fanout_cap_applied: true appears in the response, the cap fired — narrow the window or supply patient_id / patient_card . Delta sync — non-patient endpoints Use mode=1 with your last sync timestamp to capture every record created or modified since: Plain text # Get all changes since yesterday curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/treatments?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/clinical-notes?from_date=2026-06-04&to_date=2026-06-05&mode=1" # Get all changes since yesterday curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/treatments?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2026-06-04&to_date=2026-06-05&mode=1" curl "https://api-uat.dental4web.com.au/api/v1/clinical-notes?from_date=2026-06-04&to_date=2026-06-05&mode=1" Delta sync — patient endpoints No mode parameter — date values implicitly filter on the record’s last-modified timestamp. As long as the window is ≤ 370 days, the cap-bypass condition is satisfied: Plain text # Get patients modified since last sync (modification timestamp is implicit) curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-06-04&to_date=2026-06-05" # Get patients modified since last sync (modification timestamp is implicit) curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-06-04&to_date=2026-06-05" Handling pagination in syncs Large result sets are paginated. Always loop until complete, and add a small delay between calls to avoid hitting the per-org rate limit (currently 60 requests per minute during peak hours, 120 off-peak): Plain text import requests import time def sync_all_pages(endpoint, params, headers, delay=1.0): params['skip'] = 0 params['limit'] = 100 all_records = [] while True: response = requests.get(endpoint, params=params, headers=headers) data = response.json() if data.get('metadata', {}).get('patient_fanout_cap_applied'): print("Warning: cap fired — consider narrowing the window.") all_records.extend(data.get('data', [])) if len(data.get('data', [])) < params['limit']: break params['skip'] += params['limit'] time.sleep(delay) return all_records import requests import time def sync_all_pages(endpoint, params, headers, delay=1.0): params['skip'] = 0 params['limit'] = 100 all_records = [] while True: response = requests.get(endpoint, params=params, headers=headers) data = response.json() if data.get('metadata', {}).get('patient_fanout_cap_applied'): print("Warning: cap fired — consider narrowing the window.") all_records.extend(data.get('data', [])) if len(data.get('data', [])) < params['limit']: break params['skip'] += params['limit'] time.sleep(delay) return all_records • [Appointment Resources](https://developer-uat.dental4web.com.au/appointment-resources.md): The Appointment endpoints expose scheduled, confirmed, attended, and cancelled appointments from the practice’s appointment book. Read endpoints support filtering by patient, practitioner, location, date range, and book. Write endpoints support creating, updating (limited fields), cancelling, and rescheduling appointments. The API supports FHIR-compliant Appointment resources, ensuring compatibility with healthcare industry standards. FHIR R4 output is available via the dedicated /api/v1/fhir/<resource> endpoint variants, see FHIR Capabilities . • [GET Appointments](https://developer-uat.dental4web.com.au/appointment-resources/get-appointments.md): Retrieves a paginated list of appointments from the practice management system. Filter by patient, practitioner, location, date range, or appointment book to find specific appointments. Use cases Display a practitioner’s upcoming appointments for the day or week. Sync appointment schedules to external calendar applications. Build patient-facing portals showing upcoming and past appointments. Generate attendance reports for practice analytics. Response fields Field Type Description appointment_id integer Primary identifier. appointment_status string Status description (for example Attended , Confirmed ). appointment_status_codes string Status code abbreviation (for example A ). cancelled boolean True if the appointment was cancelled. failed boolean True if the patient failed to attend. attended boolean True if the patient attended. online boolean True if booked online. confirmed boolean True if the appointment is confirmed. appointment_type string Type or reason for the appointment. appointment_date date Scheduled date (YYYY-MM-DD). start_time datetime Scheduled start time. end_time datetime Scheduled end time. appointment_arrival_time datetime | null Actual arrival time. actual_start_time time | null Actual treatment start. actual_end_time time | null Actual treatment end. duration integer Duration in minutes. recall_set_linked integer 1 if linked to a recall, 0 otherwise. patient_id integer Patient identifier. practitioner_id integer Practitioner identifier. location_id integer Location identifier. appt_book_id integer Appointment book identifier. created_date datetime Record creation timestamp. created_user_id integer User who created the appointment. updated_date datetime Record last-modified timestamp. Important notes Date filtering: supports the mode parameter — see Filtering & Date Ranges . Required filters: at least one of patient_id , appointment_id , appt_book_id , practitioner_id , location_id , from_date , or to_date is required. A call with no filter returns 400 RP-4000 . Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Include relations: use include=practitioners,locations,appt_books to embed related data. patients has been removed as an include option — see the include parameter below for details. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-06-01&to_date=2026-06-10&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointments?from_date=2026-06-01&to_date=2026-06-10&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Appointment By Id](https://developer-uat.dental4web.com.au/appointment-resources/get-appointment-by-id.md): Retrieves the complete details of a single appointment by its unique identifier. Use this when you need full appointment information after receiving a notification or webhook, or when displaying appointment details in a patient portal. Use cases Look up specific appointment details from a notification reference. Display comprehensive appointment information in patient-facing applications. Verify appointment data before sending automated reminders. Retrieve appointment context for clinical workflow integration. Response fields Field Type Description appointment_id integer Primary identifier. appointment_status string Status description (for example Confirmed , Unconfirmed ). appointment_status_codes string Status code abbreviation. cancelled boolean True if the appointment was cancelled. failed boolean True if the patient failed to attend. attended boolean True if the patient attended. online boolean True if booked online. confirmed boolean True if the appointment is confirmed. appointment_type string Type or reason for the appointment. appointment_date date Scheduled date (YYYY-MM-DD). start_time datetime Scheduled start time. end_time datetime Scheduled end time. appointment_arrival_time time Actual arrival time, if recorded. actual_start_time time Actual treatment start, if recorded. actual_end_time time Actual treatment end, if recorded. duration integer Duration in minutes. recall_set_linked boolean True if linked to a recall. patient_id integer Patient identifier. practitioner_id integer Practitioner identifier. location_id integer Location identifier. appt_book_id integer Appointment book identifier. created_date datetime Record creation timestamp. created_user_id integer User who created the appointment. updated_date datetime Record last-modified timestamp. Important notes Path parameter: appointment_id is required. Not found: returns 404 if the appointment does not exist. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointments/77116" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointments/77116" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Cancelled Appointments](https://developer-uat.dental4web.com.au/appointment-resources/get-cancelled-appointments.md): Retrieves appointments that have been cancelled or where the patient failed to attend. This endpoint helps practices track cancellation patterns, identify patients who frequently cancel, and manage rebooking workflows. Use cases Identify patients with frequent cancellations for targeted outreach. Generate cancellation reports by practitioner or location. Monitor no-show trends to optimise scheduling. Trigger automated rebooking workflows. Response fields Field Type Description appointment_id integer Original appointment identifier. appointment_date date Original scheduled date. appointment_time time Original scheduled time. appointment_duration integer Duration in minutes. patient_id integer Patient identifier. practitioner_id integer Practitioner identifier. location_id integer Location identifier. appt_book_id integer Appointment book identifier. cancel_date date Date of cancellation. cancel_type string Cancelled (cancelled by the practice) or Failed (no-show / failed-to-attend). cancel_reason_id integer | null Reason identifier; correlate against the /cancel-reasons reference list. Important notes Mode behaviour: mode=0 filters by original appointment date; mode=1 filters by cancellation date. Cancel type: returns both Cancelled and Failed appointments. cancel_type is an enum: Cancelled means cancelled by the practice; Failed means a no-show / failed-to-attend. Default range: last 60 days when no from_date / to_date is supplied. ⚠️ Deprecation notice (April 2026): patients is no longer a supported include value and has been removed from this endpoint. Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2025-01-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointments/cancelled?from_date=2025-01-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Appointment Slots](https://developer-uat.dental4web.com.au/appointment-resources/get-appointment-slots.md): Returns available appointment slots across appointment books, filtered by duration, book(s), date range, day of week, and time window. Use this endpoint to drive a booking-time picker. Use cases Power a “next available appointment” widget. Discover free slots when proposing a reschedule. Audit calendar availability across the practice. Response fields Field Type Description location_id integer Location identifier. appt_book_id integer Appointment book identifier. appt_book_name string Appointment book name. slot_date date Date of the available slot. slot_start time Slot start time. slot_end time Slot end time. duration_available integer Minutes available in the slot. day_of_week integer Day of week (Monday = 1). Important notes Returns one record per available slot, per book, per day. Books are selected with appt_book_ids (comma-separated). The date range you can search depends on the selection: a single book allows up to 90 days; all books ( appt_book_ids=-1 ) allow up to 14 days. Exceeding the cap returns 400 SLT-001 . slotmode selects which slots are returned: 0 returns empty slots only (default), 1 returns empty slots plus all presets, 2 returns empty slots plus online-bookable presets. Pagination is required for large practices; per_page is capped at 500. Only the parameters listed below are accepted. An unsupported query parameter returns 400 with the message Unexpected parameters: , Please try again. Error responses Status support_code Meaning 400 SLT-001 Date span exceeds the cap (90 days single book, 14 days all books). 400 SLT-002 to_date is in the past. 400 SLT-003 start_time at or after end_time . 400 (none) An unsupported query parameter was supplied. 404 (none) No slots match the search criteria. 422 (none) Query parameter out of range (for example duration below 1, per_page above 500, page below 1, slotmode above 2). Example error responses Date span exceeds the cap ( 400 ): JSON { "data": [], "status": false, "message": "All-books range exceeds the 14 day cap (requested 120 days). Specify a single appt_book_ids value to allow up to 90 days.", "support_code": "SLT-001" } When a single book is queried beyond its larger cap, the same code is returned with the message Single-book range exceeds the 90 day cap (requested 120 days). to_date in the past ( 400 ): JSON { "data": [], "status": false, "message": "to_date must not be in the past (got 2026-05-24).", "support_code": "SLT-002" } start_time at or after end_time ( 400 ): JSON { "data": [], "status": false, "message": "start_time must be before end_time (got start='17:00', end='08:00').", "support_code": "SLT-003" } Unsupported parameter ( 400 ): JSON { "data": [], "status": false, "message": "Unexpected parameters: appt_book_id, Please try again." } No matching slots ( 404 ): JSON { "data": [], "status": false, "message": "No records found matching your search criteria." } Parameter out of range ( 422 ): JSON { "data": [], "status": false, "message": "'query -> per_page': ensure this value is less than or equal to 500" } Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointment-slots?appt_book_ids=1&from_date=2026-06-10&to_date=2026-06-20&duration=30" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointment-slots?appt_book_ids=1&from_date=2026-06-10&to_date=2026-06-20&duration=30" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [POST Appointment](https://developer-uat.dental4web.com.au/appointment-resources/post-appointment.md): Books a new appointment for a patient in a specific appointment book and time slot. Use cases An online booking system pushes a new appointment into the practice. Book appointments from a Customer Resource Management (CRM) system. Create a booking via a voice AI agent. Request body Field Type Description appt_book_id integer Required. 1 or greater. Use GET /api/v1/appointment-slots to discover bookable IDs. appointment_date string Required. YYYY-MM-DD. patient_id string Required. The unique identifier for a patient in the database. start_time string Required. HH:MM (24-hour). finish_time string Required. HH:MM, strictly after start_time . practitioner_id integer practitioner_id from /api/v1/practitioners (0 or greater). class_id integer From /api/v1/appointment-classes. Default 1. notes string Up to 1000 characters. Returns A 200 returns the created appointment object in data , with the same fields as Get appointment by ID . The notes value is stored but is not echoed back in the appointment response. Error responses Status support_code Meaning 400 AW-6042 Duration not greater than zero ( finish_time at or before start_time ). 404 AW-6010 appt_book_id not found. 404 AW-6011 practitioner_id not found. 404 AW-6012 patient_id not found. 404 AW-6051 class_id not found. 409 AW-6020 Day or source is locked. 409 AW-6021 Selected time slot is no longer available. 422 AW-6005 Outside practice operating hours. 422 AW-6040 appointment_date in the past or more than 3650 days in the future. 422 (none) Required field missing, or appt_book_id below 1, or invalid format (schema validation). notes containing characters with no ASCII equivalent, or exceeding the column width after conversion, return 400 with AW-6025 / AW-6026 (same shape as the patient PW-6025 / PW-6026 examples). For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Required fields missing ( 422 ): JSON { "data": [], "status": false, "message": "'appointment_date': field required; 'patient_id': field required; 'start_time': field required; 'finish_time': field required" } appt_book_id below 1 ( 422 ): JSON { "data": [], "status": false, "message": "'appt_book_id': ensure this value is greater than or equal to 1" } Duration not positive ( 400 ): JSON { "data": [], "status": false, "message": "appointment duration must be greater than zero (start_time='10:00:00', finish_time='09:30:00').", "support_code": "AW-6042" } Date in the past ( 422 ): JSON { "data": [], "status": false, "message": "appointment_date must not be in the past (got 2026-05-24).", "support_code": "AW-6040" } Date too far in the future ( 422 ): JSON { "data": [], "status": false, "message": "appointment_date must not be more than 3650 days in the future (got 2037-05-08).", "support_code": "AW-6040" } Outside operating hours ( 422 ): JSON { "data": [], "status": false, "message": "Selected time falls outside the practice's operating hours.", "support_code": "AW-6005" } Appointment book not found ( 404 ): JSON { "data": [], "status": false, "message": "Appointment book not found.", "support_code": "AW-6010" } Patient not found ( 404 ): JSON { "data": [], "status": false, "message": "Patient not found.", "support_code": "AW-6012" } Appointment class not found ( 404 ): JSON { "data": [], "status": false, "message": "Appointment class 99999 not found.", "support_code": "AW-6051" } Practitioner not found ( 404 ): JSON { "data": [], "status": false, "message": "Practitioner 99999 not found.", "support_code": "AW-6011" } Slot no longer available ( 409 ): JSON { "data": [], "status": false, "message": "The selected time slot is no longer available. Please choose a different time.", "support_code": "AW-6021" } Notes contain unsupported characters ( 400 ): JSON { "data": [], "status": false, "message": "One or more fields contain characters the practice management system cannot store and that have no accepted ASCII equivalent (fields: notes). Re-submit using Latin characters only. [AW-6025]", "support_code": "AW-6025" } AW-6020 (day or source locked) and AW-6026 (notes exceed the column width after conversion) return the standard envelope with their support_code . Important notes Required fields are appt_book_id , appointment_date , patient_id , start_time , and finish_time . The time slot must be available in the target appointment book; conflicts return 409 with AW-6021 . appointment_date must be no more than 3650 days (about 10 years) in the future and not in the past. Duration must be greater than zero ( finish_time strictly after start_time ). Operating hours are enforced ( AW-6005 ); outside-hours bookings are rejected. An active practitioner is required if practitioner_id is supplied. notes are capped at 1000 characters; accented characters are converted to their plain-letter equivalent; HTML and script content is removed before saving (disclosed via warnings[] ). The notes value is stored but is not echoed back in the appointment response. Notes-transformation warnings When notes contains HTML/script content, the API removes it before saving. The response carries a warnings[] entry like: JSON "warnings": [{ "type": "notes_html_stripped", "message": "HTML/script content removed from notes", "details": {"original_length": 245, "stripped_length": 198} }] See API Response Formats for the full envelope structure. Quick example Plain text curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"appt_book_id": 5, "appointment_date": "2026-06-01", "patient_id": 12345, "start_time": "14:00", "finish_time": "14:30"}' curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"appt_book_id": 5, "appointment_date": "2026-06-01", "patient_id": 12345, "start_time": "14:00", "finish_time": "14:30"}' • [PATCH Appointment](https://developer-uat.dental4web.com.au/appointment-resources/patch-appointment.md): Updates a small set of mutable fields on an existing, non-cancelled appointment. The PATCH surface is intentionally narrow. To move the appointment in time or change its book, use Reschedule appointment instead. Use cases Adjust the appointment finish time after the patient arrives (for example a shorter procedure). Update the notes field after the practitioner records additional context. Reassign the appointment to a different practitioner without changing time or book. Request body Field Type Description finish_time string Optional. HH:MM, must remain inside the book's operating hours. practitioner_id integer Optional. practitioner_id from /api/v1/practitioners (0 or greater). notes string Optional. Up to 1000 characters. At least one of finish_time , notes , or practitioner_id must be supplied. Returns A 200 returns the updated appointment object in data , with the same fields as Get appointment by ID . Error responses Status support_code Meaning 400 AW-6054 New finish_time is invalid (at or before start_time ). 400 AW-6053 No update fields provided / no effective change. 404 AW-6013 Source appointment not found or is cancelled. 404 AW-6052 New practitioner_id not found. 422 (none) Body has none of finish_time / notes / practitioner_id , or an invalid field type. notes containing characters with no ASCII equivalent, or exceeding the column width after conversion, return 400 with AW-6025 / AW-6026 . For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Source appointment not found ( 404 ): JSON { "data": [], "status": false, "message": "Source appointment not found.", "support_code": "AW-6013" } No updatable fields supplied ( 422 ): JSON { "data": [], "status": false, "message": "At least one of finish_time / notes / practitioner_id must be supplied" } No effective change ( 400 ): JSON { "data": [], "status": false, "message": "No update fields provided. Specify at least one field to change.", "support_code": "AW-6053" } Invalid finish time ( 400 ): JSON { "data": [], "status": false, "message": "Finish time is invalid.", "support_code": "AW-6054" } Practitioner not found ( 404 ): JSON { "data": [], "status": false, "message": "Practitioner 99999 not found.", "support_code": "AW-6052" } Important notes Only three fields are mutable via PATCH: finish_time , notes , and practitioner_id . At least one of these must be supplied. An empty body, or a body with no effective change, is rejected. Cancelled appointments are not visible to PATCH and return 404 AW-6013 "Source appointment not found" by design. To change appointment_date , start_time , appt_book_id , patient_id , or class_id , use POST /api/v1/appointments/{id}/reschedule . Quick example Plain text curl -X PATCH "https://api-uat.dental4web.com.au/api/v1/appointments/85799" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"finish_time": "14:45", "notes": "Extended for additional treatment"}' curl -X PATCH "https://api-uat.dental4web.com.au/api/v1/appointments/85799" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"finish_time": "14:45", "notes": "Extended for additional treatment"}' • [POST Appointment Cancel](https://developer-uat.dental4web.com.au/appointment-resources/post-appointment-cancel.md): Marks an appointment as cancelled. Cancelled appointments are no longer visible to PATCH or reschedule operations. Use cases A patient phones to cancel. The practice cancels due to clinician availability. An external booking system propagates a cancellation. Request body Field Type Description cancel_reason_id integer Optional. 1 or greater. Use a value from /api/v1/cancel-reasons. notes string Optional. Up to 1000 characters. is_copy integer Optional. 0 (default) or 1. appointment_id is supplied as a path parameter on POST /api/v1/appointments/{appointment_id}/cancel . Returns A 200 confirms the cancellation. Note: the response field name is appt_id (not appointment_id as used elsewhere). This is a known API-side inconsistency. Error responses Status support_code Meaning 404 AW-6050 Appointment not found (does not exist or already cancelled). 422 (none) cancel_reason_id below 1, or is_copy not 0/1 (schema validation). For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Appointment not found or already cancelled ( 404 ): JSON { "data": [], "status": false, "message": "Appointment not found.", "support_code": "AW-6050" } cancel_reason_id below 1 ( 422 ): JSON { "data": [], "status": false, "message": "'cancel_reason_id': ensure this value is greater than or equal to 1" } is_copy not 0 or 1 ( 422 ): JSON { "data": [], "status": false, "message": "'is_copy': ensure this value is less than or equal to 1" } Important notes By design, once cancelled the appointment is no longer visible to subsequent write operations. Re-cancelling returns 404 AW-6050 . cancel_reason_id is optional. To record a specific reason, supply a valid ID from GET /api/v1/cancel-reasons . Omitting it does not prevent cancellation. When supplied, cancel_reason_id must be 1 or greater; a value below 1 is rejected with 422 . is_copy controls whether a copy of the original is created as part of the cancel flow (useful for audit trails). It defaults to 0 (no copy) and accepts only 0 or 1. notes are capped at 1000 characters. Note: the response field name is appt_id (not appointment_id as used elsewhere). This is a known API-side inconsistency. Quick example Plain text curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments/85799/cancel" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"cancel_reason_id": 3, "is_copy": 0}' curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments/85799/cancel" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"cancel_reason_id": 3, "is_copy": 0}' • [POST Appointment Reschedule](https://developer-uat.dental4web.com.au/appointment-resources/post-appointment-reschedule.md): Moves an existing appointment to a new time slot, optionally as a copy. Use cases A patient requests a different time. The practice consolidates appointments after a cancellation. Copy an existing appointment to a follow-up slot ( is_copy: 1 ). Request body Field Type Description to_appt_book_id integer Required. 1 or greater. Use GET /api/v1/appointment-slots to discover bookable IDs. to_appointment_date string Required. YYYY-MM-DD. to_start_time string Required. HH:MM (24-hour). is_copy integer Optional. 0 moves (default), 1 copies. appointment_id is supplied as a path parameter on POST /api/v1/appointments/{appointment_id}/reschedule . Returns A 200 returns the rescheduled appointment object in data , with the same fields as Get appointment by ID . With is_copy: 1 , a new appointment is created at the destination and the original is left in place. Error responses Status support_code Meaning 404 AW-6013 Source appointment not found or is cancelled. 404 AW-6014 Destination to_appt_book_id not found. 409 AW-6020 Destination day or source is locked. 409 AW-6021 Destination time slot not available. 422 AW-6005 Destination time outside book operating hours. 422 AW-6040 to_appointment_date in the past or more than 3650 days in the future. 422 (none) Required field missing, to_appt_book_id below 1, or is_copy not 0/1 (schema validation). For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Source appointment not found ( 404 ): JSON { "data": [], "status": false, "message": "Source appointment not found.", "support_code": "AW-6013" } Destination book not found ( 404 ): JSON { "data": [], "status": false, "message": "Destination appointment book not found.", "support_code": "AW-6014" } to_appointment_date in the past ( 422 ): JSON { "data": [], "status": false, "message": "to_appointment_date must not be in the past (got 2026-05-24).", "support_code": "AW-6040" } to_appt_book_id below 1 ( 422 ): JSON { "data": [], "status": false, "message": "'to_appt_book_id': ensure this value is greater than or equal to 1" } is_copy not 0 or 1 ( 422 ): JSON { "data": [], "status": false, "message": "'is_copy': ensure this value is less than or equal to 1" } Destination time outside operating hours ( 422 ): JSON { "data": [], "status": false, "message": "Selected time falls outside the practice's operating hours.", "support_code": "AW-6005" } Destination slot no longer available ( 409 ): JSON { "data": [], "status": false, "message": "The selected time slot is no longer available. Please choose a different time.", "support_code": "AW-6021" } Important notes Only non-cancelled appointments can be rescheduled. A cancelled source returns 404 AW-6013 . is_copy: 1 creates a new appointment at the destination without moving the original. It defaults to 0 (move) and accepts only 0 or 1. Destination slot validation applies the same operating-hours, future-date cap, and slot-availability checks as Create appointment. AW-6020 (destination day or source locked) returns the standard envelope with that support_code . Quick example Plain text curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments/85799/reschedule" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"to_appt_book_id": 5, "to_appointment_date": "2026-06-15", "to_start_time": "10:00", "is_copy": 0}' curl -X POST "https://api-uat.dental4web.com.au/api/v1/appointments/85799/reschedule" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -d '{"to_appt_book_id": 5, "to_appointment_date": "2026-06-15", "to_start_time": "10:00", "is_copy": 0}' • [Financial Resources](https://developer-uat.dental4web.com.au/financial-resources.md): The Financial endpoints expose invoice, payment, discount, and refund transactions, plus per-receipt detail. `GET /api/v1/financials` requires `from_date` (HTTP 422 if omitted). Records returned are raw transactions; clients are responsible for any aggregation (e.g. running balance per patient). • [GET Financials](https://developer-uat.dental4web.com.au/financial-resources/get-financials.md): Retrieves financial transaction records including invoices, payments, discounts, and refunds. Each record represents a single transaction with its type, amount, and associated patient, practitioner, and location. Use cases Generate monthly revenue reports filtered by practitioner or location. Reconcile practice income with accounting systems. Track outstanding invoices for patient follow-up. Analyse payment patterns and health fund rebate ratios. Response fields Field Type Description location_id integer Location identifier. practitioner_id integer Practitioner identifier. patient_id integer Patient identifier. date date Transaction date. trans_type integer Transaction type (1 = Invoice, 2 = Discount, 3 = Refund, 4 = Payment). invoice_id integer | null Invoice identifier. receipt_id integer | null Receipt identifier. invoice decimal Invoice amount. discount decimal Discount amount. refund decimal Refund amount. receipt decimal Receipt / payment amount. health_fund_rebate boolean Health fund rebate flag. payment_type_id integer | null Payment type identifier. Important notes Required parameters: from_date is mandatory. Transaction types: 1 = Invoice, 2 = Discount, 3 = Refund, 4 = Payment. Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Date filtering: filters by transaction date, not the record’s last-modified timestamp. No mode parameter: this endpoint does not support the mode parameter — all calls filter by transaction date. Default range: last 365 days when no from_date / to_date is supplied. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2026-06-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/financials?from_date=2026-06-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Receipts](https://developer-uat.dental4web.com.au/financial-resources/get-receipts.md): Retrieves detailed payment information for a specific receipt, including payment methods, banking details, and deposit status. Use this for payment reconciliation, auditing, and investigating transaction discrepancies. Use cases Verify payment methods for specific transactions. Audit banking details for reconciliation accuracy. Investigate discrepancies between payment records and bank deposits. Generate detailed payment reports for accounting. Response fields Field Type Description receipt_id integer Receipt identifier. detail_id integer Line item identifier. payment_type_id integer Payment method identifier. receipt_date date Date of receipt. cheque_name string | null Cheque payee name, if cheque. bank_name string | null Bank name, if applicable. banked boolean True if deposited. amount decimal Payment amount. Important notes Path parameter: receipt_id is required. Not found: returns HTTP 404 with an empty data array and a message indicating no matching record was found. Multiple records: may return multiple detail records for split payments. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/receipts/12345" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/receipts/12345" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [Patient Resources](https://developer-uat.dental4web.com.au/patient-resources.md): The Patient endpoints expose patient demographics, recall schedules, and a separate extended-fields variant containing practice-specific custom attributes. Note the underscore in patients_extended (not patients-extended ) This resource is designed to align with the FHIR standard, making it easier to integrate with healthcare systems and ensure interoperability. FHIR R4 output is available via the dedicated /api/v1/fhir/<resource> endpoint variants, see FHIR Capabilities . • [GET Patients](https://developer-uat.dental4web.com.au/patient-resources/get-patients.md): Retrieves patient demographic records, with optional filtering by name, practitioner, location, or contact details. Email and mobile prefix searching is supported for flexible patient lookup. Use cases Search for patients by name before booking appointments. Sync patient demographics to an external CRM or practice management system. Build patient directories for reception dashboards. Perform incremental data synchronisation using date-range filters. Response fields Field Type Description patient_id integer Patient identifier. family_head_id integer Family group head identifier. patient_card string Patient card number. patient_status string Status (Active/Inactive/Deceased). title string Title (Mr/Mrs/Ms/Dr). first_name string First name. middle_name string | null Middle name. surname string Surname. preferred_name string | null Preferred name or nickname. address1 string Address line 1. address2 string | null Address line 2. postcode string Postal code. suburb string Suburb or city. state string State or territory. gender string Gender. birthdate date Date of birth. email string | null Email address. phone string | null Home phone. mobile string | null Mobile phone. new_patient_date date First visit date. latest_seen_date date | null Most recent visit. practitioner_id integer Preferred practitioner. location_id integer Preferred location. health_fund_name string | null Health fund name. insurance_fee_level string | null Fee level. medical_conditions_recorded string | null Comma-separated conditions. patient_alerts string | null Alert flags. patient_alert_notes string | null Alert notes. allergies_recorded string | null Comma-separated allergies. med_hist_last_updated date | null Medical history update date. referring_patient_id integer | null Referring patient. referral_source string | null Referral source. referral_date date | null Referral date. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Auto-injected recency window: when called without patient_id , patient_card , or a complete from_date / to_date window, this endpoint auto-injects a 90-day recency window and applies the 10,000-row broad-fanout cap. The response carries metadata.patients_default_window_injected: true , metadata.patient_fanout_cap_applied: true , metadata.patient_fanout_cap_value: 10000 , plus a warnings[] entry. Check these fields on broad calls to detect silent capping. To bypass the cap, supply both from_date and to_date spanning 370 days or fewer (the sync-window path), or a patient_id / patient_card lookup. Date filtering: see Filtering & Date Ranges . Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Email/mobile search: prefix matching is supported (for example, email=john@ matches all emails starting with “john@”). Extended detail: to retrieve extended patient detail (custom fields, communication preferences, and so on), use GET /patients/{patient_id} for the standard surface or GET /patients_extended/{patient_id} for the extended surface. The list-form /patients_extended endpoint is not available — it is detail-fetch only by patient_id in the path. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-05-09&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/patients?from_date=2026-05-09&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Patient By ID](https://developer-uat.dental4web.com.au/patient-resources/get-patient-by-id.md): Retrieves the complete demographic profile for a single patient by their unique identifier. Use this when you need full patient details for clinical workflows or after receiving a patient reference from another endpoint. Use cases Look up patient details from a notification or webhook reference. Display comprehensive patient information in clinical applications. Verify patient data before sending automated communications. Retrieve patient context for appointment booking flows. Response fields Field Type Description patient_id integer Patient identifier. family_head_id integer Family group head identifier. patient_card string Patient card number. patient_status string Status (Active/Inactive/Deceased). title string Title (Mr/Mrs/Ms/Dr). first_name string First name. middle_name string | null Middle name. surname string Surname. preferred_name string | null Preferred name or nickname. address1 string Address line 1. address2 string | null Address line 2. postcode string Postal code. suburb string Suburb or city. state string State or territory. gender string Gender. birthdate date Date of birth. email string | null Email address. phone string | null Home phone. mobile string | null Mobile phone. new_patient_date date First visit date. latest_seen_date date | null Most recent visit. practitioner_id integer Preferred practitioner. location_id integer Preferred location. health_fund_name string | null Health fund name. insurance_fee_level string | null Fee level. medical_conditions_recorded string | null Comma-separated conditions. patient_alerts string | null Alert flags. patient_alert_notes string | null Alert notes. allergies_recorded string | null Comma-separated allergies. med_hist_last_updated date | null Medical history update date. referring_patient_id integer | null Referring patient. referral_source string | null Referral source. referral_date date | null Referral date. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Path parameter: patient_id is required and must be a valid integer. Not found: returns 404 if the patient does not exist. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/patients/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/patients/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Patient Recalls](https://developer-uat.dental4web.com.au/patient-resources/get-patient-recalls.md): Retrieves recall (reminder) records for a specific patient. Recalls represent scheduled follow-up appointments such as 6-month check-ups, hygiene appointments, or treatment follow-ups. Use cases Display upcoming recalls in patient portals. Integrate recall dates with appointment booking systems. Generate recall history reports for patient communication workflows. Build automated recall reminder integrations. Response fields Field Type Description recall_type_id integer Recall type identifier. patient_id integer Patient identifier. recall_date date Scheduled recall date. reminder_date date Reminder sent or due date. reminder_type string Reminder method (SMS, Email, Letter). reminder_no integer Reminder sequence number. practitioner_id integer Associated practitioner. location_id integer Associated location. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Date filtering: supports the mode parameter — see Filtering & Date Ranges . Patient scope: only returns recalls for the specified patient. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/patients/72/recalls" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/patients/72/recalls" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Patient Extended By ID](https://developer-uat.dental4web.com.au/patient-resources/get-patient-extended-by-id.md): Retrieves extended data for a single patient by their unique identifier, including all standard fields plus practice-specific custom attributes. This is a PID-only detail-fetch — other parameters are not accepted at this endpoint. Use GET /patients for broad search. Use cases Retrieve a complete patient profile, including custom fields, for clinical display. Access practice-specific metadata for a known patient. Build patient detail views with extended information. Response fields Field Type Description patient_id integer Primary identifier. family_head_id integer Head of household patient ID. patient_card string Patient card number. family_role string Role in family (for example, 'Family Head'). patient_status string 'Active' or 'Inactive'. title string Title (Mr, Mrs, Dr, etc.). first_name string Given name. middle_name string Middle name. surname string Family or last name. preferred_name string Nickname. gender string 'male', 'female', or null. ethnicity string | null Ethnicity. birthdate date Date of birth. occupation string | null Occupation. email string Email address. phone string Home phone. mobile string Mobile phone. work_phone string Work phone. address1 string Street address line 1. address2 string Street address line 2. postcode string Postal code. suburb string City or suburb. state string State or territory. work_address1 string Work address line 1. work_address2 string Work address line 2. work_postcode string | null Work postal code. work_suburb string Work city or suburb. work_state string Work state. recall_comm_preference string Recall communication method. appointment_comm_preference string Appointment communication method. invoice_comm_preference string Invoice communication method. queries_comm_preference string Queries communication method. first_seen_date date First visit date. first_treatment_date date First treatment date. new_patient_date date New patient registration date. latest_seen_date date Most recent visit date. practitioner_id integer Primary practitioner ID. location_id integer Primary location ID. health_fund_name string | null Health fund provider. health_fund_member_id string | null Member ID. health_fund_ref_number string | null Reference number. fee_level string Fee schedule category. medicare_no string | null Medicare number. medicare_IRN string | null Medicare IRN. discount_percent decimal Default discount percentage. recall_date date | null Next recall due date. medical_conditions string | null Medical conditions. alerts string | null Patient alerts. allergies string | null Known allergies. personal_details string | null Additional personal details. med_hist_last_updated date | null Medical history last updated. referring_patient_id integer | null Referring patient. referral_source string | null Referral source. referral_date date | null Referral date. created_date datetime Record created. updated_date datetime Record modified. custom_fields object Practice-defined custom fields, keyed by field name (varies by org). Each value is a string. Important notes Path parameter: patient_id is required. Not found: returns 404 if the patient does not exist. PID-only: other parameters (including include and FHIR) are not accepted at this endpoint. Use GET /patients for broad search. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/patients_extended/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/patients_extended/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [POST Patient](https://developer-uat.dental4web.com.au/patient-resources/post-patient.md): Creates a new patient record and returns the persisted patient with a system-assigned patient_id . Duplicate patients (exact match on firstname + surname + date_of_birth ) are blocked unless force_create: 1 is set. Use cases Create a patient via an external booking system. Bulk-import patient demographics from a Customer Resource Management (CRM) system. Create a new patient booking via a voice AI agent. Request body Field Type Description firstname string Required. 1-30 characters (Latin-1). surname string Required. 1-30 characters (Latin-1). date_of_birth string Required. YYYY-MM-DD, 1900-01-01 to today. practitioner_id number Active practitioner_id from /api/v1/practitioners (0 or greater). gender_code string Exactly 1 character (for example M or F). Values other than M or F are silently stored as NULL. The response includes a warnings[] entry of type gender_sanitised when this happens. title string Up to 6 characters (for example MR, MRS, DR). middle_name string Up to 15 characters. email string Up to 60 characters. phone_home string Up to 20 characters. phone_mobile string Up to 20 characters. phone_work string Up to 20 characters. occupation string Up to 30 characters. company string Up to 100 characters. address_line1 string Up to 300 characters. address_line2 string Up to 40 characters. suburb string Up to 80 characters. state string Up to 5 characters (AU state code, for example NSW). postcode string 4 digits, 0200-9999. address_line1_work string Up to 300 characters. address_line2_work string Up to 40 characters. suburb_work string Up to 80 characters. state_work string Up to 5 characters (AU state code, for example NSW). postcode_work string 4 digits, 0200-9999. force_create integer 0 blocks duplicates (default), 1 allows duplicate creation. Returns A 200 response returns the persisted patient object in data[0] , including the system-assigned patient_id . The fields match those described on Get patient by ID . Error responses Status support_code Meaning 400 PW-6010 date_of_birth outside 1900-01-01 to today. 400 PW-6020 postcode failed Australian validation. 400 PW-6025 A field contains characters with no supported ASCII equivalent. 400 PW-6026 A field exceeds its maximum length after character conversion. 404 PW-6012 practitioner_id not found or not active. 409 PW-6001 Duplicate patient (name and date-of-birth match). Pass force_create: 1 to allow. 422 (none) Required field missing, wrong type, invalid date format, or value too long (schema validation). For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Required field missing ( 422 ): JSON { "data": [], "status": false, "message": "'surname': field required; 'date_of_birth': field required" } Invalid date format ( 422 ): JSON { "data": [], "status": false, "message": "'date_of_birth': invalid date format" } Value too long ( 422 ): JSON { "data": [], "status": false, "message": "'firstname': ensure this value has at most 30 characters" } Wrong type ( 422 ): JSON { "data": [], "status": false, "message": "'force_create': value is not a valid integer" } date_of_birth out of range ( 400 ): JSON { "data": [], "status": false, "message": "date_of_birth must be between 1900-01-01 and today (got 1800-01-01).", "support_code": "PW-6010" } Invalid postcode ( 400 ): JSON { "data": [], "status": false, "message": "postcode must be a valid Australian postcode (4 digits, range 0200-9999; got 'ABCD').", "support_code": "PW-6020" } Unsupported characters ( 400 ): JSON { "data": [], "status": false, "message": "One or more fields contain characters the practice management system cannot store and that have no accepted ASCII equivalent (fields: firstname). Re-submit using Latin characters only. [PW-6025]", "support_code": "PW-6025" } Field too long after conversion ( 400 ): JSON { "data": [], "status": false, "message": "One or more fields exceed the maximum length the practice management system accepts after special characters were converted to plain text (fields: firstname (56>30)). Shorten the affected field(s) and resubmit. [PW-6026]", "support_code": "PW-6026" } Practitioner not found ( 404 ): JSON { "data": [], "status": false, "message": "Practitioner not found.", "support_code": "PW-6012" } Duplicate patient ( 409 ): JSON { "data": [], "status": false, "message": "A patient with this name and date of birth already exists (patient ID 72, card 72).", "support_code": "PW-6001" } Important notes Required fields are firstname , surname , and date_of_birth . practitioner_id is optional. Duplicate detection is by exact match on firstname + surname + date_of_birth . Override with force_create: 1 . Character handling: accented characters are converted to their plain-letter equivalent (for example, Zoë becomes Zoe , François becomes Francois ). Characters with no supported equivalent (CJK, emoji, Cyrillic) are rejected with PW-6025 . See Validation, Limits & Character Handling. postcode must be a 4-digit Australian postcode in the range 0200-9999 (both residential and work). Quick example Plain text curl -X POST "https://api-uat.dental4web.com.au/api/v1/patients" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"firstname":"John","surname":"Mitchell","date_of_birth":"1985-03-22","postcode":"2000","state":"NSW"}' curl -X POST "https://api-uat.dental4web.com.au/api/v1/patients" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"firstname":"John","surname":"Mitchell","date_of_birth":"1985-03-22","postcode":"2000","state":"NSW"}' • [PATCH Patient](https://developer-uat.dental4web.com.au/patient-resources/patch-patient.md): Updates an existing patient record. Pass only the fields you want to change. At least one updatable field must be supplied, and unknown fields are silently ignored. Use cases Sync demographic changes from an external CRM. Update contact details (phone, email, address) after a patient visit. Correct typos in patient names. Request body Pass any subset of the following fields. At least one is required. Field Type Description firstname string 1-30 characters (Latin-1). surname string 1-30 characters (Latin-1). date_of_birth string YYYY-MM-DD, 1900-01-01 to today. practitioner_id number Active practitioner_id from /api/v1/practitioners (0 or greater). gender_code string Exactly 1 character (for example M or F). Values other than M or F are silently stored as NULL. The response includes a warnings[] entry of type gender_sanitised when this happens. title string Up to 6 characters (for example MR, MRS, DR). middle_name string Up to 15 characters. email string Up to 60 characters. phone_home string Up to 20 characters. phone_mobile string Up to 20 characters. phone_work string Up to 20 characters. occupation string Up to 30 characters. company string Up to 100 characters. address_line1 string Up to 300 characters. address_line2 string Up to 40 characters. suburb string Up to 80 characters. state string Up to 5 characters (AU state code, for example NSW). postcode string 4 digits, 0200-9999. address_line1_work string Up to 300 characters. address_line2_work string Up to 40 characters. suburb_work string Up to 80 characters. state_work string Up to 5 characters (AU state code, for example NSW). postcode_work string 4 digits, 0200-9999. Returns A 200 response returns the updated patient object in data[0] . The fields match those described on Get patient by ID . Error responses Status support_code Meaning 400 PW-6010 date_of_birth outside 1900-01-01 to today. 400 PW-6020 postcode failed Australian validation. 400 PW-6025 / PW-6026 Unsupported character, or field too long after conversion. 404 PW-6013 patient_id does not exist. 422 (none) Empty body, invalid field type/format, or path patient_id out of range. For the canonical list of error codes, see Validation, Limits & Character Handling . Example error responses Patient not found ( 404 ): JSON { "data": [], "status": false, "message": "Patient 999999999 not found.", "support_code": "PW-6013" } Empty body ( 422 ): JSON { "data": [], "status": false, "message": "At least one field must be provided for update." } Path patient_id out of range ( 422 ): JSON { "data": [], "status": false, "message": "'path -> patient_id': ensure this value is less than or equal to 999999999" } date_of_birth out of range ( 400 ): JSON { "data": [], "status": false, "message": "date_of_birth must be between 1900-01-01 and today (got 1850-01-01).", "support_code": "PW-6010" } Invalid postcode ( 400 ): JSON { "data": [], "status": false, "message": "postcode must be a valid Australian postcode (4 digits, range 0200-9999; got 'ABCD').", "support_code": "PW-6020" } Unsupported characters ( 400 ): JSON { "data": [], "status": false, "message": "One or more fields contain characters the practice management system cannot store and that have no accepted ASCII equivalent (fields: firstname). Re-submit using Latin characters only. [PW-6025]", "support_code": "PW-6025" } Field too long after conversion ( 400 ): JSON { "data": [], "status": false, "message": "One or more fields exceed the maximum length the practice management system accepts after special characters were converted to plain text (fields: firstname (56>30)). Shorten the affected field(s) and resubmit. [PW-6026]", "support_code": "PW-6026" } Important notes Path parameter patient_id must be between 1 and 999,999,999. An empty body is rejected with 422 "At least one field must be provided for update." practitioner_id must be an active practitioner_id from /api/v1/practitioners (0 or greater). All other field validation rules from Create patient apply, including accent conversion, postcode validation, and column-width caps. Quick example Plain text curl -X PATCH "https://api-uat.dental4web.com.au/api/v1/patients/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"phone_mobile":"0412345678","email":"john.mitchell@example.com"}' curl -X PATCH "https://api-uat.dental4web.com.au/api/v1/patients/72" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"phone_mobile":"0412345678","email":"john.mitchell@example.com"}' • [Treatment & Clinical Resources](https://developer-uat.dental4web.com.au/treatment-and-clinical-resources.md): The Treatment & Clinical Resources API gives access to core clinical records in the Dental4Web Practice Management System. It enables third-party systems to retrieve treatment histories, upcoming planned treatments, and clinical notes while maintaining patient privacy and security. These endpoints are designed for integration with external EHR systems, automated reporting and analytics, building patient-facing treatment history views, and supporting compliance and audit needs. • [GET Treatments](https://developer-uat.dental4web.com.au/treatment-and-clinical-resources/get-treatments.md): Retrieves completed treatment records for a patient. Each row is a performed procedure, with its item code, tooth and surface detail, fee, and treating practitioner. Use cases Review a patient’s treatment history during a consultation. Generate treatment-volume reports by practitioner or location. Reconcile completed treatments for billing verification. Track treatment patterns across the practice. Response fields Field Type Description treatment_id integer Primary identifier. patient_id integer Patient identifier. plan_date date | null Date from the treatment plan, if the treatment was planned. treatment_date date Date the treatment was performed. item_code string Procedure / item code. item_qty integer Quantity. tooth string | null Tooth number, if applicable. surface string | null Tooth surface, if applicable (for example MOD ). item_fee decimal Fee charged. practitioner_id integer Treating practitioner. location_id integer Treatment location. plan_id integer | null Associated treatment-plan identifier. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Required filters: at least one filter parameter is required. Date filtering: supports the mode parameter — see Filtering & Date Ranges . Default range: last 90 days when no from_date / to_date is supplied. Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/treatments?patient_id=72&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/treatments?patient_id=72&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Treatment Plans](https://developer-uat.dental4web.com.au/treatment-and-clinical-resources/get-treatment-plans.md): Retrieves treatment plan records showing proposed future treatments. Each record represents a planned procedure, including its estimated cost, target tooth and surface, and scheduling status. Use cases Display planned treatments to patients for informed consent. Track outstanding treatment plans awaiting completion. Estimate treatment costs for insurance pre-approvals. Schedule future appointments based on treatment-plan items. Response fields Field Type Description treatment_plan_id integer Plan identifier. treatment_plan_item_id integer Line-item identifier. patient_id integer Patient identifier. phase integer Treatment phase number. visit integer Planned visit number. planned_date date Planned treatment date. item_code string Procedure / item code. item_qty integer Quantity. tooth string | null Tooth number, if recorded. surface string | null Tooth surface, if recorded. item_fee decimal Estimated fee. practitioner_id integer Planning practitioner. location_id integer Associated location. treatment_plan_status string Plan-item status. is_alt_plan boolean Alternative-plan flag. is_plan_active boolean Active-plan flag. appointment_id integer | null Linked appointment. treatment_id integer | null Linked completed treatment. treatment_date date | null Completion date. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Required filters: at least one filter parameter is required. Date filtering: supports the mode parameter — see Filtering & Date Ranges . Default range: last 180 days when no from_date / to_date is supplied. Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/treatment-plans?patient_id=2&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/treatment-plans?patient_id=2&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Clinical Notes](https://developer-uat.dental4web.com.au/treatment-and-clinical-resources/get-clinical-notes.md): Retrieves clinical notes recorded by practitioners during patient visits. Notes include observations, diagnoses, treatment summaries, and clinical documentation. Use cases Review clinical history before follow-up appointments. Generate clinical summaries for specialist referral letters. Audit documentation compliance for regulatory requirements. Integrate clinical notes with external EHR/EMR systems. Response fields Field Type Description note_id integer Primary identifier. treatment_id integer | null Linked treatment record. patient_id integer Patient identifier. note_date date Date the note was recorded. note_source string Origin of the note. item_code string | null Associated procedure / item code. tooth string | null Tooth number, if recorded. surface string | null Tooth surface, if recorded. notes string Note content. practitioner_id integer Authoring practitioner. location_id integer Location where the note was recorded. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Required filters: at least one filter parameter is required. Date filtering: supports the mode parameter — see Filtering & Date Ranges . Default range: last 90 days when no from_date / to_date is supplied. Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/clinical-notes?patient_id=72&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/clinical-notes?patient_id=72&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [POST Clinical Notes](https://developer-uat.dental4web.com.au/treatment-and-clinical-resources/post-clinical-notes.md): Creates a clinical note for a patient. The note is stored as a clinical/treatment record, and the response returns the new record’s treatment_id . Request body Field Type Description patient_id integer Required. Patient the note belongs to. note string Required. Free-text note content, up to 20,000 characters. Submit as plain text. practitioner_id integer Authoring practitioner. If omitted, the patient's default is applied and returned in the response. location_id integer Location where the note is recorded. If omitted, the patient's default is applied and returned in the response. Returns A 200 response returns the persisted note as a clinical/treatment record, including its treatment_id (not a separate “note id”), the resolved practitioner_id and location_id , and a pagination block. JSON { "status": true, "message": "Success", "data": [ { "note_id": 8043040, "treatment_id": 1043040, "patient_id": 25407, "note_date": "2026-05-27", "note_source": "Treatment", "item_code": "9999", "tooth": "", "surface": "", "notes": "Patient reports sensitivity on upper left.", "practitioner_id": 315, "location_id": 4, "created_date": "2026-05-27T22:31:18.689000", "updated_date": "2026-05-27T22:31:18.689000" } ], "pagination": { "page": 1, "per_page": 100, "total_rows": 1, "total_pages": 1 } } Error responses Clinical notes use the standard write-validation pipeline. Missing parameters return RP-4000 , a non-integer patient_id returns TYP-INT , and a non-existent patient reuses the patient code PW-6013 . See Validation, Limits & Character Handling for the full catalogue. The examples below are live-verified against Sandbox. Status support_code Meaning 400 RP-4000 patient_id and/or note missing. 400 TYP-INT patient_id is not an integer. 400 — Content rejected by the security policy (HTML/script-shaped, or an over-length or unusual payload). 400 — org-id not associated with the supplied key. 403 — Missing or invalid x-api-key . 404 PW-6013 patient_id not found. 429 RL-001 Rate limit exceeded; honour the Retry-After header / retry_after field. 500 — Unexpected internal error. 502 — Temporary service unavailability. 503 — Service temporarily unavailable. 504 — Timeout. Required parameter missing ( 400 ): JSON { "data": [], "status": false, "message": "Missing required parameters: patient_id. Both patient_id and note parameters are required", "support_code": "RP-4000" } patient_id not an integer ( 400 ): JSON { "data": [], "status": false, "message": "patient_id must be an INTEGER; got non-numeric value", "support_code": "TYP-INT" } Patient not found ( 404 ): JSON { "data": [], "status": false, "message": "Patient 999999999 not found.", "support_code": "PW-6013" } Content rejected by security policy ( 400 ): JSON { "data": [], "status": false, "message": "Request rejected by security policy. Check your input does not contain HTML or script content." } Invalid or missing API key ( 403 ): JSON { "message": "Forbidden" } Organisation not associated with the key ( 400 ): JSON { "data": [], "status": false, "message": "Given organisation NOPE123 is not associated with provided token." } Rate limit exceeded ( 429 ): JSON { "data": [], "status": false, "message": "Rate limit exceeded. Please retry after the indicated time.", "retry_after": 60, "support_code": "RL-001" } Important notes Required fields are patient_id and note . practitioner_id and location_id are optional. note is free text up to 20,000 characters . The note is persisted as a clinical/treatment record; the 200 response returns its treatment_id (not a separate “note id”). If practitioner_id or location_id are omitted, the patient’s defaults are applied and returned in the response. The fhir query parameter is ignored on write endpoints. FHIR formatting is available on read endpoints only; use GET /api/v1/fhir/clinical-notes to read notes as FHIR Observation resources. patient_id (and practitioner_id / location_id ) must be integers; a non-numeric patient_id is rejected with TYP-INT . Submit note as plain text. Content that looks like HTML or script, and very long or unusual payloads, is rejected by the security policy with a 400 . Quick example Plain text curl -X POST "https://api-uat.dental4web.com.au/api/v1/clinical-notes" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"patient_id": 25407, "note": "Patient reports sensitivity on upper left."}' curl -X POST "https://api-uat.dental4web.com.au/api/v1/clinical-notes" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{"patient_id": 25407, "note": "Patient reports sensitivity on upper left."}' • [Reference Data Resources](https://developer-uat.dental4web.com.au/reference-data-resources.md): The Reference Data Resources API provides access to essential configuration and operational data in the Dental4Web Practice Management System. These resources are not patient treatment records, but rather supporting datasets that define the structure, people, and reference data used across other APIs. They are especially important for linking appointments, treatments, and financial transactions to the correct practitioner, location, or appointment book, integrating reference data like procedure codes and payment types, and managing user and recall reminder records. Group Endpoint Returns People GET /api/v1/practitioners Active and inactive practitioners. Optional location_id filter. People GET /api/v1/users All system users (clinical + administrative). Optional location_id . Places GET /api/v1/locations All practice locations (clinic addresses, contact info). Scheduling GET /api/v1/appointment-books All appointment books, with operating hours per book. Scheduling GET /api/v1/appointment-classes Appointment class options (e.g. Consultation, Filling). Scheduling GET /api/v1/slot-types Bookable slot types (vacant time + preset templates). Scheduling GET /api/v1/cancel-reasons Active cancel-reason IDs used by the cancel endpoint. Codes GET /api/v1/items All procedure/treatment items (e.g. ADA codes). Codes GET /api/v1/payment-types All configured payment types. Patients GET /api/v1/titles Patient title options for create/update. Recalls GET /api/v1/recalls Practice-wide recall records. Requires ≥1 filter parameter (HTTP 400 otherwise). • [GET Appointment Books](https://developer-uat.dental4web.com.au/reference-data-resources/get-appointment-books.md): Retrieves appointment book configurations for the organisation. An appointment book is a schedulable resource — typically a practitioner’s chair or room — with operating hours and an associated location. Use cases Map appointments to the correct practitioner or resource. Build scheduling interfaces that respect operating hours. Manage multiple schedules within a single location. Display available booking slots. Response fields Field Type Description appt_book_id integer Primary identifier. appt_book_name string Book / resource name. location_id integer Associated location. start_time time Operating start time. end_time time Operating end time. is_active boolean Active status. Important notes This endpoint supports only pagination and, where applicable, the location_id / include filters. There are no date or business filters. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointment-books?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointment-books?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Appointment Classes](https://developer-uat.dental4web.com.au/reference-data-resources/get-appointment-classes.md): Returns all configured appointment classes (for example Consultation, Filling, Scale and Clean). Use class_id to populate appointment-class pickers and to supply the appointment class to POST /api/v1/appointments. Response fields Field Type Description class_id integer Appointment-class identifier. class_name string Class name. is_active integer 1 if active, 0 if not. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/appointment-classes?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/appointment-classes?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Cancel Reasons](https://developer-uat.dental4web.com.au/reference-data-resources/get-cancel-reasons.md): Returns all active cancellation reasons. Use cancel_reason_id to supply the cancellation reason to POST /api/v1/appointments/{id}/cancel. Response fields Field Type Description cancel_reason_id integer Cancellation-reason identifier. reason_description string Reason text. sort_order integer Display sort order. is_system boolean True for built-in system reasons. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/cancel-reasons?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/cancel-reasons?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Locations](https://developer-uat.dental4web.com.au/reference-data-resources/get-locations.md): Retrieves the practice locations (clinics, branches) within the organisation. Locations let multi-site practices associate patient records, appointments, and billing with the correct branch. Use cases Build location selection dropdowns for booking interfaces. Display practice addresses on appointment confirmations. Filter reports and data by location. Sync the location directory to external systems. Response fields Field Type Description location_id integer Primary identifier. location_name string Location / branch name. address string Full street address. phone string Contact phone number. email string Contact email. is_active boolean Active status. updated_date datetime Record last-modified timestamp. Important notes This endpoint supports only pagination and, where applicable, the location_id / include filters. There are no date or business filters. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/locations?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/locations?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Payment Types](https://developer-uat.dental4web.com.au/reference-data-resources/get-payment-types.md): Returns all configured payment methods (Cash, EFTPOS, HealthFund variants, custom types, and so on). Use payment_type_id to interpret the payment_type_id field returned by GET /api/v1/receipts/{id}. Response fields Field Type Description payment_type_id integer Primary identifier. payment_type string Payment method name. reporting_name string Name used in reports. show_in_reports boolean Whether the type appears in reports. is_deleted boolean Soft-delete flag. Important notes This endpoint supports only pagination and, where applicable, the location_id / include filters. There are no date or business filters. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/payment-types?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/payment-types?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Practitioners](https://developer-uat.dental4web.com.au/reference-data-resources/get-practitioners.md): Retrieves the practitioners (dentists, hygienists, and other clinical staff) within the organisation. Practitioners link appointments, treatments, and billing to the correct healthcare provider. Use cases Build practitioner selection dropdowns for booking interfaces. Display practitioner information on appointment confirmations. Map appointment data to the correct practitioners for reporting. Sync the practitioner directory to external systems. Response fields Field Type Description practitioner_id integer Practitioner identifier. is_active boolean Active status. practitioner_code string Short code. full_name string Full display name. first_name string First name. surname string Surname. title string Title (Dr / Mr / Ms). role string Role (Dentist / Hygienist). location_id integer Primary location. provider_number string | null Provider number. qualifications string | null Qualifications. updated_date datetime Record last-modified timestamp. Important notes Optional filter: use location_id to filter by a specific location. All practitioners: omit location_id to retrieve every practitioner across the organisation. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/practitioners?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/practitioners?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Procedure Codes](https://developer-uat.dental4web.com.au/reference-data-resources/get-procedure-codes.md): Retrieves the procedure codes (item codes) used for treatments and billing. Each item includes its ADA code, description, category, and reporting flags, keeping treatment descriptions and billing consistent across integrations. Use cases Map item codes to procedure descriptions for display. Build treatment selection interfaces. Validate item codes during data import. Generate procedure category reports. Response fields Field Type Description item_code string Procedure code. ada_code string ADA (Australian Dental Association) code. description string Procedure description. category string Category / grouping. sub_category string Sub-category / grouping. non_reporting boolean Excluded from reporting. non_treatment boolean Not a treatment item. Important notes This endpoint supports only pagination and, where applicable, the location_id / include filters. There are no date or business filters. Large dataset: may return 1000+ records — use pagination. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/items?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/items?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Recalls](https://developer-uat.dental4web.com.au/reference-data-resources/get-recalls.md): Retrieves recall records across multiple patients, practitioners, and locations. Unlike the patient-specific /patients/{id}/recalls endpoint, this endpoint supports organisation-wide recall queries with comprehensive filtering. Use cases Generate practice-wide recall due lists for outreach campaigns. Build recall management dashboards showing upcoming reminders. Analyse recall compliance rates across locations or practitioners. Identify overdue recalls requiring urgent follow-up. Response fields Field Type Description recall_type_id integer Recall type identifier. patient_id integer Patient identifier. recall_date date Scheduled recall date. reminder_date date Reminder sent / due date. reminder_type string Reminder method (SMS, Email, Letter). reminder_no integer Reminder sequence number. practitioner_id integer Associated practitioner. location_id integer Associated location. created_date datetime Record creation timestamp. updated_date datetime Record last-modified timestamp. Important notes Required parameters: at least one filter parameter is required. Date filtering: see Filtering & Date Ranges . Default range: last 30 days plus the next 180 days (210 days total) when no from_date / to_date is supplied. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/recalls?from_date=2026-01-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/recalls?from_date=2026-01-01&to_date=2026-06-09&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Slot Types](https://developer-uat.dental4web.com.au/reference-data-resources/get-slot-types.md): Returns all bookable slot types — Vacant appointment time plus configured preset types (for example “New patient appointments” or “eAppointments: Emergency”). Use these values to populate appointment-type pickers and to inform the slot mode when calling GET /api/v1/appointment-slots. Response fields Field Type Description slot_type_id integer Slot-type identifier. slot_type string Slot-type name. is_preset boolean True if a preset slot type. is_online integer 1 if available for online booking, 0 if not. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/slot-types?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/slot-types?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Titles](https://developer-uat.dental4web.com.au/reference-data-resources/get-titles.md): Returns all available patient title options (Mr, Mrs, Dr, Miss, Master, Prof, Rev, and so on). Use title_name as the title value when calling POST /api/v1/patients and PATCH /api/v1/patients/{id}. Response fields Field Type Description title_id integer Title identifier. title_name string Title text, e.g. Mr, Ms, Dr. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/titles?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/titles?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Users](https://developer-uat.dental4web.com.au/reference-data-resources/get-users.md): Returns all system users — practitioners, hygienists, and administrative staff. Use user_id to interpret created_user_id and similar fields in write responses. Use cases Audit user activity and permissions. Track which user created or modified records. Build user management interfaces. Map created_user_id from other endpoints to user names. Response fields Field Type Description user_id integer Primary identifier. user_code string Short user code. first_name string Given name. surname string Family name. location_id integer | null Associated location (null for all locations). is_active boolean Active status. user_type string User type. Important notes This endpoint supports only pagination and, where applicable, the location_id / include filters. There are no date or business filters. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/users?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/users?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [MediaWeb PACS Resources](https://developer-uat.dental4web.com.au/mediaweb-pacs-resources.md): MediaWeb PACS endpoints provide access to Dental4Web’s digital imaging platform. You use them to search, retrieve, and bulk-download dental images in DICOM format or converted to JPEG. Key features DICOM native storage: all images are stored in DICOM format for diagnostic quality. Optional JPEG conversion: request JPEG format for viewing and display use cases (quality level 98). Pre-signed URLs: secure, time-limited download links that need no additional authentication. Bulk retrieval: download all images for a patient in a single request. Image format recommendations Use DICOM format for diagnostic purposes and archival. Use JPEG for display in non-diagnostic applications and patient portals. If JPEG conversion fails to meet quality thresholds, the original DICOM is returned. Pre-signed URL expiry Operation Expiry time Single image ( GET /mwp/images/{image_id} ) 5 minutes Bulk patient images ( GET /mwp/images/patient/{patient_id} ) 10 minutes URL lifetimes Pre-signed download URLs use a tiered expiry policy that depends on the operation that issued them. Each URL stops working once its lifetime elapses; request a fresh one by calling the originating endpoint again. Single image ( GET /mwp/images/{image_id} ): the pre-signed URL expires after 5 minutes (300 seconds). Bulk patient images ( GET /mwp/images/patient/{patient_id} ): each pre-signed URL expires after 10 minutes (600 seconds). The exact lifetime for each URL is also returned in the response. Single-image responses include an expires_in field reporting the value in seconds. For the canonical list of error codes, see Validation, Limits & Character Handling . • [GET Image Search](https://developer-uat.dental4web.com.au/mediaweb-pacs-resources/get-image-search.md): Searches for dental images stored in MediaWeb PACS and returns image metadata without the image data itself. You use the returned image_id to retrieve an individual image, or fetch every image for a patient with the bulk endpoint. Use cases Search for images by patient, study, or modality. Build an image gallery interface that shows the available studies. Identify images to download or display. Response fields Field Type Description image_id string DICOM SOP Instance UID. patient_id integer Patient identifier. patient_card string Patient card number. study_id integer Study identifier. study_date date Date of the imaging study. modality string Imaging modality (IO, PX, CT, CR, etc.). series_id integer Series identifier. Important notes MWP-enabled only: available only for organisations with MediaWeb PACS integration. Metadata only: returns image references, not the image data itself. Date defaults: when both start_date and end_date are omitted, the search defaults to the past 365 days (no future dates). Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/mwp/images?patient_id=10016&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/mwp/images?patient_id=10016&per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Single Image](https://developer-uat.dental4web.com.au/mediaweb-pacs-resources/get-single-image.md): Retrieves a single image record with a short-lived, pre-signed download URL. Parameters Field Type Description image_id string Path parameter. DICOM SOP Instance UID. Use the image_id value from GET /api/v1/mwp/images (search) or GET /api/v1/mwp/images/patient/{patient_id} (bulk). jpeg boolean Query parameter. Pass true to receive a JPEG conversion (quality ~98). If conversion fails, the response falls back to DICOM, and the format field reports which format was returned. Use cases Download specific images for diagnostic viewing. Display images in patient portals or clinical applications. Archive images to external storage systems. Response fields Field Type Description image_id string DICOM SOP Instance UID. patient_id integer Patient identifier. patient_card string Patient card number. study_id integer Study identifier. study_date date Date of the imaging study. series_id integer Series identifier. format string Format actually returned: dicom or jpeg . url string Pre-signed download URL. expires_in integer URL validity in seconds (300 for a single image). Important notes URL expiry: the pre-signed URL expires after 5 minutes (300 seconds). Format parameter: pass jpeg=true to receive a JPEG conversion. When omitted, the image is returned in DICOM format. The response format field reports the format actually returned. JPEG quality: JPEG conversion uses quality level 98. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/mwp/images/1.2.36.18057620390.9700831451752.3?jpeg=true" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/mwp/images/1.2.36.18057620390.9700831451752.3?jpeg=true" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [GET Patient Images (Bulk)](https://developer-uat.dental4web.com.au/mediaweb-pacs-resources/get-patient-images-bulk.md): Retrieves all images for a patient, each with a pre-signed download URL. This is more efficient than fetching images individually when you need several at once. Use cases Download all patient images for backup or migration. Display a complete patient imaging history. Build patient image galleries. Response fields Field Type Description image_id string DICOM SOP Instance UID. patient_id integer Patient identifier. patient_card string Patient card number. study_id integer Study identifier. study_date date Date of the imaging study. modality string Imaging modality. series_id integer Series identifier. format string Format actually returned: dicom or jpeg . url string Pre-signed download URL (valid for 600 seconds). Important notes URL expiry: the pre-signed URLs expire after 10 minutes (600 seconds). Large responses: the endpoint may return many images — use pagination. Format parameter: pass jpeg=true to receive JPEG conversions. When omitted, images are returned in DICOM format. The response format field reports the format actually returned. Quick example Plain text curl "https://api-uat.dental4web.com.au/api/v1/mwp/images/patient/10016?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" curl "https://api-uat.dental4web.com.au/api/v1/mwp/images/patient/10016?per_page=10" \ -H "x-api-key: $API_KEY" \ -H "org-id: $ORG_ID" \ -H "Accept: application/json" • [FHIR Resources](https://developer-uat.dental4web.com.au/fhir-resources.md): This section documents all FHIR R4-compliant endpoints. These endpoints return data in standard FHIR Bundle format for healthcare interoperability. **For detailed FHIR documentation, see the** [**FHIR Capabilities page**](/fhir-capabilities)**.** All FHIR endpoints: * Return data wrapped in a FHIR Bundle with `resourceType: "Bundle"` * Include inline references to related resources (Patient, Practitioner, Location) * Follow FHIR R4 specification naming conventions * Accept the same query parameters as their standard counterparts • [GET Treatments - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-treatments-fhir.md): Retrieves completed treatments as FHIR R4 Procedure resources. Each Procedure carries the item code, performed date, treating practitioner, location, fee, and quantity. The bundle also includes the referenced Practitioner and Location resources. Parameters and constraints match Get treatments . Resource mapping How each source treatment field maps onto the FHIR Procedure resource. Source field FHIR element Notes treatment_id Procedure.id Resource identifier. item_code Procedure.code.coding[].code System https://api.dental4web.com.au/fhir/codes/procedure-codes ; the procedure description is carried in display . patient_id Procedure.subject.reference In the form Patient/{patient_id} . treatment_date Procedure.performedDateTime Date precision ( YYYY-MM-DD ). practitioner_id Procedure.performer[].actor.reference In the form Practitioner/{practitioner_id} ; the practitioner name is carried in display . location_id Procedure.location.reference In the form Location/{location_id} ; the location name is carried in display . tooth / surface Procedure.bodySite null when neither is recorded. item_fee Procedure.extension[procedure-fee].valueMoney Amount in AUD. URL https://api.dental4web.com.au/fhir/extensions/procedure-fee . item_qty Procedure.extension[procedure-quantity].valueInteger URL https://api.dental4web.com.au/fhir/extensions/procedure-quantity . (fixed) Procedure.status Always completed — only performed treatments are returned. updated_date Procedure.meta.lastUpdated Record last-modified timestamp. The referenced Practitioner and Location resources are included as additional bundle entries. If a requested relation cannot be expanded (for example patients ), the bundle appends an OperationOutcome entry with a warning issue rather than failing the request. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "b41fa10c-1398-423c-9d75-576335871a29", "type": "searchset", "total": 21, "entry": [ { "resource": { "resourceType": "Procedure", "id": "104301", "status": "completed", "code": { "coding": [ { "system": "https://api.dental4web.com.au/fhir/codes/procedure-codes", "code": "114", "display": "Removal Of Calculus - first appointment" } ] }, "subject": { "reference": "Patient/72" }, "performedDateTime": "2018-02-26", "performer": [ { "actor": { "reference": "Practitioner/69", "display": "Dr Wing Gillespie" } } ], "location": { "reference": "Location/1", "display": "Crown Jewels Dental" }, "bodySite": null, "extension": [ { "url": "https://api.dental4web.com.au/fhir/extensions/procedure-fee", "valueMoney": { "value": 0.0, "currency": "AUD" } }, { "url": "https://api.dental4web.com.au/fhir/extensions/procedure-quantity", "valueInteger": 1 } ], "meta": { "lastUpdated": "2026-06-11T17:09:33.955000Z" } } }, { "resource": { "resourceType": "Practitioner", "id": "69", "identifier": [ { "system": "https://api.dental4web.com.au/fhir/codes/practitioner-codes", "value": "WGI" } ], "name": [ { "text": "Dr Wing Gillespie", "family": "Gillespie", "given": ["Wing"], "prefix": ["Dr"] } ], "active": false, "qualification": [], "meta": { "lastUpdated": "2026-06-04T17:07:33.706005Z" } } }, { "resource": { "resourceType": "Location", "id": "1", "name": "Crown Jewels Dental", "status": "active", "address": { "text": "Level 5 The Plaque Chambers 100 Root Canal Avenue CORELLA QLD 4570", "country": "Australia" }, "telecom": [ { "system": "phone", "value": "05 1234 5678" } ], "meta": { "lastUpdated": "2026-06-04T17:07:33.745000Z" } } }, { "search": { "mode": "outcome" }, "resource": { "resourceType": "OperationOutcome", "issue": [ { "severity": "warning", "code": "incomplete", "diagnostics": "Relation 'patients' was not expanded: not_supported." } ] } } ], "meta": { "lastUpdated": "2026-06-09T09:17:56.128213Z" } }, "pagination": { "page": 1, "per_page": 10, "total_rows": 21, "total_pages": 3 } } Important notes Required filters: at least one filter parameter is required. Date filtering: supports the mode parameter — see Filtering & Date Ranges . Wildcard values: use practitioner_id=-1 for all practitioners and location_id=-1 for all locations. Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Patients - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-patients-fhir.md): Retrieves patient records as FHIR R4 Patient resources. The response is a searchset Bundle containing Patient resources, with related Practitioner and Location resources included. Parameters and constraints match GET /patients , plus an optional date_of_birth filter ( YYYY-MM-DD ) that counts as a narrowing filter. Resource mapping Source field FHIR element Notes Patient ID Patient.id and Patient.identifier Identifier system is https://api.dental4web.com.au/fhir/identifier/patient-card . Name Patient.name Mapped to family , given , and prefix with use: official . Phone numbers Patient.telecom Home and mobile numbers as phone entries. Gender Patient.gender FHIR administrative gender. Date of birth Patient.birthDate Format YYYY-MM-DD . Address Patient.address Mapped to line , city , state , postalCode , and country . Treating practitioner Patient.generalPractitioner Reference to a Practitioner resource included in the bundle. Last-modified timestamp Patient.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 11, "entry": [ { "resource": { "resourceType": "Patient", "id": "6570", "identifier": [ { "system": "https://api.dental4web.com.au/fhir/identifier/patient-card", "value": "6570" } ], "active": true, "name": [ { "use": "official", "family": "Burris", "given": ["John", "Christian"], "prefix": ["Mr"] } ], "telecom": [ {"system": "phone", "value": "(01) 8015 6714", "use": "home"}, {"system": "phone", "value": "0412 345 678", "use": "mobile"} ], "gender": "male", "birthDate": "1922-03-11", "address": [ { "line": ["Ap #231-6175 Luctus. Rd.", "ADDRESS"], "city": "FLORENTINE", "state": "TAS", "postalCode": "7140", "country": "Australia" } ], "generalPractitioner": [ {"reference": "Practitioner/22", "display": "Dr Quintessa Ayala"} ], "meta": {"lastUpdated": "2025-11-19T23:34:55.407178Z"} } }, { "resource": { "resourceType": "Location", "id": "1", "name": "Smile Til You Drop Melbourne", "status": "active" } }, { "resource": { "resourceType": "Practitioner", "id": "22", "identifier": [...], "name": [...], "active": true } } ], "meta": {"lastUpdated": "2026-02-11T11:00:09.276112Z"} }, "pagination": {...} } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Patient By ID - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-patient-by-id-fhir.md): Retrieves a single patient as a FHIR R4 Patient resource. The response is a searchset Bundle containing one Patient resource. Parameters and constraints match GET /patients/{patient_id} . Resource mapping Source field FHIR element Notes Patient ID Patient.id and Patient.identifier Identifier system is https://api.dental4web.com.au/fhir/identifier/patient-card . Name Patient.name Mapped to family , given , and prefix with use: official . Phone numbers Patient.telecom Home and mobile numbers as phone entries. Gender Patient.gender FHIR administrative gender. Date of birth Patient.birthDate Format YYYY-MM-DD . Address Patient.address Mapped to line , city , state , postalCode , and country . Treating practitioner Patient.generalPractitioner Reference to a Practitioner resource included in the bundle. Last-modified timestamp Patient.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 11, "entry": [ { "resource": { "resourceType": "Patient", "id": "6570", "identifier": [ { "system": "https://api.dental4web.com.au/fhir/identifier/patient-card", "value": "6570" } ], "active": true, "name": [ { "use": "official", "family": "Burris", "given": ["John", "Christian"], "prefix": ["Mr"] } ], "telecom": [ {"system": "phone", "value": "(01) 8015 6714", "use": "home"}, {"system": "phone", "value": "0412 345 678", "use": "mobile"} ], "gender": "male", "birthDate": "1922-03-11", "address": [ { "line": ["Ap #231-6175 Luctus. Rd.", "ADDRESS"], "city": "FLORENTINE", "state": "TAS", "postalCode": "7140", "country": "Australia" } ], "generalPractitioner": [ {"reference": "Practitioner/22", "display": "Dr Quintessa Ayala"} ], "meta": {"lastUpdated": "2025-11-19T23:34:55.407178Z"} } }, { "resource": { "resourceType": "Location", "id": "1", "name": "Smile Til You Drop Melbourne", "status": "active" } }, { "resource": { "resourceType": "Practitioner", "id": "22", "identifier": [...], "name": [...], "active": true } } ], "meta": {"lastUpdated": "2026-02-11T11:00:09.276112Z"} }, "pagination": {...} } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Appointments - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-appointments-fhir.md): Retrieves appointments as FHIR R4 Appointment resources. The response is a searchset Bundle containing Appointment resources, with Patient , Practitioner , and Location participants included. Parameters and constraints match GET /appointments . Resource mapping Source field FHIR element Notes Appointment ID Appointment.id Source appointment identifier. Status Appointment.status FHIR appointment status, for example booked . Start / end time Appointment.start and Appointment.end ISO 8601 instants in UTC. Patient Appointment.participant[].actor ( Patient ) Reference to a Patient resource included in the bundle. Practitioner Appointment.participant[].actor ( Practitioner ) Reference to a Practitioner resource included in the bundle. Location Appointment.participant[].actor ( Location ) Reference to a Location resource included in the bundle. Last-modified timestamp Appointment.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 7724, "entry": [ { "resource": { "resourceType": "Appointment", "id": "76878", "status": "booked", "start": "2025-07-17T09:00:00Z", "end": "2025-07-17T10:00:00Z", "participant": [ { "actor": {"reference": "Patient/2", "display": "Emerie Snyder"}, "status": "accepted" }, { "actor": {"reference": "Practitioner/23", "display": "Dr Blake Castro"}, "status": "accepted" }, { "actor": {"reference": "Location/1", "display": "Smile Til You Drop Melbourne"}, "status": "accepted" } ], "meta": {"lastUpdated": "2026-01-08T11:02:01.319007Z"} } }, {"resource": {"resourceType": "Location", ...}}, {"resource": {"resourceType": "Practitioner", ...}}, {"resource": {"resourceType": "Patient", ...}} ] }, "pagination": {...} } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Appointment By ID - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-appointment-by-id-fhir.md): Retrieves a single appointment as a FHIR R4 Appointment resource. The response is a searchset Bundle containing one Appointment resource, with Patient , Practitioner , and Location participants included. Parameters and constraints match GET /appointments/{appointment_id} . Resource mapping Source field FHIR element Notes Appointment ID Appointment.id Source appointment identifier. Status Appointment.status FHIR appointment status, for example booked . Start / end time Appointment.start and Appointment.end ISO 8601 instants in UTC. Patient Appointment.participant[].actor ( Patient ) Reference to a Patient resource included in the bundle. Practitioner Appointment.participant[].actor ( Practitioner ) Reference to a Practitioner resource included in the bundle. Location Appointment.participant[].actor ( Location ) Reference to a Location resource included in the bundle. Last-modified timestamp Appointment.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 7724, "entry": [ { "resource": { "resourceType": "Appointment", "id": "76878", "status": "booked", "start": "2025-07-17T09:00:00Z", "end": "2025-07-17T10:00:00Z", "participant": [ { "actor": {"reference": "Patient/2", "display": "Emerie Snyder"}, "status": "accepted" }, { "actor": {"reference": "Practitioner/23", "display": "Dr Blake Castro"}, "status": "accepted" }, { "actor": {"reference": "Location/1", "display": "Smile Til You Drop Melbourne"}, "status": "accepted" } ], "meta": {"lastUpdated": "2026-01-08T11:02:01.319007Z"} } }, {"resource": {"resourceType": "Location", ...}}, {"resource": {"resourceType": "Practitioner", ...}}, {"resource": {"resourceType": "Patient", ...}} ] }, "pagination": {...} } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Clinical Notes - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-clinical-notes-fhir.md): Retrieves clinical notes as FHIR R4 Observation resources. Each note is mapped to an Observation with LOINC coding. Parameters and constraints match GET /clinical-notes . Resource mapping Source field FHIR element Notes Note ID Observation.id Source note identifier. Status Observation.status Always final . Category Observation.category Coded procedure from the HL7 observation-category code system. Item code Observation.code Dual-coded: primary LOINC code 34109-9 ("Note"), and the original dental item code under https://api.dental4web.com.au/codes/dental-item-codes . Patient Observation.subject Reference to the Patient resource. Practitioner Observation.performer Reference to the authoring Practitioner resource. Note date Observation.effectiveDateTime ISO 8601 instant in UTC. Note content Observation.valueString Free-text note content. Tooth / surface Observation.bodySite Carried as text, for example 48, OB . Last-modified timestamp Observation.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 70, "entry": [ { "resource": { "resourceType": "Observation", "id": "6051607", "status": "final", "category": [ { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/observation-category", "code": "procedure", "display": "Procedure" } ] } ], "code": { "coding": [ { "system": "http://loinc.org", "code": "34109-9", "display": "Note" }, { "system": "https://api.dental4web.com.au/codes/dental-item-codes", "code": "114", "display": "Dental item code" } ] }, "subject": {"reference": "Patient/2", "display": "Emerie Snyder"}, "performer": [{"reference": "Practitioner/29", "display": "Dr Frank Papadopoulos"}], "effectiveDateTime": "2019-12-09T00:00:00Z", "valueString": "Clinical note completed", "bodySite": {"text": "48, OB"}, "meta": {"lastUpdated": "2026-02-12T11:38:14.369000Z"} } } ] }, "pagination": {...} } Note: The primary coding is the real LOINC clinical-note code. The secondary coding carries the original dental item code from the source system. Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Practitioners - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-practitioners-fhir.md): Retrieves practitioners as FHIR R4 Practitioner resources. The response is a searchset Bundle containing Practitioner resources. Parameters and constraints match GET /practitioners . Resource mapping Source field FHIR element Notes Practitioner ID Practitioner.id Source practitioner identifier. Practitioner code Practitioner.identifier Code under https://api.dental4web.com.au/fhir/identifier/practitioner-code ; Medicare provider number under http://ns.electronichealth.net.au/id/medicare-provider-number . Name Practitioner.name Mapped to text , family , given , and prefix . Active flag Practitioner.active Whether the practitioner is currently active. Qualifications Practitioner.qualification Present as an array; may be empty. Last-modified timestamp Practitioner.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "uuid", "type": "searchset", "total": 168, "entry": [ { "resource": { "resourceType": "Practitioner", "id": "1", "identifier": [ {"system": "https://api.dental4web.com.au/fhir/identifier/practitioner-code", "value": "MHA"}, {"system": "http://ns.electronichealth.net.au/id/medicare-provider-number", "value": "1"} ], "name": [ { "text": "Dr Marshall Holman", "family": "Holman", "given": ["Marshall"], "prefix": ["Dr"] } ], "active": false, "qualification": [], "meta": {"lastUpdated": "2026-01-13T12:50:55.135001Z"} } }, {"resource": {"resourceType": "Location", ...}} ] }, "pagination": {...} } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope. • [GET Locations - FHIR](https://developer-uat.dental4web.com.au/fhir-resources/get-locations-fhir.md): Retrieves practice locations as FHIR R4 Location resources. The response is a searchset Bundle containing Location resources. Parameters and constraints match GET /locations . Resource mapping Source field FHIR element Notes Location ID Location.id Source location identifier. Name Location.name Practice location name. Status Location.status FHIR location status, for example active . Address Location.address Mapped to text and country . Phone and email Location.telecom Phone and email entries. Last-modified timestamp Location.meta.lastUpdated Record's last-modified timestamp. Sample bundle JSON { "status": true, "message": "Success", "data": { "resourceType": "Bundle", "id": "8777948c-7b12-4c54-ab85-17793b94bbb8", "type": "searchset", "total": 3, "entry": [ { "resource": { "resourceType": "Location", "id": "1", "name": "Ridiculous Smiles Cbd", "status": "active", "meta": { "lastUpdated": "2025-04-11T12:24:20.074000Z" }, "address": { "text": "Level 5 The Plaque Chambers 100 Root Canal Avenue SYDNEY NSW 2000", "country": "Australia" }, "telecom": [ { "system": "phone", "value": "05 1234 5678" }, { "system": "email", "value": "tooth@mccavityandmcplaque.com.au" } ] } }, { "resource": { "resourceType": "Location", "id": "4", "name": "Ridiculous Smiles Randwick", "status": "active", "meta": { "lastUpdated": "2025-04-11T12:25:18.430000Z" }, "address": { "text": "Level 5 The Plaque Chambers 100 Root Canal Avenue RANDWICK NSW 2031", "country": "Australia" }, "telecom": [ { "system": "phone", "value": "05 1234 5678" }, { "system": "email", "value": "tooth@mccavityandmcplaque.com.au" } ] } }, { "resource": { "resourceType": "Location", "id": "5", "name": "Ridiculous Smiles Surry Hills", "status": "active", "meta": { "lastUpdated": "2025-04-11T12:26:37.921000Z" }, "address": { "text": "Level 5 The Plaque Chambers 100 Root Canal Avenue SURRY HILLS NSW 2010", "country": "Australia" }, "telecom": [ { "system": "phone", "value": "05 1234 5678" }, { "system": "email", "value": "tooth@mccavityandmcplaque.com.au" } ] } } ], "meta": { "lastUpdated": "2026-02-18T03:04:47.922629Z" } }, "pagination": { "page": 1, "per_page": 10, "total_rows": 3, "total_pages": 1 } } Note: FHIR endpoints return Content-Type: application/json , not the FHIR-standard application/fhir+json . Bundles are wrapped inside the standard Dental API response envelope.