Webhooks
Webhooks notify your application when a render succeeds or fails. Use dashboard endpoints for account-level notifications, and per-request webhooks when one asynchronous render should notify a specific endpoint.
Delivery modes
| Mode | Configured in | Best for |
|---|---|---|
| Dashboard endpoints | BladePDF dashboard | Central event handling for all renders in your account. |
| Template render events | Dashboard endpoint | Routing automation by the template fields in the payload. |
| Per-request webhook | Laravel render call | One-off completion callbacks for a single async render. |
Events
| Event | When it fires |
|---|---|
pdf.rendered |
A render completed successfully. |
pdf.failed |
A render failed validation, timed out, hit a limit, or failed during rendering. |
Dashboard endpoints
Create a webhook endpoint in the dashboard when your application should receive render events consistently. Choose the events to subscribe to, copy the signing secret when the endpoint is created, and monitor delivery attempts from the delivery log.
- Open Webhooks in the BladePDF dashboard.
- Add an endpoint URL, for example
https://example.com/webhooks/bladepdf. - Select
pdf.rendered,pdf.failed, or both. - Store the generated signing secret in your application environment.
- Use the test action to confirm that your endpoint accepts signed events.
Dashboard endpoints receive events for local view renders, raw HTML renders, and dashboard template renders.
Template render events
A dashboard template render emits the same pdf.rendered and pdf.failed events as any other render. The payload identifies the template so your endpoint can route the event to the right workflow.
1if (($payload['type'] ?? null) === 'pdf.rendered'
2 && data_get($payload, 'render.template_name') === 'invoice.standard') {
3 SyncInvoicePdf::dispatch($payload);
4}
For local view and raw HTML renders, render.template_id is null. If you call templateName(), that value appears as render.template_name for dashboard filtering and webhook routing.
Per-request webhooks
Use webhook() when a single async render should notify an endpoint in addition to any dashboard-configured endpoints. The URL, secret, and event list are sent with that render request only. Async renders must also call storePdf().
1$submission = BladePDF::fromTemplate('invoice.standard', $context)
2 ->reference('INV-2026-0042')
3 ->storePdf()
4 ->webhook('https://example.com/bladepdf/webhook', 'whsec_request_secret')
5 ->async();
Limit a per-request webhook to specific events by passing the third argument:
1BladePDF::fromHtml($html)
2 ->storePdf()
3 ->webhook('https://example.com/bladepdf/webhook', 'whsec_request_secret', [
4 'pdf.rendered',
5 ])
6 ->async();
They are signed, queued, retried, and shown in the dashboard delivery log just like dashboard endpoints. They do not create a reusable dashboard endpoint. See Async Renders for submission and failure semantics.
Payload shape
Webhook payloads are JSON. Every delivery has a unique event id and a nested render summary.
1{
2 "id": "0190f7a8-8f7d-7c7b-9b08-2f9878d9c3b2",
3 "type": "pdf.rendered",
4 "created_at": "2026-07-01T12:34:56+00:00",
5 "workspace_id": "0190f78c-93ff-72c4-bb30-9e049e2d7a70",
6 "render": {
7 "request_id": "req_abc123",
8 "status": "success",
9 "template_id": "0190f790-2bc1-7b2f-9f9c-9dba36bfe2a4",
10 "template_name": "invoice.standard",
11 "render_ms": 420,
12 "queue_ms": 18,
13 "total_duration_ms": 491,
14 "pdf_bytes": 84213,
15 "pdf_url": "https://app.bladepdf.com/render-pdfs/...",
16 "error_message": null,
17 "occurred_at": "2026-07-01T12:34:56+00:00"
18 }
19}
| Field | Notes |
|---|---|
render.request_id | Stable id for the render request. |
render.template_id | Dashboard template record id for template renders, otherwise null. |
render.template_name | Public template identifier such as invoice.standard, or the custom templateName() value for HTML renders. |
render.pdf_url | Signed download URL when the generated PDF was stored, otherwise null. |
render.error_message | Present on pdf.failed events. |
Delivery headers
| Header | Description |
|---|---|
BladePDF-Event | The event type, such as pdf.rendered. |
BladePDF-Delivery | The delivery id. |
BladePDF-Timestamp | Unix timestamp used in the signature. |
BladePDF-Signature | HMAC SHA-256 signature in the form v1=.... |
Content-Type | application/json. |
Verify signatures
The signature is computed as HMAC_SHA256(timestamp + "." + rawBody, endpointSecret). Always verify it before processing the event.
1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Route;
3
4Route::post('/webhooks/bladepdf', function (Request $request) {
5 $secret = config('services.bladepdf.webhook_secret');
6 $timestamp = (string) $request->header('BladePDF-Timestamp');
7 $signature = (string) $request->header('BladePDF-Signature');
8 $body = $request->getContent();
9
10 $expected = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
11
12 abort_unless(hash_equals($expected, $signature), 401);
13
14 $payload = json_decode($body, true, flags: JSON_THROW_ON_ERROR);
15
16 match ($payload['type'] ?? null) {
17 'pdf.rendered' => ProcessRenderedPdf::dispatch($payload),
18 'pdf.failed' => ProcessFailedPdf::dispatch($payload),
19 default => null,
20 };
21
22 return response()->noContent();
23});
Do not re-encode JSON before verifying. Signature verification must use the exact raw body bytes received by your endpoint.
Signing secrets
Dashboard endpoints get a generated secret when the endpoint is created or rotated. Per-request webhooks use the secret you pass to webhook(). Store secrets in environment variables and rotate them if they are exposed.
Retries
BladePDF retries failed deliveries up to five attempts. Retry delays are approximately 1 minute, 5 minutes, 15 minutes, 1 hour, and 6 hours. A dashboard endpoint is marked failing after repeated failures.
Delivery log retention
Delivery attempts (status, response code, latency, response snippet) are visible in the dashboard delivery log for 90 days and are deleted automatically afterwards. If you need a longer delivery audit trail, record webhook receipts on your side using the payload id.
Idempotency
Webhooks may be delivered more than once. Store the payload id or BladePDF-Delivery header and ignore duplicates.
Endpoint rules
- Use an
https://URL in production. - Return any 2xx response after successfully accepting the event.
- Process expensive work asynchronously in your own queue.
- Keep endpoint secrets private.
- Do not make rendering success depend on the webhook endpoint being online; webhook delivery happens after the render event is recorded.