Webhook Notifications
Coming in Pro PlanReceive real-time notifications when your screenshots, PDFs, and batch jobs are ready. No polling required.
Overview
Webhook notifications allow your application to receive real-time HTTP POST callbacks when capture events occur. Instead of polling our API to check if a screenshot is ready, you configure a webhook URL and we push the results to you as soon as they are available.
Webhooks are especially useful for batch processing, where you submit multiple URLs and want to be notified when results are ready, rather than repeatedly checking status.
Available Events
capture.completedFired when a screenshot, PDF, or OG image has been successfully generated.
capture.failedFired when a capture request fails due to navigation error, timeout, or rendering issue.
batch.completedFired when all captures in a batch request have been processed (succeeded or failed).
batch.progressFired periodically during batch processing to report progress (every 10 completed items).
Configuration
Register a webhook endpoint by sending a POST request to the webhooks API. You can subscribe to specific events and provide a signing secret for verification.
curl -X POST "https://captureapi.dev/api/v1/webhooks" \
-H "X-API-Key: cap_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/api/webhook",
"events": ["capture.completed", "capture.failed", "batch.completed"],
"secret": "whsec_your_signing_secret"
}'Payload Examples
Capture Completed
Sent when a single screenshot, PDF, or OG image is successfully generated.
{
"event": "capture.completed",
"timestamp": "2026-03-25T14:30:00Z",
"request_id": "req_abc123def456",
"data": {
"url": "https://example.com",
"type": "screenshot",
"format": "png",
"width": 1280,
"height": 720,
"file_size": 245760,
"file_url": "https://captureapi.dev/files/req_abc123def456.png",
"expires_at": "2026-03-26T14:30:00Z"
}
}Capture Failed
Sent when a capture request fails for any reason.
{
"event": "capture.failed",
"timestamp": "2026-03-25T14:30:05Z",
"request_id": "req_xyz789",
"data": {
"url": "https://invalid-domain.example",
"type": "screenshot",
"error_code": "NAVIGATION_FAILED",
"error_message": "Could not navigate to URL: DNS resolution failed"
}
}Batch Completed
Sent when all items in a batch request have been processed.
{
"event": "batch.completed",
"timestamp": "2026-03-25T14:31:00Z",
"batch_id": "batch_abc123",
"data": {
"total": 10,
"succeeded": 9,
"failed": 1,
"results": [
{
"request_id": "req_001",
"url": "https://example.com",
"status": "completed",
"file_url": "https://captureapi.dev/files/req_001.png"
},
{
"request_id": "req_002",
"url": "https://invalid.example",
"status": "failed",
"error_code": "NAVIGATION_FAILED"
}
]
}
}Signature Verification
Every webhook request includes an X-CaptureAPI-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the webhook is from CaptureAPI and has not been tampered with.
import crypto from "crypto";
function verifyWebhookSignature(
payload: string,
signature: string,
secret: string
): boolean {
const expectedSignature = crypto
.createHmac("sha256", secret)
.update(payload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// In your webhook handler:
app.post("/api/webhook", (req, res) => {
const signature = req.headers["x-captureapi-signature"];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).json({ error: "Invalid signature" });
}
const { event, data } = req.body;
switch (event) {
case "capture.completed":
// Process the completed screenshot
console.log("Screenshot ready:", data.file_url);
break;
case "capture.failed":
// Handle the failure
console.error("Capture failed:", data.error_message);
break;
case "batch.completed":
// Process batch results
console.log("Batch done:", data.succeeded, "/", data.total);
break;
}
res.status(200).json({ received: true });
});Retry Policy
If your endpoint returns a non-2xx status code or times out (30 second limit), we will retry the delivery with exponential backoff.
| Attempt | Delay | Description |
|---|---|---|
| 1st retry | 30 seconds | Quick retry for transient failures |
| 2nd retry | 2 minutes | Short wait before second attempt |
| 3rd retry | 10 minutes | Medium wait |
| 4th retry | 1 hour | Extended wait |
| 5th retry | 4 hours | Final attempt before marking as failed |
After 5 failed delivery attempts, the webhook event is marked as failed. You can view failed deliveries in your dashboard and manually retry them.
Best Practices
Respond quickly
Return a 200 status code as fast as possible. Process the webhook payload asynchronously to avoid timeouts.
Verify signatures
Always verify the X-CaptureAPI-Signature header to ensure the request is authentic and has not been modified.
Handle duplicates
Use the request_id field to deduplicate events. In rare cases, the same event may be delivered more than once.
Use HTTPS
Webhook URLs must use HTTPS. We do not deliver webhooks to HTTP endpoints for security reasons.
Log everything
Store raw webhook payloads for debugging. Webhook delivery logs are available in your dashboard for 30 days.