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.createdA new policy has been generated and is ready.
policy.updatedA policy has been edited or regenerated.
policy.deletedA policy has been permanently deleted.
policy.viewedAn 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"
}
}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.