Authentication & Scopes
Tokens
- Format:
dtz_live_<secret>(production) ordtz_test_<secret>(sandbox only — production rejects test tokens). - Sent as
Authorization: Bearer dtz_live_…on every request. - Shown once at installation or rotation; Dentolize stores only a hash. Store it in a secret manager; never commit it or log it.
- Rotation: the clinic (or you, via support) rotates from the plugin settings page. The old token keeps working for a 24-hour grace window.
- Revocation is immediate (uninstall revokes all tokens).
Scopes
Scopes are granted at install time. Your listing's required scopes (requestedScopes) are all-or-nothing — the clinic either grants them all or does not install. Your listing may additionally declare optional scopes (see below), which the clinic toggles individually. Request the minimum you need — patient-PII scopes get extra scrutiny in listing review and stronger consent warnings in the install flow.
Every scope carries a PII tier that drives serializer field filtering and consent-screen grouping, in increasing sensitivity: basic (non-personal clinic/plugin data) < operational (business records without direct patient identifiers) < financial (pricing/estimate data — quotation totals, discounts) < patient_pii (direct patient identifiers / medical context).
| Scope | Tier | Grants |
|---|---|---|
availability:read |
basic | Free-slot lookup per branch/doctor |
webhooks:manage |
basic | Webhook endpoint CRUD, redelivery, test events |
settings:read |
basic | Read your installation + settings values |
settings:write |
basic | Update your own installation settings (implies settings:read) |
clinic:read |
basic | Clinic profile, branches, reference catalogs (appointment/payment types, patient tags) |
practitioners:read |
basic | Staff directory — names, roles, specialties, branch assignments (no contact details) |
procedures:read |
basic | Procedure catalog (names, codes, prices) |
patients:read |
operational | Patients with masked PII (no names/full phones) |
appointments:read / appointments:write |
operational | Appointments read / create-update-cancel |
invoices:read, payments:read |
operational | Financial documents, read-only |
practitioners:read.contact |
operational | Staff phone numbers and emails (implies practitioners:read) |
insurance:read |
operational | Insurance directory — companies, policies, policy classes (no patient linkage) |
leads:read |
operational | CRM leads + pipeline stages (lead identity is a pre-patient business contact) |
leads:write |
operational | Create leads from campaigns (409 lead_exists dedupe), stage moves, score/assignee/deal size (implies leads:read) |
communications:read |
operational | Outbound delivery log — per-channel status, 100-char content preview only |
communications:send |
operational | Queue outbound messages: APPROVED WhatsApp templates / free-text SMS; the platform resolves the patient phone (never returned) |
loyalty:read |
operational | Patient points aggregates + transactions |
loyalty:write |
operational | MANUAL earn/redeem transactions (earliest-expiring first; 409 insufficient_points) (implies loyalty:read) |
feedback:read |
operational | Appointment feedback — rating, comment, per-question answers |
tasks:read |
operational | Task instances with completion/approval state |
tasks:write |
operational | Create tasks + mark complete (approval stays staff-only) (implies tasks:read) |
inventory:read |
operational | Inventory items (stock levels, below-min filter), suppliers, orders |
inventory:write |
operational | Create PURCHASE orders + supplier-side status transitions (CONFIRMED/IN_TRANSIT only; receiving stays clinic-side) (implies inventory:read) |
analytics:read |
operational | KPI aggregates (revenue, appointments, treatments) — aggregates only, never row-level PII |
quotations:read |
financial | Quotations/estimates — totals, discounts, acceptance state (no signature images) |
claims:read |
financial | Insurance claims — amounts, statuses, rejection data, member invoice ids (claim Excel files never exposed) |
onlinepayments:read |
financial | Online (gateway) payments, read-only (payment-page tokens never exposed) |
paymentlinks:write |
financial | Generate hosted payment links on the clinic's configured gateway (implies onlinepayments:read) |
payments:write |
financial | Record external payments against invoices — full platform money path, idempotency-guarded (implies payments:read) |
invoices:write |
financial | Create cash visit invoices (POST /invoices) — full platform money path (treatment lines, totals, commissions), idempotency-guarded (implies invoices:read) |
einvoices:read |
financial | E-invoice (ZATCA) submission stream — statuses and response messages, never XML/certificates |
bookkeeping:read |
financial | Ledger export — expenses, incomes, treasury transactions, treasuries (from/to windows) |
patients:read.pii |
patient PII | Full patient identity fields (implies patients:read) |
patients:write |
patient PII | Create/update patients (implies patients:read) |
treatments:read |
patient PII | Treatment/operation records incl. steps and approval state, read-only |
laborders:read |
patient PII | Lab directory, lab item catalog, lab orders |
laborders:write |
patient PII | Lab-side updates — mark received/delivered, set shade/notes; never money fields (implies laborders:read) |
prescriptions:read |
patient PII | Prescriptions with medication lines + the medication catalog |
forms:read |
patient PII | Forms, form submissions, consent form status (signature images never exposed) |
forms:write |
patient PII | Submit forms externally (implies forms:read) |
encounters:read |
patient PII | Encounters with clinical notes and vital measurements |
encounters:write |
patient PII | Record measurements on encounters (implies encounters:read) |
files:read |
patient PII | Patient files and x-rays with short-lived download URLs |
files:write |
patient PII | Two-step file/x-ray uploads to patient records (implies files:read) |
conversations:read |
patient PII | WhatsApp conversations + full message content |
conversations:write |
patient PII | Reply within the 24h service window, assign, resolve (implies conversations:read) |
Missing scope → 403 { error: { code: "missing_scope", details: { missingScopes: [...] } } }.
Optional scopes
A listing declares two scope lists:
requestedScopes— required. Consent is all-or-nothing: they are shown as a fixed list on the consent step and always granted on install.optionalScopes— optional. The consent step renders each one in a visually separate "Optional access" group with a per-scope toggle, default OFF. The granted set isrequestedScopes ∪ (optional scopes the clinic switched on).
Guidance for listing authors:
- Keep
requestedScopesto what the core flow cannot work without; move everything else (nice-to-have surfaces, premium features) tooptionalScopes. Reviewers push back on bloated required lists. - The two lists must not overlap, and both must contain only catalog scopes — the consent UI refuses to render unknown keys and the install is blocked.
- Your integration MUST degrade gracefully when an optional scope was not granted: read the effective grants from
GET /pingorGET /installationand expect403 missing_scopeon the optional surfaces. Do not hard-fail the whole plugin. - The clinic's choice is fixed at install time in v1 — changing toggles later means reinstalling (a re-consent flow is on the roadmap). Scope
implieschains still apply to whatever ends up granted. - Webhook event grants remain all-or-nothing over
requestedEvents.
IP allowlisting
A token may carry an IP allowlist — up to 50 CIDR entries (IPv4/IPv6; a bare address means /32 / /128). When the list is non-empty, requests from any other source address are rejected with 401 { error: { code: "ip_not_allowed" } } before any data access; an empty list means any IP. The list is:
- pre-filled from the manifest's
allowedIpRangeson manifest installs (11), - editable by the clinic at any time in the plugin's Connection tab (also settable when minting or rotating a token).
Recommend shipping your egress IPs in your manifest, and remember dual-stack deployments: IPv4-mapped IPv6 client addresses are normalised to plain IPv4 before matching, but native IPv6 egress needs its own entries.
PII tiers in responses
With patients:read alone, patient responses contain stable identifiers (id, file number, gender, branch) and masked hints (phone: "+9665•••••42"). With patients:read.pii you receive names, full phone numbers, email, birth date, address, national ID and insurance identifiers. Signature images, geolocation, and internal notes are never exposed to plugins.
Every response that includes PII is recorded in the clinic's audit log together with your token id, the route, and the record ids returned.
Rate limits
- Pre-auth: 30 req/min per IP.
- Per token: 300 weighted points/min live (60 test) + 20 req/s burst. Weights: list = 3, detail = 1, write = 5.
429responses carryRetry-AfterandX-RateLimit-Limit/Remaining/Reset.