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

# Healthcare HMS integration

> Connect your hospital management system to Ondial inbound — scopes, webhook contract, mid-call flow, and admin APIs.

Optional path for **hospital management systems** (or any custom platform) that already own doctors, slots, booking, and patient identity.

Clinics that only need Google Calendar can ignore this page. Use the normal healthcare inbound wizard and choose **Google Calendar + CRM** — do **not** select HMS Custom API.

Use **your platform’s** hospital / branch / doctor IDs everywhere below. Placeholders like `YOUR_BRANCH_ID` are not real values.

## HMS platform checklist (end-to-end)

Any hospital / HMS vendor can follow this ordered list. Full request/response samples for each `use_case` are in [Tenant webhook contract](#tenant-webhook-contract).

### Phase 0 — Prerequisites

1. HMS owns patients, hospitals/branches, doctors, slots, and bookings (source of truth).
2. Ondial tenant can own inbound PSTN numbers via **Public API purchase** (when credits allow) or dashboard assign/purchase.
3. HMS exposes one public **HTTPS** webhook URL (e.g. `https://api.your-hms.com/ondial`).
4. Shared secret agreed (`YOUR_SHARED_SECRET`) for `Authorization: Bearer …`.
5. (API path) Ondial API key requested and approved (`ond_live_…`).

### Phase 1 — Build the webhook (standard contract)

Expose **one** `POST` endpoint. Every body includes `operation` + `use_case`. Switch on `use_case`.

**Minimum for appointments (any scope):**

| # | use\_case          | operation | HMS must                                                   |
| - | ------------------ | --------- | ---------------------------------------------------------- |
| 1 | `doctor_list`      | `list`    | Return doctors (`doctor_id`, `name`, `specialization?`)    |
| 2 | `doctor_slots`     | `list`    | Return open slots (`slot_id`, `datetime`, `display_label`) |
| 3 | `book_appointment` | `write`   | Create booking; return `{ "success": true, "booking_id" }` |

**Required for `global` scope only:**

| # | use\_case       | operation |
| - | --------------- | --------- |
| 4 | `category_list` | `list`    |
| 5 | `hospital_list` | `list`    |

**Optional features:**

| # | use\_case        | operation | When                                      |
| - | ---------------- | --------- | ----------------------------------------- |
| 6 | `patient_lookup` | `read`    | Patient identify at call start            |
| 7 | `lab_report`     | `read`    | Lab inquiry enabled on the agent          |
| 8 | `prescriptions`  | `read`    | Prescription routing enabled on the agent |

**Example request your HMS receives:**

```json theme={"system"}
{
  "operation": "list",
  "use_case": "doctor_slots",
  "doctor_id": "YOUR_DOCTOR_ID",
  "branch_id": "YOUR_BRANCH_ID",
  "max_slots": 3
}
```

**Example success responses:**

```json theme={"system"}
{
  "values": [
    {
      "doctor_id": "YOUR_DOCTOR_ID",
      "name": "Dr Example",
      "specialization": "Cardiology"
    }
  ]
}
```

```json theme={"system"}
{
  "values": [
    {
      "slot_id": "YOUR_SLOT_ID",
      "datetime": "2026-09-01T09:30:00+05:30",
      "display_label": "Mon 1 Sep, 9:30 AM"
    }
  ]
}
```

```json theme={"system"}
{
  "success": true,
  "booking_id": "YOUR_BOOKING_ID"
}
```

**Rules:**

* HTTPS in production; respond within timeout (default **8000** ms).
* Honor Bearer secret when configured.
* Use **stable IDs from your DB** (`branch_id` / `hospital_id` / `doctor_id` / `slot_id`).
* Hospital/doctor-scoped agents: Ondial may inject locked `branch_id` / `doctor_id` — accept them.
* `patient_lookup` failure → soft fail (call continues). Slots/book failure → hard fail (do not fake success).

### Phase 2 — Wire the agent in Ondial

Pick **one** scope per phone number:

| Scope      | HMS IDs to set on the agent       | Sync doctors?              |
| ---------- | --------------------------------- | -------------------------- |
| `global`   | none                              | No (catalogs fetched live) |
| `hospital` | `branchId` = your hospital/org ID | Yes                        |
| `doctor`   | `branchId` + `doctorId`           | Yes (optional confirm)     |

**Dashboard path:** Inbound → Healthcare → Integration type **HMS Custom API** → Base URL + secret → Appointments ON → scope + your IDs → Save → Sync doctors → Activate.

**API path (HMS backend — recommended for SaaS):**

1. `GET /api/v1/credits` — check balance before buy
2. `GET /api/v1/numbers/available` — browse pool inventory with per-number cost
3. `POST /api/v1/numbers/purchase` — buy when credits suffice (`402 insufficient_credits` if not)
4. `GET /api/v1/numbers` — confirm owned numbers
5. `GET /api/v1/companies` — pick `companyId` (scope `companies:read`; create via `POST /companies` if needed)
6. `GET /api/v1/knowledge-bases/documents` — pick `fileId`s (scope `knowledge:read`; upload via `POST …/documents` if needed)
7. `POST /api/v1/inbound-agents` (scope + `customApiBaseUrl` + `companyId` + `selectedKnowledgebases: ["fileId",…]`; pass `selectedVoice` from `GET /api/v1/voices` or `copyVoiceFromAgentId`)
8. `POST /api/v1/inbound-agents/{id}/sync-doctors` (hospital/doctor only)
9. `POST /api/v1/inbound-agents/{id}/generate-script`
10. `POST /api/v1/inbound-agents/{id}/activate` with `{ "activate": true }`

Create enables **Appointments** and `hcBookingPath: custom_api` automatically when scope + Custom API URL are set. If `companyId` is omitted, Ondial auto-picks the latest company.

Create example (hospital):

```json theme={"system"}
{
  "phoneNumber": "+91xxxxxxxxxx",
  "category": "healthcare_reception",
  "appointmentScope": "hospital",
  "branchId": "YOUR_BRANCH_ID",
  "companyId": "YOUR_COMPANY_ID",
  "selectedKnowledgebases": ["YOUR_FILE_ID"],
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "doctorsSyncEnabled": true
}
```

### Phase 3 — Runtime (live call)

```mermaid theme={"system"}
sequenceDiagram
  participant Caller
  participant Ondial
  participant HMS
  Caller->>Ondial: Inbound call
  opt PatientLookupOn
    Ondial->>HMS: patient_lookup
    HMS-->>Ondial: found or false
  end
  alt GlobalScope
    Ondial->>HMS: category_list hospital_list doctor_list
  end
  Ondial->>HMS: doctor_slots
  HMS-->>Ondial: values
  Ondial->>HMS: book_appointment
  HMS-->>Ondial: booking_id
  Ondial->>Caller: Confirm booking
```

| Scope    | Expected calls on your webhook                                        |
| -------- | --------------------------------------------------------------------- |
| Global   | catalogs → `doctor_slots` → `book_appointment`                        |
| Hospital | locked `branch_id`; `doctor_slots` → `book_appointment` (after sync)  |
| Doctor   | locked `branch_id` + `doctor_id`; `doctor_slots` → `book_appointment` |

### Phase 4 — Ongoing management

| Task                          | How                                                                                                |
| ----------------------------- | -------------------------------------------------------------------------------------------------- |
| Check credits / buy number    | `GET /api/v1/credits` → `GET /api/v1/numbers/available` → `POST /api/v1/numbers/purchase`          |
| Change scope / IDs / URL      | Dashboard or `PATCH /api/v1/inbound-agents/{id}`                                                   |
| Delete agent (keep DID)       | `DELETE /api/v1/inbound-agents/{id}`                                                               |
| Refresh doctor cache          | `POST /api/v1/inbound-agents/{id}/sync-doctors` (hospital/doctor; global → `422 sync_not_allowed`) |
| Activate / deactivate         | `POST /api/v1/inbound-agents/{id}/activate`                                                        |
| Dry-run lookup (no live call) | `POST /api/v1/inbound-agents/{id}/crm/lookup`                                                      |
| Stop the number               | Deactivate the agent in Ondial                                                                     |

### Phase 5 — Go-live verification

1. Webhook reachable over HTTPS with Bearer auth.
2. Sync doctors returns your real doctor list (hospital/doctor scopes).
3. Test call produces `doctor_slots` then `book_appointment` in HMS logs.
4. Booking ID exists in your HMS.
5. Google Calendar is **not** required on this path.

## Architecture

The voice agent never calls your HMS directly. It calls Ondial mid-call APIs; Ondial POSTs to your HTTPS webhook.

```mermaid theme={"system"}
flowchart LR
  Voice[Voice agent] --> Ondial[Ondial mid-call APIs]
  Ondial --> Tenant[Your customApiBaseUrl]
  Admin[Sync doctors] --> Sync["POST /api/v1/inbound-agents/id/sync-doctors"]
  Sync --> Tenant
```

| When                              | Ondial endpoint                                 | What Ondial POSTs to your webhook                          |
| --------------------------------- | ----------------------------------------------- | ---------------------------------------------------------- |
| Call start (if patient lookup on) | Internal validate hook                          | `operation: read`, `use_case: patient_lookup`              |
| Categories / hospitals / doctors  | `POST /api/inbound/crm`                         | `list` + `category_list` / `hospital_list` / `doctor_list` |
| Lab report (if enabled)           | `POST /api/inbound/crm`                         | `read` + `lab_report`                                      |
| Prescription (if enabled)         | `POST /api/inbound/crm`                         | `read` + `prescriptions`                                   |
| Slot search                       | `POST /api/inbound/slots`                       | `list` + `doctor_slots`                                    |
| Book appointment                  | `POST /api/inbound/book`                        | `write` + `book_appointment`                               |
| Sync doctors (admin)              | `POST /api/v1/inbound-agents/{id}/sync-doctors` | `list` + `doctor_list`                                     |

## What you get

| Scope (`appointmentScope`) | Config                                | What callers can book                             |
| -------------------------- | ------------------------------------- | ------------------------------------------------- |
| `global`                   | No `branchId` / `doctorId`            | Any hospital / specialty / doctor in your network |
| `hospital`                 | `branchId` = your hospital/org ID     | Only doctors at that hospital                     |
| `doctor`                   | `branchId` + `doctorId` from your HMS | Only that doctor (goes straight to slots)         |

**One Ondial phone number = one scope.** Buy or assign a number, then attach the scope. Reuse the same webhook URL on many numbers.

On a **global** number the voice agent supports both:

1. Hospital → category → doctor → slots → book
2. Category → doctor → hospital → slots → book

## Setup in the dashboard

<Steps>
  <Step title="Own an Ondial number">
    Purchase via Public API (`GET /credits` → `GET /numbers/available` → `POST /numbers/purchase`) or in the [dashboard](https://dashboard.ondial.ai).
  </Step>

  <Step title="Open inbound setup">
    Choose category **Healthcare Reception**. Features start **off**.
  </Step>

  <Step title="Pick integration type">
    Select **HMS Custom API** (not Google Calendar + CRM).
  </Step>

  <Step title="Connect your webhook">
    Enter your HTTPS **Base URL** and optional **Bearer secret**. Production must use `https://`.
  </Step>

  <Step title="Enable features and scope">
    Turn on **Appointments**. Choose scope: `global`, `hospital` (your branch/org ID), or `doctor` (your branch + doctor ID). Optionally enable lab report / prescription inquiry and patient lookup.
  </Step>

  <Step title="Save, sync, activate">
    Save the agent. For hospital/doctor scopes, click **Sync doctors from HMS**. Open Review & Launch and activate.
  </Step>
</Steps>

Day-to-day HMS backends can do the same via Public API without opening the dashboard after the first wire-up.

## Setup via Public API (HMS admin)

<Steps>
  <Step title="Get an API key">
    [Request API access](/get-started/guides/request-api-access). Use `Authorization: Bearer ond_live_…` from your HMS backend.
  </Step>

  <Step title="Own Ondial numbers">
    From your HMS backend: `GET /api/v1/credits` → `GET /api/v1/numbers/available` → `POST /api/v1/numbers/purchase` (pool inventory; credits debited). Or purchase in the [dashboard](https://dashboard.ondial.ai). Then `GET /api/v1/numbers` to list owned DIDs.
  </Step>

  <Step title="Implement your webhook">
    Expose one HTTPS URL. Ondial POSTs `{ operation, use_case, ...fields }`. See [webhook contract](#tenant-webhook-contract).
  </Step>

  <Step title="Create an inbound agent">
    Optionally `GET /api/v1/companies` and `GET /api/v1/knowledge-bases/documents` to pick ids. Then `POST /api/v1/inbound-agents` with `appointmentScope`, Custom API URL, flags, and optionally `companyId` + `selectedKnowledgebases: ["fileId", …]`. Optional `copyVoiceFromAgentId` to copy voice/language from an existing agent. This sets **Appointments ON** and `hcBookingPath: custom_api` automatically. Agent starts **inactive**.
  </Step>

  <Step title="Sync doctors (hospital / doctor only)">
    `POST /api/v1/inbound-agents/{id}/sync-doctors`. Global numbers skip this — catalogs are fetched live during the call.
  </Step>

  <Step title="Generate script">
    `POST /api/v1/inbound-agents/{id}/generate-script`. Wait until `inboundScriptStatus` is `ready` (poll `GET …/inbound-agents/{id}`).
  </Step>

  <Step title="Activate">
    `POST /api/v1/inbound-agents/{id}/activate` with `{ "activate": true }`.
  </Step>
</Steps>

## Create examples

**Global helpdesk number**

```json theme={"system"}
{
  "phoneNumber": "+91xxxxxxxxxx",
  "category": "healthcare_reception",
  "name": "Network helpdesk",
  "appointmentScope": "global",
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "validateHookEnabled": true
}
```

**Hospital line**

```json theme={"system"}
{
  "phoneNumber": "+91yyyyyyyyyy",
  "category": "healthcare_reception",
  "name": "Main hospital line",
  "appointmentScope": "hospital",
  "branchId": "YOUR_BRANCH_ID",
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "doctorsSyncEnabled": true
}
```

Then [sync doctors](/api-reference/endpoint/inbound-agents/sync-inbound-doctors) and [activate](/api-reference/endpoint/inbound-agents/activate-inbound-agent).

## HMS admin UX (your platform)

Hospital staff use **your** HMS admin UI only. Your backend calls Ondial with one tenant API key (`ond_live_…`). Never expose the API key to the browser.

### Recommended screens

| Screen              | Ondial calls                                                                                                   | Notes                                                                                |
| ------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Credits & numbers   | `GET /api/v1/credits`, `GET /api/v1/numbers`, `GET /api/v1/numbers/available`, `POST /api/v1/numbers/purchase` | Disable **Buy** when `credits < cost`; show `402 insufficient_credits` from purchase |
| Voice agents list   | `GET /api/v1/inbound-agents`                                                                                   | Show number, scope, status, branch/doctor IDs                                        |
| Create / edit agent | `POST` or `PATCH /api/v1/inbound-agents`, then sync + activate                                                 | Scope picker: Global / Hospital / Doctor                                             |

### Server module (HMS codebase)

Store in HMS env (server-side only):

* `ONDIAL_BASE_URL` — e.g. `https://dashboard.ondial.ai`
* `ONDIAL_API_KEY` — `ond_live_…` from Ondial API Access

All HMS UI requests go **HMS browser → HMS API → Ondial Public API**. Map HMS hospital/doctor records to `branchId` / `doctorId` on create.

### Buy-number sequence

```mermaid theme={"system"}
sequenceDiagram
  participant UI as HMS_Admin_UI
  participant HMS as HMS_Backend
  participant Ondial as Ondial_v1

  UI->>HMS: Open Numbers
  HMS->>Ondial: GET credits
  HMS->>Ondial: GET numbers/available
  Ondial-->>HMS: inventory + cost per number
  UI->>HMS: Buy selected DID
  HMS->>Ondial: POST numbers/purchase
  alt sufficient credits
    Ondial-->>HMS: purchased + creditsRemaining
  else insufficient
    Ondial-->>HMS: 402 insufficient_credits
  end
```

**Purchase example**

```http theme={"system"}
POST /api/v1/numbers/purchase
Authorization: Bearer ond_live_...
Content-Type: application/json

{ "phoneNumberId": "66f..." }
```

```json theme={"system"}
{
  "purchased": [{ "id": "66f...", "number": "+91xxxxxxxxxx", "cost": 7 }],
  "totalCost": 7,
  "creditsRemaining": 493
}
```

### Create agent after purchase (all scopes)

Use `phoneNumberId` from purchase response or `phoneNumber` E.164 from `GET /api/v1/numbers`.

**Global** — no sync:

```json theme={"system"}
{
  "phoneNumberId": "66f...",
  "category": "healthcare_reception",
  "appointmentScope": "global",
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "copyVoiceFromAgentId": "OPTIONAL_TEMPLATE_AGENT_ID"
}
```

Then `POST …/generate-script` → `POST …/activate`.

**Hospital** — sync then script then activate:

```json theme={"system"}
{
  "phoneNumberId": "66f...",
  "appointmentScope": "hospital",
  "branchId": "YOUR_BRANCH_ID",
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "doctorsSyncEnabled": true,
  "copyVoiceFromAgentId": "OPTIONAL_TEMPLATE_AGENT_ID"
}
```

**Doctor** — sync then script then activate:

```json theme={"system"}
{
  "phoneNumberId": "66f...",
  "appointmentScope": "doctor",
  "branchId": "YOUR_BRANCH_ID",
  "doctorId": "YOUR_DOCTOR_ID",
  "customApiBaseUrl": "https://YOUR_HMS_HOST/ondial",
  "customApiSecret": "YOUR_SHARED_SECRET",
  "slotsEnabled": true,
  "bookingEnabled": true,
  "copyVoiceFromAgentId": "OPTIONAL_TEMPLATE_AGENT_ID"
}
```

Credit top-up stays on the Ondial dashboard or your existing billing flow; Public API exposes **read balance** and **debit on purchase** only.

## Tenant webhook contract

Ondial POSTs JSON to `customApiBaseUrl` (the exact URL you configured — no path suffix is appended).

**Requirements**

* HTTPS in production
* Respond within your configured timeout (default **8000** ms; patient lookup often **5000** ms)
* When `customApiSecret` is set, expect `Authorization: Bearer YOUR_SHARED_SECRET`

```http theme={"system"}
POST /ondial HTTP/1.1
Host: YOUR_HMS_HOST
Authorization: Bearer YOUR_SHARED_SECRET
Content-Type: application/json

{
  "operation": "list",
  "use_case": "doctor_list",
  "branch_id": "YOUR_BRANCH_ID"
}
```

**Locked ids:** hospital and doctor scopes inject `branch_id` (and `doctor_id` for doctor scope) on every Custom API payload when missing. **Global never injects** a configured hospital — the caller-selected hospital is sent as chosen on the call.

### `patient_lookup` — `read`

Request:

```json theme={"system"}
{
  "operation": "read",
  "use_case": "patient_lookup",
  "mobile": "+91XXXXXXXXXX",
  "branch_id": "YOUR_BRANCH_ID"
}
```

Response (found):

```json theme={"system"}
{
  "found": true,
  "row": {
    "name": "Patient display name",
    "patient_id": "YOUR_PATIENT_ID"
  }
}
```

Response (not found): `{ "found": false }`

### `category_list` — `list`

Request: `{ "operation": "list", "use_case": "category_list" }`

Response:

```json theme={"system"}
{
  "values": [
    { "category_id": "YOUR_CATEGORY_ID", "name": "Cardiology" }
  ]
}
```

### `hospital_list` — `list`

Request may include `category_id` and/or `doctor_id` filters.

```json theme={"system"}
{
  "values": [
    { "hospital_id": "YOUR_HOSPITAL_ID", "name": "Main campus" }
  ]
}
```

### `doctor_list` — `list`

Request may include `branch_id`, `hospital_id`, `category_id`, and/or `doctor_id`.

```json theme={"system"}
{
  "values": [
    {
      "doctor_id": "YOUR_DOCTOR_ID",
      "name": "Dr Example",
      "specialization": "Cardiology"
    }
  ]
}
```

Used mid-call on global numbers and when you run **Sync doctors** on hospital/doctor agents.

### `doctor_slots` — `list`

Request:

```json theme={"system"}
{
  "operation": "list",
  "use_case": "doctor_slots",
  "doctor_id": "YOUR_DOCTOR_ID",
  "branch_id": "YOUR_BRANCH_ID",
  "max_slots": 3,
  "filtered_date": "2026-09-01"
}
```

Response:

```json theme={"system"}
{
  "values": [
    {
      "slot_id": "YOUR_SLOT_ID",
      "datetime": "2026-09-01T09:30:00+05:30",
      "display_label": "Mon 1 Sep, 9:30 AM"
    }
  ]
}
```

### `book_appointment` — `write`

Request:

```json theme={"system"}
{
  "operation": "write",
  "use_case": "book_appointment",
  "doctor_id": "YOUR_DOCTOR_ID",
  "slot_id": "YOUR_SLOT_ID",
  "patient_name": "Caller name",
  "mobile": "+91XXXXXXXXXX",
  "branch_id": "YOUR_BRANCH_ID"
}
```

Response:

```json theme={"system"}
{
  "success": true,
  "booking_id": "YOUR_BOOKING_ID"
}
```

`record_id` is also accepted as an alias for `booking_id`.

### `lab_report` — `read` (optional feature)

When lab report inquiry is enabled on the agent:

```json theme={"system"}
{
  "operation": "read",
  "use_case": "lab_report",
  "inquiry_id": "YOUR_LAB_ID",
  "customer_phone": "+91XXXXXXXXXX"
}
```

Response: `{ "found": true, "row": { … } }` or `{ "found": false }`.

### `prescriptions` — `read` (optional feature)

When prescription routing is enabled:

```json theme={"system"}
{
  "operation": "read",
  "use_case": "prescriptions",
  "prescription_id": "YOUR_RX_ID",
  "customer_phone": "+91XXXXXXXXXX"
}
```

Response: `{ "found": true, "row": { … } }` or `{ "found": false }`.

### Failures

* `patient_lookup` timeout or error → call still accepted (anonymous). Do not hang up.
* Slots / book errors → return failure. Ondial must not confirm a booking your HMS rejected.

## Validation

| Rule                                      | Result                                  |
| ----------------------------------------- | --------------------------------------- |
| `global` with `branchId` or `doctorId`    | `422` `global_scope_locks_forbidden`    |
| `hospital` without `branchId`             | `422` `branch_id_required`              |
| `hospital` with locked `doctorId`         | `422` `hospital_scope_doctor_forbidden` |
| `doctor` missing `branchId` or `doctorId` | `422` `doctor_scope_ids_required`       |
| Number not owned                          | `422` `phone_not_owned`                 |
| Sync doctors on a global agent            | `422` `sync_not_allowed`                |

## Related APIs

* [Create inbound agent](/api-reference/endpoint/inbound-agents/create-inbound-agent)
* [Update inbound agent](/api-reference/endpoint/inbound-agents/update-inbound-agent)
* [Sync doctors](/api-reference/endpoint/inbound-agents/sync-inbound-doctors)
* [List owned numbers](/api-reference/endpoint/inbound-agents/list-owned-numbers)
* [Activate](/api-reference/endpoint/inbound-agents/activate-inbound-agent)
* [Inbound CRM lookup](/api-reference/endpoint/inbound-agents/inbound-crm-lookup) (dry-run read without a live call)
