{"openapi":"3.1.0","info":{"title":"Praxis Data Service","description":"The system-of-record for Praxis client and practice data: a canonical, delta-pullable, non-PHI store and command API in front of NexHealth, so every app reads a datum once instead of syncing NexHealth per app.\n\nRead reference data — practices, locations, providers, and services — and a change feed you can poll with `?updated_since=` to pick up what changed. Issue live booking commands (book, cancel, confirm, reschedule), create patients, and look up patient and appointment details. Patient and appointment data is fetched live from the practice management system on each request and is not stored by this service.\n\nAuthenticate with your service token: `Authorization: Bearer <token>`. Signed-in DDS admins get the token pre-filled for the try-it-out panels below.\n\nPick your language in the introduction panel for the typed Python and TypeScript SDKs.\n\n## Where it sits\n\n`15+ dental PMS` (Dentrix, Open Dental, Eaglesoft, Curve, Denticon) → **NexHealth**, the PMS anti-corruption layer that normalizes them into one contract → **this service**, the single egress: a canonical non-PHI store, the sync engine, the read/command API, and the Synchronizer MCP → **your apps** (voice, chat, notifications, dashboard), which pull deltas by `?updated_since` instead of each syncing NexHealth. Patients, availability, and booking stay live passthrough to NexHealth; everything else is the system-of-record slice this service owns.\n\n## Beyond NexHealth: the system-of-record only this service adds\n\nThese endpoints have no NexHealth equivalent. Each runs against this server (set `$BASE`, `$TOKEN`, `$CLIENT`); the live **Try it** and typed Python / TypeScript samples sit on the operation below.\n\n**Delta pull** · `GET /v1/clients/{id}/changes`\n\nEvery canonical row carries `updated_at`, so a consumer resumes from its last cursor instead of re-syncing NexHealth per app. No delta API exists upstream; this is the pull contract the whole ecosystem shares.\n\n```sh\ncurl -H \"Authorization: Bearer $TOKEN\" \\\n  \"$BASE/v1/clients/$CLIENT/changes?updated_since=2026-07-01T00:00:00Z\"\n```\n\n**Appointment-event feed** · `GET /v1/clients/{id}/appointments/changes`\n\nA resumable, non-PHI trigger feed for reminders: render fields inline, `patient_id` resolved to contact at send time. NexHealth emits raw webhooks; this is the synthesized delta a consumer can replay.\n\n```sh\ncurl -H \"Authorization: Bearer $TOKEN\" \\\n  \"$BASE/v1/clients/$CLIENT/appointments/changes?updated_since=2026-07-01T00:00:00Z\"\n```\n\n**Member-role projection** · `PUT /v1/clients/{id}/members`\n\nOrg membership plus per-location roles, replace-set from your identity authority. An identity model NexHealth has no concept of; the effective-role resolver powers per-location authorization.\n\n```sh\ncurl -X PUT -H \"Authorization: Bearer $TOKEN\" -H 'content-type: application/json' \\\n  \"$BASE/v1/clients/$CLIENT/members\" \\\n  -d '{\"members\": [{\"clerk_user_id\": \"user_2abc\", \"role\": \"org_owner\",\n         \"locations\": [{\"location_id\": \"loc_5001\", \"role\": \"front_desk\"}]}]}'\n```\n\n**Practice enrollment** · `POST /v1/practices`\n\nOnboard by NexHealth subdomain and institution id; the scheduler and discovery loop back-fill the tenant. No NexHealth onboarding call: enrollment is the Data Service's own contract.\n\n```sh\ncurl -X POST -H \"Authorization: Bearer $TOKEN\" -H 'content-type: application/json' \\\n  \"$BASE/v1/practices\" \\\n  -d '{\"subdomain\": \"smile-makers-dental-care\", \"external_institution_id\": 22349}'\n```","license":{"name":""},"version":"0.1.0","x-scalar-sdk-installation":[{"lang":"Python","description":"Install the latest from `main` (org-private, rolling release, no PyPI):\n\n```sh\nuv add \"git+https://github.com/DDSMarketingorg/praxis-data-service@main#subdirectory=sdk/python\"\n```\n\n```python\nfrom praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_providers\n\n# base_url defaults to the production service; override only for a non-prod host.\nclient = AuthenticatedClient(token=\"<service-token>\")\nproviders = get_providers.sync(client=client, client_id=\"acme-dental\")\n```"},{"lang":"TypeScript","description":"`@ddsmarketingorg/praxis-data-service-sdk` publishes to the org's **GitHub Packages** npm registry on every merge to `main`. Add an `.npmrc` next to your `package.json` so the scope resolves there:\n\n```ini\n@ddsmarketingorg:registry=https://npm.pkg.github.com\n//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}\n```\n\n`GITHUB_TOKEN` needs the `read:packages` scope (in same-org CI the default `GITHUB_TOKEN` already has it; for local dev or a cross-org consumer, use a PAT with `read:packages`). Then install the latest:\n\n```sh\nnpm install @ddsmarketingorg/praxis-data-service-sdk\n```\n\n```ts\nimport { client, getProviders } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getProviders({ path: { client_id: \"acme-dental\" } });\n```"}]},"servers":[{"url":"https://synchronizer.ai.ddsmarketing.io","description":"Production"}],"paths":{"/health":{"get":{"tags":["meta"],"summary":"Health check","description":"Returns 200 OK when the service is up. No authentication required.","operationId":"health","responses":{"200":{"description":"Service is healthy","content":{"text/plain":{"schema":{"type":"string"},"example":"ok"}}}},"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.meta import health\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = health.sync(client=client)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, health } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await health();"}]}},"/openapi.json":{"get":{"tags":["meta"],"summary":"OpenAPI document","description":"Returns this OpenAPI 3.1 document. No authentication required.","operationId":"openapi_doc","responses":{"200":{"description":"OpenAPI document","content":{"application/json":{"schema":{"type":"object"},"example":{"openapi":"3.1.0","info":{"title":"Praxis Data Service","version":"0.1.0"}}}}}},"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.meta import openapi_doc\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = openapi_doc.sync(client=client)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, openapiDoc } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await openapiDoc();"}]}},"/v1/clients/{client_id}":{"get":{"tags":["clients"],"summary":"Get client profile","description":"Returns the practice profile. 404 if the practice does not exist. No PHI.","operationId":"get_client_profile","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The practice profile","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientProfile"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_client_profile\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_client_profile.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getClientProfile } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getClientProfile({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/appointment-type-compat":{"post":{"tags":["appointments"],"summary":"Appointment-type / slot compatibility verdict","description":"Given a slot's operatory/provider/location and an intended appointment_type_id, returns whether that type is offered for the slot per the synced working hours — the book-time guard against a NexHealth 400. No PHI; nothing is stored.","operationId":"appointment_type_compat","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The slot's operatory/provider/location and the intended appointment_type_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentTypeCompatRequest"}}},"required":true},"responses":{"200":{"description":"The compatibility verdict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentTypeCompat"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import appointment_type_compat\nfrom praxis_data_service_client.models import AppointmentTypeCompatRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = AppointmentTypeCompatRequest.from_dict({\"appointment_type_id\": 84, \"operatory_id\": 271651, \"provider_id\": 521, \"location_id\": 318981})\nresult = appointment_type_compat.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, appointmentTypeCompat } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await appointmentTypeCompat({ path: { client_id: \"<client_id>\" }, body: {\"appointment_type_id\":84,\"operatory_id\":271651,\"provider_id\":521,\"location_id\":318981} });"}]}},"/v1/clients/{client_id}/appointments":{"post":{"tags":["appointments"],"summary":"Book appointment","description":"Books an appointment in the practice management system. If the slot was taken since you fetched availability, the request returns 409 Conflict. Send an Idempotency-Key to make retries safe.","operationId":"book_appointment","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Optional client-supplied key. On the first call the command executes and the result is\nstored under this key; a retry with the same key and the same request body replays the\nstored response WITHOUT re-executing the write. The same key with a different request\nbody returns 409.","required":false,"schema":{"type":"string"}}],"requestBody":{"description":"The appointment to book: provider, location, patient, and the requested slot.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookAppointmentRequest"}}},"required":true},"responses":{"201":{"description":"Appointment booked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookAppointmentResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import book_appointment\nfrom praxis_data_service_client.models import BookAppointmentRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = BookAppointmentRequest.from_dict({\"location_id\": 318981, \"provider_id\": 521, \"appointment_type_id\": 84, \"patient_id\": 90211, \"start_time\": \"2026-07-01T15:00:00Z\", \"end_time\": \"2026-07-01T15:30:00Z\", \"note\": \"New patient — first visit\", \"notify_patient\": True})\nresult = book_appointment.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, bookAppointment } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await bookAppointment({ path: { client_id: \"<client_id>\" }, body: {\"location_id\":318981,\"provider_id\":521,\"appointment_type_id\":84,\"patient_id\":90211,\"start_time\":\"2026-07-01T15:00:00Z\",\"end_time\":\"2026-07-01T15:30:00Z\",\"note\":\"New patient — first visit\",\"notify_patient\":true} });"}]}},"/v1/clients/{client_id}/appointments/changes":{"get":{"tags":["clients"],"summary":"Appointment-event delta feed","description":"The appointment-event delta feed — poll it to schedule reminders / review-requests without warehousing appointments. Returns a page of changed appointment events keyset-paginated on (updated_at, appointment_id) with an opaque next_cursor, each row enriched with location_name, timezone, provider_name and appointment type so you can render a reminder without a second call.\n\n**Poll loop.** Start a feed with `updated_since` (RFC-3339); then follow `next_cursor` until it is null, persist the last cursor, and resume from it on the next poll (cursor wins when both are present). Dedupe by `appointment_id` — a row re-emits whenever the appointment changes.\n\n**Status is a trigger, not ground truth.** A row signals that an appointment changed; a webhook can lag and the row is retained until 7 days past its start_time. Before sending, RE-VERIFY the appointment live (it may have been cancelled or moved after the row you hold) and suppress on cancelled/absent. Each row carries `patient_id` only — resolve it to patient contact via the live patient passthrough at send time (contact is never in this feed).\n\nPHI trigger feed (DR-SCOPE carve-out): scope-gated (the token needs the GET_APPOINTMENT_CHANGES tool + the tenant) and every disclosed row is phi_access_log-audited before the body returns (fail-closed — an audit-write failure returns 503, nothing disclosed). Consumers poll — there is no push.","operationId":"get_appointment_changes","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"RFC-3339 start timestamp. Use to START a feed. Ignored when cursor is also present.","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"Opaque keyset cursor from a previous page's next_cursor. Use to CONTINUE a feed. Takes precedence over updated_since.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A page of appointment events plus the next cursor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentChanges"}}}},"400":{"description":"Bad request — neither updated_since nor cursor supplied, or an invalid timestamp/cursor","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"},"503":{"$ref":"#/components/responses/ServiceUnavailable"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_appointment_changes\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_appointment_changes.sync(client=client, client_id=\"<client_id>\", updated_since=\"<updated_since>\", cursor=\"<cursor>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getAppointmentChanges } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getAppointmentChanges({ path: { client_id: \"<client_id>\" }, query: { updated_since: \"<updated_since>\", cursor: \"<cursor>\" } });"}]}},"/v1/clients/{client_id}/appointments/{appointment_id}/cancel":{"post":{"tags":["appointments"],"summary":"Cancel appointment","description":"Cancels an appointment in the practice management system. Send an Idempotency-Key to make retries safe.","operationId":"cancel_appointment","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"appointment_id","in":"path","description":"Appointment id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"Idempotency-Key","in":"header","description":"Optional client-supplied key. On the first call the command executes and the result is\nstored under this key; a retry with the same key and the same request body replays the\nstored response WITHOUT re-executing the write. The same key with a different request\nbody returns 409.","required":false,"schema":{"type":"string"}}],"requestBody":{"description":"The cancellation request: the reason recorded against the appointment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelAppointmentRequest"}}},"required":true},"responses":{"200":{"description":"Appointment cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelAppointmentResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import cancel_appointment\nfrom praxis_data_service_client.models import CancelAppointmentRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = CancelAppointmentRequest.from_dict({\"reason\": \"Patient requested a later date\"})\nresult = cancel_appointment.sync(client=client, client_id=\"<client_id>\", appointment_id=123, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, cancelAppointment } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await cancelAppointment({ path: { client_id: \"<client_id>\", appointment_id: 123 }, body: {\"reason\":\"Patient requested a later date\"} });"}]}},"/v1/clients/{client_id}/appointments/{appointment_id}/confirm":{"post":{"tags":["appointments"],"summary":"Confirm appointment","description":"Confirms an appointment in the practice management system. Send an Idempotency-Key to make retries safe.","operationId":"confirm_appointment","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"appointment_id","in":"path","description":"Appointment id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"Idempotency-Key","in":"header","description":"Optional client-supplied key. On the first call the command executes and the result is\nstored under this key; a retry with the same key and the same request body replays the\nstored response WITHOUT re-executing the write. The same key with a different request\nbody returns 409.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Appointment confirmed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmAppointmentResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import confirm_appointment\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = confirm_appointment.sync(client=client, client_id=\"<client_id>\", appointment_id=123)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, confirmAppointment } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await confirmAppointment({ path: { client_id: \"<client_id>\", appointment_id: 123 } });"}]}},"/v1/clients/{client_id}/appointments/{appointment_id}/reschedule":{"post":{"tags":["appointments"],"summary":"Reschedule appointment","description":"Reschedules an appointment in the practice management system. When location_id is supplied, the new slot is checked against current availability and the request returns 409 Conflict if it is no longer free. Send an Idempotency-Key to make retries safe.","operationId":"reschedule_appointment","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"appointment_id","in":"path","description":"Appointment id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"Idempotency-Key","in":"header","description":"Optional client-supplied key. On the first call the command executes and the result is\nstored under this key; a retry with the same key and the same request body replays the\nstored response WITHOUT re-executing the write. The same key with a different request\nbody returns 409.","required":false,"schema":{"type":"string"}}],"requestBody":{"description":"The new window for the appointment, with an optional provider and target location.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleAppointmentRequest"}}},"required":true},"responses":{"200":{"description":"Appointment rescheduled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleAppointmentResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import reschedule_appointment\nfrom praxis_data_service_client.models import RescheduleAppointmentRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = RescheduleAppointmentRequest.from_dict({\"new_start_time\": \"2026-07-02T16:00:00Z\", \"location_id\": 318981, \"notify_patient\": True})\nresult = reschedule_appointment.sync(client=client, client_id=\"<client_id>\", appointment_id=123, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, rescheduleAppointment } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await rescheduleAppointment({ path: { client_id: \"<client_id>\", appointment_id: 123 }, body: {\"new_start_time\":\"2026-07-02T16:00:00Z\",\"location_id\":318981,\"notify_patient\":true} });"}]}},"/v1/clients/{client_id}/availability":{"get":{"tags":["appointments"],"summary":"Get available appointment slots","description":"Returns open appointment slots for a location over a date window, fetched live from the practice management system. Build a booking request from a returned slot's start/end and ids; get the appointment_type_id from `GET /services` (its `pms_ref.external_id`). Slots are not patient data and are not stored.","operationId":"get_availability","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"location_id","in":"query","description":"Location id to query slots for (required).","required":true,"schema":{"type":"integer","format":"int64"},"example":318981},{"name":"start_date","in":"query","description":"Inclusive start of the date window (YYYY-MM-DD).","required":true,"schema":{"type":"string"},"example":"2026-07-01"},{"name":"end_date","in":"query","description":"Inclusive end of the date window (YYYY-MM-DD).","required":true,"schema":{"type":"string"},"example":"2026-07-07"},{"name":"provider_id","in":"query","description":"Narrow to a single provider; omit for all providers.","required":false,"schema":{"type":"integer","format":"int64"},"example":521},{"name":"appointment_type_id","in":"query","description":"Narrow to a single appointment type; omit for any.","required":false,"schema":{"type":"integer","format":"int64"},"example":84}],"responses":{"200":{"description":"Open slots in the window","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailableSlot"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.appointments import get_availability\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_availability.sync(client=client, client_id=\"<client_id>\", location_id=318981, start_date=\"2026-07-01\", end_date=\"2026-07-07\", provider_id=521, appointment_type_id=84)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getAvailability } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getAvailability({ path: { client_id: \"<client_id>\" }, query: { location_id: 318981, start_date: \"2026-07-01\", end_date: \"2026-07-07\", provider_id: 521, appointment_type_id: 84 } });"}]}},"/v1/clients/{client_id}/changes":{"get":{"tags":["clients"],"summary":"Delta-pull feed","description":"Returns a page of changed entities (practices, locations, providers, services) keyset-paginated on (updated_at, id), plus an opaque next_cursor. Start a feed with updated_since (an RFC-3339 timestamp); continue with cursor from the previous page's next_cursor (cursor wins when both are present). No row sharing an exact updated_at is skipped or duplicated across pages. Consumers poll this — no push.","operationId":"get_changes","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"RFC-3339 start timestamp; returns entities changed at or after this point. Use to START a feed. Ignored when cursor is also present.","required":false,"schema":{"type":"string","format":"date-time"}},{"name":"cursor","in":"query","description":"Opaque keyset cursor from a previous page's next_cursor. Use to CONTINUE a feed. Takes precedence over updated_since.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A page of changed entities plus the next cursor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangesEnvelope"}}}},"400":{"description":"Bad request — neither updated_since nor cursor supplied, or an invalid timestamp/cursor","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_changes\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_changes.sync(client=client, client_id=\"<client_id>\", updated_since=\"<updated_since>\", cursor=\"<cursor>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getChanges } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getChanges({ path: { client_id: \"<client_id>\" }, query: { updated_since: \"<updated_since>\", cursor: \"<cursor>\" } });"}]}},"/v1/clients/{client_id}/insurance-plans":{"get":{"tags":["clients"],"summary":"List insurance plans","description":"Returns the practice's synced insurance-plan catalog (not patient coverage — that is live passthrough). No PHI. Ordered by updated_at then native PMS id, and capped at 5000 rows per call: this catalog is large for some practices (one holds 12,447 plans). Page forward by passing the last row's updated_at back as updated_since; the bound is inclusive, so dedupe by id.","operationId":"get_insurance_plans","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"updated_since","in":"query","description":"RFC-3339 timestamp; returns only plans changed at or after this point (INCLUSIVE). Omit for the start of the catalog. Paging by feeding the last row's updated_at back re-delivers that row and any sibling sharing its exact timestamp, so dedupe by id — inclusive can only duplicate, whereas a strict bound would silently skip those siblings.","required":false,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Insurance plans for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/InsurancePlan"}}}}},"400":{"description":"updated_since is not a valid RFC-3339 timestamp"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_insurance_plans\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_insurance_plans.sync(client=client, client_id=\"<client_id>\", updated_since=\"<updated_since>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getInsurancePlans } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getInsurancePlans({ path: { client_id: \"<client_id>\" }, query: { updated_since: \"<updated_since>\" } });"}]}},"/v1/clients/{client_id}/knowledge":{"get":{"tags":["clients"],"summary":"Get knowledge bundle","description":"Returns one assembled bundle: the practice profile plus its locations, providers, and services. 404 if the practice does not exist. No PHI.","operationId":"get_knowledge","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The assembled knowledge bundle","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeBundle"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_knowledge\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_knowledge.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getKnowledge } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getKnowledge({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/locations":{"get":{"tags":["clients"],"summary":"List locations","description":"Returns every location for the practice, ordered by id. No PHI.","operationId":"get_locations","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Locations for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Location"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_locations\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_locations.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getLocations } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getLocations({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/members":{"put":{"tags":["members"],"summary":"Replace a practice's member projection","description":"Replace-set the practice's complete member roster — org-wide roles and per-location role grants — from the identity authority. Last-write-wins; an empty `members` list clears the projection. Requires a token scoped for the member sync.","operationId":"sync_members","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The practice's complete member set (org roles + per-location grants).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncMembersBody"}}},"required":true},"responses":{"200":{"description":"Projection replaced; row counts written","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncMembersResponse"}}}},"400":{"description":"Malformed member set","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.members import sync_members\nfrom praxis_data_service_client.models import SyncMembersBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = SyncMembersBody.from_dict({\"members\": [{\"clerk_user_id\": \"user_2abc...\", \"role\": \"org_owner\", \"locations\": [{\"location_id\": \"loc_5001\", \"role\": \"front_desk\"}]}]})\nresult = sync_members.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, syncMembers } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await syncMembers({ path: { client_id: \"<client_id>\" }, body: {\"members\":[{\"clerk_user_id\":\"user_2abc...\",\"role\":\"org_owner\",\"locations\":[{\"location_id\":\"loc_5001\",\"role\":\"front_desk\"}]}]} });"}]}},"/v1/clients/{client_id}/operatories":{"get":{"tags":["clients"],"summary":"List operatories","description":"Returns every synced operatory (chair / room) for the practice, ordered by PMS id. No PHI.","operationId":"get_operatories","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Operatories for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Operatory"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_operatories\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_operatories.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getOperatories } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getOperatories({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/patients":{"get":{"tags":["patients"],"summary":"List patients","description":"Cursor-paginated patient list for a location. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"list_patients","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"location_id","in":"query","description":"Location id (required — patient lists are scoped to one location).","required":true,"schema":{"type":"integer","format":"int64"},"example":318981},{"name":"cursor","in":"query","description":"Opaque cursor from the previous page's end_cursor; omit to start at the first page.","required":false,"schema":{"type":"string"},"example":"eyJpZCI6NDJ9"},{"name":"per_page","in":"query","description":"Page size (max 100; larger values may return a single result per page).","required":false,"schema":{"type":"integer","format":"int64"},"example":100}],"responses":{"200":{"description":"One cursor-paginated page of patients","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPatientsPageDto"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import list_patients\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = list_patients.sync(client=client, client_id=\"<client_id>\", location_id=318981, cursor=\"eyJpZCI6NDJ9\", per_page=100)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, listPatients } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await listPatients({ path: { client_id: \"<client_id>\" }, query: { location_id: 318981, cursor: \"eyJpZCI6NDJ9\", per_page: 100 } });"}]},"post":{"tags":["patients"],"summary":"Create patient","description":"Creates a patient in the practice management system. Send an Idempotency-Key to make retries safe.","operationId":"create_patient","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"Optional client-supplied key. On the first call the command executes and the result is\nstored under this key; a retry with the same key and the same request body replays the\nstored response WITHOUT re-executing the write. The same key with a different request\nbody returns 409.","required":false,"schema":{"type":"string"}}],"requestBody":{"description":"The patient to create: home location, name, date of birth, and contact details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePatientRequest"}}},"required":true},"responses":{"201":{"description":"Patient created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePatientResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import create_patient\nfrom praxis_data_service_client.models import CreatePatientRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = CreatePatientRequest.from_dict({\"location_id\": 318981, \"provider_id\": 521, \"first_name\": \"Jordan\", \"last_name\": \"Rivera\", \"dob\": \"1990-04-12\", \"phone\": \"+15550001234\", \"email\": \"jordan.rivera@example.com\"})\nresult = create_patient.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, createPatient } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await createPatient({ path: { client_id: \"<client_id>\" }, body: {\"location_id\":318981,\"provider_id\":521,\"first_name\":\"Jordan\",\"last_name\":\"Rivera\",\"dob\":\"1990-04-12\",\"phone\":\"+15550001234\",\"email\":\"jordan.rivera@example.com\"} });"}]}},"/v1/clients/{client_id}/patients/lookup":{"get":{"tags":["patients"],"summary":"Look up patient","description":"Searches patients by name/dob/phone across the practice's configured locations, deduped by id. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"lookup_patient","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"name","in":"query","description":"Patient name fragment (first, last, or full name).","required":false,"schema":{"type":"string"},"example":"Jordan Rivera"},{"name":"phone","in":"query","description":"Patient phone number, in any common format.","required":false,"schema":{"type":"string"},"example":"+15550001234"},{"name":"dob","in":"query","description":"Date of birth (YYYY-MM-DD).","required":false,"schema":{"type":"string","format":"date"},"example":"1990-04-12"},{"name":"location_id","in":"query","description":"Location id to scope the search to; omit to search across all configured locations.","required":false,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The matching patients (deduped by id)","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientSummaryDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import lookup_patient\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = lookup_patient.sync(client=client, client_id=\"<client_id>\", name=\"Jordan Rivera\", phone=\"+15550001234\", dob=\"1990-04-12\", location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, lookupPatient } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await lookupPatient({ path: { client_id: \"<client_id>\" }, query: { name: \"Jordan Rivera\", phone: \"+15550001234\", dob: \"1990-04-12\", location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}":{"get":{"tags":["patients"],"summary":"Get patient","description":"Fetches one patient record. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to. Omit (or 0) to use the practice default.","required":false,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient record","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientDetailDto"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatient } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatient({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/alerts":{"get":{"tags":["patients"],"summary":"Get patient alerts","description":"Clinical/administrative alerts on a patient record. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_alerts","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The patient's alerts","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientAlertDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_alerts\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_alerts.sync(client=client, client_id=\"<client_id>\", patient_id=123)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientAlerts } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientAlerts({ path: { client_id: \"<client_id>\", patient_id: 123 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/appointments":{"get":{"tags":["patients"],"summary":"Get patient appointments","description":"Appointment history for a patient across the given locations. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_appointments","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_ids","in":"query","description":"Comma-separated location ids to scope the history to; omit for all locations.","required":false,"schema":{"type":"string"},"example":"318981,318982"}],"responses":{"200":{"description":"The patient's appointment history","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientAppointmentDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_appointments\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_appointments.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_ids=\"318981,318982\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientAppointments } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientAppointments({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_ids: \"318981,318982\" } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/balance":{"get":{"tags":["patients"],"summary":"Get account balance","description":"The patient's account balance. There is no patient-level balance in NexHealth — balances are per guarantor — so this resolves the patient's guarantor from the path patient_id and returns that guarantor's balances. Requires a service token with financial access, and the practice must have financial reads enabled. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_balance","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id; its guarantor is resolved to fetch the balance","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id the balance is scoped to (required — balances are per location).","required":true,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The guarantor balances for the location","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatientBalanceDto"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_balance\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_balance.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientBalance } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientBalance({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/claims-summary":{"get":{"tags":["patients"],"summary":"Get claims summary","description":"Insurance claim summaries for a patient at a location. Requires a service token with financial access, and the practice must have financial reads enabled. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_claims_summary","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to (required).","required":true,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient's claim summaries","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClaimSummaryDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_claims_summary\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_claims_summary.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientClaimsSummary } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientClaimsSummary({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/documents":{"get":{"tags":["patients"],"summary":"Get patient documents","description":"Document records attached to a patient (metadata only — no content). Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_documents","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The patient's document records","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientDocumentDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_documents\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_documents.sync(client=client, client_id=\"<client_id>\", patient_id=123)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientDocuments } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientDocuments({ path: { client_id: \"<client_id>\", patient_id: 123 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/insurance":{"get":{"tags":["patients"],"summary":"List patient insurance","description":"Insurance coverage records for a patient. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"list_patient_insurance","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to. Omit (or 0) to use the practice default.","required":false,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient's insurance coverage records","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientInsuranceDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import list_patient_insurance\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = list_patient_insurance.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, listPatientInsurance } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await listPatientInsurance({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/payment-history":{"get":{"tags":["patients"],"summary":"Get payment history","description":"Payment history for a patient at a location. Requires a service token with financial access, and the practice must have financial reads enabled. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_payment_history","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to (required).","required":true,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient's payment history","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PaymentRecordDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_payment_history\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_payment_history.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientPaymentHistory } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientPaymentHistory({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/payments":{"post":{"tags":["patients"],"summary":"Record a payment","description":"Record a patient payment in the practice management system. Requires a service token with financial access, and the practice must have financial operations enabled. The amount is a positive decimal string; `transaction_id` is the idempotency key (the upstream dedupes on it, so a retried submit records the payment once). This is a live write — nothing is stored by this service beyond the disclosure audit row.","operationId":"record_payment","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id the payment is recorded for","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"description":"The payment to record: amount, location, and the idempotency transaction id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPaymentRequest"}}},"required":true},"responses":{"201":{"description":"The payment was recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPaymentResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import record_payment\nfrom praxis_data_service_client.models import RecordPaymentRequest\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = RecordPaymentRequest.from_dict({\"location_id\": 318981, \"amount\": \"138.00\", \"currency\": \"USD\", \"transaction_id\": \"pos-7f3a9c01\", \"payment_type_id\": 3})\nresult = record_payment.sync(client=client, client_id=\"<client_id>\", patient_id=123, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, recordPayment } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await recordPayment({ path: { client_id: \"<client_id>\", patient_id: 123 }, body: {\"location_id\":318981,\"amount\":\"138.00\",\"currency\":\"USD\",\"transaction_id\":\"pos-7f3a9c01\",\"payment_type_id\":3} });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/recalls":{"get":{"tags":["patients"],"summary":"Get patient recalls","description":"Recall (re-care) records for a patient. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_recalls","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to. Omit (or 0) to use the practice default.","required":false,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient's recall records","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PatientRecallDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_recalls\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_recalls.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientRecalls } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientRecalls({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/sms":{"get":{"tags":["patients"],"summary":"Get patient SMS thread","description":"The SMS conversation thread for a patient. Message bodies are PHI. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_sms_thread","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"location_id","in":"query","description":"Location id to scope the read to. Omit (or 0) to use the practice default.","required":false,"schema":{"type":"integer","format":"int64"},"example":318981}],"responses":{"200":{"description":"The patient's SMS thread","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SmsMessageDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_sms_thread\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_sms_thread.sync(client=client, client_id=\"<client_id>\", patient_id=123, location_id=318981)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientSmsThread } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientSmsThread({ path: { client_id: \"<client_id>\", patient_id: 123 }, query: { location_id: 318981 } });"}]}},"/v1/clients/{client_id}/patients/{patient_id}/treatment-plans":{"get":{"tags":["patients"],"summary":"Get treatment plans","description":"Treatment plans for a patient (scoped to the patient — no location). Requires a service token with financial access, and the practice must have financial reads enabled. Patient data is fetched live from the practice management system on each request and is not stored by this service.","operationId":"get_patient_treatment_plans","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"patient_id","in":"path","description":"Patient id","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"The patient's treatment plans","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TreatmentPlanDto"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalServerError"},"502":{"$ref":"#/components/responses/BadGateway"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.patients import get_patient_treatment_plans\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_patient_treatment_plans.sync(client=client, client_id=\"<client_id>\", patient_id=123)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPatientTreatmentPlans } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPatientTreatmentPlans({ path: { client_id: \"<client_id>\", patient_id: 123 } });"}]}},"/v1/clients/{client_id}/pms-events":{"get":{"tags":["clients"],"summary":"PMS event bus (non-PHI signal feed)","description":"The non-PHI PMS event bus — poll it to react to any NexHealth resource event (payments, treatment plans, forms, insurance, procedures, messages, …) without warehousing or polling NexHealth. Each row is a SIGNAL: resource_type, event name, the resource's own opaque NexHealth id, its location, and a monotonic cursor id. It carries NO patient linkage and NO content — fetch any detail (who / how much / what) live via the patient/resource passthrough.\n\n**Poll loop.** Start at `?after=0`; read the page, persist `next_after`, and resume from it. When a poll returns no events, you are caught up — resume from the same `next_after` next time. Optionally scope to one `resource_type`.\n\nAppointment events are NOT here — they have their own richer feed at `/v1/clients/{client_id}/appointments/changes`. Non-PHI feed: service-token auth, no scope gate, no audit. Consumers poll — there is no push.","operationId":"get_pms_events","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","description":"Resume cursor — the last id seen. Omit or 0 to start the feed.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"resource_type","in":"query","description":"Optional filter to one NexHealth resource type, e.g. Payment.","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"A page of event-bus rows plus the next cursor","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PmsEventsEnvelope"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_pms_events\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_pms_events.sync(client=client, client_id=\"<client_id>\", after=123, resource_type=\"<resource_type>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getPmsEvents } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getPmsEvents({ path: { client_id: \"<client_id>\" }, query: { after: 123, resource_type: \"<resource_type>\" } });"}]}},"/v1/clients/{client_id}/providers":{"get":{"tags":["clients"],"summary":"List providers","description":"Returns every provider / clinician for the practice. No PHI.","operationId":"get_providers","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Providers for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Provider"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_providers\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_providers.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getProviders } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getProviders({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/providers/{provider_id}/role":{"put":{"tags":["clients"],"summary":"Set a provider's clinical role","description":"Sets (or clears, with `role: null`) a synced provider's clinical role — the dimension a service's `required_provider_role` scheduling constraint resolves against. Non-PHI config, sourced from the practice's onboarding/config, not a PMS: NexHealth's provider payload carries no role.","operationId":"set_provider_role","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"provider_id","in":"path","description":"NexHealth provider id of the provider","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"description":"The clinical role to set for the provider (`null` clears it).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderRoleBody"}}},"required":true},"responses":{"200":{"description":"Role updated; the normalized body is returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderRoleBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import set_provider_role\nfrom praxis_data_service_client.models import ProviderRoleBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = ProviderRoleBody.from_dict({})\nresult = set_provider_role.sync(client=client, client_id=\"<client_id>\", provider_id=123, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, setProviderRole } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await setProviderRole({ path: { client_id: \"<client_id>\", provider_id: 123 }, body: {} });"}]}},"/v1/clients/{client_id}/recall-types":{"get":{"tags":["clients"],"summary":"List recall types","description":"Returns every synced recall type (the catalog, not patient recalls — those are live passthrough) for the practice. No PHI.","operationId":"get_recall_types","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Recall types for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecallType"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_recall_types\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_recall_types.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getRecallTypes } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getRecallTypes({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/routing":{"get":{"tags":["clients"],"summary":"Get routing config","description":"Returns the DID/subdomain to client routing slice, derived from the practice management system connection and locations. 404 when the client has no enabled connection. No PHI.","operationId":"get_routing","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The routing config","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutingConfig"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_routing\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_routing.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getRouting } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getRouting({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/services":{"get":{"tags":["clients"],"summary":"List services","description":"Returns every service / appointment type for the practice. No PHI.","operationId":"get_services","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Services for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_services\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_services.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getServices } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getServices({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/services/{appointment_type_id}/scheduling-constraints":{"put":{"tags":["clients"],"summary":"Set service scheduling constraints","description":"Replaces a service's scheduling-constraint policy — the min-notice / available-days / allowed-providers / required-role / operatory / working-hour-label rules the booking engine enforces on availability, booking, and reschedule. PUT semantics: omitted fields reset that dimension to unconstrained. Non-PHI config, sourced from the practice's onboarding/config, not a PMS.","operationId":"set_scheduling_constraints","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"appointment_type_id","in":"path","description":"NexHealth appointment-type id of the service","required":true,"schema":{"type":"integer","format":"int64"}}],"requestBody":{"description":"The full scheduling-constraint policy to set for the service.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingConstraintsBody"}}},"required":true},"responses":{"200":{"description":"Constraints updated; the normalized policy is returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulingConstraintsBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import set_scheduling_constraints\nfrom praxis_data_service_client.models import SchedulingConstraintsBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = SchedulingConstraintsBody.from_dict({})\nresult = set_scheduling_constraints.sync(client=client, client_id=\"<client_id>\", appointment_type_id=123, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, setSchedulingConstraints } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await setSchedulingConstraints({ path: { client_id: \"<client_id>\", appointment_type_id: 123 }, body: {} });"}]}},"/v1/clients/{client_id}/usage":{"get":{"tags":["clients"],"summary":"Get usage breakdown","description":"Returns a per-call aggregate over the half-open window [from, to): patient-data request counts grouped by location and by action. No PHI.","operationId":"get_usage","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","description":"RFC-3339 window start (inclusive)","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","description":"RFC-3339 window end (exclusive). Must be strictly after from.","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Usage breakdown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageBreakdown"}}}},"400":{"description":"Bad request — from/to not valid RFC-3339, or from >= to","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_usage\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_usage.sync(client=client, client_id=\"<client_id>\", from=\"<from>\", to=\"<to>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getUsage } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getUsage({ path: { client_id: \"<client_id>\" }, query: { from: \"<from>\", to: \"<to>\" } });"}]}},"/v1/clients/{client_id}/webhook-subscriptions":{"get":{"tags":["webhook_subscriptions"],"summary":"List webhook subscriptions","description":"Every webhook subscription for the practice, oldest first.","operationId":"list_webhook_subscriptions","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Subscriptions for the practice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionListResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.webhook_subscriptions import list_webhook_subscriptions\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = list_webhook_subscriptions.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, listWebhookSubscriptions } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await listWebhookSubscriptions({ path: { client_id: \"<client_id>\" } });"}]},"post":{"tags":["webhook_subscriptions"],"summary":"Register a webhook subscription","description":"Register a delivery endpoint and the event types to receive. The signing secret is server-generated and returned in the response.","operationId":"create_webhook_subscription","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The subscription to register: a delivery URL and the event types to receive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookSubscriptionBody"}}},"required":true},"responses":{"201":{"description":"Subscription created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"}}}},"400":{"description":"Invalid webhook URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.webhook_subscriptions import create_webhook_subscription\nfrom praxis_data_service_client.models import CreateWebhookSubscriptionBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = CreateWebhookSubscriptionBody.from_dict({\"name\": \"Brain delta sync\", \"url\": \"https://brain.example/webhooks/praxis\", \"events\": [\"appointment.booked\", \"location.updated\"], \"is_active\": True})\nresult = create_webhook_subscription.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, createWebhookSubscription } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await createWebhookSubscription({ path: { client_id: \"<client_id>\" }, body: {\"name\":\"Brain delta sync\",\"url\":\"https://brain.example/webhooks/praxis\",\"events\":[\"appointment.booked\",\"location.updated\"],\"is_active\":true} });"}]}},"/v1/clients/{client_id}/webhook-subscriptions/{subscription_id}":{"get":{"tags":["webhook_subscriptions"],"summary":"Get a webhook subscription","description":"Fetch a single webhook subscription, including its signing secret, by id.","operationId":"get_webhook_subscription","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","description":"Webhook subscription id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.webhook_subscriptions import get_webhook_subscription\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_webhook_subscription.sync(client=client, client_id=\"<client_id>\", subscription_id=\"<subscription_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getWebhookSubscription } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getWebhookSubscription({ path: { client_id: \"<client_id>\", subscription_id: \"<subscription_id>\" } });"}]},"delete":{"tags":["webhook_subscriptions"],"summary":"Delete a webhook subscription","description":"Permanently remove a webhook subscription by id. Returns 404 if it does not exist.","operationId":"delete_webhook_subscription","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","description":"Webhook subscription id","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.webhook_subscriptions import delete_webhook_subscription\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = delete_webhook_subscription.sync(client=client, client_id=\"<client_id>\", subscription_id=\"<subscription_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, deleteWebhookSubscription } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await deleteWebhookSubscription({ path: { client_id: \"<client_id>\", subscription_id: \"<subscription_id>\" } });"}]},"patch":{"tags":["webhook_subscriptions"],"summary":"Update a webhook subscription","description":"Partial update — omitted fields are left unchanged. An empty events array clears the event set.","operationId":"update_webhook_subscription","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}},{"name":"subscription_id","in":"path","description":"Webhook subscription id","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The fields to change; omitted fields are left unchanged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookSubscriptionBody"}}},"required":true},"responses":{"200":{"description":"Updated subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"}}}},"400":{"description":"Invalid webhook URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.webhook_subscriptions import update_webhook_subscription\nfrom praxis_data_service_client.models import UpdateWebhookSubscriptionBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = UpdateWebhookSubscriptionBody.from_dict({\"name\": \"Brain delta sync (paused)\", \"url\": \"https://brain.example/webhooks/praxis\", \"events\": [\"appointment.booked\"], \"is_active\": False})\nresult = update_webhook_subscription.sync(client=client, client_id=\"<client_id>\", subscription_id=\"<subscription_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, updateWebhookSubscription } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await updateWebhookSubscription({ path: { client_id: \"<client_id>\", subscription_id: \"<subscription_id>\" }, body: {\"name\":\"Brain delta sync (paused)\",\"url\":\"https://brain.example/webhooks/praxis\",\"events\":[\"appointment.booked\"],\"is_active\":false} });"}]}},"/v1/clients/{client_id}/working-hour-labels":{"get":{"tags":["clients"],"summary":"List working-hour labels","description":"Returns every synced working-hour label (appointment-type scoping) for the practice. No PHI.","operationId":"get_working_hour_labels","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Working-hour labels for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkingHourLabel"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_working_hour_labels\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_working_hour_labels.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getWorkingHourLabels } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getWorkingHourLabels({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/clients/{client_id}/working-hours":{"get":{"tags":["clients"],"summary":"List working hours","description":"Returns every synced working-hours row (one weekday per location) for the practice. No PHI.","operationId":"get_working_hours","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Working-hours rows for the practice","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkingHour"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.clients import get_working_hours\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nresult = get_working_hours.sync(client=client, client_id=\"<client_id>\")"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, getWorkingHours } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await getWorkingHours({ path: { client_id: \"<client_id>\" } });"}]}},"/v1/practices":{"post":{"tags":["practices"],"summary":"Enroll an onboarded practice into the sync engine","description":"Idempotently create the practice + an enabled PMS connection + config from a NexHealth subdomain + institution id. The scheduler/reconciler then sync it; the discovery loop catches practices added directly in NexHealth. Requires a token scoped for enrollment.","operationId":"enroll_practice","requestBody":{"description":"The practice's NexHealth subdomain + institution id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollPracticeBody"}}},"required":true},"responses":{"200":{"description":"Practice enrolled (idempotent)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollPracticeResponse"}}}},"400":{"description":"Missing/blank subdomain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"description":"Institution already enrolled under a different practice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.practices import enroll_practice\nfrom praxis_data_service_client.models import EnrollPracticeBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = EnrollPracticeBody.from_dict({\"subdomain\": \"smile-makers-dental-care\", \"external_institution_id\": 22349, \"display_name\": \"Smile Makers Dental Care\", \"primary_email\": \"front-desk@smilemakers.example\", \"escalation_phone\": \"+15550001234\"})\nresult = enroll_practice.sync(client=client, body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, enrollPractice } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await enrollPractice({ body: {\"subdomain\":\"smile-makers-dental-care\",\"external_institution_id\":22349,\"display_name\":\"Smile Makers Dental Care\",\"primary_email\":\"front-desk@smilemakers.example\",\"escalation_phone\":\"+15550001234\"} });"}]}},"/v1/practices/{client_id}/flags":{"put":{"tags":["practices"],"summary":"Set a practice's feature flags","description":"Replaces the practice's feature-flag set. Every flag defaults to false, so an omitted flag is off. This is the authority consumers gate paid features on. Requires a token scoped for SET_PRACTICE_FLAGS.","operationId":"set_practice_flags","parameters":[{"name":"client_id","in":"path","description":"Practice / client identifier","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The complete desired flag set. Unknown keys are rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetFeatureFlagsBody"}}},"required":true},"responses":{"200":{"description":"Flags updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlags"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"description":"No such practice","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"422":{"description":"Unknown or malformed flag key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"500":{"$ref":"#/components/responses/InternalServerError"}},"security":[{"BearerAuth":[]}],"x-codeSamples":[{"lang":"Python","label":"Python SDK","source":"from praxis_data_service_client import AuthenticatedClient\nfrom praxis_data_service_client.api.practices import set_practice_flags\nfrom praxis_data_service_client.models import SetFeatureFlagsBody\n\nclient = AuthenticatedClient(token=\"<service-token>\")\nbody = SetFeatureFlagsBody.from_dict({\"appointment_booking\": True, \"insurance_verification\": False, \"outbound_reminders\": False})\nresult = set_practice_flags.sync(client=client, client_id=\"<client_id>\", body=body)"},{"lang":"TypeScript","label":"TypeScript SDK","source":"import { client, setPracticeFlags } from \"@ddsmarketingorg/praxis-data-service-sdk\";\n\nclient.setConfig({ headers: { Authorization: \"Bearer <service-token>\" } });\nconst { data } = await setPracticeFlags({ path: { client_id: \"<client_id>\" }, body: {\"appointment_booking\":true,\"insurance_verification\":false,\"outbound_reminders\":false} });"}]}}},"components":{"schemas":{"ActionCount":{"type":"object","description":"One (action, count) pair in a usage breakdown — the secondary operational axis.","required":["action","count"],"properties":{"action":{"type":"string","description":"Action, e.g. BOOK, CANCEL, SEARCH","example":"BOOK"},"count":{"type":"integer","format":"int64","description":"Number of patient-data requests for this action in the window","example":17}},"example":{"action":"BOOK","count":17}},"AppointmentChanges":{"type":"object","description":"Appointment-event delta page: the changed appointment events plus an opaque `next_cursor`\n(null when the feed is exhausted). Same keyset-cursor contract as [`ChangesEnvelope`].","required":["changes","next_cursor"],"properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentEvent"},"description":"Changed appointment events in this page, ordered by `updated_at` ascending"},"next_cursor":{"type":["string","null"],"description":"Opaque base64 keyset cursor for the next page; pass back as ?cursor=. Null when exhausted.","example":"eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0wMVQwOTowMDowMFoifQ"}},"example":{"changes":[{"client_id":"practice_abc","appointment_id":"9001","patient_id":"50231","status":"booked","start_time":"2026-07-02T14:30:00Z","end_time":"2026-07-02T15:00:00Z","location_id":"318981","location_name":"Main Office","timezone":"America/Chicago","provider_id":"521","provider_name":"Dr. Smith","appointment_type_id":"84","appt_type":"New Patient Exam","updated_at":"2026-07-01T09:00:00Z"}],"next_cursor":"eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNy0wMVQwOTowMDowMFoifQ"}},"AppointmentEvent":{"type":"object","description":"One appointment-event trigger row on the `/appointments/changes` feed (DR-SCOPE carve-out).\n\nMinimal trigger metadata — NOT a patient record: appointment id / patient id / time / status /\nlocation / provider only. `patient_id` is the passthrough handle the consumer resolves to\ncontact at send time; the response body carries it (the point of the feed) but `Debug` is\nhand-rolled to redact it so a stray `{:?}` never leaks the PHI linkage into a log or span.","required":["client_id","appointment_id","patient_id","status","start_time","updated_at"],"properties":{"client_id":{"type":"string","description":"Practice / client the appointment belongs to","example":"practice_abc"},"appointment_id":{"type":"string","description":"`NexHealth` appointment id (the keyset id on this feed)","example":"9001"},"patient_id":{"type":"string","description":"`NexHealth` patient id — the passthrough handle; resolve to contact at send time","example":"50231"},"status":{"type":"string","description":"FHIR appointment status; `cancelled` signals a cancellation","example":"booked"},"start_time":{"type":"string","format":"date-time","description":"Appointment start (RFC-3339) — drives the reminder schedule","example":"2026-07-02T14:30:00Z"},"end_time":{"type":["string","null"],"format":"date-time","description":"Appointment end (RFC-3339), when the source provides it","example":"2026-07-02T15:00:00Z"},"location_id":{"type":["string","null"],"description":"`NexHealth` location id, when present","example":"318981"},"location_name":{"type":["string","null"],"description":"Location name enriched from the canonical store; null if the location is not yet synced","example":"Main Office"},"timezone":{"type":["string","null"],"description":"IANA timezone of the location enriched from the canonical store; use to format the reminder time","example":"America/Chicago"},"provider_id":{"type":["string","null"],"description":"`NexHealth` provider id, when present","example":"521"},"provider_name":{"type":["string","null"],"description":"Provider display name enriched from the canonical store; null if the provider is not yet synced","example":"Dr. Smith"},"appointment_type_id":{"type":["string","null"],"description":"`NexHealth` appointment-type id, when present","example":"84"},"appt_type":{"type":["string","null"],"description":"Appointment-type / service name enriched from the canonical store; null if not yet synced","example":"New Patient Exam"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 change timestamp — the value the consumer polls by (the feed cursor)","example":"2026-07-01T09:00:00Z"}},"example":{"client_id":"practice_abc","appointment_id":"9001","patient_id":"50231","status":"booked","start_time":"2026-07-02T14:30:00Z","end_time":"2026-07-02T15:00:00Z","location_id":"318981","location_name":"Main Office","timezone":"America/Chicago","provider_id":"521","provider_name":"Dr. Smith","appointment_type_id":"84","appt_type":"New Patient Exam","updated_at":"2026-07-01T09:00:00Z"}},"AppointmentTypeCompat":{"type":"object","description":"The compatibility verdict for an appointment type against a slot's operatory/provider.","required":["verdict","available_type_ids"],"properties":{"verdict":{"type":"string","description":"One of `ok` (bookable / nothing to check), `mismatch` (typed slot, requested type not offered\n— booking it 400s at `NexHealth`), `untyped_only` (type on no `working_hours` → book generic),\nor `unknown` (insufficient data → treat as OK, `NexHealth` is the final authority).","example":"mismatch"},"available_type_ids":{"type":"array","items":{"type":"integer","format":"int64"},"description":"On `mismatch`, the sorted appointment types the slot's operatory/provider actually offers\n(e.g. `[84, 90]`); empty otherwise."}},"example":{"verdict":"mismatch","available_type_ids":[84,90]}},"AppointmentTypeCompatRequest":{"type":"object","description":"Request body for the appointment-type / slot compatibility verdict.\n\nEvery axis is optional: the verdict is `ok` (nothing to check) when `appointment_type_id` is\nabsent, and unions offered types across all locations when `location_id` is absent.","properties":{"appointment_type_id":{"type":["integer","null"],"format":"int64","description":"The appointment type the caller intends to book the slot under; omit → verdict `ok`.","example":84},"operatory_id":{"type":["integer","null"],"format":"int64","description":"The slot's operatory (chair) id, if operatory-keyed.","example":271651},"provider_id":{"type":["integer","null"],"format":"int64","description":"The slot's provider id, if provider-keyed.","example":521},"location_id":{"type":["integer","null"],"format":"int64","description":"The slot's location id; omit to union offered types across every location.","example":318981}},"example":{"appointment_type_id":84,"operatory_id":271651,"provider_id":521,"location_id":318981}},"AvailableSlot":{"type":"object","description":"One open appointment slot. Non-PHI: open times plus the location / provider / operatory /\nappointment-type ids the slot is offered under.","required":["location_id","start_time","end_time","book_untyped"],"properties":{"location_id":{"type":"integer","format":"int64","description":"Location the slot belongs to (PMS-native id).","example":318981},"provider_id":{"type":["integer","null"],"format":"int64","description":"Provider the slot is offered with; null when the slot is not provider-specific.","example":521},"operatory_id":{"type":["integer","null"],"format":"int64","description":"Operatory (chair / room) the slot occupies; null when not applicable.","example":271651},"start_time":{"type":"string","format":"date-time","description":"Slot start, RFC-3339.","example":"2026-07-01T15:00:00Z"},"end_time":{"type":"string","format":"date-time","description":"Slot end, RFC-3339.","example":"2026-07-01T15:30:00Z"},"appointment_type_id":{"type":["integer","null"],"format":"int64","description":"Appointment type this slot satisfies. Null on a slot from the type-less retry — see\n`book_untyped`, and do NOT assume the type you asked for applies.","example":84},"book_untyped":{"type":"boolean","description":"True when this is a GENERIC opening the requested appointment type was NOT confirmed for\n(the practice management system returned nothing for the typed query, so it was retried\nwithout the type). Book it WITHOUT an appointment type: sending the type you asked for is\nrejected upstream as not configured for the slot. False on an ordinary slot.","example":false}},"example":{"location_id":318981,"provider_id":521,"operatory_id":271651,"start_time":"2026-07-01T15:00:00Z","end_time":"2026-07-01T15:30:00Z","appointment_type_id":84,"book_untyped":false}},"BookAppointmentRequest":{"type":"object","description":"Request body for POST /v1/clients/{client_id}/appointments.","required":["location_id","provider_id","appointment_type_id","patient_id","start_time","end_time"],"properties":{"location_id":{"type":"integer","format":"int64","description":"Location where the slot lives, in the practice management system.","example":318981},"provider_id":{"type":"integer","format":"int64","description":"Provider id; pass `0` to mean \"any provider\".","example":521},"appointment_type_id":{"type":"integer","format":"int64","description":"Appointment-type id — determines the appointment's duration and billing code.","example":84},"patient_id":{"type":"integer","format":"int64","description":"Patient id — the existing patient record to attach the appointment to.","example":90211},"start_time":{"type":"string","format":"date-time","description":"RFC-3339 start time","example":"2026-07-01T15:00:00Z"},"end_time":{"type":"string","format":"date-time","description":"RFC-3339 end time","example":"2026-07-01T15:30:00Z"},"note":{"type":["string","null"],"description":"Free-text clinical note attached to the appointment at creation; `None` if not provided.","example":"New patient — first visit"},"office_hours":{"type":["object","null"],"description":"The caller's own weekly office hours — the same authoritative blob the MCP tools accept.\n\nA practice's real weekly hours are ADMIN-authored in the caller's store; the synced `NexHealth`\nrows are per-provider availability and assert no closure at all (ITS-512). Supplied, this\nREPLACES the synced weekly base and its omitted weekdays count as closed, so a write onto a day\nthe practice is shut is refused pre-PMS. Omitted (or not hours-shaped) leaves the synced rows as\nthe only weekly signal. Shape: `{\"monday\": {\"open\": \"09:00\", \"close\": \"17:00\"}, \"tuesday\":\n\"closed\", ...}` — full / 3-letter / numeric day keys, an optional `ranges` list for split days,\nan optional `regular` nesting."},"notify_patient":{"type":"boolean","description":"Whether the PMS should send its own booking confirmation to the patient. Defaults to `true`\nwhen omitted, which is the behaviour every caller got before this field existed.\n\nSet it to `false` when the caller sends its own confirmation — a voice agent that books and\nthen texts the patient itself would otherwise have the patient receive two messages, one\nfrom the PMS and one from the caller (ITS-478 follow-up).","example":true}},"example":{"location_id":318981,"provider_id":521,"appointment_type_id":84,"patient_id":90211,"start_time":"2026-07-01T15:00:00Z","end_time":"2026-07-01T15:30:00Z","note":"New patient — first visit"}},"BookAppointmentResponse":{"type":"object","description":"Response body for a successful appointment booking.","required":["appointment_id","status","start_time","end_time"],"properties":{"appointment_id":{"type":"integer","format":"int64","description":"Appointment id assigned by the practice management system, stable for the lifetime of the appointment.","example":778812},"status":{"type":"string","description":"The appointment's status after booking (FHIR `Appointment.status`, kebab-case).","example":"booked"},"start_time":{"type":"string","format":"date-time","description":"RFC-3339 start time","example":"2026-07-01T15:00:00Z"},"end_time":{"type":"string","format":"date-time","description":"RFC-3339 end time","example":"2026-07-01T15:30:00Z"}},"example":{"appointment_id":778812,"status":"booked","start_time":"2026-07-01T15:00:00Z","end_time":"2026-07-01T15:30:00Z"}},"CancelAppointmentRequest":{"type":"object","description":"Request body for POST /v1/clients/{client_id}/appointments/{id}/cancel.","properties":{"reason":{"type":["string","null"],"description":"Human-readable cancellation reason recorded against the appointment; `None` when the caller omits it.","example":"Patient requested a later date"}},"example":{"reason":"Patient requested a later date"}},"CancelAppointmentResponse":{"type":"object","description":"Response body for a successful appointment cancellation.","required":["appointment_id","status"],"properties":{"appointment_id":{"type":"integer","format":"int64","description":"Id of the cancelled appointment — echoed from the path parameter for client convenience.","example":778812},"status":{"type":"string","description":"The appointment's status after cancellation (FHIR `Appointment.status`, kebab-case).","example":"cancelled"}},"example":{"appointment_id":778812,"status":"cancelled"}},"ChangedEntity":{"type":"object","description":"One row in the delta-pull feed: an entity that changed after the caller's cursor.","required":["client_id","entity","entity_id","op","updated_at"],"properties":{"client_id":{"type":"string","description":"Practice / client the changed entity belongs to","example":"practice_abc"},"entity":{"type":"string","description":"Entity type: practice, location, provider, or service","example":"location"},"entity_id":{"type":"string","description":"Entity primary-key ID","example":"loc_1"},"op":{"type":"string","description":"Write operation: upsert (the entity was created/updated — refresh your copy) or delete (the entity was removed — purge your copy).","enum":["upsert","delete"],"examples":["upsert"]},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"client_id":"practice_abc","entity":"location","entity_id":"loc_1","op":"upsert","updated_at":"2026-06-16T12:00:00Z"}},"ChangesEnvelope":{"type":"object","description":"Delta-pull page: the changed entities plus an opaque `next_cursor`. `next_cursor` is null\nwhen the feed is exhausted (a non-full page).","required":["changes","next_cursor"],"properties":{"changes":{"type":"array","items":{"$ref":"#/components/schemas/ChangedEntity"},"description":"Changed entities in this page, ordered by `updated_at` ascending"},"next_cursor":{"type":["string","null"],"description":"Opaque base64 keyset cursor for the next page; pass back as ?cursor=. Null when the\nfeed is exhausted.","example":"eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNi0xNlQxMjowMDowMFoifQ"}},"example":{"changes":[{"client_id":"practice_abc","entity":"location","entity_id":"loc_1","op":"upsert","updated_at":"2026-06-16T12:00:00Z"}],"next_cursor":"eyJ1cGRhdGVkX2F0IjoiMjAyNi0wNi0xNlQxMjowMDowMFoifQ"}},"ClaimSummaryDto":{"type":"object","description":"One insurance claim summary.\n\n`note` is free-text (may carry PHI) and `totals` is financial PHI-adjacent; a redacting `Debug`\nis hand-written.","required":["claim_id","status"],"properties":{"claim_id":{"type":"integer","format":"int64","description":"Claim id.","example":8801},"status":{"type":"string","description":"The claim's status, as reported by the practice management system.","example":"Submitted"},"date_of_service":{"type":["string","null"],"format":"date","description":"Date of service (`YYYY-MM-DD`), when present.","example":"2026-05-12"},"location_id":{"type":["integer","null"],"format":"int64","description":"Location the claim is for, when present. Not PHI."},"provider_id":{"type":["integer","null"],"format":"int64","description":"Provider who performed the claimed procedures, when present. Not PHI."},"guarantor_id":{"type":["integer","null"],"format":"int64","description":"Financially-responsible guarantor, when present. Not PHI."},"primary_insurance_plan_id":{"type":["integer","null"],"format":"int64","description":"Primary insurance plan the claim is submitted to, when present. Not PHI."},"secondary_insurance_plan_id":{"type":["integer","null"],"format":"int64","description":"Secondary insurance plan (coordination of benefits), when present. Not PHI."},"sent_at":{"type":["string","null"],"description":"When the claim was sent to insurance, when present. Not PHI."},"received_at":{"type":["string","null"],"description":"When the insurance response (EOB) was received, when present. Not PHI."},"note":{"type":["string","null"],"description":"Free-text claim note, when present. PHI-adjacent."},"totals":{"type":"object","description":"Aggregated money totals for a claim. Each amount is financial PHI-adjacent (redacted in `Debug`).","properties":{"amount_billed_to_insurance":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"estimated_insurance_payment":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"insurance_payment":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"write_off":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}}}},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"claim_id":8801,"status":"Submitted","date_of_service":"2026-05-12"}},"ClientProfile":{"type":"object","description":"Practice profile. Projected from `practices`; no PHI.","required":["id","display_name","default_language","updated_at","flags"],"properties":{"id":{"type":"string","description":"Practice primary key — the `client_id` used across all API responses","example":"practice_abc"},"display_name":{"type":"string","description":"Practice name shown in consumer UIs, e.g. \"Smile Bright Dental\"","example":"Smile Bright Dental"},"timezone_default":{"type":["string","null"],"description":"IANA timezone identifier, e.g. America/Chicago","example":"America/Chicago"},"default_language":{"type":"string","description":"BCP-47 language tag, e.g. en","example":"en"},"website":{"type":["string","null"],"description":"Practice public website URL; null when not configured","example":"https://smilebright.example"},"primary_email":{"type":["string","null"],"description":"Primary contact email for the practice; null when not configured","example":"hello@smilebright.example"},"escalation_phone":{"type":["string","null"],"description":"E.164 escalation phone number for urgent contact; null when not configured","example":"+15550001234"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"},"flags":{"type":"object","description":"Per-practice feature toggles.\n\n**Every flag defaults to `false`** — a practice opts in, so an absent or unset flag always reads\nas off. This is the single source of truth for whether a paid feature is active for a practice:\na consumer gates on it rather than keeping its own per-tenant switch. Two switches that must\nagree is precisely how six practices ended up with nobody scheduling their reminders at all:\nConcierge had handed them off, the other service had not picked them up, and neither side was\nwrong because no single place said what was true.","properties":{"appointment_booking":{"type":"boolean","description":"AI-assisted appointment booking is enabled for this practice.","example":true},"insurance_verification":{"type":"boolean","description":"Insurance-verification workflows are enabled.","example":false},"outbound_reminders":{"type":"boolean","description":"Outbound appointment reminders (SMS/email) are enabled. Off unless the practice has the\noption; a consumer that schedules reminders MUST gate on this.","example":false}},"example":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false}},"pms_ref":{"type":"object","description":"PMS crosswalk for a reference entity.\n\nMaps our opaque canonical `id` to the upstream practice-management-system record, so a consumer\ncan join our rows to a PMS-keyed replica (and recover the native id the command API expects).\nPresent only on PMS-synced rows; absent on practice-authored rows.","required":["system","external_id"],"properties":{"system":{"type":"string","description":"Upstream PMS identifier, e.g. \"NEXHEALTH\"","example":"NEXHEALTH"},"external_id":{"type":"string","description":"The entity's native primary id in the PMS, as a string (e.g. `NexHealth`'s numeric id)","example":"318981"},"foreign_id":{"type":["string","null"],"description":"EMR-level foreign id from the PMS's own crosswalk; null when absent","example":"A1001"},"foreign_id_type":{"type":["string","null"],"description":"Type qualifier for `foreign_id`, e.g. `open_dental`; null when absent","example":"open_dental"}},"example":{"system":"NEXHEALTH","external_id":"318981"}}},"example":{"id":"practice_abc","display_name":"Smile Bright Dental","timezone_default":"America/Chicago","default_language":"en","website":"https://smilebright.example","primary_email":"hello@smilebright.example","escalation_phone":"+15550001234","updated_at":"2026-06-16T12:00:00Z","flags":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false},"pms_ref":{"system":"NEXHEALTH","external_id":"20858"}}},"ConfirmAppointmentResponse":{"type":"object","description":"Response body for a successful appointment confirmation.","required":["appointment_id","status"],"properties":{"appointment_id":{"type":"integer","format":"int64","description":"Id of the confirmed appointment — echoed from the path parameter for client convenience.","example":778812},"status":{"type":"string","description":"The appointment's status after confirmation (FHIR `Appointment.status`, kebab-case;\nconfirming resolves the appointment to `booked`).","example":"booked"}},"example":{"appointment_id":778812,"status":"booked"}},"CreatePatientRequest":{"type":"object","description":"Request body for POST /v1/clients/{client_id}/patients.","required":["location_id","provider_id","first_name","last_name","dob","phone","email"],"properties":{"location_id":{"type":"integer","format":"int64","description":"Home location for the new patient — determines which practice chart the record lands in.","example":318981},"provider_id":{"type":"integer","format":"int64","description":"Responsible provider id the patient is registered under. Required by the practice management\nsystem; must be one of the practice's known providers (validated server-side).","example":521},"first_name":{"type":"string","description":"Patient's legal first name as it appears in the chart.","example":"Jordan"},"last_name":{"type":"string","description":"Patient's legal last name as it appears in the chart.","example":"Rivera"},"dob":{"type":"string","format":"date","description":"Date of birth, ISO-8601 (YYYY-MM-DD).","example":"1990-04-12"},"phone":{"type":"string","description":"E.164 phone number — required by the practice management system on create.","example":"+15550001234"},"email":{"type":"string","description":"Patient contact email address — required by the practice management system on create.","example":"jordan.rivera@example.com"}},"example":{"location_id":318981,"provider_id":521,"first_name":"Jordan","last_name":"Rivera","dob":"1990-04-12","phone":"+15550001234","email":"jordan.rivera@example.com"}},"CreatePatientResponse":{"type":"object","description":"Response body for a successful patient creation.","required":["patient_id"],"properties":{"patient_id":{"type":"integer","format":"int64","description":"Patient id assigned by the practice management system for the newly created chart record.","example":90211}},"example":{"patient_id":90211}},"CreateWebhookSubscriptionBody":{"type":"object","description":"Create-subscription request body.","required":["name","url"],"properties":{"name":{"type":"string","description":"Human-readable label for the subscription.","example":"Brain delta sync"},"url":{"type":"string","description":"Delivery endpoint. Must be an `http`/`https` URL.","example":"https://brain.example/webhooks/praxis"},"events":{"type":"array","items":{"type":"string"},"description":"Entity-change event types to deliver (free-form, e.g. `appointment.booked`).","example":["appointment.booked","location.updated"]},"is_active":{"type":"boolean","description":"Whether deliveries are active. Defaults to `true`.","example":true}},"example":{"name":"Brain delta sync","url":"https://brain.example/webhooks/praxis","events":["appointment.booked","location.updated"],"is_active":true}},"EnrollPracticeBody":{"type":"object","description":"The enrollment request: a `NexHealth` institution's `subdomain` + numeric id, and an optional\ndisplay name (defaults to the subdomain). `practice_id == subdomain` by convention.","required":["subdomain","external_institution_id"],"properties":{"subdomain":{"type":"string","description":"The `NexHealth` subdomain — the tenant key and the practice id.","example":"smile-makers-dental-care"},"external_institution_id":{"type":"integer","format":"int64","description":"The `NexHealth` institution id (`external_institution_id`).","example":22349},"display_name":{"type":["string","null"],"description":"Display name for the practice; defaults to the subdomain when omitted/blank.","example":"Smile Makers Dental Care"},"primary_email":{"type":["string","null"],"description":"The practice's primary email address. Optional; omitting it leaves any stored value alone.","example":"front-desk@smilemakers.example"},"escalation_phone":{"type":["string","null"],"description":"The practice's escalation phone, E.164. Optional; omitting it leaves any stored value alone.","example":"+15550001234"}},"example":{"subdomain":"smile-makers-dental-care","external_institution_id":22349,"display_name":"Smile Makers Dental Care"}},"EnrollPracticeResponse":{"type":"object","description":"Echoes the enrolled tenant back to the caller.","required":["practice_id","subdomain"],"properties":{"practice_id":{"type":"string","description":"The enrolled practice id (== subdomain).","example":"smile-makers-dental-care"},"subdomain":{"type":"string","description":"The `NexHealth` subdomain.","example":"smile-makers-dental-care"}},"example":{"practice_id":"smile-makers-dental-care","subdomain":"smile-makers-dental-care"}},"ErrorBody":{"type":"object","description":"Standard error response body.","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable agent error message","example":"that slot was just taken — pick another time"}},"example":{"error":"that slot was just taken — pick another time"}},"FeatureFlags":{"type":"object","description":"Per-practice feature toggles.\n\n**Every flag defaults to `false`** — a practice opts in, so an absent or unset flag always reads\nas off. This is the single source of truth for whether a paid feature is active for a practice:\na consumer gates on it rather than keeping its own per-tenant switch. Two switches that must\nagree is precisely how six practices ended up with nobody scheduling their reminders at all:\nConcierge had handed them off, the other service had not picked them up, and neither side was\nwrong because no single place said what was true.","properties":{"appointment_booking":{"type":"boolean","description":"AI-assisted appointment booking is enabled for this practice.","example":true},"insurance_verification":{"type":"boolean","description":"Insurance-verification workflows are enabled.","example":false},"outbound_reminders":{"type":"boolean","description":"Outbound appointment reminders (SMS/email) are enabled. Off unless the practice has the\noption; a consumer that schedules reminders MUST gate on this.","example":false}},"example":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false}},"GuarantorBalanceDto":{"type":"object","description":"One guarantor balance record.\n\nThere is no patient-level balance — balances are per guarantor and per location.\n`total_balance` is financial PHI-adjacent; a redacting `Debug` is hand-written.","required":["id","guarantor_id","location_id","total_balance"],"properties":{"id":{"type":"integer","format":"int64","description":"Guarantor-balance row id. Not PHI.","example":6601},"guarantor_id":{"type":"integer","format":"int64","description":"The guarantor this balance belongs to. Not PHI.","example":4410},"location_id":{"type":"integer","format":"int64","description":"The location the balance is scoped to. Not PHI.","example":318981},"total_balance":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"write_off_estimate":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"insurance_estimate":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"guarantor_portion":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"total_balance_under_30":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"total_balance_31_60":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"total_balance_61_90":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"total_balance_over_90":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"id":6601,"guarantor_id":4410,"location_id":318981,"total_balance":{"amount":"240.00","currency":"USD"}}},"InsurancePlan":{"type":"object","description":"Synced insurance-plan catalog entry. Projected from `insurance_plans`; no PHI (the plan\ncatalog, not a patient's coverage — patient insurance is live passthrough).","required":["id","updated_at"],"properties":{"id":{"type":"string","description":"The plan's native PMS id","example":"ins_88"},"name":{"type":["string","null"],"description":"Plan name; null when unset upstream","example":"PPO Premier"},"carrier":{"type":["string","null"],"description":"Carrier name; null when unset upstream","example":"Delta Dental"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"id":"ins_88","name":"PPO Premier","carrier":"Delta Dental","updated_at":"2026-06-16T12:00:00Z"}},"KnowledgeBundle":{"type":"object","description":"Assembled knowledge bundle: practice profile + locations + providers + services.","required":["client_id","institution","locations","providers","services"],"properties":{"client_id":{"type":"string","description":"Practice primary key — matches `institution.id`","example":"practice_abc"},"institution":{"type":"object","description":"Practice profile. Projected from `practices`; no PHI.","required":["id","display_name","default_language","updated_at","flags"],"properties":{"id":{"type":"string","description":"Practice primary key — the `client_id` used across all API responses","example":"practice_abc"},"display_name":{"type":"string","description":"Practice name shown in consumer UIs, e.g. \"Smile Bright Dental\"","example":"Smile Bright Dental"},"timezone_default":{"type":["string","null"],"description":"IANA timezone identifier, e.g. America/Chicago","example":"America/Chicago"},"default_language":{"type":"string","description":"BCP-47 language tag, e.g. en","example":"en"},"website":{"type":["string","null"],"description":"Practice public website URL; null when not configured","example":"https://smilebright.example"},"primary_email":{"type":["string","null"],"description":"Primary contact email for the practice; null when not configured","example":"hello@smilebright.example"},"escalation_phone":{"type":["string","null"],"description":"E.164 escalation phone number for urgent contact; null when not configured","example":"+15550001234"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"},"flags":{"type":"object","description":"Per-practice feature toggles.\n\n**Every flag defaults to `false`** — a practice opts in, so an absent or unset flag always reads\nas off. This is the single source of truth for whether a paid feature is active for a practice:\na consumer gates on it rather than keeping its own per-tenant switch. Two switches that must\nagree is precisely how six practices ended up with nobody scheduling their reminders at all:\nConcierge had handed them off, the other service had not picked them up, and neither side was\nwrong because no single place said what was true.","properties":{"appointment_booking":{"type":"boolean","description":"AI-assisted appointment booking is enabled for this practice.","example":true},"insurance_verification":{"type":"boolean","description":"Insurance-verification workflows are enabled.","example":false},"outbound_reminders":{"type":"boolean","description":"Outbound appointment reminders (SMS/email) are enabled. Off unless the practice has the\noption; a consumer that schedules reminders MUST gate on this.","example":false}},"example":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false}},"pms_ref":{"type":"object","description":"PMS crosswalk for a reference entity.\n\nMaps our opaque canonical `id` to the upstream practice-management-system record, so a consumer\ncan join our rows to a PMS-keyed replica (and recover the native id the command API expects).\nPresent only on PMS-synced rows; absent on practice-authored rows.","required":["system","external_id"],"properties":{"system":{"type":"string","description":"Upstream PMS identifier, e.g. \"NEXHEALTH\"","example":"NEXHEALTH"},"external_id":{"type":"string","description":"The entity's native primary id in the PMS, as a string (e.g. `NexHealth`'s numeric id)","example":"318981"},"foreign_id":{"type":["string","null"],"description":"EMR-level foreign id from the PMS's own crosswalk; null when absent","example":"A1001"},"foreign_id_type":{"type":["string","null"],"description":"Type qualifier for `foreign_id`, e.g. `open_dental`; null when absent","example":"open_dental"}},"example":{"system":"NEXHEALTH","external_id":"318981"}}},"example":{"id":"practice_abc","display_name":"Smile Bright Dental","timezone_default":"America/Chicago","default_language":"en","website":"https://smilebright.example","primary_email":"hello@smilebright.example","escalation_phone":"+15550001234","updated_at":"2026-06-16T12:00:00Z","flags":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false},"pms_ref":{"system":"NEXHEALTH","external_id":"20858"}}},"locations":{"type":"array","items":{"$ref":"#/components/schemas/Location"},"description":"All active and inactive locations for this practice"},"providers":{"type":"array","items":{"$ref":"#/components/schemas/Provider"},"description":"All providers linked to this practice across all locations"},"services":{"type":"array","items":{"$ref":"#/components/schemas/Service"},"description":"All appointment types / services offered by this practice"}},"example":{"client_id":"practice_abc","institution":{"id":"practice_abc","display_name":"Smile Bright Dental","timezone_default":"America/Chicago","default_language":"en","updated_at":"2026-06-16T12:00:00Z","flags":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false}},"locations":[],"providers":[],"services":[]}},"ListPatientsPageDto":{"type":"object","description":"One cursor-paginated page of patients (`GET /v1/clients/{client_id}/patients`).\n\nThere is no total count — only `end_cursor` and `has_next_page`.","required":["patients","has_next_page"],"properties":{"patients":{"type":"array","items":{"$ref":"#/components/schemas/PatientSummaryDto"},"description":"The patients on this page."},"end_cursor":{"type":["string","null"],"description":"Cursor to pass as `cursor` for the next page, when `has_next_page` is true. Not PHI.","example":"eyJpZCI6NDJ9"},"has_next_page":{"type":"boolean","description":"Whether another page exists after this one. Not PHI.","example":true}},"example":{"patients":[{"id":90211,"first_name":"Jordan","last_name":"Rivera","phone":"+15550001234","email":"jordan.rivera@example.com","dob":"1990-04-12","location_id":318981}],"end_cursor":"eyJpZCI6NDJ9","has_next_page":true}},"Location":{"type":"object","description":"Practice location. Projected from `practice_locations`; no PHI.","required":["id","name","address","timezone","phones","is_active","updated_at"],"properties":{"id":{"type":"string","description":"Opaque location primary key (stable across syncs)","example":"loc_1"},"name":{"type":"string","description":"Human-readable location name as configured in the practice management system","example":"Main Office"},"address":{"type":"object","description":"Physical address sub-object in a Location.","properties":{"street":{"type":["string","null"],"description":"Street address line, e.g. \"123 Main St\"","example":"123 Main St"},"city":{"type":["string","null"],"description":"City name","example":"Springfield"},"state":{"type":["string","null"],"description":"Two-letter US state abbreviation, e.g. \"IL\"","example":"IL"},"zip":{"type":["string","null"],"description":"Five-digit ZIP code","example":"62701"},"lat":{"type":["number","null"],"format":"double","description":"WGS-84 latitude in decimal degrees; null when geocoding is unavailable","example":39.78},"lng":{"type":["number","null"],"format":"double","description":"WGS-84 longitude in decimal degrees; null when geocoding is unavailable","example":-89.65}},"example":{"street":"123 Main St","city":"Springfield","state":"IL","zip":"62701","lat":39.78,"lng":-89.65}},"timezone":{"type":"string","description":"IANA timezone identifier, e.g. America/Chicago","example":"America/Chicago"},"phones":{"type":"array","items":{"type":"string"},"description":"E.164 phone numbers","example":["+15550001234"]},"is_active":{"type":"boolean","description":"Whether the location is currently accepting bookings","example":true},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"},"pms_ref":{"type":"object","description":"PMS crosswalk for a reference entity.\n\nMaps our opaque canonical `id` to the upstream practice-management-system record, so a consumer\ncan join our rows to a PMS-keyed replica (and recover the native id the command API expects).\nPresent only on PMS-synced rows; absent on practice-authored rows.","required":["system","external_id"],"properties":{"system":{"type":"string","description":"Upstream PMS identifier, e.g. \"NEXHEALTH\"","example":"NEXHEALTH"},"external_id":{"type":"string","description":"The entity's native primary id in the PMS, as a string (e.g. `NexHealth`'s numeric id)","example":"318981"},"foreign_id":{"type":["string","null"],"description":"EMR-level foreign id from the PMS's own crosswalk; null when absent","example":"A1001"},"foreign_id_type":{"type":["string","null"],"description":"Type qualifier for `foreign_id`, e.g. `open_dental`; null when absent","example":"open_dental"}},"example":{"system":"NEXHEALTH","external_id":"318981"}}},"example":{"id":"loc_1","name":"Main Office","address":{"street":"123 Main St","city":"Springfield","state":"IL","zip":"62701"},"timezone":"America/Chicago","phones":["+15550001234"],"is_active":true,"updated_at":"2026-06-16T12:00:00Z","pms_ref":{"system":"NEXHEALTH","external_id":"318981"}}},"LocationCount":{"type":"object","description":"One (location_id, count) pair in a usage breakdown — the primary billable axis (usage is metered per location). location_id is null when no single location is in scope.","required":["location_id","count"],"properties":{"location_id":{"type":["integer","null"],"format":"int64","description":"Location id the requests were made against, or null for requests not tied to a single location","example":318981},"count":{"type":"integer","format":"int64","description":"Number of patient-data requests for this location in the window","example":42}},"example":{"location_id":318981,"count":42}},"LocationRoleDto":{"type":"object","description":"A per-location role grant within a [`MemberDto`].","required":["location_id","role"],"properties":{"location_id":{"type":"string","description":"The location id the grant applies at.","example":"loc_5001"},"role":{"type":"string","description":"The member's role at this location.","example":"front_desk"}}},"MemberDto":{"type":"object","description":"One member: the Clerk user, an optional org-wide role, and any per-location role grants.","required":["clerk_user_id"],"properties":{"clerk_user_id":{"type":"string","description":"The Clerk user id.","example":"user_2abc..."},"role":{"type":["string","null"],"description":"The org-wide role, or omitted/null when the user has only per-location roles.","example":"org_owner"},"locations":{"type":"array","items":{"$ref":"#/components/schemas/LocationRoleDto"},"description":"Per-location role grants (each location must be one the practice owns)."}}},"Operatory":{"type":"object","description":"Synced operatory (chair / room). Projected from `operatories`; no PHI.","required":["id","is_active","updated_at"],"properties":{"id":{"type":"string","description":"The operatory's native PMS id (e.g. `NexHealth` operatory id)","example":"1201"},"location_id":{"type":["string","null"],"description":"The PMS location id this operatory belongs to; null when unscoped","example":"5001"},"name":{"type":["string","null"],"description":"Operatory display name, e.g. \"Op 1\"; null when unset upstream","example":"Op 1"},"is_active":{"type":"boolean","description":"Whether the operatory is currently active","example":true},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"id":"1201","location_id":"5001","name":"Op 1","is_active":true,"updated_at":"2026-06-16T12:00:00Z"}},"PatientAlertDto":{"type":"object","description":"A clinical/administrative alert attached to a patient record.\n\n`message` may carry PHI (e.g. allergy detail); a redacting `Debug` is hand-written.","required":["id","message"],"properties":{"id":{"type":"integer","format":"int64","description":"Alert id. Not PHI.","example":4471},"message":{"type":"string","description":"Alert message text. PHI-adjacent.","example":"Severe penicillin allergy"},"severity":{"type":["string","null"],"description":"Severity level string (e.g. `\"high\"`), when present. Not PHI.","example":"high"},"disabled_at":{"type":["string","null"],"description":"When the alert was disabled/archived; `null` ⇒ active. Filter active alerts on this. Not PHI."},"created_at":{"type":["string","null"],"description":"ISO-8601 creation timestamp, when present. Not PHI."},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"id":4471,"message":"Severe penicillin allergy","severity":"high","disabled_at":null}},"PatientAppointmentDto":{"type":"object","description":"One appointment entry from a patient's appointment history (times, ids, status, and context).\n\n`note` is free-text and may carry PHI; a redacting `Debug` is hand-written.","required":["appointment_id","location_id","start_time","end_time","status"],"properties":{"appointment_id":{"type":"integer","format":"int64","description":"Appointment id.","example":778812},"location_id":{"type":"integer","format":"int64","description":"Location the appointment is booked at.","example":318981},"provider_id":{"type":["integer","null"],"format":"int64","description":"Provider the appointment is booked with, when present.","example":521},"appointment_type_id":{"type":["integer","null"],"format":"int64","description":"Appointment-type (service) id, when present.","example":84},"start_time":{"type":"string","format":"date-time","description":"Appointment start time (RFC-3339).","example":"2026-07-01T15:00:00Z"},"end_time":{"type":"string","format":"date-time","description":"Appointment end time (RFC-3339).","example":"2026-07-01T15:30:00Z"},"status":{"type":"string","description":"The appointment's status (e.g. `\"Booked\"`, `\"Cancelled\"`).","example":"Booked"},"operatory_id":{"type":["integer","null"],"format":"int64","description":"Operatory (room) the appointment is booked in, when present.","example":3},"provider_name":{"type":["string","null"],"description":"Provider display name, when present (staff name — not patient PHI).","example":"Dr. Ada Byron"},"note":{"type":["string","null"],"description":"Free-text appointment note, when present. PHI-adjacent."},"timezone":{"type":["string","null"],"description":"IANA timezone name for the appointment time, when present.","example":"America/New_York"},"timezone_offset":{"type":["string","null"],"description":"UTC offset for the appointment time (e.g. `\"-04:00\"`), when present."},"cancelled_at":{"type":["string","null"],"description":"When the appointment was cancelled, when present."},"confirmed_at":{"type":["string","null"],"description":"When the appointment was confirmed, when present."},"checked_out_at":{"type":["string","null"],"description":"When the patient was checked out, when present."},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present."}},"example":{"appointment_id":778812,"location_id":318981,"provider_id":521,"appointment_type_id":84,"start_time":"2026-07-01T15:00:00Z","end_time":"2026-07-01T15:30:00Z","status":"Booked","operatory_id":3,"provider_name":"Dr. Ada Byron","timezone":"America/New_York"}},"PatientBalanceDto":{"type":"object","description":"The balance read body: the guarantor balances for the location.","required":["guarantor_balances"],"properties":{"guarantor_balances":{"type":"array","items":{"$ref":"#/components/schemas/GuarantorBalanceDto"},"description":"The guarantor balance rows for this location."}},"example":{"guarantor_balances":[{"id":6601,"guarantor_id":4410,"location_id":318981,"total_balance":{"amount":"240.00","currency":"USD"}}]}},"PatientDetailDto":{"type":"object","description":"A full patient record (`GET /v1/clients/{client_id}/patients/{patient_id}`).\n\nPatient data is fetched live from the practice management system on each request and is not\nstored by this service.","required":["id","location_ids"],"properties":{"id":{"type":"integer","format":"int64","description":"Patient id. Not PHI.","example":90211},"first_name":{"type":["string","null"],"description":"Patient first name. PHI.","example":"Jordan"},"last_name":{"type":["string","null"],"description":"Patient last name. PHI.","example":"Rivera"},"phone":{"type":["string","null"],"description":"Patient phone number. PHI.","example":"+15550001234"},"email":{"type":["string","null"],"description":"Patient email address. PHI.","example":"jordan.rivera@example.com"},"dob":{"type":["string","null"],"description":"Date of birth (`YYYY-MM-DD`). PHI.","example":"1990-04-12"},"gender":{"type":["string","null"],"description":"Patient gender (`Male`/`Female`/`Other`), when present. PHI-adjacent.","example":"Female"},"location_ids":{"type":"array","items":{"type":"integer","format":"int64"},"description":"All location ids this patient is registered at. Not PHI. (The whole-object\n`#[schema(example = …)]` above carries the array example; a per-field array example trips\nvacuum's `oas3-valid-schema-example` array-vs-items check, so it is intentionally omitted.)"}},"example":{"id":90211,"first_name":"Jordan","last_name":"Rivera","phone":"+15550001234","email":"jordan.rivera@example.com","dob":"1990-04-12","location_ids":[318981]}},"PatientDocumentDto":{"type":"object","description":"One document record attached to a patient.\n\n`filename`/`url` are PHI-adjacent (filenames embed patient text; the URL is a live document\nhandle), so a hand-written `Debug` redacts them; `id`/`document_type`/`created_at` are safe.","required":["id"],"properties":{"id":{"type":"integer","format":"int64","description":"Document id.","example":7781},"document_type":{"type":["string","null"],"description":"Document type label (e.g. `\"X-Ray\"`, `\"Consent Form\"`), when present.","example":"X-Ray"},"filename":{"type":["string","null"],"description":"Original filename, when present. PHI-adjacent.","example":"xray-2026-05-12.pdf"},"url":{"type":["string","null"],"description":"Download URL, when present. PHI-adjacent.","example":"https://nexhealth.example/doc/7781"},"created_at":{"type":["string","null"],"format":"date-time","description":"ISO-8601 timestamp when the document was created, when present.","example":"2026-05-12T18:03:00Z"}},"example":{"id":7781,"document_type":"X-Ray","created_at":"2026-05-12T18:03:00Z"}},"PatientInsuranceDto":{"type":"object","description":"One insurance coverage record for a patient.\n\n`member_id`/`group_id` are PHI-adjacent (they can uniquely identify a person); a redacting\n`Debug` is hand-written. The plan name is not PHI and is shown.","required":["id"],"properties":{"id":{"type":"integer","format":"int64","description":"Patient-insurance record id. Not PHI.","example":5521},"insurance_plan_name":{"type":["string","null"],"description":"Insurance plan name (e.g. `\"Delta Dental PPO\"`). Not PHI.","example":"Delta Dental PPO"},"member_id":{"type":["string","null"],"description":"Insurance member id. PHI-adjacent.","example":"DD123456789"},"group_id":{"type":["string","null"],"description":"Insurance group id. PHI-adjacent.","example":"GRP-4410"},"active":{"type":["boolean","null"],"description":"Whether the coverage is active. Not PHI.","example":true},"priority":{"type":["integer","null"],"format":"int64","description":"Coverage priority (1 = primary). Not PHI.","example":1},"subscription_relation":{"type":["string","null"],"description":"Enrollee's relationship to the subscriber (e.g. `\"self\"`, `\"spouse\"`). Not PHI.","example":"self"},"plan_id":{"type":["integer","null"],"format":"int64","description":"FK to the insurance plan (correlate via the insurance-plans read). Not PHI.","example":8841},"insurance_type":{"type":["string","null"],"description":"Plan type (e.g. `\"dental\"`, `\"medical\"`). Not PHI.","example":"dental"},"effective_date":{"type":["string","null"],"description":"Coverage activation date (`YYYY-MM-DD`), when present. Not PHI."},"expiration_date":{"type":["string","null"],"description":"Coverage end date (`YYYY-MM-DD`), when present. Not PHI."},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"id":5521,"insurance_plan_name":"Delta Dental PPO","member_id":"DD123456789","group_id":"GRP-4410"}},"PatientRecallDto":{"type":"object","description":"One recall record for a patient (scheduling metadata: due date + recall type).","required":["id"],"properties":{"id":{"type":"integer","format":"int64","description":"Recall id.","example":9912},"due_date":{"type":["string","null"],"format":"date","description":"Date this recall is due (`YYYY-MM-DD`), when present.","example":"2026-09-01"},"recall_type":{"type":["string","null"],"description":"Recall type label (e.g. `\"Prophy\"`, `\"Perio\"`), when present.","example":"Prophy"}},"example":{"id":9912,"due_date":"2026-09-01","recall_type":"Prophy"}},"PatientSummaryDto":{"type":"object","description":"One patient summary in a list/lookup result.\n\nPatient data is fetched live from the practice management system on each request and is not\nstored by this service.","required":["id"],"properties":{"id":{"type":"integer","format":"int64","description":"Patient id. Not PHI.","example":90211},"first_name":{"type":["string","null"],"description":"Patient first name. PHI.","example":"Jordan"},"last_name":{"type":["string","null"],"description":"Patient last name. PHI.","example":"Rivera"},"phone":{"type":["string","null"],"description":"Patient phone number. PHI.","example":"+15550001234"},"email":{"type":["string","null"],"description":"Patient email address. PHI.","example":"jordan.rivera@example.com"},"dob":{"type":["string","null"],"description":"Date of birth (`YYYY-MM-DD`). PHI.","example":"1990-04-12"},"location_id":{"type":["integer","null"],"format":"int64","description":"Location id this record is scoped to. Not PHI.","example":318981}},"example":{"id":90211,"first_name":"Jordan","last_name":"Rivera","phone":"+15550001234","email":"jordan.rivera@example.com","dob":"1990-04-12","location_id":318981}},"PaymentRecordDto":{"type":"object","description":"One payment history entry.\n\n`payment_amount` is financial PHI-adjacent; a redacting `Debug` is hand-written.","required":["payment_id","payment_amount"],"properties":{"payment_id":{"type":"integer","format":"int64","description":"Payment id. Not PHI.","example":9001},"payment_amount":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}},"paid_at":{"type":["string","null"],"format":"date","description":"Date the payment was made, when present.","example":"2026-05-12"},"payment_type_id":{"type":["integer","null"],"format":"int64","description":"Payment-type id, when present. Not PHI.","example":3},"location_id":{"type":["integer","null"],"format":"int64","description":"Location the payment was recorded at, when present. Not PHI."},"guarantor_id":{"type":["integer","null"],"format":"int64","description":"Guarantor the payment applies to, when present. Not PHI."},"provider_id":{"type":["integer","null"],"format":"int64","description":"Provider who received the payment, when present. Not PHI."},"charge_id":{"type":["integer","null"],"format":"int64","description":"Charge this payment is applied to, when present. Not PHI."},"claim_id":{"type":["integer","null"],"format":"int64","description":"Insurance claim this payment is linked to, when present. Not PHI."},"payment_plan_id":{"type":["integer","null"],"format":"int64","description":"Payment plan this payment belongs to, when present. Not PHI."},"insurance_plan_id":{"type":["integer","null"],"format":"int64","description":"Insurance plan the payment is associated with, when present. Not PHI."},"description":{"type":["string","null"],"description":"Free-text payment description, when present. PHI-adjacent."},"transaction_id":{"type":["string","null"],"description":"External payment-processor transaction id, when present. Not PHI."},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"payment_id":9001,"payment_amount":{"amount":"138.00","currency":"USD"},"paid_at":"2026-05-12","payment_type_id":3}},"PmsEvent":{"type":"object","description":"One non-PHI event-bus row: a signal that a `NexHealth` resource event occurred, carrying only the\nopaque resource id (fetch detail live) — no patient linkage or content.","required":["id","resource_type","event","external_id","location_id","received_at"],"properties":{"id":{"type":"integer","format":"int64","description":"Monotonic cursor id — pass the largest one in a page back as `?after=` to continue.","example":4210},"resource_type":{"type":"string","description":"`NexHealth` resource type, e.g. Payment / `TreatmentPlan` / `FormResponse`.","example":"Payment"},"event":{"type":"string","description":"The webhook event name, e.g. `payment_created`.","example":"payment_created"},"external_id":{"type":["string","null"],"description":"The resource's own opaque `NexHealth` id — fetch its detail live-passthrough — or null.","example":"99001"},"location_id":{"type":["string","null"],"description":"The resource's location id, or null.","example":"318981"},"received_at":{"type":"string","format":"date-time","description":"RFC-3339 time the Data Service received the event.","example":"2026-07-22T10:00:00Z"}},"example":{"id":4210,"resource_type":"Payment","event":"payment_created","external_id":"99001","location_id":"318981","received_at":"2026-07-22T10:00:00Z"}},"PmsEventsEnvelope":{"type":"object","description":"A page of the `pms_events` bus plus the cursor to continue.","required":["events","next_after"],"properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/PmsEvent"},"description":"The events in this page, ascending by `id`."},"next_after":{"type":"integer","format":"int64","description":"The largest `id` in this page — pass back as `?after=` to continue. Equals the request's\n`after` when the page is empty (no new events yet).","example":4210}},"example":{"events":[{"id":4210,"resource_type":"Payment","event":"payment_created","external_id":"99001","location_id":"318981","received_at":"2026-07-22T10:00:00Z"}],"next_after":4210}},"Provider":{"type":"object","description":"Practice provider / clinician. Projected from `practice_providers`; no PHI.","required":["id","name","location_ids","is_active","updated_at"],"properties":{"id":{"type":"string","description":"Opaque provider primary key (stable across syncs)","example":"prov_1"},"name":{"type":"string","description":"Full legal name as stored in the practice management system","example":"Dr. Jane Smith"},"display_name":{"type":["string","null"],"description":"Preferred display name shown to patients; falls back to `name` when null","example":"Dr. Smith"},"role":{"type":["string","null"],"description":"Clinical role, e.g. \"Dentist\" or \"Hygienist\"; null when unset","example":"Dentist"},"location_ids":{"type":"array","items":{"type":"string"},"description":"Location IDs this provider is associated with","example":["loc_1"]},"is_active":{"type":"boolean","description":"Whether the provider is currently accepting bookings","example":true},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"},"pms_ref":{"type":"object","description":"PMS crosswalk for a reference entity.\n\nMaps our opaque canonical `id` to the upstream practice-management-system record, so a consumer\ncan join our rows to a PMS-keyed replica (and recover the native id the command API expects).\nPresent only on PMS-synced rows; absent on practice-authored rows.","required":["system","external_id"],"properties":{"system":{"type":"string","description":"Upstream PMS identifier, e.g. \"NEXHEALTH\"","example":"NEXHEALTH"},"external_id":{"type":"string","description":"The entity's native primary id in the PMS, as a string (e.g. `NexHealth`'s numeric id)","example":"318981"},"foreign_id":{"type":["string","null"],"description":"EMR-level foreign id from the PMS's own crosswalk; null when absent","example":"A1001"},"foreign_id_type":{"type":["string","null"],"description":"Type qualifier for `foreign_id`, e.g. `open_dental`; null when absent","example":"open_dental"}},"example":{"system":"NEXHEALTH","external_id":"318981"}}},"example":{"id":"prov_1","name":"Dr. Jane Smith","display_name":"Dr. Smith","role":"Dentist","location_ids":["loc_1"],"is_active":true,"updated_at":"2026-06-16T12:00:00Z","pms_ref":{"system":"NEXHEALTH","external_id":"521"}}},"ProviderRoleBody":{"type":"object","description":"A provider's clinical role for role-scoped scheduling constraints. `null` clears it.","properties":{"role":{"type":["string","null"],"description":"The provider's clinical role (e.g. \"Hygienist\", \"Dentist\"); `null` clears it. Matched\ncase-insensitively by a service's `required_provider_role` constraint.","default":null}},"additionalProperties":false,"example":{"role":"Hygienist"}},"RecallType":{"type":"object","description":"Synced recall type. Projected from `recall_types`; no PHI.","required":["id","updated_at"],"properties":{"id":{"type":"string","description":"The recall type's native PMS id","example":"rc_5"},"name":{"type":["string","null"],"description":"Recall type name, e.g. \"Prophy\"; null when unset upstream","example":"Prophy"},"default_interval_months":{"type":["integer","null"],"format":"int32","description":"Default recall interval in months; null when unset upstream","example":6},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"id":"rc_5","name":"Prophy","default_interval_months":6,"updated_at":"2026-06-16T12:00:00Z"}},"RecordPaymentRequest":{"type":"object","description":"Request body for POST /v1/clients/{client_id}/patients/{patient_id}/payments.","required":["location_id","amount","transaction_id"],"properties":{"location_id":{"type":"integer","format":"int64","description":"Location the payment is recorded against (the practice management system's location id).","example":318981},"amount":{"type":"string","description":"Payment amount as a **positive** decimal string (e.g. `\"138.00\"`). Rejected (422) if not a\npositive number — the service never records a silent zero or a sign-flipped charge.","example":"138.00"},"currency":{"type":["string","null"],"description":"ISO currency code. Defaults to `\"USD\"` when omitted.","example":"USD"},"transaction_id":{"type":"string","description":"Caller-supplied idempotency key (`NexHealth` `transaction_id`, max 50 characters).","example":"pos-7f3a9c01"},"payment_type_id":{"type":["integer","null"],"format":"int64","description":"Optional practice-management payment-type id (e.g. cash / card / cheque).","example":3}},"example":{"location_id":318981,"amount":"138.00","currency":"USD","transaction_id":"pos-7f3a9c01","payment_type_id":3}},"RecordPaymentResponse":{"type":"object","description":"The recorded-payment body: the upstream payment id + the amount `NexHealth` echoed back.","required":["payment_id","payment_amount"],"properties":{"payment_id":{"type":"integer","format":"int64","description":"The payment id assigned by the practice management system. Not PHI.","example":9001},"payment_amount":{"type":"object","description":"A monetary value: a decimal **string** amount plus a currency code.\n\nThe amount is a string (not a float) so no rounding is introduced.","required":["amount","currency"],"properties":{"amount":{"type":"string","description":"Decimal amount as a string (e.g. `\"138.00\"`).","example":"138.00"},"currency":{"type":"string","description":"ISO currency code (e.g. `\"USD\"`).","example":"USD"}},"example":{"amount":"138.00","currency":"USD"}}},"example":{"payment_id":9001,"payment_amount":{"amount":"138.00","currency":"USD"}}},"RescheduleAppointmentRequest":{"type":"object","description":"Request body for POST /v1/clients/{client_id}/appointments/{id}/reschedule.","required":["new_start_time"],"properties":{"new_start_time":{"type":"string","format":"date-time","description":"RFC-3339 start time. `NexHealth`'s PATCH keeps the appointment's existing duration, so no end\nis accepted — the booked (and fenced) window is always `[new_start, new_start + existing_duration]`.","example":"2026-07-02T16:00:00Z"},"provider_id":{"type":["integer","null"],"format":"int64","description":"Reschedule cannot change the provider (`NexHealth`'s PATCH has no `provider_id`). Supplying a\nvalue is rejected with 422 — omit it and the appointment keeps its current provider; to move\nproviders, cancel and rebook. Shown without an example so generated SDK snippets do not\ndemonstrate a request guaranteed to 422."},"location_id":{"type":["integer","null"],"format":"int64","description":"Target location for the new slot. When supplied, the server checks the new window\nagainst current availability and rejects it with 409 Conflict if the slot is no longer\nfree; omit it and only the window shape is validated. Optional, but recommended so the\navailability check runs.","example":318981},"office_hours":{"type":["object","null"],"description":"The caller's own weekly office hours — the same authoritative blob the MCP tools accept.\n\nA practice's real weekly hours are ADMIN-authored in the caller's store; the synced `NexHealth`\nrows are per-provider availability and assert no closure at all (ITS-512). Supplied, this\nREPLACES the synced weekly base and its omitted weekdays count as closed, so a write onto a day\nthe practice is shut is refused pre-PMS. Omitted (or not hours-shaped) leaves the synced rows as\nthe only weekly signal. Shape: `{\"monday\": {\"open\": \"09:00\", \"close\": \"17:00\"}, \"tuesday\":\n\"closed\", ...}` — full / 3-letter / numeric day keys, an optional `ranges` list for split days,\nan optional `regular` nesting."},"notify_patient":{"type":"boolean","description":"Whether the PMS should notify the patient of the new time. Defaults to `true` when omitted.\nSet it to `false` when the caller sends its own notification, so the patient is not told\ntwice (ITS-478 follow-up).","example":true}},"example":{"new_start_time":"2026-07-02T16:00:00Z","location_id":318981}},"RescheduleAppointmentResponse":{"type":"object","description":"Response body for a successful appointment reschedule.","required":["appointment_id","status","start_time","end_time"],"properties":{"appointment_id":{"type":"integer","format":"int64","description":"Id of the rescheduled appointment — echoed from the path parameter for client convenience.","example":778812},"status":{"type":"string","description":"The appointment's status after rescheduling (FHIR `Appointment.status`, kebab-case).","example":"booked"},"start_time":{"type":"string","format":"date-time","description":"RFC-3339 start time","example":"2026-07-02T16:00:00Z"},"end_time":{"type":"string","format":"date-time","description":"RFC-3339 end time","example":"2026-07-02T16:30:00Z"}},"example":{"appointment_id":778812,"status":"booked","start_time":"2026-07-02T16:00:00Z","end_time":"2026-07-02T16:30:00Z"}},"RoutingConfig":{"type":"object","description":"DID/subdomain to client routing slice. No PHI.","required":["client_id","subdomain","system","default_location_id","is_enabled","locations"],"properties":{"client_id":{"type":"string","description":"Practice primary key the routing config belongs to","example":"practice_abc"},"subdomain":{"type":"string","description":"Practice subdomain","example":"smilebright"},"system":{"type":"string","description":"Practice management system identifier, e.g. NEXHEALTH","example":"NEXHEALTH"},"default_location_id":{"type":["integer","null"],"format":"int64","description":"Default location id, or null","example":318981},"is_enabled":{"type":"boolean","description":"Whether the practice management system connection is enabled","example":true},"locations":{"type":"array","items":{"$ref":"#/components/schemas/RoutingLocation"},"description":"Routable locations available under this practice management system connection"}},"example":{"client_id":"practice_abc","subdomain":"smilebright","system":"NEXHEALTH","default_location_id":318981,"is_enabled":true,"locations":[{"id":"318981","name":"Main Office"}]}},"RoutingLocation":{"type":"object","description":"One location a consumer can route to.","required":["id","name"],"properties":{"id":{"type":"string","description":"Location id as a string (cast from i64 for JSON consistency)","example":"318981"},"name":{"type":"string","description":"Human-readable location name used by routing logic","example":"Main Office"}},"example":{"id":"318981","name":"Main Office"}},"SchedulingConstraintsBody":{"type":"object","description":"Per-service scheduling-constraint policy. PUT replaces the whole policy; omitted fields reset to unconstrained. Unknown keys are rejected.","properties":{"min_notice_hours":{"type":["integer","null"],"format":"int32","description":"Minimum advance-booking window in hours; null = no minimum.","default":null,"minimum":0},"available_days":{"type":"array","items":{"type":"integer","format":"int32","maximum":7,"minimum":1},"description":"ISO weekdays the service is offered (1 = Mon … 7 = Sun); empty = any day.","default":[]},"provider_ids":{"type":"array","items":{"type":"string"},"description":"Canonical provider-id allowlist; empty = any provider.","default":[]},"required_provider_role":{"type":["string","null"],"description":"Restrict to providers of this clinical role (e.g. \"Hygienist\"); null = no role gate. The role\nmust match ≥1 active provider (populated via `PUT .../providers/{id}/role`, ITS-348) or the\nwrite is rejected 422 — a role no provider holds would fail closed and block all availability.","default":null},"operatory_ids":{"type":"array","items":{"type":"integer","format":"int64"},"description":"`NexHealth` operatory-id allowlist (availability only); empty = any operatory.","default":[]},"working_hour_label_id":{"type":["string","null"],"description":"`NexHealth` working-hour-label scoping for availability; null = no scoping.","default":null},"requires_sedation":{"type":"boolean","description":"Whether the service requires sedation (advisory; not enforced).","default":false}},"additionalProperties":false,"example":{"min_notice_hours":24,"available_days":[2,4],"provider_ids":["sub_prv_521"],"operatory_ids":[11,22],"working_hour_label_id":"7","requires_sedation":false}},"Service":{"type":"object","description":"Practice service / appointment type. Projected from `practice_services`; no PHI.","required":["id","name","default_duration_min","bookable_online","location_ids","scheduling_constraints","is_active","updated_at"],"properties":{"id":{"type":"string","description":"Opaque service / appointment-type primary key (stable across syncs)","example":"svc_1"},"name":{"type":"string","description":"Human-readable service name shown to patients, e.g. \"New Patient Exam\"","example":"New Patient Exam"},"category":{"type":["string","null"],"description":"Grouping category for the service, e.g. \"Preventive\"; null when uncategorized","example":"Preventive"},"description":{"type":["string","null"],"description":"Long-form description displayed in booking UIs; null when absent","example":"Comprehensive first-visit exam"},"default_duration_min":{"type":"integer","format":"int32","description":"Default appointment duration in minutes as configured in the practice management system","example":60},"bookable_online":{"type":"boolean","description":"Whether patients may book this service through an online channel","example":true},"location_ids":{"type":"array","items":{"type":"string"},"description":"Location IDs that offer this service","example":["loc_1"]},"scheduling_constraints":{"type":"object","description":"Scheduling constraints blob: `provider_ids`, `available_days`, `min_notice_hours`,\n`pre_op_instructions`, etc."},"is_active":{"type":"boolean","description":"Whether this service is currently offered / visible to consumers","example":true},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"},"pms_ref":{"type":"object","description":"PMS crosswalk for a reference entity.\n\nMaps our opaque canonical `id` to the upstream practice-management-system record, so a consumer\ncan join our rows to a PMS-keyed replica (and recover the native id the command API expects).\nPresent only on PMS-synced rows; absent on practice-authored rows.","required":["system","external_id"],"properties":{"system":{"type":"string","description":"Upstream PMS identifier, e.g. \"NEXHEALTH\"","example":"NEXHEALTH"},"external_id":{"type":"string","description":"The entity's native primary id in the PMS, as a string (e.g. `NexHealth`'s numeric id)","example":"318981"},"foreign_id":{"type":["string","null"],"description":"EMR-level foreign id from the PMS's own crosswalk; null when absent","example":"A1001"},"foreign_id_type":{"type":["string","null"],"description":"Type qualifier for `foreign_id`, e.g. `open_dental`; null when absent","example":"open_dental"}},"example":{"system":"NEXHEALTH","external_id":"318981"}}},"example":{"id":"svc_1","name":"New Patient Exam","category":"Preventive","description":"Comprehensive first-visit exam","default_duration_min":60,"bookable_online":true,"location_ids":["loc_1"],"scheduling_constraints":{"min_notice_hours":24},"is_active":true,"updated_at":"2026-06-16T12:00:00Z","pms_ref":{"system":"NEXHEALTH","external_id":"84"}}},"SetFeatureFlagsBody":{"type":"object","description":"The complete desired flag set. Omitted flags are off; unknown keys are rejected with a 422.","properties":{"appointment_booking":{"type":"boolean","description":"AI-assisted appointment booking is enabled for this practice.","default":false,"example":true},"insurance_verification":{"type":"boolean","description":"Insurance-verification workflows are enabled.","default":false,"example":false},"outbound_reminders":{"type":"boolean","description":"Outbound appointment reminders (SMS/email) are enabled.","default":false,"example":false}},"additionalProperties":false,"example":{"appointment_booking":true,"insurance_verification":false,"outbound_reminders":false}},"SmsMessageDto":{"type":"object","description":"One SMS message from a patient's conversation thread.\n\n`body` is free-text patient input and is therefore PHI; a redacting `Debug` is hand-written.\n`direction` and `sent_at` are not PHI.","required":["id"],"properties":{"id":{"type":"integer","format":"int64","description":"SMS message id. Not PHI.","example":33120},"direction":{"type":["string","null"],"description":"Message direction (`\"inbound\"` or `\"outbound\"`). Not PHI.","example":"inbound"},"body":{"type":["string","null"],"description":"Message body text. PHI.","example":"Running 10 minutes late"},"sent_at":{"type":["string","null"],"format":"date-time","description":"ISO-8601 timestamp when the message was sent, when present. Not PHI.","example":"2026-07-01T14:50:00Z"},"delivery_status":{"type":["string","null"],"description":"Delivery state (e.g. `\"delivered\"`, `\"failed\"`), when present. Not PHI.","example":"delivered"},"error_description":{"type":["string","null"],"description":"Delivery error reason (e.g. `\"recipient has unsubscribed\"`), when present. System text, not PHI.","example":"recipient has unsubscribed"}},"example":{"id":33120,"direction":"inbound","body":"Running 10 minutes late","sent_at":"2026-07-01T14:50:00Z"}},"SyncMembersBody":{"type":"object","description":"The replace-set request body: the practice's COMPLETE member set. An empty `members` clears it.","required":["members"],"properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/MemberDto"},"description":"Every member of the practice. Sending the full set each time is intentional — the Data Service\nreplaces the projection wholesale (last-write-wins).","example":[{"clerk_user_id":"user_2abc...","role":"org_owner","locations":[{"location_id":"loc_5001","role":"front_desk"}]}]}},"example":{"members":[{"clerk_user_id":"user_2abc...","role":"org_owner","locations":[{"location_id":"loc_5001","role":"front_desk"}]}]}},"SyncMembersResponse":{"type":"object","description":"The row counts written, echoed for the caller to reconcile against.","required":["members","location_scopes"],"properties":{"members":{"type":"integer","description":"Members written (`practice_members` rows).","example":1,"minimum":0},"location_scopes":{"type":"integer","description":"Per-location grants written (`member_location_scope` rows).","example":1,"minimum":0}},"example":{"members":1,"location_scopes":1}},"TreatmentPlanDto":{"type":"object","description":"One treatment plan entry (name + status + procedure count).","required":["plan_id","status","procedure_count"],"properties":{"plan_id":{"type":"integer","format":"int64","description":"Treatment-plan id.","example":2201},"name":{"type":["string","null"],"description":"Plan name, when present.","example":"Crown + root canal"},"status":{"type":"string","description":"The plan's status, as reported by the practice management system.","example":"Proposed"},"procedure_count":{"type":"integer","description":"Number of procedures on the plan.","example":3,"minimum":0},"updated_at":{"type":["string","null"],"description":"ISO-8601 last-modified timestamp, when present. Not PHI."}},"example":{"plan_id":2201,"name":"Crown + root canal","status":"Proposed","procedure_count":3}},"UpdateWebhookSubscriptionBody":{"type":"object","description":"Partial-update request body. Every field is optional; an omitted (or `null`) field is left\nunchanged. An empty `events` array explicitly clears the event set.","properties":{"name":{"type":["string","null"],"description":"New label, or omit/`null` to leave unchanged.","example":"Brain delta sync (paused)"},"url":{"type":["string","null"],"description":"New delivery endpoint (`http`/`https`), or omit/`null` to leave unchanged.","example":"https://brain.example/webhooks/praxis"},"events":{"type":["array","null"],"items":{"type":"string"},"description":"Replacement event set; an empty array clears it; omit/`null` to leave unchanged.","example":["appointment.booked"]},"is_active":{"type":["boolean","null"],"description":"Toggle delivery on/off, or omit/`null` to leave unchanged.","example":false}},"example":{"name":"Brain delta sync (paused)","is_active":false}},"UsageBreakdown":{"type":"object","description":"Per-window usage aggregate: patient-data request counts grouped by location_id (the per-location billable axis — usage is metered per location) and by action (operational). No PHI.","required":["window","total","by_location","by_action"],"properties":{"window":{"type":"object","description":"The half-open [from, to) window a usage aggregate counted against.","required":["from","to"],"properties":{"from":{"type":"string","format":"date-time","description":"RFC-3339 window start (inclusive)","example":"2026-06-01T00:00:00Z"},"to":{"type":"string","format":"date-time","description":"RFC-3339 window end (exclusive)","example":"2026-07-01T00:00:00Z"}},"example":{"from":"2026-06-01T00:00:00Z","to":"2026-07-01T00:00:00Z"}},"total":{"type":"integer","format":"int64","description":"Total patient-data requests in the window","example":59},"by_location":{"type":"array","items":{"$ref":"#/components/schemas/LocationCount"},"description":"Request counts grouped by location — the per-location billable axis"},"by_action":{"type":"array","items":{"$ref":"#/components/schemas/ActionCount"},"description":"Request counts grouped by action — the operational breakdown axis"}},"example":{"window":{"from":"2026-06-01T00:00:00Z","to":"2026-07-01T00:00:00Z"},"total":59,"by_location":[{"location_id":318981,"count":42}],"by_action":[{"action":"BOOK","count":17}]}},"WebhookSubscriptionListResponse":{"type":"object","description":"List response.","required":["items","total_count"],"properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscriptionResponse"},"description":"The practice's webhook subscriptions."},"total_count":{"type":"integer","description":"Total number of subscriptions returned.","example":0,"minimum":0}},"example":{"items":[],"total_count":0}},"WebhookSubscriptionResponse":{"type":"object","description":"A webhook subscription as returned by the API.","required":["id","practice_id","name","url","events","secret","is_active","created_at","updated_at"],"properties":{"id":{"type":"string","description":"Server-assigned subscription id (UUID).","example":"8f3b2c1a-0d4e-4a6b-9c2d-1e2f3a4b5c6d"},"practice_id":{"type":"string","description":"Owning practice / client id.","example":"practice_abc"},"name":{"type":"string","description":"Human-readable label for the subscription.","example":"Brain delta sync"},"url":{"type":"string","description":"Delivery endpoint (`http`/`https`).","example":"https://brain.example/webhooks/praxis"},"events":{"type":"array","items":{"type":"string"},"description":"Entity-change event types delivered to this subscription.","example":["appointment.booked"]},"secret":{"type":"string","description":"Delivery-signing secret (server-generated). Returned on every read.","example":"whsec_3f9a..."},"is_active":{"type":"boolean","description":"Whether deliveries are currently active.","example":true},"created_at":{"type":"string","format":"date-time","description":"RFC-3339 creation timestamp.","example":"2026-06-16T12:00:00Z"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 last-update timestamp.","example":"2026-06-16T12:00:00Z"}},"example":{"id":"8f3b2c1a-0d4e-4a6b-9c2d-1e2f3a4b5c6d","practice_id":"practice_abc","name":"Brain delta sync","url":"https://brain.example/webhooks/praxis","events":["appointment.booked"],"secret":"whsec_3f9a...","is_active":true,"created_at":"2026-06-16T12:00:00Z","updated_at":"2026-06-16T12:00:00Z"}},"WorkingHour":{"type":"object","description":"Synced working-hours row (one weekday for a location). Projected from `working_hours`; no PHI.","required":["id","weekday","is_open","ranges","is_recurring","appointment_type_ids","updated_at"],"properties":{"id":{"type":"string","description":"The working-hours row's native PMS id","example":"wh_9001"},"location_id":{"type":["string","null"],"description":"The PMS location id these hours apply to; null when unscoped","example":"5001"},"weekday":{"type":"integer","format":"int32","description":"ISO-8601 day of week (1 = Monday … 7 = Sunday)","example":1},"is_open":{"type":"boolean","description":"Whether the location is open on this weekday","example":true},"ranges":{"type":"array","items":{"type":"object"},"description":"Open-time ranges for this weekday, e.g. `[{\"begin\":\"09:00\",\"end\":\"17:00\"}]`","example":[{"begin":"09:00","end":"17:00"}]},"specific_date":{"type":["string","null"],"description":"The ONE-OFF date this block applies to (`YYYY-MM-DD`), or null for a weekly-recurring block.","example":"2026-06-17"},"is_recurring":{"type":"boolean","description":"Whether this block recurs weekly. `NexHealth` populates `weekday` even on a one-off or\ncustom-recurrence block, so a weekday alone must NOT be read as the standing weekly schedule —\nonly rows with `is_recurring: true` describe recurring office hours.","example":true},"record_id":{"type":["string","null"],"description":"The VENDOR record id this row came from, as an integer.\n\n`id` above is the storage key, which carries a `-{weekday}` suffix when one `NexHealth`\nrecord covers several weekdays — so it is not always numeric. `record_id` is the vendor's own\nid, identical across those sibling rows, and is what a consumer should group or join on.\nNull only for a row whose `raw_source` is absent.\n\nA numeric string, like every other id in this API — the reason `id` is unusable as a join key\nis its `-{weekday}` suffix, not its JSON type, so this stays a string for consistency.","example":"107676484"},"provider_id":{"type":["string","null"],"description":"The provider whose availability this block describes; null when unscoped.\n\n`NexHealth`'s `working_hours` is per-provider/per-operatory availability, NOT practice office\nhours — every record carries a provider or operatory. A consumer computing office hours from\nthese rows must union them, and cannot read a closed day from their absence.","example":"494980996"},"operatory_id":{"type":["string","null"],"description":"The operatory (chair) this block describes; null when unscoped. Joins `Operatory.id`.","example":"276724"},"working_hour_label_id":{"type":["string","null"],"description":"The `NexHealth` working-hour label (day-part / scoping tag) id; null when unlabelled.","example":"76690"},"appointment_type_ids":{"type":"array","items":{"type":"string"},"description":"Appointment types this block is bookable for. Always an array, empty when the block is\nuntyped or the row was synced before the `appointment_types` include was requested.\n\nJoins `AppointmentType.id`."},"is_active":{"type":["boolean","null"],"description":"The vendor's own `active` flag. Distinct from `is_open`, which is `active && has_window` — an\nactive block with no times is `is_active: true` but `is_open: false`.","example":true},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"id":"wh_9001","location_id":"5001","weekday":1,"is_open":true,"ranges":[{"begin":"09:00","end":"17:00"}],"specific_date":null,"is_recurring":true,"record_id":"9001","provider_id":"494980996","operatory_id":"276724","working_hour_label_id":"76690","appointment_type_ids":["1212144"],"is_active":true,"updated_at":"2026-06-16T12:00:00Z"}},"WorkingHourLabel":{"type":"object","description":"Synced working-hour label (`NexHealth` appointment-type scoping). Projected from\n`working_hour_labels`; no PHI.","required":["id","updated_at"],"properties":{"id":{"type":"string","description":"The label's native PMS id","example":"wl_31"},"name":{"type":["string","null"],"description":"Label name, e.g. \"Hygiene\"; null when unset upstream","example":"Hygiene"},"location_id":{"type":["string","null"],"description":"The PMS location this label is scoped to, when the vendor supplies one.\n\n`NexHealth` returns `null` here on every row observed in prod, and its `working_hour_labels`\ncollection is NOT partitioned by location — querying with a `location_id` returns the same\nset. So labels are institution-global in practice. Exposed as a passthrough so a consumer\nthat requires the field can read it and a future population needs no contract change; a\nconsumer must not treat null as grounds for dropping the label.","example":"352558"},"updated_at":{"type":"string","format":"date-time","description":"RFC-3339 timestamp","example":"2026-06-16T12:00:00Z"}},"example":{"id":"wl_31","name":"Hygiene","location_id":null,"updated_at":"2026-06-16T12:00:00Z"}}},"responses":{"BadGateway":{"description":"The practice management system was unavailable or did not respond in time","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"Conflict":{"description":"Conflict — slot taken, appointment locked, or idempotency-key reuse with a different body","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"Forbidden":{"description":"Token is valid but not authorized for this client or operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"InternalServerError":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"ServiceUnavailable":{"description":"A required dependency (e.g. the PHI audit log) was unavailable; request refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"TooManyRequests":{"description":"Rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"Unauthorized":{"description":"Missing or invalid Authorization: Bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}},"UnprocessableEntity":{"description":"Validation failed upstream","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorBody"}}}}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Service-token auth. Sent as `Authorization: Bearer <service-token>`; timing-safe compared server-side against the configured token set — the same RFC-6750 scheme as the /mcp endpoint."}}},"tags":[{"name":"clients","description":"Reference data — practices, locations, providers, services, and related lookups — plus two delta feeds you poll by `?updated_since=` then follow `next_cursor`: the non-PHI reference change feed (`/changes`) and the scope-gated appointment-event feed (`/appointments/changes`, for scheduling reminders)."},{"name":"patients","description":"Patient records, fetched live from the practice management system on each request and not stored by this service."},{"name":"appointments","description":"Live booking commands — book, cancel, confirm, and reschedule appointments in the practice management system."},{"name":"meta","description":"Service metadata and health: `/health` and the generated `/openapi.json`."},{"name":"webhook_subscriptions","description":"Manage the webhook endpoints that receive change events: register, list, read, update, and delete delivery endpoints."},{"name":"members","description":"Provisioning — replace a practice's member/role projection (org membership + per-location roles), synced from the identity authority."},{"name":"practices","description":"Provisioning — enroll an onboarded practice into the sync engine from its NexHealth subdomain + institution id."}],"webhooks":{"nexhealthEvent":{"post":{"tags":["webhooks"],"summary":"NexHealth webhook (inbound)","description":"NexHealth push to the Data Service's registered webhook endpoint — NexHealth POSTs the raw event envelope to the registered webhook URL; consumers do NOT call this (no service token). Authenticity is the HMAC-SHA256 signature alone: SHA-256 over `timestamp + \".\" + base64(raw request body)`, verified with that endpoint's per-endpoint HMAC secret. The receiver returns 503 when `NEXHEALTH_WEBHOOK_SECRET` is unset, enforces a ~300s replay window, and processes each event exactly once (idempotent on the envelope `id`). No patient data is stored: patient/appointment events emit a PHI-free change-log only.","operationId":"nexhealthWebhook","parameters":[{"name":"signature","in":"header","required":true,"schema":{"type":"string"},"description":"Hex HMAC-SHA256 tag over `timestamp + \".\" + base64(raw body)`, keyed by the endpoint's per-endpoint secret. Verified on receipt."},{"name":"timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"ISO-8601 event timestamp; the first half of the signed message. Deliveries outside the ~300s replay window are rejected."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["resource_type","event_name","subdomain","data"],"example":{"resource_type":"Appointment","event_name":"appointment_insertion.complete","event_time":"2026-06-16T12:00:00Z","subdomain":"smilebright","institution_id":20858,"webhook_subscription_id":4471,"data":{"appointment":{"id":778812}}},"properties":{"resource_type":{"type":"string","description":"PascalCase resource name, e.g. `Appointment`, `Location`, `Patient`.","example":"Appointment"},"event_name":{"type":"string","description":"Event verb, e.g. `appointment_insertion.complete` or `location_updated`. Match on prefix, not equality.","example":"appointment_insertion.complete"},"event_time":{"type":"string","description":"ISO-8601 timestamp of the event.","example":"2026-06-16T12:00:00Z"},"subdomain":{"type":"string","description":"Tenant routing key; resolved to the practice it belongs to. An unknown subdomain is rejected and writes nothing.","example":"smilebright"},"institution_id":{"type":"integer","description":"Numeric NexHealth institution id.","example":20858},"webhook_subscription_id":{"type":"integer","description":"Which subscription fired this event.","example":4471},"data":{"type":"object","description":"Resource-specific payload object.","example":{"appointment":{"id":778812}}}}}}}},"responses":{"200":{"description":"Accepted — processed, or a duplicate that was already processed (each event is handled exactly once)."},"400":{"description":"Missing/invalid `signature`/`timestamp` header, malformed body, missing envelope `id`, or unknown subdomain."},"401":{"description":"HMAC verification failed or the timestamp is outside the replay window."},"500":{"description":"Handler error while dispatching a verified event; the event is not marked processed, so NexHealth re-delivers it."},"503":{"description":"The receiver is not configured (`NEXHEALTH_WEBHOOK_SECRET` is unset) or temporarily unavailable; NexHealth re-delivers."}}}}}}