Skip to content
Developers

Webhooks reference

Signed webhook endpoints and their deliveries.

List webhook endpoints

GET/webhook-endpoints

Scope:
webhooks
Rate class:
light
Operation id:
list_webhook_endpoints

Lists the workspace's webhook endpoints, oldest first (at most 5). A single endpoint the dashboard had before is moved into this list the first time it is needed. Needs the webhooks scope. The signing secret is never in a list.

Parameters

NameInTypeDescription
limitqueryintegeroptionalItems per page, 1 to 200.Range: 1 to 200. Default: 50.
cursorquerystringoptionalThe next_cursor of the previous page. Opaque: never build one.

Answer 200

OK. A page of WebhookEndpoint objects, with next_cursor and has_more.

FieldTypeDescription
dataarray of objectThe items of this page, in the endpoint's fixed order.
data[].idintegerThe endpoint's id.
data[].urlstringWhere deliveries are POSTed (https, a public address).
data[].eventsarray of stringThe events it takes: names from GET /webhook-events.
data[].enabledbooleanFalse while the endpoint is paused: no event is sent to it.
data[].descriptionstring or nullYour note about the endpoint.
data[].sourcestringWhere it was made: the dashboard, this API, or moved from the single endpoint the dashboard had before.One of: dashboard, api, migrated.
data[].signedbooleanTrue when deliveries carry a PerfBee-Signature. An endpoint moved from before without a secret is unsigned until you rotate one.
data[].previous_secret_valid_untilstring or nullWhile a rotation overlaps: when the old secret stops signing (ISO 8601 UTC); otherwise null.
data[].legacy_secret_header_untilstring or nullOnly on an endpoint moved from before: when the old plain X-PerfBee-Secret header stops (ISO 8601 UTC); otherwise null.
data[].last_delivery_atstring or nullWhen the last attempt was made (ISO 8601 UTC); null before the first.
data[].last_statusstring or nullThe last delivery's state: delivered, retrying or failed.
data[].consecutive_failuresintegerFailed attempts in a row; a delivered event ends the run.
data[].created_atstring or nullWhen the endpoint was made (ISO 8601 UTC).
has_morebooleanTrue when another page follows.
next_cursorstring or nullPass as cursor to get the next page; null on the last page.
Example 200 answer of List webhook endpoints
{
  "data": [
    {
      "id": 3,
      "url": "https://hooks.example.com/perfbee",
      "events": [
        "scan_data_ready",
        "scan_failed"
      ],
      "enabled": true,
      "description": "CI gate",
      "source": "api",
      "signed": true,
      "last_delivery_at": "2026-11-01T09:30:12Z",
      "last_status": "delivered",
      "consecutive_failures": 0,
      "created_at": "2026-10-20T14:02:00Z"
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

Problems this call documents

Create a webhook endpoint

POST/webhook-endpoints

Scope:
webhooks
Rate class:
light
Operation id:
create_webhook_endpoint

Adds an endpoint that receives the events you pick, signed with PerfBee-Signature. The answer carries the signing secret: it is shown once, here, and cannot be read again (rotate it to get a new one). The address must be an https URL on a public host. Needs the webhooks scope.

Request body

application/json, required.

FieldTypeDescription
urlstringrequiredAn https:// address on a public host, without credentials. It is checked again at every delivery.Range: at most 512 characters.
eventsarray of stringrequiredThe events to send: names from GET /webhook-events.Range: at most 40 items.
descriptionstring or nulloptionalA note for yourself.Range: at most 200 characters.
Example request body of Create a webhook endpoint
{
  "url": "https://hooks.example.com/perfbee",
  "events": [
    "scan_data_ready",
    "scan_failed"
  ],
  "description": "CI gate"
}

Answer 201

Created. The body is a WebhookEndpointSecret.

FieldTypeDescription
idintegerThe endpoint's id.
urlstringWhere deliveries are POSTed (https, a public address).
eventsarray of stringThe events it takes: names from GET /webhook-events.
enabledbooleanFalse while the endpoint is paused: no event is sent to it.
descriptionstring or nullYour note about the endpoint.
sourcestringWhere it was made: the dashboard, this API, or moved from the single endpoint the dashboard had before.One of: dashboard, api, migrated.
signedbooleanTrue when deliveries carry a PerfBee-Signature. An endpoint moved from before without a secret is unsigned until you rotate one.
previous_secret_valid_untilstring or nullWhile a rotation overlaps: when the old secret stops signing (ISO 8601 UTC); otherwise null.
legacy_secret_header_untilstring or nullOnly on an endpoint moved from before: when the old plain X-PerfBee-Secret header stops (ISO 8601 UTC); otherwise null.
last_delivery_atstring or nullWhen the last attempt was made (ISO 8601 UTC); null before the first.
last_statusstring or nullThe last delivery's state: delivered, retrying or failed.
consecutive_failuresintegerFailed attempts in a row; a delivered event ends the run.
created_atstring or nullWhen the endpoint was made (ISO 8601 UTC).
secretstringThe signing secret (whsec_...). Shown ONCE, here: store it now, it cannot be read again.
Example 201 answer of Create a webhook endpoint
{
  "id": 3,
  "url": "https://hooks.example.com/perfbee",
  "events": [
    "scan_data_ready",
    "scan_failed"
  ],
  "enabled": true,
  "description": "CI gate",
  "source": "api",
  "signed": true,
  "last_delivery_at": "2026-11-01T09:30:12Z",
  "last_status": "delivered",
  "consecutive_failures": 0,
  "created_at": "2026-10-20T14:02:00Z",
  "secret": "whsec_p3Qx1kT0n8a5ZyV2mL7cR4dB9wUeHsJfGiNoAqXtYbc"
}

Problems this call documents

Get a webhook endpoint

GET/webhook-endpoints/{endpoint_id}

Scope:
webhooks
Rate class:
light
Operation id:
get_webhook_endpoint

One endpoint of the workspace: where it sends, which events, whether it is paused, how its last delivery went. Needs the webhooks scope. The signing secret is never returned.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.

Answer 200

OK. The body is a WebhookEndpoint.

FieldTypeDescription
idintegerThe endpoint's id.
urlstringWhere deliveries are POSTed (https, a public address).
eventsarray of stringThe events it takes: names from GET /webhook-events.
enabledbooleanFalse while the endpoint is paused: no event is sent to it.
descriptionstring or nullYour note about the endpoint.
sourcestringWhere it was made: the dashboard, this API, or moved from the single endpoint the dashboard had before.One of: dashboard, api, migrated.
signedbooleanTrue when deliveries carry a PerfBee-Signature. An endpoint moved from before without a secret is unsigned until you rotate one.
previous_secret_valid_untilstring or nullWhile a rotation overlaps: when the old secret stops signing (ISO 8601 UTC); otherwise null.
legacy_secret_header_untilstring or nullOnly on an endpoint moved from before: when the old plain X-PerfBee-Secret header stops (ISO 8601 UTC); otherwise null.
last_delivery_atstring or nullWhen the last attempt was made (ISO 8601 UTC); null before the first.
last_statusstring or nullThe last delivery's state: delivered, retrying or failed.
consecutive_failuresintegerFailed attempts in a row; a delivered event ends the run.
created_atstring or nullWhen the endpoint was made (ISO 8601 UTC).
Example 200 answer of Get a webhook endpoint
{
  "id": 3,
  "url": "https://hooks.example.com/perfbee",
  "events": [
    "scan_data_ready",
    "scan_failed"
  ],
  "enabled": true,
  "description": "CI gate",
  "source": "api",
  "signed": true,
  "last_delivery_at": "2026-11-01T09:30:12Z",
  "last_status": "delivered",
  "consecutive_failures": 0,
  "created_at": "2026-10-20T14:02:00Z"
}

Problems this call documents

Change or pause a webhook endpoint

PATCH/webhook-endpoints/{endpoint_id}

Scope:
webhooks
Rate class:
light
Operation id:
update_webhook_endpoint

Changes the fields you send: the address (its delivery history starts again), the events, the note, or enabled (false pauses it: nothing is sent while it is paused). Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.

Request body

application/json, required.

FieldTypeDescription
descriptionstring or nulloptionalA new note, or null to clear it.Range: at most 200 characters.
enabledboolean or nulloptionalfalse pauses the endpoint, true resumes it.
eventsarray of string or nulloptionalThe new list of events.Range: at most 40 items.
urlstring or nulloptionalA new address; the endpoint's delivery history starts again.Range: at most 512 characters.
Example request body of Change or pause a webhook endpoint
{
  "enabled": false
}

Answer 200

OK. The body is a WebhookEndpoint.

FieldTypeDescription
idintegerThe endpoint's id.
urlstringWhere deliveries are POSTed (https, a public address).
eventsarray of stringThe events it takes: names from GET /webhook-events.
enabledbooleanFalse while the endpoint is paused: no event is sent to it.
descriptionstring or nullYour note about the endpoint.
sourcestringWhere it was made: the dashboard, this API, or moved from the single endpoint the dashboard had before.One of: dashboard, api, migrated.
signedbooleanTrue when deliveries carry a PerfBee-Signature. An endpoint moved from before without a secret is unsigned until you rotate one.
previous_secret_valid_untilstring or nullWhile a rotation overlaps: when the old secret stops signing (ISO 8601 UTC); otherwise null.
legacy_secret_header_untilstring or nullOnly on an endpoint moved from before: when the old plain X-PerfBee-Secret header stops (ISO 8601 UTC); otherwise null.
last_delivery_atstring or nullWhen the last attempt was made (ISO 8601 UTC); null before the first.
last_statusstring or nullThe last delivery's state: delivered, retrying or failed.
consecutive_failuresintegerFailed attempts in a row; a delivered event ends the run.
created_atstring or nullWhen the endpoint was made (ISO 8601 UTC).
Example 200 answer of Change or pause a webhook endpoint
{
  "id": 3,
  "url": "https://hooks.example.com/perfbee",
  "events": [
    "scan_data_ready",
    "scan_failed"
  ],
  "enabled": true,
  "description": "CI gate",
  "source": "api",
  "signed": true,
  "last_delivery_at": "2026-11-01T09:30:12Z",
  "last_status": "delivered",
  "consecutive_failures": 0,
  "created_at": "2026-10-20T14:02:00Z"
}

Problems this call documents

Delete a webhook endpoint

DELETE/webhook-endpoints/{endpoint_id}

Scope:
webhooks
Rate class:
light
Operation id:
delete_webhook_endpoint

Removes the endpoint and its delivery list. Nothing is sent to it from now on. Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.

Answer 204

Done. There is no body.

Problems this call documents

List an endpoint's deliveries

GET/webhook-endpoints/{endpoint_id}/deliveries

Scope:
webhooks
Rate class:
light
Operation id:
list_webhook_deliveries

The endpoint's deliveries, newest first, kept for 14 days: one row per event sent to it, with its state, tries and the receiver's answer. A delivery that is not yet delivered carries its payload (a failed one for 3 days). Filter with status. Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.
statusquerystringoptionalOnly deliveries in this state: pending, retrying, delivered or failed.
limitqueryintegeroptionalItems per page, 1 to 200.Range: 1 to 200. Default: 50.
cursorquerystringoptionalThe next_cursor of the previous page. Opaque: never build one.

Answer 200

OK. A page of WebhookDelivery objects, with next_cursor and has_more.

FieldTypeDescription
dataarray of objectThe items of this page, in the endpoint's fixed order.
data[].idintegerThe delivery's id (one event sent to one endpoint, with its retries).
data[].eventstringThe event, or test for a test delivery.
data[].event_idstringThe event's id, the same as id in the payload and in PerfBee-Event-Id; it stays the same on every attempt and every redelivery.
data[].statusstringpending (waiting for its first attempt), retrying, delivered or failed.One of: pending, retrying, delivered, failed.
data[].attemptintegerThe number of the attempt made last (0 before the first).
data[].max_attemptsintegerHow many attempts a delivery gets.
data[].http_statusinteger or nullThe receiver's HTTP status on the last attempt; null when it did not answer.
data[].errorstring or nullWhy the last attempt failed when there was no usable answer: a short sentence.
data[].duration_msinteger or nullHow long the last attempt took.
data[].next_attempt_atstring or nullWhile retrying: when the next attempt is due (ISO 8601 UTC).
data[].created_atstring or nullWhen the event was queued for this endpoint (ISO 8601 UTC).
data[].payloadobject or nullThe body sent, kept until the delivery is delivered; a failed delivery keeps it for 3 days. null otherwise.
has_morebooleanTrue when another page follows.
next_cursorstring or nullPass as cursor to get the next page; null on the last page.
Example 200 answer of List an endpoint's deliveries
{
  "data": [
    {
      "id": 4182,
      "event": "scan_data_ready",
      "event_id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
      "status": "failed",
      "attempt": 5,
      "max_attempts": 5,
      "http_status": 503,
      "duration_ms": 412,
      "created_at": "2026-11-01T09:30:12Z",
      "payload": {
        "api_version": "v1",
        "data": {
          "crawl_id": "0f7c9a52-3d4b-4a39-8c0e-5e2a9d1b6f10",
          "mode": "full",
          "site_id": 7
        },
        "event": "scan_data_ready",
        "event_description": "Crawl data ready",
        "id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
        "timestamp": "2026-11-01T09:30:12.512Z"
      }
    }
  ],
  "has_more": true,
  "next_cursor": "eyJ2IjoxfQ.opaque"
}

Problems this call documents

Send a failed delivery again

POST/webhook-endpoints/{endpoint_id}/deliveries/{delivery_id}/redeliver

Scope:
webhooks
Rate class:
heavy
Operation id:
redeliver_webhook_delivery

Starts a new chain of attempts for a failed delivery: a new delivery with the same event id and payload, answered at once with state pending (the attempts run in the background). Only a failed delivery whose payload is still stored (3 days) can be sent again. Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.
delivery_idpathintegerrequiredThe delivery's id, from GET /webhook-endpoints/{endpoint_id}/deliveries.

Answer 202

Accepted. The body is a WebhookDelivery.

FieldTypeDescription
idintegerThe delivery's id (one event sent to one endpoint, with its retries).
eventstringThe event, or test for a test delivery.
event_idstringThe event's id, the same as id in the payload and in PerfBee-Event-Id; it stays the same on every attempt and every redelivery.
statusstringpending (waiting for its first attempt), retrying, delivered or failed.One of: pending, retrying, delivered, failed.
attemptintegerThe number of the attempt made last (0 before the first).
max_attemptsintegerHow many attempts a delivery gets.
http_statusinteger or nullThe receiver's HTTP status on the last attempt; null when it did not answer.
errorstring or nullWhy the last attempt failed when there was no usable answer: a short sentence.
duration_msinteger or nullHow long the last attempt took.
next_attempt_atstring or nullWhile retrying: when the next attempt is due (ISO 8601 UTC).
created_atstring or nullWhen the event was queued for this endpoint (ISO 8601 UTC).
payloadobject or nullThe body sent, kept until the delivery is delivered; a failed delivery keeps it for 3 days. null otherwise.
Example 202 answer of Send a failed delivery again
{
  "id": 4182,
  "event": "scan_data_ready",
  "event_id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
  "status": "failed",
  "attempt": 5,
  "max_attempts": 5,
  "http_status": 503,
  "duration_ms": 412,
  "created_at": "2026-11-01T09:30:12Z",
  "payload": {
    "api_version": "v1",
    "data": {
      "crawl_id": "0f7c9a52-3d4b-4a39-8c0e-5e2a9d1b6f10",
      "mode": "full",
      "site_id": 7
    },
    "event": "scan_data_ready",
    "event_description": "Crawl data ready",
    "id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
    "timestamp": "2026-11-01T09:30:12.512Z"
  }
}

Problems this call documents

Rotate the signing secret

POST/webhook-endpoints/{endpoint_id}/rotate-secret

Scope:
webhooks
Rate class:
light
Operation id:
rotate_webhook_secret

Makes a new signing secret and returns it, once. The old secret keeps signing for 24 hours beside the new one (both signatures are sent), so a receiver can switch without a gap. On an endpoint moved from the old single endpoint this also stops the plain X-PerfBee-Secret header. Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.

Answer 200

OK. The body is a WebhookEndpointSecret.

FieldTypeDescription
idintegerThe endpoint's id.
urlstringWhere deliveries are POSTed (https, a public address).
eventsarray of stringThe events it takes: names from GET /webhook-events.
enabledbooleanFalse while the endpoint is paused: no event is sent to it.
descriptionstring or nullYour note about the endpoint.
sourcestringWhere it was made: the dashboard, this API, or moved from the single endpoint the dashboard had before.One of: dashboard, api, migrated.
signedbooleanTrue when deliveries carry a PerfBee-Signature. An endpoint moved from before without a secret is unsigned until you rotate one.
previous_secret_valid_untilstring or nullWhile a rotation overlaps: when the old secret stops signing (ISO 8601 UTC); otherwise null.
legacy_secret_header_untilstring or nullOnly on an endpoint moved from before: when the old plain X-PerfBee-Secret header stops (ISO 8601 UTC); otherwise null.
last_delivery_atstring or nullWhen the last attempt was made (ISO 8601 UTC); null before the first.
last_statusstring or nullThe last delivery's state: delivered, retrying or failed.
consecutive_failuresintegerFailed attempts in a row; a delivered event ends the run.
created_atstring or nullWhen the endpoint was made (ISO 8601 UTC).
secretstringThe signing secret (whsec_...). Shown ONCE, here: store it now, it cannot be read again.
Example 200 answer of Rotate the signing secret
{
  "id": 3,
  "url": "https://hooks.example.com/perfbee",
  "events": [
    "scan_data_ready",
    "scan_failed"
  ],
  "enabled": true,
  "description": "CI gate",
  "source": "api",
  "signed": true,
  "last_delivery_at": "2026-11-01T09:30:12Z",
  "last_status": "delivered",
  "consecutive_failures": 0,
  "created_at": "2026-10-20T14:02:00Z",
  "secret": "whsec_p3Qx1kT0n8a5ZyV2mL7cR4dB9wUeHsJfGiNoAqXtYbc"
}

Problems this call documents

Send a test event

POST/webhook-endpoints/{endpoint_id}/test

Scope:
webhooks
Rate class:
heavy
Operation id:
test_webhook_endpoint

Sends one test event to the endpoint now, signed like a real one, and answers with the delivery: whether it arrived, the receiver's HTTP status and how long it took. A failed test is not retried and does not count towards the endpoint's failure run. Needs the webhooks scope.

Parameters

NameInTypeDescription
endpoint_idpathintegerrequiredThe webhook endpoint's id, from GET /webhook-endpoints.

Answer 200

OK. The body is a WebhookDelivery.

FieldTypeDescription
idintegerThe delivery's id (one event sent to one endpoint, with its retries).
eventstringThe event, or test for a test delivery.
event_idstringThe event's id, the same as id in the payload and in PerfBee-Event-Id; it stays the same on every attempt and every redelivery.
statusstringpending (waiting for its first attempt), retrying, delivered or failed.One of: pending, retrying, delivered, failed.
attemptintegerThe number of the attempt made last (0 before the first).
max_attemptsintegerHow many attempts a delivery gets.
http_statusinteger or nullThe receiver's HTTP status on the last attempt; null when it did not answer.
errorstring or nullWhy the last attempt failed when there was no usable answer: a short sentence.
duration_msinteger or nullHow long the last attempt took.
next_attempt_atstring or nullWhile retrying: when the next attempt is due (ISO 8601 UTC).
created_atstring or nullWhen the event was queued for this endpoint (ISO 8601 UTC).
payloadobject or nullThe body sent, kept until the delivery is delivered; a failed delivery keeps it for 3 days. null otherwise.
Example 200 answer of Send a test event
{
  "id": 4182,
  "event": "scan_data_ready",
  "event_id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
  "status": "failed",
  "attempt": 5,
  "max_attempts": 5,
  "http_status": 503,
  "duration_ms": 412,
  "created_at": "2026-11-01T09:30:12Z",
  "payload": {
    "api_version": "v1",
    "data": {
      "crawl_id": "0f7c9a52-3d4b-4a39-8c0e-5e2a9d1b6f10",
      "mode": "full",
      "site_id": 7
    },
    "event": "scan_data_ready",
    "event_description": "Crawl data ready",
    "id": "evt_5f0c2b7e9a1d4c3e8b6a7d9f01234567",
    "timestamp": "2026-11-01T09:30:12.512Z"
  }
}

Problems this call documents