Overview
Webhooks deliver a POST request to an endpoint you control every time something
happens in your workspace — an appointment is booked, a patient record changes,
a vitals scan completes.
Every delivery carries the same JSON envelope. Only data differs between
events, and each event documents its own shape under
Payloads.
Setting up a webhook
Section titled “Setting up a webhook”-
Go to Settings > Developers > Webhooks. The Developers section is only visible to users with the Developer role.
-
Click Webhook (or Create Webhook if you have none yet) and enter your Endpoint URL. It must be a publicly reachable
https://URL —http://endpoints are rejected, because payloads carry patient data. -
On the edit page that opens next, check the Events the webhook should receive. New webhooks are subscribed to the four
appointment.*events, and a webhook with nothing selected receives those same four events. -
Switch Enabled on and click Save Changes. New webhooks are created disabled, so nothing is delivered until you turn it on.
A workspace can have several webhooks. Each one has its own endpoint, its own event selection and its own enabled state, so you can send appointment events to one service and scan events to another.
The event envelope
Section titled “The event envelope”{ "id": "ckq7v1w1k0008zqxk7g3l5n2q", "name": "vitalsScan.complete", "createdAt": "2026-02-04T09:01:12.000Z", "business": { "id": "ckq7v1w1k0001zqxk3f8a9d4t" }, "data": { "id": "ckq7v1w1k0006zqxk4r8p2s5w", "status": "SUCCEEDED" }}| Field | Type | Description |
|---|---|---|
id |
string |
Unique identifier for this event delivery. |
name |
string |
The name of the event, in resource.action format. |
createdAt |
string |
ISO 8601 timestamp of when the event was recorded by Upvio. |
business.id |
string |
The business the event belongs to. Always the business that owns the webhook. |
data |
object |
The event payload. See Payloads. |
Every data payload also repeats the business as data.businessId, so it can be
read on its own without the envelope.
Request format
Section titled “Request format”Deliveries are sent as POST requests with a JSON body and these headers:
| Header | Value |
|---|---|
Content-Type |
application/json |
Accept |
application/json |
User-Agent |
Upvio Webhooks/1.0 |
Responding to a delivery
Section titled “Responding to a delivery”Return any status code below 400 to acknowledge a delivery. Anything from
400 up, along with a connection error or a timeout, is recorded as a failed
delivery.
Acknowledge first and do the work afterwards. If your handler calls out to other services before responding, a slow third party turns into a failed Upvio delivery.
// Next.js route handlerexport const POST = async (request: Request) => { const event = await request.json()
// Acknowledge straight away, then process out of band. await queue.push(event)
return new Response(null, { status: 200 })}Inspecting deliveries
Section titled “Inspecting deliveries”Every attempt is logged. Open the actions menu next to a webhook in Settings > Developers > Webhooks and select View delivery logs to see its delivery history: the request payload, the response status and body, and how long your endpoint took to reply. This is the fastest way to tell whether a missing event was never sent or was sent and rejected.
Securing your endpoint
Section titled “Securing your endpoint”Deliveries are not signed, so your endpoint cannot verify them from the request alone. Until signing is available:
- Keep the URL secret. Include a long, random path segment or query
parameter in the endpoint (
https://example.com/webhooks/upvio/9f2c…) and reject any request that does not carry it. - Do not trust the payload as proof. If a delivery triggers something sensitive, re-read the record from the API before acting on it.
- Expect duplicates. Deliveries carry the event
id; store it and ignore anidyou have already handled. - Expect unknown events. New event types are added over time. Ignore any
nameyou do not recognize instead of failing the request.