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

# Core concepts

> Partners & keys, practices & grants, calls & outcomes, call requests, and webhooks — the model behind the API.

Five concepts explain everything in the Partner API.

## Partners & API keys

A **partner app** represents your organization on our platform. Apps are created by the GrowDental team (invite-only) and hold your contact details, BAA status, and webhook endpoints.

Each app has one or more **API keys**:

* Format: `gd_live_...` — a prefix plus a 43-character random secret (\~256 bits of entropy).
* Shown **once** at creation; we store only a SHA-256 hash, so a lost key can never be recovered — only replaced.
* Each key carries **scopes** (e.g. `calls:read`, `call-requests:write`) that bound what it can do.
* Keys are rate limited individually: 600 requests/minute by default, 60/minute for bulk writes.

Kill switches exist at four granularities — app suspended, key revoked, practice grant revoked, webhook endpoint disabled — so access can be narrowed without tearing everything down.

## Practices & grants

A **practice** is a dental office using GrowDental's voice agents. Your app never sees all practices — it sees exactly the practices we have **granted** to it, typically the ones you share a business relationship with.

* `GET /practices` lists your granted practices (`id`, `name`, `slug`, `timezone`, `status`, `vertical`). A grant can narrow your key's scopes per practice, so a scope your key holds may still be refused for a specific practice.
* Every practice-scoped request is validated against your grants. Practices you have not been granted return **404, not 403** — practice IDs cannot be probed.

The grant is the tenancy boundary of the whole API: key → app → granted practices, checked on every request.

## Calls & outcomes

A **call** is one phone conversation between a practice's voice agent and a person — inbound or outbound. After every call, post-call processing classifies an **outcome** (e.g. `appointment_booked`, `callback_requested`, `voicemail`, `no_answer`) and writes a natural-language **summary**.

* `GET /practices/{practiceId}/calls` lists calls; `GET /calls/{callId}` returns one call.
* **Transcripts** are PHI: the `transcript` field is populated only with the `transcripts:read` scope and a signed BAA.
* **Recordings** are PHI too: `GET /calls/{callId}/recording` (scope `recordings:read` + BAA) responds with a `302` redirect to a short-lived signed URL. Raw storage URLs are never exposed — follow the redirect immediately and re-request when you need the audio again.

## Call requests

A **call request** is how you trigger outbound calling: `POST /practices/{practiceId}/call-requests` with 1–50 contacts, a purpose, optional free-text context, and an optional calling window.

The platform dialer takes it from there — practice-local calling hours, retry ladders, and concurrency caps all apply automatically. You influence what the agent says **only through** `purpose` and `context`; prompts and agent internals are not exposed.

Each contact progresses `pending → calling → completed | failed` (or `skipped`), and completed contacts link to the resulting call via `call_id`. Track progress by polling `GET /call-requests/{callRequestId}` or by subscribing to the `call_request.completed` event.

<Warning>
  Always send an `Idempotency-Key` header when creating call requests. A blind
  network retry without one can dial patients twice.
</Warning>

## Webhooks & events

Instead of polling, register **webhook endpoints** (`POST /webhook-endpoints`) to receive events:

| Event                    | Fired when                                             |
| ------------------------ | ------------------------------------------------------ |
| `call.completed`         | A call finishes and its outcome is classified          |
| `call.recording.ready`   | A call's recording is processed and fetchable          |
| `call_request.completed` | Every contact in one of your call requests is terminal |

Delivery semantics, in one breath: **at-least-once, unordered, signed, PHI-minimal.**

* Every delivery carries an `X-GrowDental-Signature` header (`t=<unix>,v1=<HMAC-SHA256>`) — [verify it](/guides/receiving-call-outcomes) before trusting the payload.
* Retries use exponential backoff; endpoints that keep failing are auto-disabled until you re-enable them.
* De-duplicate on the `X-GrowDental-Delivery-Id` header.
* Payloads contain IDs, outcomes, and API links — never transcripts, recordings, or patient details. PHI moves only over authenticated API pulls.

## Coming later

Phase 2 adds **patient intake** (batch push of patients, appointments, and treatments through canonical schemas — drafts already in the API Reference) and segment/queue read-back. A partner-facing scheduling contract for mid-call booking into your system is planned behind that. These docs will grow as each phase ships; see the [Changelog](/changelog).
