Dashboard
API Reference

API reference

Webhooks

Webhooks let your server receive real-time notifications whenever a policy event occurs in your PolicifyAI workspace. No polling required.

Available events

policy.created

A new policy has been generated and is ready.

policy.updated

A policy has been edited or regenerated.

policy.deleted

A policy has been permanently deleted.

policy.viewed

An embedded policy was loaded on a site (once per hour per policy per domain).

Payload structure

All webhook deliveries send a JSON payload with this structure:

{
  "event": "policy.created",
  "webhook_id": "wh_abc123",
  "delivery_id": "del_xyz789",
  "created_at": "2025-01-01T12:00:00Z",
  "data": {
    "policy_id": "pol_abc123",
    "policy_type": "privacy-policy",
    "business_name": "Acme Ltd",
    "domain": "https://acme.com",
    "jurisdiction": "GB",
    "language": "en",
    "client_id": null,
    "status": "ready",
    "created_at": "2025-01-01T12:00:00Z",
    "updated_at": "2025-01-01T12:01:30Z"
  }
}
💡 The data.content_html field is not included in webhook payloads to keep delivery sizes small. Fetch the full policy using GET /policies/{id} if needed.

Signature verification

Every webhook delivery includes an X-PolicifyAI-Signature header — an HMAC-SHA256 hex digest of the raw request body, keyed with your webhook secret. Always verify this signature before processing.

Node.js

const crypto = require('crypto')

// Express middleware
app.post('/webhooks/policify', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-policifyai-signature']
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex')

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

  const event = JSON.parse(req.body)
  // Process event...
  res.sendStatus(200)
})

Python

import hmac
import hashlib

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

PHP

$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_POLICIFYAI_SIGNATURE'];
$expected = hash_hmac('sha256', $payload, getenv('WEBHOOK_SECRET'));

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit;
}

Retries

PolicifyAI retries failed deliveries (non-2xx responses or timeouts) up to 5 times with exponential backoff:

Attempt 1

Immediate

Attempt 2

30 s

Attempt 3

2 min

Attempt 4

10 min

Attempt 5

30 min

After 5 failed attempts the webhook is automatically disabled and you receive an email. You can re-enable it from the API & Webhooks page. Any delivery can be manually replayed from the delivery history.

Response requirements

Your endpoint must return a 2xx HTTP status within 10 seconds. If you need to do heavy processing, respond immediately with 200 OK and process asynchronously. Do not return 3xx redirects — PolicifyAI does not follow them.

Testing webhooks

In the Agency Hub, open the webhook detail page and click Send test event. This sends a policy.created payload with synthetic data to your endpoint so you can verify your handler works without needing to generate a real policy.