Webhook Endpoints
Manage multiple webhook URLs with per-endpoint secrets and event filters.
The endpoint-based webhook system lets you register multiple destinations per organization, each with its own secret, its own status, and its own subscription to a subset of event types. This is the recommended model for all new integrations.
Compare to the legacy single-URL webhook, which is kept for backward compatibility but only supports one URL per org.
Endpoints
| Method | Path | Required role | Description |
|---|---|---|---|
GET | /v1/developer/webhook-endpoints | admin+ | List endpoints |
POST | /v1/developer/webhook-endpoints | admin+ | Create an endpoint |
PATCH | /v1/developer/webhook-endpoints/{endpoint_id} | admin+ | Update label / URL / events / status |
DELETE | /v1/developer/webhook-endpoints/{endpoint_id} | admin+ | Delete an endpoint |
POST | /v1/developer/webhook-endpoints/{endpoint_id}/test | admin+ | Send a signed test delivery |
Endpoint object
{
"id": "c4d5e6f7-...",
"label": "Production — Call events",
"url": "https://example.com/thunderphone/hook",
"events": ["telephony.incoming", "telephony.complete"],
"status": "active",
"secret_hint": "a1b2…9f0e",
"created_at": "2026-04-20T18:24:10.113Z",
"updated_at": "2026-04-20T18:24:10.113Z"
}| Field | Type | Description |
|---|---|---|
id | UUID | Endpoint id |
label | string | Display name, 1–120 chars |
url | string | HTTPS URL; http://localhost allowed for dev |
events | array of string | Subscribed event types (see valid values). Empty array subscribes to all events except the explicit-only per-turn events (telephony.turn / web.turn) |
status | string | active, disabled (manually paused), or failing (auto-set when a delivery exhausts its 24 h retry schedule without a single 2xx) |
secret_hint | string | First 4 and last 4 characters of the signing secret with an ellipsis (a1b2…9f0e) — enough to cross-reference the secret you saved locally without exposing the full value |
created_at, updated_at | timestamp |
Valid event types
events is validated against this exact set — values outside the list
return 400. See Events catalog for each type's
payload shape.
telephony.incoming,telephony.complete,telephony.tool,telephony.turnweb.incoming,web.complete,web.tool,web.turncall.gradedissue.reportedtest-call.completedalert.triggered
Endpoint statuses
active— deliveries flow normally.disabled— manually paused viaPATCH. No requests are sent. We never change adisabledendpoint's status; flipping it back toactiveis always your call.failing— set automatically when a delivery to the endpoint burns through its entire retry schedule (8 attempts over 24 hours) without ever getting a 2xx. A failing endpoint receives no further traffic. Once the endpoint is fixed,PATCHits status back toactive; deliveries whose retry schedule hasn't run out yet resume where they left off.
List endpoints
curl https://api.thunderphone.com/v1/developer/webhook-endpoints \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns an array of Endpoint objects.
Create an endpoint
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Production — Call events",
"url": "https://example.com/thunderphone/hook",
"events": ["telephony.incoming", "telephony.complete"]
}'result = requests.post(
"https://api.thunderphone.com/v1/developer/webhook-endpoints",
headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
json={
"label": "Production — Call events",
"url": "https://example.com/thunderphone/hook",
"events": ["telephony.incoming", "telephony.complete"],
},
).json()
secret = result["secret"]
endpoint_id = result["id"]Request fields
| Field | Type | Required | Description |
|---|---|---|---|
label | string | yes | 1–120 chars |
url | string | yes | HTTPS URL (http allowed only for localhost / 127.0.0.1) |
events | array | no | Empty/omitted subscribes to all events except telephony.turn / web.turn, which require an explicit subscription. Must use the values listed in Valid event types; duplicates are removed |
Returns 201 Created with the Endpoint object plus
an extra top-level secret field containing the raw signing key — a
48-character hex string:
{
"id": "c4d5e6f7-…",
"label": "Production — Call events",
"url": "https://example.com/thunderphone/hook",
"events": ["telephony.incoming", "telephony.complete"],
"status": "active",
"secret_hint": "a1b2…9f0e",
"created_at": "2026-04-20T18:24:10.113Z",
"updated_at": "2026-04-20T18:24:10.113Z",
"secret": "a1b2c37e08d94f5b16a2c8d90e7f3a4b5c6d7e8f90a19f0e"
}Update an endpoint
curl -X PATCH https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-... \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "Production — Call + Grade events",
"events": ["telephony.incoming", "telephony.complete", "call.graded"]
}'| Field | Type | Description |
|---|---|---|
label | string | |
url | string | |
events | array | |
status | string | active or disabled. Set active to re-enable an endpoint the server marked failing |
Returns 200 OK with the updated Endpoint object.
Send a test delivery
Send a synthetic webhook.test event to one endpoint using the normal
delivery pipeline, including canonical JSON serialization,
X-ThunderPhone-Signature, delivery recording, and retry bookkeeping.
The test targets the selected endpoint regardless of its events filter.
curl -X POST https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-.../test \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"The endpoint receives an envelope like:
{
"data": {
"message": "ThunderPhone webhook test",
"sent_at": "2026-07-17T20:12:34.567890+00:00"
},
"event_id": "2ad6507c-7d19-4498-9b2d-7e8f944ab5a1",
"type": "webhook.test"
}The API returns 200 OK after the first attempt, even if the destination
returns an error. Inspect success, status, response_code, and error
for the delivery outcome:
{
"success": true,
"event_id": "2ad6507c-7d19-4498-9b2d-7e8f944ab5a1",
"event_type": "webhook.test",
"status": "delivered",
"response_code": 204,
"error": ""
}webhook.test is synthetic and cannot be added to an endpoint's events
subscription. If the first attempt fails, the delivery follows the same
retry schedule as normal event deliveries.
Delete an endpoint
curl -X DELETE https://api.thunderphone.com/v1/developer/webhook-endpoints/c4d5e6f7-... \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"Returns 204 No Content. Delivery to the URL stops immediately;
in-flight retries are abandoned.