ThunderPhone 2.0 is live.Self-serve, from 2¢/min.Read the announcement

Webhooks

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

MethodPathRequired roleDescription
GET/v1/developer/webhook-endpointsadmin+List endpoints
POST/v1/developer/webhook-endpointsadmin+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}/testadmin+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"
}
FieldTypeDescription
idUUIDEndpoint id
labelstringDisplay name, 1–120 chars
urlstringHTTPS URL; http://localhost allowed for dev
eventsarray of stringSubscribed event types (see valid values). Empty array subscribes to all events except the explicit-only per-turn events (telephony.turn / web.turn)
statusstringactive, disabled (manually paused), or failing (auto-set when a delivery exhausts its 24 h retry schedule without a single 2xx)
secret_hintstringFirst 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_attimestamp

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.turn
  • web.incoming, web.complete, web.tool, web.turn
  • call.graded
  • issue.reported
  • test-call.completed
  • alert.triggered

Endpoint statuses

  • active — deliveries flow normally.
  • disabled — manually paused via PATCH. No requests are sent. We never change a disabled endpoint's status; flipping it back to active is 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, PATCH its status back to active; deliveries whose retry schedule hasn't run out yet resume where they left off.

List endpoints

cURL
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
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"]
  }'
Python
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

FieldTypeRequiredDescription
labelstringyes1–120 chars
urlstringyesHTTPS URL (http allowed only for localhost / 127.0.0.1)
eventsarraynoEmpty/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
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"]
  }'
FieldTypeDescription
labelstring
urlstring
eventsarray
statusstringactive 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
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
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.