Webhooks

Be told when something happens, instead of asking.

A webhook endpoint is an organization-wide subscription to a list of events. Deliveries are signed with HMAC-SHA256 over `<timestamp>.<body>` and sent as `X-Vocalis-Signature: t=…,v1=…`; verify it before trusting a payload, and reject a timestamp that is not recent. Each delivery carries `X-Vocalis-Event-Id`, which is the same on every retry — use it as an idempotency key. Failures are retried with exponential backoff; a 4xx other than 408 or 429 is treated as a refusal and not retried, and an endpoint that fails continuously is switched off rather than being sent events into a void.

The signing secret is returned exactly once, when the endpoint is created and again when it is rotated. There is no endpoint that reads it back.

List endpoints

Every webhook endpoint in the organization.

GET/api/v1/webhook-endpoints

Responses

{
"data": {
"endpoints": [
{
"id": "c41a…",
"name": "Ops receiver",
"url": "https://hooks.example.com/vocalis",
"events": [
"call.ended",
"call.analysis.completed"
],
"secretPrefix": "whsec_9Qa3Kd",
"isEnabled": true,
"consecutiveFailures": 0,
"lastSuccessAt": "2026-09-15T13:58:02.000Z"
}
]
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X GET "https://voice.evryhealth.com/api/v1/webhook-endpoints" \
-H "Authorization: Bearer YOUR_API_KEY"

Create an endpoint

Subscribe a URL to a list of events. The URL is checked at creation: it must be public http(s), and one resolving to a private or link-local address is refused.

POST/api/v1/webhook-endpoints

Responses

{
"data": {
"id": "c41a…",
"name": "Ops receiver",
"url": "https://hooks.example.com/vocalis",
"events": [
"call.ended",
"intelligence.speaker.identified"
],
"secret": "whsec_9Qa3Kd…",
"secretPrefix": "whsec_9Qa3Kd"
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X POST "https://voice.evryhealth.com/api/v1/webhook-endpoints" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ops receiver",
"url": "https://hooks.example.com/vocalis",
"events": [
"call.ended",
"intelligence.speaker.identified"
]
}'

Get an endpoint

One endpoint. The signing secret is never returned here.

GET/api/v1/webhook-endpoints/{endpointId}

Path parameters

ParameterTypeDescription
endpointIdrequired
string

Endpoint id.

Responses

{
"data": {
"id": "c41a…",
"name": "Ops receiver",
"url": "https://hooks.example.com/vocalis",
"events": [
"call.ended"
],
"secretPrefix": "whsec_9Qa3Kd",
"isEnabled": true
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X GET "https://voice.evryhealth.com/api/v1/webhook-endpoints/{endpointId}" \
-H "Authorization: Bearer YOUR_API_KEY"

Update an endpoint

Change the URL, the subscription, or pause it. Re-enabling clears the failure count, so an endpoint switched off after an outage starts clean.

PATCH/api/v1/webhook-endpoints/{endpointId}

Path parameters

ParameterTypeDescription
endpointIdrequired
string

Endpoint id.

Responses

{
"data": {
"id": "c41a…",
"events": [
"call.ended"
]
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X PATCH "https://voice.evryhealth.com/api/v1/webhook-endpoints/{endpointId}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
"call.ended"
],
"isEnabled": true
}'

Rotate the secret

Issue a new signing secret, effective immediately. There is no overlap window — an overlap is a period in which a leaked old secret still signs valid-looking traffic, and rotation usually happens because the old one leaked. Have the receiver ready for the new secret before calling this.

POST/api/v1/webhook-endpoints/{endpointId}/rotate-secret

Path parameters

ParameterTypeDescription
endpointIdrequired
string

Endpoint id.

Responses

{
"data": {
"id": "c41a…",
"secret": "whsec_2Pb7Xm…"
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X POST "https://voice.evryhealth.com/api/v1/webhook-endpoints/{endpointId}/rotate-secret" \
-H "Authorization: Bearer YOUR_API_KEY"

Delivery log

What we tried to send, how many times, and what the receiver said. Payloads are not returned — they were sent to you, and they are encrypted at rest because they carry PHI.

GET/api/v1/webhook-endpoints/{endpointId}/deliveries

Path parameters

ParameterTypeDescription
endpointIdrequired
string

Endpoint id.

Query parameters

ParameterTypeDescription
status
string

pending, in_flight, succeeded, failed or dead_lettered.

limit
number

Up to 200.

Default: 50

cursor
string

From meta.pagination.nextCursor.

Responses

{
"data": {
"deliveries": [
{
"id": "7f21…",
"eventType": "call.ended",
"eventId": "1789525082142-0",
"status": "succeeded",
"attemptCount": 1,
"responseStatus": 200,
"createdAt": "2026-09-15T14:07:44.400Z"
}
]
},
"meta": {
"requestId": "req_…",
"pagination": {
"nextCursor": null,
"limit": 50
}
}
}
Request
curl -X GET "https://voice.evryhealth.com/api/v1/webhook-endpoints/{endpointId}/deliveries" \
-H "Authorization: Bearer YOUR_API_KEY"

Delete an endpoint

Stop delivering. Queued deliveries for it are abandoned rather than chased.

DELETE/api/v1/webhook-endpoints/{endpointId}

Path parameters

ParameterTypeDescription
endpointIdrequired
string

Endpoint id.

Responses

{
"data": {
"deleted": true
},
"meta": {
"requestId": "req_…"
}
}
Request
curl -X DELETE "https://voice.evryhealth.com/api/v1/webhook-endpoints/{endpointId}" \
-H "Authorization: Bearer YOUR_API_KEY"