Allscreenshots Docs
API reference

Webhooks

Receive notifications when screenshots are ready

Webhooks

Receive HTTP notifications when async jobs, bulk jobs, or scheduled captures complete. Webhooks eliminate the need for polling and enable real-time integrations.

Setting up webhooks

Include webhookUrl in your request to enable notifications:

{
  "url": "https://example.com",
  "webhookUrl": "https://your-server.com/webhooks/screenshot",
  "webhookSecret": "your-secret-key"
}

Webhook payload

When a job completes, we send a POST request to your webhook URL:

Successful capture

{
  "event": "job.completed",
  "jobId": "job_abc123xyz",
  "status": "completed",
  "timestamp": "2025-01-15T10:30:05Z",
  "result": {
    "url": "https://storage.allscreenshots.com/screenshots/abc123.png",
    "expiresAt": "2025-01-30T10:30:05Z",
    "size": 245678,
    "width": 1920,
    "height": 1080,
    "format": "png",
    "renderTime": 1234
  }
}

Failed capture

{
  "event": "job.failed",
  "jobId": "job_abc123xyz",
  "status": "failed",
  "timestamp": "2025-01-15T10:31:00Z",
  "error": {
    "code": "timeout",
    "message": "Page load timed out after 60 seconds"
  }
}

Bulk job completed

{
  "event": "bulk.completed",
  "bulkJobId": "bulk_abc123xyz",
  "status": "completed",
  "timestamp": "2025-01-15T10:30:45Z",
  "summary": {
    "total": 10,
    "completed": 9,
    "failed": 1
  },
  "jobs": [
    {
      "jobId": "job_001",
      "url": "https://example1.com",
      "status": "completed",
      "result": { ... }
    },
    {
      "jobId": "job_002",
      "url": "https://example2.com",
      "status": "failed",
      "error": { ... }
    }
  ]
}

Scheduled capture

{
  "event": "schedule.captured",
  "scheduleId": "sched_abc123xyz",
  "captureId": "cap_001",
  "timestamp": "2025-01-15T14:00:05Z",
  "result": {
    "url": "https://storage.allscreenshots.com/screenshots/abc123.png",
    "size": 245678,
    "width": 1920,
    "height": 1080
  }
}

Verifying signatures

To ensure webhooks are from AllScreenshots, verify the signature using your webhook secret.

Signature header

Every webhook includes a signature in the X-Allscreenshots-Signature header:

X-Allscreenshots-Signature: sha256=abc123...

Verification code

const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express middleware
app.post('/webhooks/screenshot', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-allscreenshots-signature'];
  const payload = req.body.toString();

  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const event = JSON.parse(payload);
  // Process the webhook...

  res.sendStatus(200);
});

Python

import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

# Flask example
@app.route('/webhooks/screenshot', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-Allscreenshots-Signature')
    payload = request.get_data()

    if not verify_webhook(payload, signature, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.get_json()
    # Process the webhook...

    return '', 200

Always verify webhook signatures in production. Without verification, attackers could send fake webhook payloads to your endpoint.

Responding to webhooks

Success response

Return a 2xx status code to acknowledge receipt:

app.post('/webhooks/screenshot', (req, res) => {
  // Process webhook asynchronously if needed
  processWebhook(req.body).catch(console.error);

  // Respond immediately
  res.sendStatus(200);
});

Retry behavior

If your endpoint returns an error or times out, we retry with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook is marked as failed.

Webhook endpoints must respond within 30 seconds. For long processing, acknowledge immediately and process asynchronously.

Event types

EventDescription
job.completedAsync screenshot job completed successfully
job.failedAsync screenshot job failed
bulk.completedBulk job completed (all URLs processed)
schedule.capturedScheduled capture completed
schedule.failedScheduled capture failed

Best practices

Use a dedicated endpoint

Create a separate endpoint for AllScreenshots webhooks:

https://your-app.com/webhooks/allscreenshots

Process asynchronously

Don't block the webhook response with heavy processing:

app.post('/webhooks/screenshot', async (req, res) => {
  // Acknowledge immediately
  res.sendStatus(200);

  // Process in background
  try {
    await processScreenshot(req.body);
  } catch (error) {
    console.error('Webhook processing failed:', error);
  }
});

Store webhook payloads

Log incoming webhooks for debugging:

app.post('/webhooks/screenshot', async (req, res) => {
  // Store for debugging
  await db.webhookLogs.create({
    receivedAt: new Date(),
    headers: req.headers,
    body: req.body,
  });

  // Process...
  res.sendStatus(200);
});

Handle duplicates

Webhooks may be delivered more than once. Use the jobId to deduplicate:

app.post('/webhooks/screenshot', async (req, res) => {
  const { jobId } = req.body;

  // Check if already processed
  const existing = await db.processedJobs.findOne({ jobId });
  if (existing) {
    return res.sendStatus(200); // Already processed
  }

  // Mark as processing
  await db.processedJobs.create({ jobId, processedAt: new Date() });

  // Process webhook...
  res.sendStatus(200);
});

Testing webhooks

Use tools like webhook.site or ngrok to test locally:

# Start ngrok tunnel
ngrok http 3000

# Use the ngrok URL as your webhook
{
  "url": "https://example.com",
  "webhookUrl": "https://abc123.ngrok.io/webhooks/screenshot"
}

On this page