Webhooks

Async rendering processes jobs in the background instead of blocking the HTTP connection. Enable it by setting "async": true in your request. Optionally, add a webhook_url to have PixDoc POST the result to your endpoint when the render completes. You can always poll for the result regardless of whether you set a webhook.

Async rendering is available on the Starter plan and above. Free plan requests that include "async": true will receive a 403 Forbidden response.

Enabling Async Mode

Set "async": true in your request body to enable async mode. The API returns immediately with a 202 Accepted response containing a request_id and poll_url.

Optionally, you can also include:

  • webhook_url — PixDoc will POST the result to this endpoint when the render completes. Use with webhook_secret for signature verification.
  • Polling — Use the poll_url from the 202 response to check status at any time. This works whether or not you set a webhook.

You can use both together: webhook as primary delivery, polling as fallback.

With Webhook

Set "async": true and add a webhook_url to receive the result via webhook delivery. You can optionally provide a webhook_secret to enable signature verification.

curl -X POST https://pixdoc.dev/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "html": "<h1>Invoice #1042</h1><p>Amount: $250.00</p>",
  "async": true,
  "webhook_url": "https://your-app.com/webhooks/pixdoc",
  "webhook_secret": "whsec_your_secret_here",
  "options": {
    "format": "Letter"
  }
}'

Poll-Only (No Webhook)

Set "async": true without a webhook_url to queue the render and poll for the result:

curl -X POST https://pixdoc.dev/api/v1/pdf \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "html": "<h1>Hello</h1>",
  "async": true
}'

202 Response

When async mode is enabled, the API returns immediately with a 202 Accepted status:

{
  "request_id": "req_abc123def456",
  "status": "queued",
  "message": "Render queued."
}

Webhook Payload

Once the render completes, PixDoc sends a POST request to your webhook_url with a JSON payload. The event field indicates whether the render succeeded or failed.

render.completed

{
  "event": "render.completed",
  "request_id": "req_abc123def456",
  "status": "completed",
  "metadata": {"order_id": "ORD-1234", "customer": "acme"},
  "output_url": "https://store.pixdoc.dev/renders/req_abc123def456.pdf",
  "duration_ms": 1230,
  "created_at": "2026-03-28T12:00:00Z",
  "completed_at": "2026-03-28T12:00:01Z"
}

The output_url is a signed URL valid for 24 hours. Download the file before it expires.

render.failed

{
  "event": "render.failed",
  "request_id": "req_abc123def456",
  "status": "failed",
  "metadata": {"order_id": "ORD-1234", "customer": "acme"},
  "error": {
    "code": "RENDER_FAILED",
    "message": "Page timed out after 30000ms"
  },
  "created_at": "2026-03-28T12:00:00Z",
  "failed_at": "2026-03-28T12:00:31Z"
}

Verifying Signatures

If you provide a webhook_secret in your request, PixDoc signs every webhook delivery with an HMAC-SHA256 signature in the X-PixDoc-Signature header. Always verify this signature to ensure the payload was sent by PixDoc and has not been tampered with.

const crypto = require('crypto');

// Use the raw request body (before JSON parsing)
const signature = req.headers['x-pixdoc-signature'];
const expected = 'sha256=' + crypto
.createHmac('sha256', webhookSecret)
.update(rawBody)
.digest('hex');

if (signature !== expected) {
throw new Error('Invalid signature');
}

Always verify signatures against the raw request body, not the parsed JSON. Parsing and re-serializing may change the byte representation and cause verification to fail.

Polling Fallback

If your webhook endpoint is unavailable or you need to check the status of a render manually, you can poll the render status endpoint.

GET https://pixdoc.dev/api/v1/renders/:requestId
curl https://pixdoc.dev/api/v1/renders/req_abc123def456 \
-H "Authorization: Bearer YOUR_API_KEY"

Poll Response

{
  "request_id": "req_abc123def456",
  "status": "completed",
  "metadata": {"order_id": "ORD-1234", "customer": "acme"},
  "output_url": "https://store.pixdoc.dev/renders/req_abc123def456.pdf",
  "duration_ms": 1230,
  "created_at": "2026-03-28T12:00:00Z",
  "completed_at": "2026-03-28T12:00:01Z"
}

Retry Behavior

If your webhook endpoint does not respond with a 2xx status code, PixDoc retries delivery up to 3 times with exponential backoff:

AttemptDelay
1st retry5 seconds
2nd retry30 seconds
3rd retry2 minutes

After all retries are exhausted, the webhook delivery is marked as failed. You can still retrieve the render result using the polling endpoint.

Your webhook endpoint should return a 2xx response as quickly as possible. Process the payload asynchronously to avoid timeouts. PixDoc waits up to 10 seconds for a response before considering the delivery failed.

Plan Requirements

Async rendering with webhooks is available on the Starter plan and above.

PlanWebhooks
FreeNot available (returns 403)
StarterIncluded
ProIncluded