Webhooks
Receive real-time notifications when screenshots complete or fail. Webhooks are sent for both individual screenshots and batch requests.
Supported Events
| Event | Trigger |
|---|---|
screenshot.completed |
Individual screenshot captured successfully |
screenshot.failed |
Individual screenshot capture failed |
batch.completed |
All screenshots in a batch finished |
batch.failed |
Batch processing failed |
Configuration
Configure your webhook URL in the Dashboard Settings:
- Go to Dashboard → Webhooks
- Enter your webhook endpoint URL
- Copy your webhook signing secret
- Use the Send Test button to verify your endpoint
All screenshot and batch requests will automatically send webhooks to your configured URL.
Webhook Payload
screenshot.completed
Sent when an individual screenshot is captured successfully:
{ "event": "screenshot.completed", "id": "req_abc123def456", "timestamp": "2024-01-18T12:00:00Z", "data": { "url": "https://example.com", "screenshot_url": "https://cdn.renderscreenshot.com/screenshots/ab/cd/abcd1234.png", "width": 1200, "height": 630, "size": 123456, "format": "png", "cached": false } }
screenshot.failed
Sent when a screenshot capture fails:
{ "event": "screenshot.failed", "id": "req_abc123def456", "timestamp": "2024-01-18T12:00:00Z", "data": { "url": "https://example.com", "error": "Page failed to load within 30 seconds" } }
batch.completed
Sent when all screenshots in a batch have finished processing:
{ "event": "batch.completed", "id": "batch_abc123", "timestamp": "2024-01-18T12:00:00Z", "data": { "summary": { "total": 10, "completed": 9, "failed": 1 }, "results_url": "https://api.renderscreenshot.com/v1/batches/batch_abc123" } }
batch.failed
Sent when batch processing fails entirely:
{ "event": "batch.failed", "id": "batch_abc123", "timestamp": "2024-01-18T12:00:00Z", "data": { "summary": { "total": 10, "completed": 0, "failed": 10 }, "results_url": "https://api.renderscreenshot.com/v1/batches/batch_abc123" } }
Webhook Headers
Every webhook request includes these headers:
POST /webhooks/screenshots HTTP/1.1 Host: your-server.com Content-Type: application/json X-Webhook-ID: whk_abc123def456 X-Webhook-Timestamp: 1705579200 X-Webhook-Signature: sha256=a1b2c3d4e5f6... User-Agent: RenderScreenshot-Webhook/1.0
| Header | Description |
|---|---|
X-Webhook-ID |
Unique identifier for this webhook delivery |
X-Webhook-Timestamp |
Unix timestamp when the webhook was sent |
X-Webhook-Signature |
HMAC-SHA256 signature for verification |
Verifying Signatures
Always verify webhook signatures to ensure authenticity. Use the signing secret from your dashboard.
Java
import com.renderscreenshot.sdk.Webhook; import com.renderscreenshot.sdk.Webhook.WebhookEvent; // In your servlet or controller String signature = request.getHeader("X-Webhook-Signature"); String timestamp = request.getHeader("X-Webhook-Timestamp"); String payload = /* read request body as string */; if (Webhook.verify(payload, signature, timestamp, System.getenv("WEBHOOK_SECRET"))) { WebhookEvent event = Webhook.parse(payload); if ("screenshot.completed".equals(event.getType())) { System.out.println("Screenshot ready: " + event.getData()); } else if ("screenshot.failed".equals(event.getType())) { System.out.println("Screenshot failed: " + event.getData()); } response.setStatus(200); } else { response.setStatus(401); }
Node.js
import { verifyWebhook, parseWebhook } from 'renderscreenshot'; // In your route handler (Express example) app.post('/webhooks/screenshot', (req, res) => { const signature = req.headers['x-webhook-signature']; const timestamp = req.headers['x-webhook-timestamp']; const payload = JSON.stringify(req.body); if (verifyWebhook(payload, signature, timestamp, process.env.WEBHOOK_SECRET)) { const event = parseWebhook(req.body); if (event.type === 'screenshot.completed') { console.log('Screenshot ready:', event.data.response?.url); } else if (event.type === 'screenshot.failed') { console.error('Screenshot failed:', event.data.error?.message); } res.status(200).send('OK'); } else { res.status(401).send('Invalid signature'); } });
PHP
function verifyWebhook($payload, $signature, $timestamp, $secret) { $expected = hash_hmac('sha256', "$timestamp.$payload", $secret); return hash_equals("sha256=$expected", $signature); } // In your endpoint $signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE']; $timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP']; $payload = file_get_contents('php://input'); if (verifyWebhook($payload, $signature, $timestamp, $_ENV['WEBHOOK_SECRET'])) { // Process webhook } else { http_response_code(401); }
Python
import os from flask import Flask, request, jsonify from renderscreenshot import verify_webhook, parse_webhook app = Flask(__name__) @app.route('/webhooks/screenshot', methods=['POST']) def handle_webhook(): signature = request.headers.get('X-Webhook-Signature') timestamp = request.headers.get('X-Webhook-Timestamp') payload = request.get_data(as_text=True) if verify_webhook(payload, signature, timestamp, os.environ['WEBHOOK_SECRET']): event = parse_webhook(request.json) if event['type'] == 'screenshot.completed': print('Screenshot ready:', event['data'].get('response', {}).get('url')) elif event['type'] == 'screenshot.failed': print('Screenshot failed:', event['data'].get('error', {}).get('message')) return jsonify({'status': 'ok'}), 200 else: return jsonify({'error': 'Invalid signature'}), 401
Ruby
def verify_webhook(payload, signature, timestamp, secret) expected = OpenSSL::HMAC.hexdigest( 'SHA256', secret, "#{timestamp}.#{payload}" ) Rack::Utils.secure_compare("sha256=#{expected}", signature) end # In your controller signature = request.headers['X-Webhook-Signature'] timestamp = request.headers['X-Webhook-Timestamp'] payload = request.raw_post if verify_webhook(payload, signature, timestamp, ENV['WEBHOOK_SECRET']) # Process webhook else head :unauthorized end
Retry Policy
Failed webhook deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | ~1 minute |
| 3 | ~5 minutes |
| 4 | ~30 minutes |
| 5 | ~2 hours |
After 5 failed attempts, the webhook is abandoned.
Responding to Webhooks
Return a 2xx status code to acknowledge receipt:
HTTP/1.1 200 OK
Any non-2xx response triggers a retry.
Testing Webhooks
Dashboard Test Button
Use the Send Test button in your webhook settings to send a test payload to your endpoint.
Local Development
Use tools like webhook.site or ngrok to test webhooks locally:
# Using ngrok ngrok http 3000 # Then configure your webhook URL as: # https://abc123.ngrok.io/webhooks/screenshots
Best Practices
- Always verify signatures - Prevent spoofed webhook requests
- Respond quickly - Return 200 immediately, process asynchronously
- Handle duplicates - Webhooks may be delivered more than once
- Log webhook IDs - Use
X-Webhook-IDfor debugging and deduplication
See Also
- Batch Screenshots - Batch processing with webhook notifications
- Error Codes - Understanding error responses
- SDKs - Webhook verification helpers in all official SDKs
- Dashboard Webhooks - Configure your webhook settings