Skip to content
CastPDF
API reference

Webhook endpoints

Create, change, disable and delete webhook endpoints, rotate their signing secret, send test events and read or retry the delivery log.

On this page

Webhooks tell your server when PDFs and batches are ready, so you never have to poll. These nine endpoints manage where the events go; the webhooks guide explains the events themselves, the CastPDF-Signature header and how to verify it, and the retry schedule. Webhooks are included in the Starter, Growth, Pro and Scale plans.

A live key manages live endpoints (which receive live events), a test key test endpoints (test events); neither sees the other's. All responses carry cache-control: no-store. Listing, disabling and deleting also work after a downgrade to a plan without webhooks, so you can always clean up.

GET /v1/webhooks

Lists the endpoints of the key's mode, newest first.

curl
curl https://api.castpdf.com/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY"

200 with { "data": [endpoint, ...] }, each an endpoint object. The list is not paginated: a workspace has at most 10 endpoints.

POST /v1/webhooks

Adds an endpoint and returns its signing secret, only this once. Store it right away; you need it to verify the signature.

Request
curl https://api.castpdf.com/v1/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/castpdf-webhook", "events": ["pdf.created", "pdf.failed"]}'
const res = await fetch('https://api.castpdf.com/v1/webhooks', {
  method: 'POST',
  headers: { Authorization: 'Bearer YOUR_API_KEY', 'Content-Type': 'application/json' },
  body: JSON.stringify({ url: 'https://example.com/castpdf-webhook', events: ['pdf.created', 'pdf.failed'] }),
});
const endpoint = await res.json();
console.log(endpoint.secret); // store it now: it is never shown again
import requests

res = requests.post(
    "https://api.castpdf.com/v1/webhooks",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"url": "https://example.com/castpdf-webhook", "events": ["pdf.created", "pdf.failed"]},
    timeout=30,
)
endpoint = res.json()
print(endpoint["secret"])  # store it now: it is never shown again
201
{
  "id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
  "url": "https://example.com/castpdf-webhook",
  "description": null,
  "events": ["pdf.created", "pdf.failed"],
  "mode": "live",
  "status": "enabled",
  "disabled_reason": null,
  "secret_prefix": "whsec_AbCd",
  "created_at": "2026-10-03T10:15:02.123Z",
  "updated_at": "2026-10-03T10:15:02.123Z",
  "secret": "whsec_AbCdEfGhIjKlMnOpQrStUvWxYz012345"
}
FieldDescription
urlstringRequiredAn https URL on the public internet, on port 443, 80 or 8443, up to 2,048 characters, without a user name or password.
eventsarrayRequiredOne or more of pdf.created, pdf.failed, batch.completed, each once.
descriptionstring or nullUp to 200 characters, for you.
mode"live" or "test"Must match the key's mode, which is the default.
CodeWhen
feature_not_available (402)Your plan does not include webhooks
invalid_request (400)Validation failed, the workspace already has 10 endpoints, or the URL is not accepted: then details.field is "url" and details.reason is https_required, private_address, port, dns, credentials, scheme, invalid or too_long

The endpoint object

FieldDescription
idstring (UUID)The endpoint's id.
urlstringWhere events are sent.
descriptionstring or nullYour note.
eventsarrayThe event types it receives.
modestringlive or test.
statusstringenabled or disabled.
disabled_reasonstring or nullmanual (you disabled it) or failing (disabled after 24 hours of failed deliveries); null while enabled.
secret_prefixstringThe first 10 characters of the signing secret, to tell secrets apart.
created_at, updated_atstringWhen it was created and last changed.

GET /v1/webhooks/:id

Returns one endpoint object, without the secret.

curl
curl https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bearer YOUR_API_KEY"

Errors: webhook_not_found (404), also for an endpoint of the other mode.

PUT /v1/webhooks/:id

Changes any of url, events, description and enabled; send at least one.

curl
curl -X PUT https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}'
FieldDescription
url, events, descriptionAs for POST /v1/webhooks. A new url is checked the same way.
enabledbooleanfalse stops deliveries (disabled_reason: "manual"); true starts them again, also after an automatic disable (disabled_reason: "failing").

200 with the endpoint. Enabling an endpoint or changing its url or events needs a plan with webhooks (feature_not_available otherwise); disabling it never does. Errors: invalid_request (400), webhook_not_found (404).

DELETE /v1/webhooks/:id

Deletes the endpoint and its delivery log.

curl
curl -X DELETE https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b \
  -H "Authorization: Bearer YOUR_API_KEY"

204 with no body. Errors: webhook_not_found (404).

POST /v1/webhooks/:id/rotate-secret

Creates a new signing secret and returns the endpoint with secret, shown only this once. For 24 hours, deliveries are signed with both secrets (two v1 values in the signature header), so you can switch your server to the new secret without missing events.

curl
curl -X POST https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/rotate-secret \
  -H "Authorization: Bearer YOUR_API_KEY"

200 with the endpoint and its new secret. Errors: feature_not_available (402), webhook_not_found (404).

POST /v1/webhooks/:id/test

Sends a sample pdf.created event marked "test_event": true, once (no retries). Use it to check that your server answers and verifies the signature.

curl
curl -X POST https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/test \
  -H "Authorization: Bearer YOUR_API_KEY"
202
{ "delivery_id": "4d3c2b1a-0f9e-4d8c-9b6a-5f4e3d2c1b0a" }

The result appears in the delivery log a moment later. Test events and retries together are limited to 10 a minute per endpoint (rate_limited with details.reason: "webhook_sends" and a retry-after header). Errors: feature_not_available (402), webhook_not_found (404), rate_limited (429).

GET /v1/webhooks/:id/deliveries

The delivery log of the last 30 days, newest first.

curl
curl "https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/deliveries?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
200
{
  "data": [
    {
      "id": "4d3c2b1a-0f9e-4d8c-9b6a-5f4e3d2c1b0a",
      "event_id": "evt_6f1d2c3b4a5e4f609b7a1c2d3e4f5a6b",
      "event_type": "pdf.created",
      "status": "succeeded",
      "attempts": 1,
      "response_status": 200,
      "response_body": "ok",
      "last_error": null,
      "duration_ms": 182,
      "test_event": false,
      "created_at": "2026-10-03T10:15:03.000Z",
      "last_attempt_at": "2026-10-03T10:15:03.120Z",
      "next_attempt_at": null,
      "event": {
        "id": "evt_6f1d2c3b4a5e4f609b7a1c2d3e4f5a6b",
        "type": "pdf.created",
        "created_at": "2026-10-03T10:15:03.000Z",
        "livemode": true,
        "data": {
          "id": "3f0c9a4e-6b1d-4c2a-9a57-0d8e4b7f21c3",
          "status": "succeeded",
          "pages": 2,
          "bytes": 48213,
          "mode": "print",
          "test": false,
          "template_id": "8d1f2c3b-4a5e-4f60-9b7a-1c2d3e4f5a6b",
          "template_version": 4,
          "error_code": null,
          "created_at": "2026-10-03T10:15:02.123Z",
          "expires_at": "2026-10-10T10:15:02.123Z",
          "url": "https://api.castpdf.com/v1/files/3f0c9a4e-6b1d-4c2a-9a57-0d8e4b7f21c3?exp=1791627302&sig=Jx0k...",
          "filename": "invoice-042.pdf",
          "metadata": { "order_id": "1042" },
          "batch_id": null
        }
      }
    }
  ],
  "next_before": null
}
Query parameterDescription
limit1 to 100. Default 20.
beforeThe previous page's next_before.
FieldDescription
statusstringpending (waiting for its next attempt at next_attempt_at), succeeded or failed (no more attempts).
attemptsintegerAttempts made so far.
response_status, response_bodyinteger, string, or nullYour server's last answer; response_body keeps its first 1,024 characters.
last_errorstring or nullWhy the last attempt failed, or null.
duration_msinteger or nullHow long the last attempt took.
test_eventbooleantrue for a test event.
eventobjectThe event exactly as it was sent: id, type, created_at, livemode and data (plus test_event on test events).

Errors: webhook_not_found (404).

POST /v1/webhooks/:id/deliveries/:deliveryId/retry

Sends a finished delivery (succeeded or failed) once more, now: one attempt, not a new retry schedule. The endpoint must be enabled.

curl
curl -X POST https://api.castpdf.com/v1/webhooks/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b/deliveries/4d3c2b1a-0f9e-4d8c-9b6a-5f4e3d2c1b0a/retry \
  -H "Authorization: Bearer YOUR_API_KEY"

202 with { "delivery_id": "..." }, the same delivery, which shows the new attempt in the log. Retries share the limit of 10 a minute per endpoint with test events.

CodeWhen
invalid_request (400)The endpoint is disabled
feature_not_available (402)Your plan does not include webhooks
webhook_not_found (404)No such endpoint for this key's mode
not_found (404)No such delivery, or it is still pending
rate_limited (429)Over the limit for test events and retries
Updated Something unclear? [email protected], a real person replies.