Skip to main content
Webhooks let you receive automatic POST notifications when screenshot events occur, instead of polling the API.

How it works

  1. Configure a webhook endpoint in your dashboard
  2. Screenshotly sends a POST request to your URL when events occur
  3. Your server processes the event and responds with a 2xx status code

Event types

EventDescription
screenshot.createdA screenshot capture has been initiated
screenshot.completedA screenshot has been successfully captured
screenshot.failedA screenshot capture has failed
screenshot.deletedA screenshot has been deleted

Webhook payload

When an event occurs, Screenshotly sends a POST request with a JSON body:
{
  "event": "screenshot.completed",
  "event_id": "evt_67b8a1c2d3e4f5a6",
  "timestamp": "2025-05-14T10:00:03.000Z",
  "data": {
    "screenshot": {
      "_id": "67b8a1c2d3e4f5a6b7c8d9e0",
      "url": "https://example.com",
      "status": "completed",
      "imageUrl": "https://cdn.screenshotly.dev/screenshots/67b8a1c2d3e4f5a6b7c8d9e0.png",
      "metadata": {
        "fileSize": 245760,
        "format": "png",
        "dimensions": { "width": 1280, "height": 1024 }
      }
    },
    "user": {
      "id": "67a1b2c3d4e5f6a7b8c9d0e1"
    }
  }
}
For screenshot.failed events, an error field is included:
{
  "event": "screenshot.failed",
  "event_id": "evt_67b8a1c2d3e4f5a7",
  "timestamp": "2025-05-14T10:00:30.000Z",
  "data": {
    "screenshot": {
      "_id": "67b8a1c2d3e4f5a6b7c8d9e1",
      "url": "https://example.com",
      "status": "failed"
    },
    "user": {
      "id": "67a1b2c3d4e5f6a7b8c9d0e1"
    },
    "error": "Page navigation timed out."
  }
}

Webhook headers

Each webhook delivery includes these headers:
HeaderDescription
Content-Typeapplication/json
User-AgentScreenshotly-Webhooks/1.0
X-Webhook-SignatureHMAC-SHA256 signature for verification
X-Webhook-EventThe event type (e.g., screenshot.completed)
X-Webhook-IDYour webhook configuration ID
X-Webhook-TimestampISO 8601 timestamp of the event

Signature verification

Every webhook delivery is signed using your webhook’s secret key with HMAC-SHA256. Verify the signature to ensure the request is from Screenshotly:
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

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

Handling webhooks

Here’s a complete Express.js handler with signature verification:
const express = require('express');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.SCREENSHOTLY_WEBHOOK_SECRET;

app.post('/webhook/screenshots', (req, res) => {
  // Verify signature
  const signature = req.headers['x-webhook-signature'];
  const expectedSignature = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (signature !== expectedSignature) {
    return res.status(401).send('Invalid signature');
  }

  const { event, event_id, data } = req.body;

  switch (event) {
    case 'screenshot.completed':
      console.log(`Screenshot ready: ${data.screenshot.imageUrl}`);
      break;
    case 'screenshot.failed':
      console.error(`Screenshot failed: ${data.error}`);
      break;
    case 'screenshot.deleted':
      console.log(`Screenshot deleted: ${data.screenshot._id}`);
      break;
  }

  // Respond quickly with 200 to acknowledge receipt
  res.status(200).send('OK');
});

Retry behavior

If your webhook endpoint returns a non-2xx status code or times out (10 second limit), Screenshotly retries delivery with exponential backoff. You can configure retry policies in the dashboard:
  • Max retries: 0–10 attempts (default: 3)
  • Retry delay: 100–60,000ms (default: 1,000ms)
  • Backoff multiplier: 1–5x (default: 2x)
Delivery logs are retained for 90 days.

Best practices

  • Verify signatures — always validate the X-Webhook-Signature header to ensure requests are from Screenshotly
  • Respond quickly — return a 200 response immediately, then process the payload asynchronously
  • Handle duplicates — use the event_id field for deduplication, as the same event may be delivered more than once
  • Use HTTPS — always use an HTTPS endpoint for your webhook URL
  • Monitor delivery logs — check your webhook’s delivery history in the dashboard to debug failures