Notifications & webhooks
PG Hero can tell you the moment a prospect engages with a landing page - by email, Slack, or a JSON webhook you can wire into your CRM or automation tool.
What triggers an alert
| Event | Fires when |
|---|---|
| view | Someone opens a landing page |
| qr_scan | Someone scans the QR code in their box |
| cta_click | Someone clicks a CTA on the page (book a demo, etc.) |
| page.ready | A page made through the API is written and live, so its link can be sent (webhook only) |
| page.failed | A page made through the API couldn't be written (webhook only) |
Human-facing alerts (email + Slack) are throttled per page so a hot page doesn't spam you: views alert at most once per 30 minutes, scans and clicks at most once per 5 minutes, and a QR scan suppresses the redundant "is viewing" alert that follows the redirect. The events webhook is not throttled - it receives every event, since machines are expected to do their own filtering.
Email alerts
On by default for every rep, sent to the address you sign in with, for pages you created. Turn them off under Settings → Your Profile → Notifications.
Slack alerts
- In Slack: Apps → Incoming Webhooks → Add to Slack, pick a channel, copy the webhook URL (
https://hooks.slack.com/services/…). - Paste it in one of two places: Your Profile for your personal channel (used for pages you created), or Organization settings (admin) as the org-wide fallback for any rep without a personal webhook.
Events webhook (CRM / Zapier / your endpoint)
Org admins can set an Events webhook URL under Settings → Organization → Notifications & integrations. Every page event is delivered as:
POST <your URL>
Content-Type: application/json
User-Agent: PGHero-Webhook/1.0
X-PGHero-Timestamp: 1751879643
X-PGHero-Signature-V2: sha256=<hex hmac over "<timestamp>.<body>">
X-PGHero-Signature: sha256=<hex hmac over body> (legacy, will be removed)
{
"event": "qr_scan",
"occurred_at": "2026-07-07T09:14:03.120000+00:00",
"variant": {
"id": "6f0c…",
"title": "Acme Ltd · Jane Smith",
"url": "https://pghero.co.uk/v/acme-jane-x1"
},
"target": { "id": "9a1b…", "company_name": "Acme Ltd", "website": "https://acme.com" },
"contact": { "id": "c2d3…", "name": "Jane Smith", "role": "VP Sales", "email": "jane@example.com" },
"rep": { "uid": "u-…", "name": "Riley Rep", "email": "riley@example.com" },
"payload": { "label": "Book a demo" }
}contact is null for company-level pages. payload carries event extras (the clicked CTA label, the scanned QR code).
Your endpoint should return a 2xx quickly. Delivery is best-effort with a 6-second timeout and no retries - treat it as a signal stream, not a ledger; the full event history is always available in PG Hero analytics.
Verifying the signature
When you save a webhook URL, PG Hero generates a signing secret (shown on the same settings card). Each request carries a unix timestamp in X-PGHero-Timestamp and, in X-PGHero-Signature-V2, an HMAC-SHA256 over the string "<timestamp>.<raw body>". Check the timestamp is recent (5 minutes is a sensible tolerance), then verify the signature:
# Python (FastAPI / Flask)
import hashlib, hmac, time
def verify(secret: str, body: bytes, timestamp: str, header: str, tolerance: int = 300) -> bool:
try:
if abs(time.time() - int(timestamp)) > tolerance:
return False
except (TypeError, ValueError):
return False
msg = timestamp.encode() + b"." + body
expected = "sha256=" + hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header or "")// Node
const crypto = require("crypto");
function verify(secret, rawBody, timestamp, header, tolerance = 300) {
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > tolerance) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret)
.update(timestamp + ".").update(rawBody).digest("hex");
const a = Buffer.from(expected), b = Buffer.from(header || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Compute the HMAC over the raw request body bytes (don't re-serialise the parsed JSON - key order matters). Reject requests with a missing or invalid signature or a stale timestamp. The older X-PGHero-Signature header (HMAC over the body alone) is still sent for one release so existing receivers keep working; move to V2 now.
The payload object only ever carries label, code, source and href as short strings. Deliveries are capped at 60 per minute per workspace.
To rotate the secret: clear the webhook URL, save, then set it again - a new secret is generated.
Security notes
- Webhook URLs must be public
http(s)endpoints; PG Hero refuses to POST to private, loopback or cloud-metadata addresses. - The events payload includes contact details (name, role, email). Only org owners/admins can set or read the webhook settings - point it somewhere you trust.
- Alert emails and Slack messages escape all visitor-supplied strings, and the public event endpoint is rate-limited per IP.
Questions or need another event type? Ask your PG Hero contact.