Webhooks
Webhooks notify your application in real-time when resources change. Every webhook payload is HMAC-SHA256 signed for security.This page is the reference — payload, headers, settings. For how to build
against it, see Webhooks in Practice: the
handler shape, worked scenarios, and the four mistakes that only surface in
production.
Setting Up Webhooks
- Go to Webhooks in the Developer Dashboard
- Click Create Webhook
- Enter your endpoint URL (must be HTTPS)
- Select which events to subscribe to
- Save — you’ll receive a webhook secret for signature verification
Endpoint requirements
The endpoint must be reachable on the public internet overhttps://. URLs
that resolve to a non-routable address are rejected — loopback, link-local
(including cloud metadata addresses), private and carrier-grade-NAT ranges,
and internal names such as *.internal or *.local. Credentials embedded in
the URL (https://user:pass@…) are rejected too; use the signing secret to
authenticate us instead.
This is enforced again at delivery time, not only when you save the webhook,
so an endpoint whose DNS later points somewhere internal stops receiving
deliveries rather than turning us into a proxy for it.
Redirects are not followed. A 3xx response counts as a failed delivery
and is retried. Register the final URL.
Event Format
Events follow the pattern{resource}.{action}:
Payload Structure
data.environment is live or test, depending on which key produced the
event. Check it. Webhooks are registered per organisation, not per
environment, so one endpoint receives sandbox and production events alike —
and resourceId only exists in the datastore its own environment writes to, so
fetching a test resource with a live key returns 404. Branch on it before you
let an event drive real work: sandbox testing should not send a patient a real
appointment reminder.
data.tenantId identifies the tenant partition that produced the event: the sub-organization ID when the triggering API key belongs to a sub-org, otherwise your organization ID. Use it to route events per customer behind a single endpoint.
Environment Filtering
A webhook receives both environments by default, because registrations are per organisation rather than per key. When adding an endpoint you can pin it:
Pin your production receiver to live. Otherwise sandbox testing drives real
work — the same
patient.created that fires a genuine appointment reminder —
and resourceId will point at a record that exists only in the sandbox
datastore, so fetching it with a live key returns 404.
Existing webhooks are unpinned and keep receiving both, so nothing changes
until you choose otherwise.
Sub-Organization Filtering
Webhooks are registered at the organization level, and by default an endpoint receives events from all of your traffic — organization keys and every sub-organization. When adding an endpoint you can optionally pin it to a single sub-organization: a pinned endpoint only receives events produced by that sub-org’s API keys. Either way,data.tenantId always carries the originating tenant.
Headers
Verifying Signatures
Always verify the signature before processing a webhook. Verify against the raw request body — re-serializing parsed JSON changes the bytes and the signature will not match. PreferX-Clinik-Signature-V2: it covers the timestamp as well as the body, so
you can reject a stale replay. X-Clinik-Signature covers the body alone and
remains supported for existing integrations, but on its own it cannot tell a
fresh delivery from a replayed one.
Event Subscription
You can subscribe to:Available Events
All 62 resource types support.created, .updated, and .deleted events:
Clinical: patient, encounter, observation, condition, allergy, assessment, care-plan, care-team, goal, risk-assessment, family-member-history
Medications: medication, prescription, medication-dispense, medication-statement, medication-knowledge, immunization, immunization-evaluation, immunization-recommendation, nutrition-order
Scheduling: appointment, appointment-response, schedule, slot
Documentation: note, document, consent, intake, media
Diagnostics: lab, specimen, imaging-study
Administrative: practitioner, practitioner-role, organization, location, healthcare-service, person
Workflow: task, service-request, device-request, activity-definition, plan-definition
Devices: device, device-use-statement
Billing and Insurance: account, charge-item, claim, claim-response, coverage, eligibility-request, eligibility-response, explanation-of-benefit, invoice, payment-notice, payment-reconciliation, enrollment-request, enrollment-response, vision-prescription
Quality and Audit: measure, measure-report, audit-event
Retry Policy
Failed deliveries are retried with exponential backoff:
After 5 failed attempts, the delivery is marked as failed. You can view delivery logs in the Dashboard under Analytics > Webhook Logs.
Retries are per endpoint. If you have several webhooks subscribed to the
same event and one of them fails, only that one is retried — the endpoints that
already received the event are not sent it again.
A delivery refused because the destination is not routable is not retried:
the address will not become valid on its own. Fix the URL and the next event
delivers normally.
Best Practices
- Always return
200quickly — process events asynchronously - Verify
X-Clinik-Signature-V2on every request, against the raw body - Reject deliveries whose timestamp is outside your tolerance window
- Deduplicate on
X-Clinik-Delivery-Id— delivery is at-least-once, so the same event can legitimately arrive twice - Use specific event subscriptions instead of
*when possible