# EHR

EHR is the NewInstance electronic health records product. This section covers the parts that are stable in this phase: reference code lookups, the outbound webhook subscriptions that tell your systems when something happens, and the event log those webhooks are built on.

## How events and webhooks fit together

Every meaningful change inside EHR (a program created, a location updated, a break-the-glass session started, and so on) is appended to a per-organization event log with an increasing sequence number. A webhook subscription tells the platform which of those event types you want pushed to a URL you control; the **Send a test event** request lets you verify a receiver end to end without waiting for a real change.

If you would rather pull than have events pushed to you, poll **List events** with `sinceSeq` set to the `nextSeq` you got back last time.

## Webhook URL and secret rules

- HTTPS only. Loopback, private, link-local, carrier-grade NAT and unspecified addresses are rejected, both as literal IPs and after resolving the hostname.
- The signing secret (`whsec_...`) is generated on creation and returned in the response body exactly once. After that, only a masked preview (`whsec_••••` plus the last four characters) is ever shown.
- Set `includePhi: true` only when delivered payloads may contain protected health information, and pair it with a `phiAcknowledgmentText` of at least 20 characters confirming you understand that.

## Scheduling

**List appointments** and **Create an appointment** need the `scheduling` scope, and **Look up a client by MRN** needs `clients-read`. Instants are always ISO 8601 in UTC, and every appointment carries the IANA zone it was booked in, so a caller in another zone can render the local wall time without guessing. A key holding `scheduling` alone sees the slots but no client identifiers; add `clients-read` to see which client each appointment belongs to.

Appointments protected by an additional consent requirement are not returned to an API key at all: they are absent from the page, from the total and from every filter, and a lookup of such a client answers exactly as an unknown record does. They still hold their slot, so a booking over one comes back as a clash without naming what is in the way.

The inbound SMS webhook is the one EHR endpoint that takes no API key: it is authenticated by your provider's request signature instead.

## Permission scopes for EHR API keys

| Scope code | Description |
|------------|-------------|
| `full-access` | All EHR permissions |
| `webhooks` | Create, update, delete and test webhook subscriptions, and read the event log |
| `scheduling` | List and create appointments |
| `clients-read` | Look up a client by medical record number, and see client identifiers on appointments |

Reference code lookups only need the key to carry any `ehr` product scope, full access or a specific group such as `webhooks`, not a dedicated scope of their own.

**GET /api/v1/ehr/reference-codes**: List reference codes (fetch id `endpoint:6ae152d083f212a997c813c4`)

**GET /api/v1/ehr/webhooks**: List webhook subscriptions (fetch id `endpoint:6ae152d083f212a997c813c5`)

**POST /api/v1/ehr/webhooks**: Create a webhook subscription (fetch id `endpoint:6ae152d083f212a997c813c6`)

**POST /api/v1/ehr/webhooks/{ehrWebhookId}/test**: Send a test event (fetch id `endpoint:6ae152d083f212a997c813c7`)

**GET /api/v1/ehr/events**: List events (fetch id `endpoint:6ae152d083f212a997c813c8`)

**GET /api/v1/ehr/appointments**: List appointments (fetch id `endpoint:6ae152d083f212a997c813c9`)

**POST /api/v1/ehr/appointments**: Create an appointment (fetch id `endpoint:6ae152d083f212a997c813ca`)

**GET /api/v1/ehr/billing/claims**: List claims (fetch id `endpoint:6ae152d083f212a997c813cb`)

**GET /api/v1/ehr/billing/claims/{ehrClaimId}**: Read one claim (fetch id `endpoint:6ae152d083f212a997c813cc`)

**GET /api/v1/ehr/billing/charges/{ehrChargeId}**: Read one charge and its ledger (fetch id `endpoint:6ae152d083f212a997c813cd`)

**POST /api/v1/ehr/clients/lookup**: Look up a client by MRN (fetch id `endpoint:6ae152d083f212a997c813ce`)

**POST /api/v1/ehr/sms/inbound**: Inbound SMS webhook (fetch id `endpoint:6ae152d083f212a997c813cf`)

**DELETE /api/v1/ehr/webhooks/{ehrWebhookId}**: Delete a webhook subscription (fetch id `endpoint:6ae152d083f212a997c813d0`)

**PATCH /api/v1/ehr/webhooks/{ehrWebhookId}**: Update a webhook subscription (fetch id `endpoint:6ae152d083f212a997c813d1`)
