Webhook deliveries
Read every webhook delivery with its request body and attempts, and send a delivery again, with the AbuseZero REST API.
A webhook delivery is one event sent to one endpoint, with every attempt AbuseZero made. The delivery log keeps deliveries for 30 days, with the exact request body and each attempt's status code, timing and reply.
Examples on this page use a server client:
import { AbuseZero } from "abusezero/server";
const abusezero = new AbuseZero({ key: process.env.ABUSEZERO_SECRET_KEY!, outage: "throw" });The webhook delivery object
{
"object": "webhook_delivery",
"id": "whd_1f6b3d84-7a2c-4e95-8b10-6d4e2a9c7f35",
"endpoint": "whe_7d2e9a40-3c1b-4f65-8e27-b9a0c4d61f53",
"event": { "id": "evt_9b3e7c21-4d6a-4f58-a1e2-7c0d5b8f3a64", "type": "evaluation.completed" },
"kind": "event",
"status": "pending",
"attempts": 2,
"last_status_code": 503,
"last_error": null,
"next_attempt_at": "2026-10-29T14:07:47.000Z",
"created_at": "2026-10-29T14:02:47.000Z",
"completed_at": null
}| Field | Type | Description |
|---|---|---|
object | "webhook_delivery" | The object type. |
id | string | The delivery ID, starting with whd_. |
endpoint | string | The endpoint ID, starting with whe_. |
event | object | The event's id (evt_…) and type. |
kind | event, test or resend | event for a delivery of a new event, test for a test event, resend for a delivery sent again. |
status | string | See Delivery statuses. |
attempts | integer | How many attempts were made. |
last_status_code | integer or null | The HTTP status of the last reply, or null when there was none. |
last_error | string or null | Why the last attempt got no reply, or why the delivery wasn't sent. See Delivery errors. |
next_attempt_at | string or null | When the next attempt is due. null once the delivery is finished. |
created_at | string | When the delivery was created. |
completed_at | string or null | When the delivery succeeded, failed or was marked not sent. |
GET /v1/webhook-deliveries/{id} adds two fields:
| Field | Type | Description |
|---|---|---|
payload | object | The exact request body: the event object. |
attempt_log | array | Every attempt, oldest first. See below. |
attempt_log field | Type | Description |
|---|---|---|
attempt | integer | The attempt number, from 1. |
started_at | string | When the attempt started. |
duration_ms | integer | How long the attempt took, in milliseconds. |
status_code | integer or null | The HTTP status of the reply. |
error | string or null | Why there was no reply. See Delivery errors. |
counted | boolean | Whether the attempt counted as a delivery. See What counts as a delivery. |
response | string or null | The first 1 KB of your reply's body. |
Delivery statuses
status | Dashboard | Meaning |
|---|---|---|
pending | Sending | Waiting for its next attempt, or being sent. |
held | Waiting for credits | The workspace has no included deliveries or credits left. The delivery waits up to 12 hours for them. |
succeeded | Delivered | The endpoint replied 2xx. |
failed | Failed | Every attempt failed. |
not_sent | Not sent | The delivery was never sent: the endpoint was paused, or no included deliveries or credits arrived within 12 hours. last_error says which. |
New statuses can be added within v1.
Delivery errors
error | Dashboard | Meaning | Counted |
|---|---|---|---|
timeout | Timed out after 10 seconds | The endpoint didn't reply within 10 seconds. | Yes |
connection_failed | Couldn't connect | The connection was refused, reset or never accepted. | Yes |
tls_failed | Certificate problem | The endpoint's certificate isn't valid for its host name, or can't be trusted. | Yes |
domain_not_found | Domain doesn't exist | The host name doesn't exist. | Yes |
address_blocked | Address isn't public | The host name points to an address that isn't on the public internet. AbuseZero sends only to public addresses. | No |
lookup_failed | Address lookup failed on our side | AbuseZero couldn't look up the host name. An event delivery is retried. | No |
internal_error | Failed on our side | The attempt failed inside AbuseZero. An event delivery is retried. | No |
endpoint_paused | Not sent: endpoint paused | The endpoint was paused before the delivery was sent. | No |
no_credits | Not sent: no included deliveries or credits left | No included deliveries or credits arrived within 12 hours. | No |
New errors can be added within v1. Treat an error you don't recognize as a failed attempt.
What counts as a delivery
Each workspace's plan includes deliveries every calendar month (UTC). Beyond them, deliveries use credits. See your plan on the pricing page and your usage in Workspace settings → Billing & Subscriptions.
| Counts | Doesn't count |
|---|---|
Any HTTP reply from your endpoint, including 3xx, 4xx and 5xx | Test events |
| A connection your endpoint refuses, resets or never accepts | Attempts that fail on AbuseZero's side (lookup_failed, internal_error) |
| A timeout after 10 seconds | Addresses that aren't public (address_blocked) |
| A certificate problem, or a host name that doesn't exist | Deliveries held or not sent |
| Every retry and every resend |
Each attempt's counted field says how it was counted. Only counted failures move an endpoint toward being paused.
List webhook deliveries
Returns deliveries from the last 30 days, most recent first.
| Method | Path | Scope | Idempotency-Key |
|---|---|---|---|
GET | /v1/webhook-deliveries | webhooks:read | Not used |
Also available as abusezero webhook-deliveries list in the AbuseZero CLI, abusezero_list_webhook_deliveries on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.
Query parameters
| Parameter | Type | Required | Limits | Description |
|---|---|---|---|---|
endpoint_id | string | No | A whe_ ID | Only deliveries to this endpoint. |
limit | integer | No | 1 to 100, default 50 | How many deliveries to return. |
cursor | string | No | The next_cursor from the previous page, unchanged. |
Example
const page = await abusezero.call("/v1/webhook-deliveries", {
input: { endpoint_id: "whe_7d2e9a40-3c1b-4f65-8e27-b9a0c4d61f53", limit: 20 },
});Response
Returns 200 with a list of webhook delivery objects, without payload and attempt_log.
{
"object": "list",
"data": [
{
"object": "webhook_delivery",
"id": "whd_1f6b3d84-7a2c-4e95-8b10-6d4e2a9c7f35",
"endpoint": "whe_7d2e9a40-3c1b-4f65-8e27-b9a0c4d61f53",
"event": { "id": "evt_9b3e7c21-4d6a-4f58-a1e2-7c0d5b8f3a64", "type": "evaluation.completed" },
"kind": "event",
"status": "succeeded",
"attempts": 3,
"last_status_code": 200,
"last_error": null,
"next_attempt_at": null,
"created_at": "2026-10-29T14:02:47.000Z",
"completed_at": "2026-10-29T14:07:48.000Z"
}
],
"next_cursor": null
}Retrieve a webhook delivery
Returns a delivery with its exact request body and every attempt.
| Method | Path | Scope | Idempotency-Key |
|---|---|---|---|
GET | /v1/webhook-deliveries/{id} | webhooks:read | Not used |
Also available as abusezero webhook-deliveries get in the AbuseZero CLI, abusezero_get_webhook_delivery on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The delivery ID, starting with whd_. |
Example
const delivery = await abusezero.call("/v1/webhook-deliveries/whd_1f6b3d84-7a2c-4e95-8b10-6d4e2a9c7f35");Response
Returns 200.
{
"object": "webhook_delivery",
"id": "whd_1f6b3d84-7a2c-4e95-8b10-6d4e2a9c7f35",
"endpoint": "whe_7d2e9a40-3c1b-4f65-8e27-b9a0c4d61f53",
"event": { "id": "evt_9b3e7c21-4d6a-4f58-a1e2-7c0d5b8f3a64", "type": "evaluation.completed" },
"kind": "event",
"status": "succeeded",
"attempts": 2,
"last_status_code": 200,
"last_error": null,
"next_attempt_at": null,
"created_at": "2026-10-29T14:02:47.000Z",
"completed_at": "2026-10-29T14:03:48.000Z",
"payload": {
"object": "event",
"id": "evt_9b3e7c21-4d6a-4f58-a1e2-7c0d5b8f3a64",
"type": "evaluation.completed",
"api_version": "v1",
"created_at": "2026-10-29T14:02:47.000Z",
"workspace": "ws_2c8f4a1d-7e3b-4c69-9d05-6a1f3e7b2c48",
"data": { "object": "evaluation", "id": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93", "result": "deny" }
},
"attempt_log": [
{ "attempt": 1, "started_at": "2026-10-29T14:02:47.000Z", "duration_ms": 10003, "status_code": null, "error": "timeout", "counted": true, "response": null },
{ "attempt": 2, "started_at": "2026-10-29T14:03:47.000Z", "duration_ms": 184, "status_code": 200, "error": null, "counted": true, "response": "ok" }
]
}payload.data is shortened here. The real body holds the full object.
Errors
| Code | Status | Meaning |
|---|---|---|
WEBHOOK_DELIVERY_NOT_FOUND | 404 | No delivery with this ID exists in the workspace, or it's older than the 30 days the delivery log keeps. |
INVALID_FIELD | 422 | The ID doesn't start with whd_. |
Resend a webhook delivery
Sends the delivery's event to the same endpoint again, right away, as a new delivery with kind: "resend". The event ID and body are unchanged, so your duplicate check recognizes it.
| Method | Path | Scope | Idempotency-Key |
|---|---|---|---|
POST | /v1/webhook-deliveries/{id}/resend | webhooks:write | Required |
Also available as abusezero webhook-deliveries resend in the AbuseZero CLI, abusezero_resend_webhook_delivery on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.
- Any delivery in the log can be sent again, whatever its status.
- A resend is attempted once, and counts as a delivery.
- The endpoint must be active. Resume a paused endpoint first.
- With an OAuth token, the signed-in member must be the workspace owner or an admin, as in the dashboard.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The delivery ID, starting with whd_. |
Example
const resend = await abusezero.call("/v1/webhook-deliveries/whd_1f6b3d84-7a2c-4e95-8b10-6d4e2a9c7f35/resend", {
method: "POST",
idempotencyKey: "resend-whd_1f6b3d84",
});Response
Returns 201 with the new webhook delivery object, with kind: "resend" and status: "pending".
Errors
| Code | Status | Meaning |
|---|---|---|
WEBHOOK_DELIVERY_NOT_FOUND | 404 | No delivery with this ID exists in the workspace, or it's older than the delivery log keeps. |
WEBHOOK_ENDPOINT_PAUSED | 409 | The endpoint is paused. Resume it in Developers → Webhooks, then send again. |
PERMISSION_REQUIRED | 403 | With an OAuth token, the signed-in member isn't the workspace owner or an admin. |
For every other code, see REST API errors.
Retention
Deliveries, their attempts and their request bodies are kept for 30 days. Deleting an endpoint deletes its deliveries. A data deletion removes the deliveries of the evaluations and outcomes it deletes.
Related
How is this guide?
Last updated on