> ## Documentation Index
> Fetch the complete documentation index at: https://build.practicehub.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Security & data handling

> How the API isolates, authenticates, logs and delivers — written from the implementation, for your security review

This page answers the questions a security or procurement review asks about integrating with the PracticeHub API. Every statement here describes how the platform is built today; where something is a policy rather than a mechanism, it says so. For questionnaires that need more, [contact us](mailto:support@practicehub.io) and we'll answer from the same source.

## Tenancy and data residency

* **One database per account.** Each clinic account is a tenant with its own database. The API resolves the account from the request host (`{clinic}.{region}.practicehub.io` — the practice's own login host, in its region) *before* routing, so a credential is bound to the database of the account it was issued on and is refused elsewhere.
* **Five regions.** Accounts are hosted in London, Frankfurt, Ohio, Singapore or Sydney, and the API for an account is served from that account's region. Webhook events (record ids only — see below) are delivered through a regional Svix environment: EU for London and Frankfurt accounts, US for Ohio, Singapore and Sydney.
* **Credentials are account-bound.** API keys belong to an integration record on the account; OAuth connector tokens belong to a team member on the account. Neither authenticates against another account.

## Authentication and credentials

<AccordionGroup>
  <Accordion title="API keys">
    * Format `phk_…`; the prefix lets secret scanners recognise a leaked key.
    * Only a **SHA-256 hash** is stored; the plain key is shown once at creation and cannot be retrieved afterwards.
    * **Scoped**: read & write, or read-only. A read-only key is refused on every write path (REST and MCP alike) — enforced once, in the write service, not per endpoint.
    * Optional **expiry** (90 days / 1 year) and **rotation** with a 24-hour grace period for the old key. Revocation is immediate.
    * Created, rotated and revoked only by signed-in team members with the developer permission, from **Developers → API Keys**; every one of those actions is written to the account's activity log.
  </Accordion>

  <Accordion title="OAuth connectors (Claude, ChatGPT and other MCP clients)">
    * OAuth 2.1 with **PKCE**, dynamic client registration with an allow-list of redirect hosts, and per-account consent screens shown to the signed-in team member.
    * Access tokens live **1 hour**; refresh tokens **30 days** and rotate on use.
    * **Opt-in per role**: a team member can only connect an AI app if their role has *Connect AI apps* enabled by an administrator.
    * **Acts as the person, with their role**: a connector can only read, write or delete what the team member's PracticeHub role allows, including own-patients / own-calendar ringfencing.
    * **Cut-off on every call**: a deactivated team member, or one whose role loses the permission, is refused on the next request — not at the next token refresh.
    * Connections are visible and revocable in-app (**Developers → API Keys → Connected AI apps**); each authorisation and revocation is in the activity log.
  </Accordion>
</AccordionGroup>

## Transport and abuse controls

* HTTPS is required for every API call and every webhook endpoint.
* **Rate limits per credential** (currently 600 requests/minute per key), with `X-RateLimit-*` headers and `Retry-After` on `429`, so one integration cannot exhaust another's allowance on the same account.
* Every response carries an **`X-Request-Id`**; quote it to support and it can be traced end to end.
* `Idempotency-Key` on `POST` guarantees a retried create is not applied twice.

## Logging and what is stored about your calls

* The account's **Developers → API Logs** page is a filterable report of every API request: time, calling key or connector user, method, path, resource and record id, query-parameter *names*, status, the API's error code and message, latency, response size, idempotent-replay flag, IP and user agent, request id — with usage, error and latency summaries and CSV export.
* Those log rows are **PHI-free by construction**: no request or response bodies are stored, query-parameter *values* are never stored, and the only free text kept is the API's own error message (generic wording that never echoes submitted values).
* Log rows are retained for **90 days**, then pruned automatically.
* Data changes made through the API are attributed in the record's own history to the integration or team member that made them (for example an appointment log entry *"Created via API: Booking widget"*).

## Webhooks (coming soon)

Not yet switched on for any account; the following describes the design that will ship.

* Payloads are **thin**: event type, event id, timestamp, and the entity type + record id — no field values, so no PHI transits the delivery provider. Consumers fetch the record over the authenticated API.
* Every delivery is **signed** ([Standard Webhooks](https://www.standardwebhooks.com/)); endpoints, signing secrets, delivery attempts and replays are managed by the account in its own portal.
* Use HTTPS endpoints.

## Files

Files uploaded through the API are stored in PracticeHub's object storage and served only via short-lived signed URLs to authenticated callers.

## Change management

* **Additive only within the current version**: new resources, attributes, event types and optional inputs may appear; nothing is renamed, removed or made required. Anything else ships as a new version with a published deprecation window.
* The OpenAPI document is generated from the running code and checked in CI for freshness and for breaking changes on every change.
* Every contract change is dated in the [changelog](/changelog).
* The legacy `/api` carries `Deprecation` and `Sunset` headers ahead of its retirement.

## Your responsibilities

* Treat API keys as secrets: server-side only, never in client-side code, rotate on staff change, use read-only keys where writes aren't needed.
* Verify webhook signatures before acting on a delivery.
* Use the least scope and the shortest key expiry that fits the integration.
* Data you fetch is your account's patient data; handle and retain it under your own obligations to the clinic.

<Note>
  Certifications, penetration-test summaries, sub-processor lists and data-processing agreements are provided on request as part of contracting — [support@practicehub.io](mailto:support@practicehub.io).
</Note>
