ProxioDocs
API Reference

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

FieldTypeNotes
idstringThis delivery's id. Matches the X-Proxio-Delivery header your endpoint received.
webhook_idstringThe endpoint this delivery was sent to.
event_typestringOne of the event catalog types.
event_idstringThe dedup key for the underlying event. See id and event_id below.
statusstringPENDING (queued or retrying), DELIVERED (a 2xx landed), or FAILED (attempts exhausted, the endpoint was disabled, or the URL failed its SSRF re-check).
attemptsintegerAttempts in the current cycle. See attempts and redelivery_count.
redelivery_countintegerHow many times this delivery has been replayed via redeliver. 0 until the first replay.
response_statusinteger | nullYour endpoint's HTTP status on the last attempt. null if no response was ever received.
response_bodystring | nullThe first 512 bytes of your endpoint's response body, \n[truncated] appended if it was longer. null if no response was received.
errorstring | nullThe 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.
payloadobjectThe exact JSON body that was (or will be) sent, data field of the event envelope.
last_attempt_atstring | nullWhen the most recent attempt ran.
last_redelivered_atstring | nullWhen this delivery was last replayed, null until the first replay.
created_atstringWhen 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"])  # PENDING
import { 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) // PENDING

200 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:

CodeHTTPWhen
NOT_FOUND404Unknown delivery, or it aged out of the 30-day window and its payload was already dropped.
CONFLICT409The endpoint is currently disabled, re-enable it with PATCH /webhooks/{id} first.
CONFLICT409The delivery is already PENDING (mid-flight), wait for the attempt in progress to finish.
RATE_LIMITED429More 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

ParameterValuesNotes
typean event typeExact match. An unrecognized type fails with VALIDATION_ERROR rather than returning an empty page, so a typo doesn't read as "nothing happened".
deliveredtrue | falsetrue keeps only DELIVERED rows; false keeps everything that isn't.
webhook_idan endpoint idNarrows to one endpoint (same rows GET /webhooks/{id}/deliveries would return). An id you don't own returns NOT_FOUND.
limit, cursorsee 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.

On this page