Webhook deliveries
Read-only access to delivery attempt history for any of your webhook endpoints. Useful for debugging integration issues, reconciling missed events, and audit trails for compliance reviews.
The webhook_delivery object
{
"object": "webhook_delivery",
"id": "ckwhd_xxxxxxxxxxxx",
"endpoint_id": "ckwhe_xxxxxxxxxxxx",
"event_type": "payout_paid",
"status": "delivered",
"attempts": 1,
"response_status": 200,
"response_body": "{\"received\":true}",
"error_message": null,
"next_attempt_at": null,
"delivered_at": "2026-05-05T12:34:56Z",
"created_at": "2026-05-05T12:34:56Z"
}Status values
| Status | Description |
|---|---|
pending | Queued, awaiting first attempt |
delivered | Receiver returned 2xx |
failed | Receiver returned non-2xx OR timed out — will retry |
giving_up | All retry attempts exhausted (7 by default) — won’t retry again |
GETList deliveries
1-100, default 50
Cursor
Filter to one endpoint
Filter — pending | delivered | failed | giving_up
Filter to one event type, e.g. payout_paid
Standard cursor-paginated list.
# Recent failed deliveries on a specific endpoint
curl "https://api.swappr.me/api/v1/webhook_deliveries?endpoint_id=ckwhe_xxx&status=failed&limit=20" \
-H "Authorization: Bearer sk_test_..."{
"object": "list",
"has_more": true,
"data": [
{ "object": "webhook_delivery", "id": "...", "status": "failed", ... },
...
]
}POSTReplay a delivery
Re-fires the event to the same endpoint the original delivery targeted (surgical — not a fan-out to every subscriber). It creates a new delivery attempt with its own id, signed with the endpoint’s current secret; the original delivery row is never mutated. Replay is allowed regardless of the original’s status (delivered, failed, giving_up, pending) — the intent is “re-fire this event”. On a failed re-attempt the new row is left retriable, so the standard webhook-retry path picks it up. The response’s replayed_from_id links back to the original delivery.
Requires the webhook_manage permission. Returns 400 if the endpoint is inactive, 404 if the delivery isn’t found.
curl -X POST https://api.swappr.me/api/v1/webhook_deliveries/ckwhd_xxx/replay \
-H "Authorization: Bearer sk_test_..."{
"object": "webhook_delivery",
"id": "ckwhd_newattempt",
"endpoint_id": "ckwhe_xxx",
"event_type": "payout_paid",
"status": "delivered",
"attempts": 1,
"response_status": 200,
"error_message": null,
"replayed_from_id": "ckwhd_xxx",
"created_at": "2026-06-09T12:00:00.000Z",
"delivered_at": "2026-06-09T12:00:00.420Z"
}Common debugging flows
”Why didn’t I get a webhook for payout X?”
- List deliveries for that payout’s reference period:
curl "https://api.swappr.me/api/v1/webhook_deliveries?event_type=payout_paid&limit=50" \ -H "Authorization: Bearer sk_test_..." - Look for the delivery with
data.reference = po_xxx. Check itsstatusandresponse_status. - If
failedorgiving_up, theerror_message+response_bodywill tell you why. - Replay the delivery if needed —
POST /v1/webhook_deliveries/{id}/replay(see Replay a delivery) or the dashboard’s per-delivery Retry button.
”Receiver is timing out”
Filter to status=failed and look at the error_message field. Common patterns:
Timeout after 10s— your handler is too slow. Acknowledge the webhook fast (within 5 seconds) and process async.- Connection refused / DNS error — your endpoint is down. Re-deploy + replay deliveries.
”Need a missed event for reconciliation”
Webhooks dropped after 7 retries land in giving_up status. The data is still queryable here — pull the body from data.payload (when admin tooling exposes it; the public API doesn’t expose payload to keep response sizes manageable).
Replay any delivery via POST /v1/webhook_deliveries/{id}/replay (see Replay a delivery) or the dashboard’s per-delivery Retry button.
Rate limits
This endpoint counts toward the read tier (120/min). Polling for status changes is fine, but use webhooks themselves as the primary signal — this endpoint is for retrospective debugging, not real-time feeds.