Open in
Webhook Deliveries
Inspect recent webhook delivery outcomes without exposing event payloads or transcripts.
Use the delivery history endpoint to confirm whether ThunderPhone attempted a webhook, the safe outcome category, and whether another retry is scheduled. It merges the recommended webhook endpoint system with the legacy single-URL call-finished webhook.
The same recent history appears in the dashboard under Agents → select an agent → Webhooks → Recent deliveries. Each row identifies the endpoint label and the safe URL origin used by its latest attempt.
This history is recent operational state, not an immutable audit log. The endpoint system stores only the latest outcome for each queued delivery, and deleting an endpoint also deletes its delivery rows. Retrieve any records you need to retain before deleting an endpoint.
List deliveries
GET /v1/developer/webhook-deliveriesRequires an organization admin. Authenticate with a server API key, an admin session
token, or OAuth authorized for the organization with integrations:read. OAuth access
still uses the current user's live organization role, so members cannot read this route.
curl "https://api.thunderphone.com/v1/developer/webhook-deliveries?status=retrying&limit=50" \
-H "Authorization: Bearer sk_live_YOUR_API_KEY"deliveries = requests.get(
"https://api.thunderphone.com/v1/developer/webhook-deliveries",
headers={"Authorization": "Bearer sk_live_YOUR_API_KEY"},
params={"agent_id": 42, "since": "2026-09-18T00:00:00Z"},
).json()Filters
| Parameter | Type | Description |
|---|---|---|
agent_id | integer | Deliveries associated with one agent |
endpoint_id | UUID or legacy | One endpoint, or only the legacy single-URL webhook |
event_type | string | Exact event type, such as telephony.complete |
status | string | delivered, retrying, or failed |
since | RFC 3339 timestamp | Result-change window; defaults to 7 days ago and is clamped to at most 30 days ago |
limit | integer | Page size, 1–50; defaults to 50 |
cursor | string | Opaque next_cursor from the previous page; reuse it only with the same organization, filters, since, and limit |
Response
{
"results": [
{
"id": "0b6f8a6e-4e1c-4d8e-9a43-7f1d2c3b4a59",
"source": "endpoint",
"endpoint_id": "00000000-0000-4000-8000-000000000001",
"endpoint_label": "n8n production",
"url": "https://n8n.example.com",
"event_type": "telephony.complete",
"call_id": 1001,
"status": "retrying",
"attempt_count": 2,
"http_status": 404,
"failure_category": "http_error",
"last_attempt_at": "2026-09-19T16:20:00Z",
"next_retry_at": "2026-09-19T16:25:00Z",
"duration_ms": 412,
"is_test": false,
"can_resend": false,
"was_resent": false,
"created_at": "2026-09-19T16:15:00Z",
"resent_at": null
}
],
"limit": 50,
"has_more": false,
"next_cursor": null
}source is endpoint for the endpoint-based system and legacy for the
single-URL call-finished webhook. Legacy rows use endpoint_id: "legacy".
url is the sanitized origin used by the most recent recorded attempt. It is
null for rows created before destination snapshots were available.
http_status can be null when no response was received or the legacy success
path did not retain the status code. failure_category is timeout, dns, tls,
connection, http_error, other, or null when there is no failure. Legacy rows
created before destination snapshots were added return a generic label and url: null.
next_retry_at is present only while the delivery is retrying.
id is the event's event_id, the same value the envelope carries; with endpoint_id
it identifies one delivery. It is null on legacy rows. duration_ms is how long the
endpoint took to answer, or to fail, on the latest attempt; it is null before an attempt
reached the endpoint and on legacy rows. is_test marks test sends. can_resend is true
for a real delivery whose retries ran out (failed); see Resend a delivery.
was_resent is true once someone has resent it, and resent_at says when. created_at is
when the event was first queued for the endpoint; a resend changes last_attempt_at, not
created_at.
Interpreting common failures
404from n8n: confirm the workflow is active, the webhook usesPOST, and the production URL is configured instead of n8n's test URL.401or403: the destination rejected authentication. Check its credentials, authorization policy, and ThunderPhone signature verification.timeout: the destination did not answer before the webhook timeout. Check its availability and response time.dns: check that the configured hostname resolves publicly.tls: check the certificate chain, hostname, and certificate expiry.connection: check that the destination accepts and keeps HTTPS connections.other: the stored failure could not be safely classified; no raw response is exposed.
Resend a delivery
POST /v1/developer/webhook-endpoints/{endpoint_id}/deliveries/{id}/resendSends a failed delivery to its endpoint once more, right away, and returns the updated
delivery row plus resent. Requires an organization admin, the same as the list.
- The envelope keeps its
event_id, so a receiver that already processed the event can drop the copy. It is signed when it is sent, with the endpoint's current secret. - Only real deliveries whose retries ran out can be resent. A delivered, still-retrying or
test delivery gets
409with acode(already_delivered,already_retrying,test_delivery). Turn a paused or disabled endpoint back on first (endpoint_not_active). - A resend is one attempt. If it fails, the delivery stays
failedand the endpoint is not paused again. - A webhook that no longer subscribes to the event's type gets
409withevent_not_subscribed. - Pass
idempotency_key, a value unique to the click or request. Repeating the request with the key of the delivery's latest resend sends nothing and returnsresent: false. - Resends are limited per endpoint (
429when exceeded).
curl -X POST "https://api.thunderphone.com/v1/developer/webhook-endpoints/00000000-0000-4000-8000-000000000001/deliveries/0b6f8a6e-4e1c-4d8e-9a43-7f1d2c3b4a59/resend" \
-H "Authorization: Bearer sk_live_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"idempotency_key": "8c1f4a2e-resend-1"}'