Deliveries
The durable webhook delivery log and replay. GET /webhooks/{id}/deliveries and /deliveries/{deliveryId} hold 30 days of history per endpoint, POST .../redeliver replays one, and GET /events is the account-wide view across every endpoint, with type, delivered, and webhook_id filters.
GET /webhooks/{id} carries only the 10 most recent deliveries, a health
snapshot. This page covers the full log: every delivery for the last 30
days, per endpoint or across your whole account, plus replaying one that
failed, or one that already succeeded and you want to send again.
Scopes: read to list and read, write to redeliver.
The delivery log for one endpoint
GET /webhooks/{id}/deliveries returns the endpoint's deliveries, newest
first, cursor-paginated. Each row carries the full
payload that was sent, so replaying or inspecting a past event never needs a
second fetch.
curl "https://dashboard.proxio.net/api/v1/webhooks/clwh_2b8n/deliveries?limit=20" \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.get(
f"{BASE}/webhooks/clwh_2b8n/deliveries",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"limit": 20},
timeout=15,
)
resp.raise_for_status()
body = resp.json()
print(body["data"], body["meta"]["retention_days"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(`${BASE}/webhooks/clwh_2b8n/deliveries?limit=20`, {
headers: { Authorization: `Bearer ${API_KEY}` },
})
const body = await res.json()
console.log(body.data, body.meta.retention_days)200 response:
{
"data": [
{
"id": "cldlv_3k9v",
"webhook_id": "clwh_2b8n",
"event_type": "usage.threshold_reached",
"event_id": "usage.threshold_reached:clpkg_2a9x:2026-08-16T09:30:00.000Z:50000:80",
"status": "FAILED",
"attempts": 6,
"redelivery_count": 0,
"response_status": 500,
"response_body": "{\"error\":\"internal\"}",
"error": null,
"payload": { "service_id": "clpkg_2a9x", "threshold": 80, "used": { "...": "..." }, "limit": { "...": "..." } },
"last_attempt_at": "2026-08-16T09:35:40.000Z",
"last_redelivered_at": null,
"created_at": "2026-08-16T09:30:00.000Z"
}
],
"meta": {
"next_cursor": null,
"has_more": false,
"retention_days": 30,
"request_id": "req_8Ke2jP4mQ"
}
}meta.retention_days is always 30 today, it rides along so your code can
read the window from the response rather than hardcoding it. A delivery older
than the window simply stops appearing, there's no tombstone or placeholder
row for it.
Fields
| Field | Type | Notes |
|---|---|---|
id | string | This delivery's id. Matches the X-Proxio-Delivery header your endpoint received. |
webhook_id | string | The endpoint this delivery was sent to. |
event_type | string | One of the event catalog types. |
event_id | string | The dedup key for the underlying event. See id and event_id below. |
status | string | PENDING (queued or retrying), DELIVERED (a 2xx landed), or FAILED (attempts exhausted, the endpoint was disabled, or the URL failed its SSRF re-check). |
attempts | integer | Attempts in the current cycle. See attempts and redelivery_count. |
redelivery_count | integer | How many times this delivery has been replayed via redeliver. 0 until the first replay. |
response_status | integer | null | Your endpoint's HTTP status on the last attempt. null if no response was ever received. |
response_body | string | null | The first 512 bytes of your endpoint's response body, \n[truncated] appended if it was longer. null if no response was received. |
error | string | null | The transport-level failure (timeout, DNS failure, connection refused, up to 255 characters), only set when there was no response at all. null whenever response_status is set. |
payload | object | The exact JSON body that was (or will be) sent, data field of the event envelope. |
last_attempt_at | string | null | When the most recent attempt ran. |
last_redelivered_at | string | null | When this delivery was last replayed, null until the first replay. |
created_at | string | When the underlying event was emitted. |
attempts and redelivery_count
Two counters, two different histories
attempts counts tries in the delivery's current cycle, the original
send plus its automatic retries,
up to 6. Calling redeliver starts a new cycle: attempts
resets to 0 and climbs again from the replay, exactly as if the delivery
were brand new. redelivery_count is the one field that survives that
reset, it's incremented on every replay and is never touched by the
automatic retry loop. A delivery that failed all 6 attempts, got replayed,
and succeeded on the first try of the replay reports
attempts: 1, redelivery_count: 1, not a running total of 7.
id and event_id
A webhook event can fan out to more than one endpoint. id identifies this
delivery, to this endpoint, and it's the value your handler saw in the
X-Proxio-Delivery header, use it to look up the exact delivery a support
ticket refers to. event_id identifies the underlying event and is the
same string on every endpoint that event fanned out to, use it to recognize
that two different deliveries (different id, different webhook_id) were
triggered by the same thing happening once. See
GET /events below for where this distinction
matters most.
Get one delivery
GET /webhooks/{id}/deliveries/{deliveryId} returns a single delivery in the
same shape as a list row. It exists so that, holding only the
X-Proxio-Delivery header value your endpoint received, you can look that
exact delivery up directly instead of paging through the log for it.
curl https://dashboard.proxio.net/api/v1/webhooks/clwh_2b8n/deliveries/cldlv_3k9v \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.get(
f"{BASE}/webhooks/clwh_2b8n/deliveries/cldlv_3k9v",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/webhooks/clwh_2b8n/deliveries/cldlv_3k9v`,
{ headers: { Authorization: `Bearer ${API_KEY}` } },
)
console.log((await res.json()).data)An unknown or foreign delivery id, or one that has aged out of the 30-day
window, returns NOT_FOUND.
Redeliver
POST /webhooks/{id}/deliveries/{deliveryId}/redeliver re-sends the same
delivery. It doesn't create a new event, it re-enqueues the exact row, so
event_id is untouched and no duplicate event is ever manufactured.
Replaying an already-delivered event is the main use case
A DELIVERED row is redeliverable on purpose, not just a FAILED one. "The
endpoint was down, I fixed it, send it again" is exactly what this endpoint
is for, and the most common reason to call it. There's no restriction on
redelivering something that already succeeded.
A replay resets attempts to 0 and starts a fresh 6-attempt cycle with the
normal backoff schedule,
increments redelivery_count, and clears the previous attempt's
response_status / response_body / error so they don't linger next to a
result that hasn't happened yet.
curl -X POST \
https://dashboard.proxio.net/api/v1/webhooks/clwh_2b8n/deliveries/cldlv_3k9v/redeliver \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x" \
-H "Idempotency-Key: $(uuidgen)"import uuid
import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.post(
f"{BASE}/webhooks/clwh_2b8n/deliveries/cldlv_3k9v/redeliver",
headers={
"Authorization": f"Bearer {API_KEY}",
"Idempotency-Key": str(uuid.uuid4()),
},
timeout=15,
)
resp.raise_for_status()
print(resp.json()["data"]["status"]) # PENDINGimport { randomUUID } from "node:crypto"
const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/webhooks/clwh_2b8n/deliveries/cldlv_3k9v/redeliver`,
{
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Idempotency-Key": randomUUID(),
},
},
)
console.log((await res.json()).data.status) // PENDING200 response: the updated delivery, in the same shape as a list row, with
status: "PENDING", attempts: 0, and redelivery_count incremented.
A redeliver can fail with:
| Code | HTTP | When |
|---|---|---|
NOT_FOUND | 404 | Unknown delivery, or it aged out of the 30-day window and its payload was already dropped. |
CONFLICT | 409 | The endpoint is currently disabled, re-enable it with PATCH /webhooks/{id} first. |
CONFLICT | 409 | The delivery is already PENDING (mid-flight), wait for the attempt in progress to finish. |
RATE_LIMITED | 429 | More than 20 redeliveries per minute for your account. Retry-After is set. |
Account-wide event log
GET /events returns every delivery your account produced in the retention
window, across all of your webhook endpoints, newest first,
cursor-paginated. It's the same kind of row as the
per-endpoint log above, just not scoped to one webhook_id.
Query parameters
| Parameter | Values | Notes |
|---|---|---|
type | an event type | Exact match. An unrecognized type fails with VALIDATION_ERROR rather than returning an empty page, so a typo doesn't read as "nothing happened". |
delivered | true | false | true keeps only DELIVERED rows; false keeps everything that isn't. |
webhook_id | an endpoint id | Narrows to one endpoint (same rows GET /webhooks/{id}/deliveries would return). An id you don't own returns NOT_FOUND. |
limit, cursor | see Pagination |
Fan-out is not collapsed
If one event fans out to three subscribed endpoints, it appears here as
three rows, one per webhook_id, each with its own id but the same
event_id. This endpoint deliberately does not invent a single merged
identity for the event, doing so would mean minting an id that no delivery,
header, or signature your servers ever saw actually carries. If you want the
fan-out view, group the rows you get back by event_id yourself, that
grouping is exact and uses only values your endpoints already received.
curl "https://dashboard.proxio.net/api/v1/events?type=order.paid&delivered=false&limit=20" \
-H "Authorization: Bearer pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"import requests
BASE = "https://dashboard.proxio.net/api/v1"
API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
resp = requests.get(
f"{BASE}/events",
headers={"Authorization": f"Bearer {API_KEY}"},
params={"type": "order.paid", "delivered": "false", "limit": 20},
timeout=15,
)
resp.raise_for_status()
for row in resp.json()["data"]:
print(row["event_id"], row["webhook_id"], row["status"])const BASE = "https://dashboard.proxio.net/api/v1"
const API_KEY = "pxo_9fJ2kQ7xR4mN8pL1dW6vB3cH5tZ0aYqS7dK2mN9x"
const res = await fetch(
`${BASE}/events?type=order.paid&delivered=false&limit=20`,
{ headers: { Authorization: `Bearer ${API_KEY}` } },
)
const { data } = await res.json()
for (const row of data) console.log(row.event_id, row.webhook_id, row.status)200 response: an array of rows in the same shape as the
per-endpoint delivery log, plus meta.retention_days.
Related pages
Webhooks
Receive events from Proxio instead of polling, as a signed JSON envelope or delivered to Discord or Slack. Full event catalog, payload shape, an X-Proxio-Signature verification walkthrough in Python and Node.js (t=,v1= scheme with 300s tolerance, JSON format only), retry and backoff, auto-disable, secret rotation, and a test endpoint.
API Changelog
Additive-tagged changes to the Proxio API. Under the v1 stability policy, new fields, endpoints, events, and error codes ship continuously without breaking existing clients. Describes the current v1 surface.

