Skip to main content

Webhooks

Webhooks allow you to receive real-time HTTP notifications when events occur in your Pictify account. Use them to trigger workflows, update databases, or integrate with third-party services.

Supported Events

EventDescription
render.completedImage, GIF, or PDF render finished successfully
render.failedRender failed with an error
binding.updatedBinding data refreshed
binding.failedBinding data fetch failed

Creating a Webhook

Dashboard

  1. Go to Settings > Webhooks
  2. Click Create Webhook
  3. Select the event type
  4. Enter your endpoint URL
  5. Save and copy the signing secret

API

curl -X POST https://api.pictify.io/webhook-subscriptions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "render.completed",
    "targetUrl": "https://your-server.com/webhooks/pictify",
    "platform": "custom"
  }'
Response includes the signing secret (only shown once):
{
  "subscription": {
    "uid": "wh_abc123",
    "event": "render.completed",
    "targetUrl": "https://your-server.com/webhooks/pictify",
    "status": "active",
    "secret": "whsec_xyz789..."
  }
}

Webhook Payload

All webhooks include these headers:
Content-Type: application/json
X-Pictify-Signature: t=1706515260,v1=abc123...
X-Pictify-Event: render.completed
X-Pictify-Delivery-Id: del_xyz789

render.completed

{
  "event": "render.completed",
  "timestamp": "2026-01-29T10:30:00Z",
  "data": {
    "type": "image",
    "source": "api",
    "imageId": "img_abc123",
    "url": "https://cdn.pictify.io/renders/abc123.png",
    "userStorageUrl": "https://your-bucket.s3.amazonaws.com/abc123.png",
    "width": 1200,
    "height": 630,
    "templateId": "tmpl_xyz789",
    "userId": "user_123",
    "renderedAt": "2026-01-29T10:30:00Z"
  }
}

render.failed

{
  "event": "render.failed",
  "timestamp": "2026-01-29T10:30:00Z",
  "data": {
    "type": "image",
    "templateId": "tmpl_xyz789",
    "error": "Template not found",
    "errorCode": "TEMPLATE_NOT_FOUND"
  }
}

Signature Verification

Always verify webhook signatures to ensure requests are from Pictify and haven’t been tampered with.
The signature header format is:
X-Pictify-Signature: t=1706515260,v1=abc123...
Where:
  • t = Unix timestamp when the webhook was sent
  • v1 = HMAC-SHA256 signature of {timestamp}.{payload}

Verification Steps

  1. Extract timestamp and signature from header
  2. Reject if timestamp is older than 5 minutes (replay protection)
  3. Compute expected signature: HMAC-SHA256(secret, "{timestamp}.{payload}")
  4. Compare signatures using constant-time comparison

Code Examples

import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,
  signatureHeader: string,
  secret: string
): boolean {
  const parts: Record<string, string> = {};
  for (const pair of signatureHeader.split(',')) {
    const [key, value] = pair.split('=');
    parts[key] = value;
  }

  const timestamp = parseInt(parts.t, 10);
  const providedSignature = parts.v1;

  // Reject if timestamp is older than 5 minutes
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
    throw new Error('Webhook timestamp too old');
  }

  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

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

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

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

    const event = JSON.parse(payload);
    console.log('Received event:', event.event);

    res.status(200).send('OK');
  } catch (error) {
    res.status(400).send(error.message);
  }
});

Managing Webhooks

List Webhooks

curl https://api.pictify.io/webhook-subscriptions \
  -H "Authorization: Bearer $API_KEY"

Pause / Resume a Webhook

Pause and resume are currently available via the dashboard only. API support is coming soon.

Delete a Webhook

curl -X DELETE https://api.pictify.io/webhook-subscriptions/{uid} \
  -H "Authorization: Bearer $API_KEY"

Filters

Filter webhooks to receive only specific events:
curl -X POST https://api.pictify.io/webhook-subscriptions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "render.completed",
    "targetUrl": "https://your-server.com/webhooks",
    "filters": {
      "templateId": "tmpl_abc123"
    }
  }'

Delivery & Retries

  • Webhooks are delivered within seconds of events
  • Failed deliveries retry with exponential backoff: 1min, 5min, 30min, 2hr, 24hr
  • After 5 failed attempts, the webhook is paused
  • Check delivery status in the dashboard or via API

Best Practices

  1. Always verify signatures - Protect against spoofed requests
  2. Respond quickly - Return 2xx within 30 seconds, process async
  3. Handle duplicates - Use delivery ID for idempotency
  4. Monitor failures - Set up alerts for webhook delivery issues
  5. Use HTTPS - Never use HTTP endpoints in production