Webhooks
Send signed alerts to Zapier, Make, n8n or your own code.
A webhook sends each alert as a JSON POST to a URL you choose. Use it to route alerts into Zapier, Make, n8n or your own service.
Add a webhook
- Open Settings → Integrations.
- Under Webhooks, paste your endpoint URL, choose which alerts it receives, and click Add endpoint.
- Copy the signing secret that appears. You can show it again later with the key button on the webhook's row.
The URL must be public and use https://. A workspace can have up to 10 webhooks. Each one can be switched off, or sent a test event, from its row.
What Peersheep sends
Every request carries these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
Peersheep-Event | alert.created, or ping for a test |
Peersheep-Signature | t=<unix seconds>,v1=<signature> |
The body of an alert.created event:
{
"id": "5f0c…",
"event": "alert.created",
"createdAt": "2026-09-28T09:00:00.000Z",
"data": {
"alertId": "alert_…",
"competitor": { "name": "Acme AI", "domain": "acme.ai" },
"page": { "type": "Pricing", "url": "https://acme.ai/pricing" },
"signal": "high",
"summary": "Acme raised the Pro plan from $49 to $79 a month and removed the free tier.",
"reasoning": "…",
"changeExcerpt": "- Pro $49/mo\n+ Pro $79/mo",
"dashboardUrl": "https://app.peersheep.com/alerts/alert_…"
}
}reasoning and changeExcerpt can be null.
Check the signature
v1 is a hex HMAC-SHA256 of <t>.<raw request body>, keyed with the webhook's signing secret. Compute it over the raw body, before parsing the JSON, and compare it with v1. Reject requests whose t is more than a few minutes old.
import crypto from "node:crypto";
function isFromPeersheep(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Delivery
Peersheep waits up to 8 seconds for a response and treats any 2xx as delivered. Each webhook's row shows the status code and time of its last delivery. Failed deliveries aren't retried, so respond quickly and do slow work after replying.