Getting connected
Where do I get an API key, and why doesn't my old one work?
Where do I get an API key, and why doesn't my old one work?
x-practicehub-key keys are for the legacy /api only; new API keys are Bearer tokens that look like phk_…. See Authentication.What is my base URL? The docs say {clinic}.
What is my base URL? The docs say {clinic}.
https://<your-clinic>.<region>.practicehub.io/v3/api — exactly the host the practice signs in on; the region host is the one in the practice’s login URL — neptune (London), mercury (Frankfurt), vulcan (Ohio), jupiter (Singapore) or apollo (Sydney). The docs use {clinic}.{region} / your-clinic.your-region because there is no single URL — which is also why the “Try it” button is off on the reference pages: copy the request into your terminal with your host instead.Which account/timezone/currency am I actually talking to?
Which account/timezone/currency am I actually talking to?
GET /me — returns the integration, its scopes and the account (id, name, timezone, currency). Make it your first call.Is there a sandbox?
Is there a sandbox?
Errors
401 unauthenticated
401 unauthenticated
Authorization: Bearer header, a typo in the key, an expired or revoked key — or the right key on the wrong clinic host (keys are per account). Check the host first.403 key_read_only
403 key_read_only
403 permission_denied / connector_not_permitted
403 permission_denied / connector_not_permitted
404 on a record I know exists
404 on a record I know exists
404 means you cannot have it.405 unsupported_operation
405 unsupported_operation
practitioners, locations, numbers, addresses, patient_payment_methods, …), some can’t be deleted (appointment_types, referral_sources, …). GET /schema/{resource} lists what is allowed. See Resources.422 with errors — but I sent exactly what the docs show
422 with errors — but I sent exactly what the docs show
errors: it names the attribute. Common ones: start not YYYY-MM-DD HH:MM:SS; an unknown attribute or filter name (they are refused, not ignored); a numbers.*.number that isn’t a real phone number for the iso2 given, or a number without a type; amount with more than 2 decimals; a metadata key over 40 chars.429 rate_limited
429 rate_limited
Retry-After seconds. If you hit it in normal operation you are polling something — use filters, page_size=100, cursors and webhooks. See Best practices.Where do I see what my integration sent?
Where do I see what my integration sent?
X-Request-Id (no bodies). Quote the X-Request-Id when you contact support.Data & behaviour
Why are appointment times different from what I expect by an hour or more?
Why are appointment times different from what I expect by an hour or more?
start/end, invoice_date, payment_date and availability slots are clinic-local with no offset; created/updated are UTC. Don’t convert the former; use /me for the timezone. See Best practices → Time.Why are amounts strings?
Why are amounts strings?
"45.00" in, "45.00" out — parse into a decimal type.Why did my POST /appointments succeed but the appointment isn't there — or is there twice?
Why did my POST /appointments succeed but the appointment isn't there — or is there twice?
Idempotency-Key on creates: a retry with the same key returns the original response instead of booking again. See Writing.I updated a patient's phone number and their other numbers disappeared.
I updated a patient's phone number and their other numbers disappeared.
numbers is reconciled by id: send the stored numbers with their id (you get them on every read) to keep/update them, add new ones without an id, and leave out the ones to remove. A list with no ids at all is treated as “replace everything”. Same model as line_items on invoices.Why don't voided payments show up in my export?
Why don't voided payments show up in my export?
payments.deleted / payment_allocations.deleted webhooks. Voided invoices stay visible with state: "void". See the accounting example.Can I read clinical notes / send SMS / charge a card?
Can I read clinical notes / send SMS / charge a card?
card_payment_not_allowed). See Resources for what is and isn’t in v1.Availability shows nothing / 422 not_online_bookable / availability_not_configured
Availability shows nothing / 422 not_online_bookable / availability_not_configured
visibility=online (the default) only offers what the practice has enabled for online booking — location, appointment type and practitioner. visibility=all shows the staff view. availability_not_configured means there is no rota or service for that location + type at all. See Booking.Do webhooks tell me what changed?
Do webhooks tell me what changed?
My webhook endpoint got disabled.
My webhook endpoint got disabled.
Can I filter by my own ids?
Can I filter by my own ids?
metadata and filter ?metadata[your_key]=eq:value (or =null for “not yet synced”). See Metadata.Legacy API
Why is the legacy /api being retired?
Why is the legacy /api being retired?
When does it switch off?
When does it switch off?
Deprecation now and will carry Sunset with the date once it is fixed, plus a Link to the migration guide. Both APIs run side by side until then.Do my legacy filters and field names still work?
Do my legacy filters and field names still work?
?field=op:value filter language are unchanged; two tightenings (unknown attribute → 422; no operator → eq). Update auth, base URL, response parsing and the update verb — usually an afternoon. See Migrating.X-Request-Id from the response.