All integrations
Live today

PolicifyAI on Webhooks

Signed, retrying event delivery to any HTTPS endpoint.

PolicifyAI posts a signed JSON event to your endpoint whenever something happens - policy published, updated or deleted, consent recorded, DSAR submitted, cookie scan completed, integration connected/disconnected. Every delivery is HMAC-SHA256 signed, retried with exponential backoff (30s → 2m → 10m → 1h, up to 5 attempts), and logged with response code and payload preview you can replay. Available on all paid plans.

Set up in 10 minutes

10 minutes
  1. 1Dashboard → Integrations → Webhooks → Add endpoint (HTTPS only).
  2. 2Copy the generated signing secret and store it server-side - it is shown once.
  3. 3Subscribe to the events you care about via checkboxes.
  4. 4Verify X-PolicifyAI-Signature on your receiver using the code below, then return 2xx within 5 seconds. Use "Send test" to confirm the wiring.
Standard event payload
{
  "event_id": "evt_1a2b3c4d5e",
  "type": "policy.published",
  "created_at": "2026-06-22T09:00:00Z",
  "workspace_id": "ws_acme",
  "api_version": "v1",
  "data": {
    "policy_id": "pol_abc123",
    "policy_type": "privacy_policy",
    "version": 4,
    "jurisdictions": ["GB", "EU", "US-CA"],
    "hosted_url": "https://policifyai.com/p/abc123"
  }
}
Verify the signature - Node.js
const crypto = require('crypto')

function verify(rawBody, header, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  const a = Buffer.from(header || '', 'utf8')
  const b = Buffer.from(expected, 'utf8')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

// Express: use express.raw({ type: 'application/json' }) so rawBody is intact.
app.post('/webhooks/policify', express.raw({ type: '*/*' }), (req, res) => {
  if (!verify(req.body, req.header('X-PolicifyAI-Signature'), process.env.POLICIFY_SECRET)) {
    return res.status(401).end()
  }
  const event = JSON.parse(req.body.toString())
  // handle event.type ...
  res.status(200).end()
})
Verify the signature - Python (Flask)
import hmac, hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = b"your_signing_secret"

@app.post("/webhooks/policify")
def policify():
    raw = request.get_data()  # raw bytes - do not use request.json first
    sig = request.headers.get("X-PolicifyAI-Signature", "")
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        abort(401)
    event = request.get_json()
    # handle event["type"] ...
    return "", 200
Verify the signature - PHP
<?php
$secret = getenv('POLICIFY_SECRET');
$raw    = file_get_contents('php://input');
$sig    = $_SERVER['HTTP_X_POLICIFYAI_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $raw, $secret);

if (! hash_equals($expected, $sig)) {
    http_response_code(401);
    exit;
}
$event = json_decode($raw, true);
// handle $event['type'] ...
http_response_code(200);
Verify the signature - Go
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"io"
	"net/http"
	"os"
)

func handler(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	mac := hmac.New(sha256.New, []byte(os.Getenv("POLICIFY_SECRET")))
	mac.Write(body)
	expected := hex.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(expected), []byte(r.Header.Get("X-PolicifyAI-Signature"))) {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	w.WriteHeader(http.StatusOK)
}
Verify the signature - Ruby
require 'openssl'

post '/webhooks/policify' do
  raw = request.body.read
  sig = request.env['HTTP_X_POLICIFYAI_SIGNATURE'].to_s
  expected = OpenSSL::HMAC.hexdigest('SHA256', ENV['POLICIFY_SECRET'], raw)
  halt 401 unless Rack::Utils.secure_compare(expected, sig)
  event = JSON.parse(raw)
  # handle event['type'] ...
  status 200
end

Why PolicifyAI

Frequently asked

What events are available?+

policy.published, policy.updated, policy.deleted, consent.recorded, consent_banner_shown, dsar.submitted, cookie_scan.completed, integration.connected, integration.disconnected - with more added over time.

How do you prevent replay attacks?+

Each event carries a unique event_id and created_at timestamp. Reject events older than your tolerance window and de-dupe on event_id (idempotency).

Which plans include webhooks?+

All paid plans. Free accounts can browse the docs but need to upgrade to add endpoints.

Also available