Skip to main content
The API is additive within a version: new resources, attributes, event types and optional inputs may appear at any time; nothing is renamed, removed or made required. Anything that would break a client goes to a new version with a published deprecation window. Each entry below is dated and marks additive changes with Added and behaviour clarifications with Changed.
Postman, API Logs report, base path
Added
  • Postman collection and environments generated from the OpenAPI document — every endpoint, bearer auth from {{api_key}}, ids chained between requests, safe-by-default guards for updates and deletes.
  • Developers → API Logs is now a report: window presets or a custom range in the clinic timezone; filters by credential (integration or individual key, connector user), method, resource, outcome, status code, error code, latency, path and request id; usage summary (requests, success rate, 4xx / 429 / 5xx, p50 / p95 latency); requests-over-time chart; breakdowns by credential and endpoint; top error codes; per-request detail; CSV export. Each log row now also records the calling key, resource and record id, the API’s error code and message, response size, idempotent-replay flag and user agent — still no bodies or parameter values.
  • Records created through the API show the integration name (for example “API: Booking widget”) as their creator in PracticeHub’s ledger and patient file, instead of a generic system label.
Changed
  • Base path is /v3/api (https://{clinic}.{region}.practicehub.io/v3/api/...); the MCP server is at /v3/api/mcp. Pre-release material that showed /v3/api/v1 is superseded.
  • API keys are issued as the bare secret (phk_…); the Developers → API Keys page shows each key as phk_•••••••••••• plus its last four characters. Keys issued earlier in the {id}|phk_… form keep working.
Availability
Added
  • Typed metadata: values may be strings, numbers or booleans and read back typed; numeric operands to gt/gte/lt/lte/between compare numerically. New contains operator (case-insensitive substring, no escaping) on attributes and metadata alike.
  • patients reads now embed numbers[] and address in the same shape the write accepts (loaded per page, not per row) — a patient round-trips in one call.
  • Cursor pagination on every list: ?cursor= to start, follow links.next; constant cost per page, stable under concurrent writes; meta: {per_page, has_more}. page/page_size unchanged. See Querying.
  • GET /availability — free, bookable slots for a location and appointment type over up to 31 days, in the patient (visibility=online) or front-desk (visibility=all) view; meta.unavailable_dates for days with nothing free. See Booking.
  • MCP check_availability tool on the same engine.
  • allow_clash on POST /appointments for deliberate overbooking.
  • DELETE /invoices/{id}?void_payments=true — also void manual payments the void leaves with nothing to pay (documented; MCP write_entity takes it in attributes).
  • PATCH /invoices/{id} reconciles line_items by id — lines keep their ids across edits; without line_items it changes the header only.
  • invoices now read back number, state, type, invoice_date, note, subtotal, balance, patient_balance, third_party_balance; payments read payment_date and status; appointment_types read online_booking and active (all filterable; sortable where it makes sense).
  • Resources & roadmap and Security & data handling pages.
Added
  • MCP check_availability accepts rescheduled_appointment_id, matching GET /availability, so an assistant can find a new time for an existing appointment without its current slot counting as busy.
Changed
  • Connected AI apps (OAuth connectors) now act with the team member’s PracticeHub role: reads, writes and deletes need the same permissions as in the app, and own-patients / own-calendar roles are ringfenced. Refusals are 403 permission_denied. API keys are unchanged. See Connectors.
Changed
  • PATCH /patients/{id} reconciles numbers by id (as line_items already did): send each stored number’s id to update it in place, omit an id to add, leave a stored number out to remove it. Ids come back on every patient read. A list with no ids at all keeps the previous replace-everything behaviour.
Fixed
  • patients numbers[].type is now required (mobile / home / work / other); a number without one was silently stored as mobile. Every read already returns the type.
  • PATCH /invoices/{id} with note: null clears the note (it was previously ignored); an absent note still leaves it alone.
  • PATCH /files/{id} may change type only between generic, custom_content and diagnostic_imagepatient_profile (image-only) and patient_log (linked to a log entry) are fixed at upload.
  • Writes to reference resources (appointment_types, locations, practitioners, referral_sources, …) are validated by column type — booleans, integers, amounts, dates, enums, string lengths — so a wrong-typed value is a 422 naming the attribute rather than a server error. The OpenAPI schemas carry the types.
  • patients.sex now reads as male / female / other (or null when unknown) and is filtered by those words — the same values it accepts on write. It previously read back the stored code.
  • GET /availability answers 422 availability_not_configured (with the engine’s reason) for a location or appointment type that has no calendar/type settings, instead of an error.
  • line_items is writable: POST (add a line to an invoice), PATCH, DELETE — one line at a time, re-totalling the invoice; PUT /invoices/{id} still replaces the whole set. patient_payment_methods stays read-only.
Changed
  • practitioners, locations, practitioner_availabilities, numbers and addresses are read-only for now (405 unsupported_operation on write). The app creates practitioners (user + roles + licence), locations (address + calendar settings) and availability (series, broadcasts) through flows a plain column write would skip; phone numbers and addresses are normalised through patients. Writes return when they are routed through those services. Reads are unchanged.
  • POST /appointments now refuses a time that is not free in the practitioner’s diary with 409 slot_unavailable (previously the appointment was written regardless, as a staff click is). Group bookings (group_master_id) are unaffected — they already answered 409 group_full.
  • An invoice can no longer be created, or edited down to, zero line items (422 on create, 409 billing_rule on the last DELETE /line_items/{id}).
Initial release
Added
  • REST API at /v3/api — 25 resources with uniform list / get / create / update / delete, the legacy filter syntax, sorting and pagination.
  • GET /schema and GET /schema/{resource} — attributes, filterable/sortable flags, write operations and per-operation input rules.
  • GET /me — the credential, its scopes, and the account (name, timezone, currency).
  • API keys (phk_…), read & write or read only, optional expiry, rotation with a 24 h grace period.
  • Idempotency-Key on POST — 24 h replay with Idempotent-Replayed: true.
  • metadata on every record — your own key/values, filterable with metadata[key]=op:value.
  • Machine-readable error code on every error alongside message (and errors for input problems).
  • MCP server at /v3/api/mcp — generic list_entity_types / query_entities / get_entity / write_entity tools plus curated patient and appointment tools; API keys or OAuth 2.1 connectors (per-role opt-in, in-app revoke).
  • Webhooks — coming soon, not yet switched on for any account; the contract is documented: {resource}.created/updated/deleted for every observed resource plus appointments.rescheduled/cancelled/missed/arrived/reinstated; thin signed payloads; self-serve portal under Developers → Webhooks.
  • X-Request-Id on every response, mirrored on Developers → API Logs (retained 90 days).
Changed (relative to the legacy /api)
  • Filters on unknown attributes are 422 (legacy did not validate them); a value without an operator prefix is eq.
  • Lists always have a stable order (record id is the final tiebreaker).
  • Writes to patients, appointments, payments, invoices, allocations and files run through the same services as the PracticeHub app; deletes of payments/invoices void them.
  • See Migrating from the legacy API for the full envelope and behaviour comparison.