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
codeand 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.
- 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/v1is superseded. - API keys are issued as the bare secret (
phk_…); the Developers → API Keys page shows each key asphk_••••••••••••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/betweencompare numerically. Newcontainsoperator (case-insensitive substring, no escaping) on attributes and metadata alike. patientsreads now embednumbers[]andaddressin 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, followlinks.next; constant cost per page, stable under concurrent writes;meta: {per_page, has_more}.page/page_sizeunchanged. 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_datesfor days with nothing free. See Booking.- MCP
check_availabilitytool on the same engine. allow_clashonPOST /appointmentsfor deliberate overbooking.DELETE /invoices/{id}?void_payments=true— also void manual payments the void leaves with nothing to pay (documented; MCPwrite_entitytakes it inattributes).PATCH /invoices/{id}reconcilesline_itemsbyid— lines keep their ids across edits; withoutline_itemsit changes the header only.invoicesnow read backnumber,state,type,invoice_date,note,subtotal,balance,patient_balance,third_party_balance;paymentsreadpayment_dateandstatus;appointment_typesreadonline_bookingandactive(all filterable; sortable where it makes sense).- Resources & roadmap and Security & data handling pages.
- MCP
check_availabilityacceptsrescheduled_appointment_id, matchingGET /availability, so an assistant can find a new time for an existing appointment without its current slot counting as busy.
- 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.
PATCH /patients/{id}reconcilesnumbersbyid(asline_itemsalready did): send each stored number’sidto 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.
patientsnumbers[].typeis now required (mobile/home/work/other); a number without one was silently stored asmobile. Every read already returns the type.PATCH /invoices/{id}withnote: nullclears the note (it was previously ignored); an absentnotestill leaves it alone.PATCH /files/{id}may changetypeonly betweengeneric,custom_contentanddiagnostic_image—patient_profile(image-only) andpatient_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 a422naming the attribute rather than a server error. The OpenAPI schemas carry the types. patients.sexnow reads asmale/female/other(ornullwhen unknown) and is filtered by those words — the same values it accepts on write. It previously read back the stored code.GET /availabilityanswers422 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_itemsis 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_methodsstays read-only.
practitioners,locations,practitioner_availabilities,numbersandaddressesare read-only for now (405 unsupported_operationon 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 throughpatients. Writes return when they are routed through those services. Reads are unchanged.POST /appointmentsnow refuses a time that is not free in the practitioner’s diary with409 slot_unavailable(previously the appointment was written regardless, as a staff click is). Group bookings (group_master_id) are unaffected — they already answered409 group_full.- An invoice can no longer be created, or edited down to, zero line items (
422on create,409 billing_ruleon the lastDELETE /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 /schemaandGET /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-KeyonPOST— 24 h replay withIdempotent-Replayed: true.metadataon every record — your own key/values, filterable withmetadata[key]=op:value.- Machine-readable error
codeon every error alongsidemessage(anderrorsfor input problems). - MCP server at
/v3/api/mcp— genericlist_entity_types/query_entities/get_entity/write_entitytools 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/deletedfor every observed resource plusappointments.rescheduled/cancelled/missed/arrived/reinstated; thin signed payloads; self-serve portal under Developers → Webhooks. X-Request-Idon every response, mirrored on Developers → API Logs (retained 90 days).
/api)- Filters on unknown attributes are
422(legacy did not validate them); a value without an operator prefix iseq. - 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.