Send leads to Zapier and other tools with webhooks
Push every new lead into your CRM, spreadsheet, or Slack the moment it's captured — with a step-by-step Zapier setup.
Webhooks are how leads leave Perch. Whenever a page captures a new lead, Perch immediately POSTs it as signed JSON to every endpoint you’ve added. The payload is Zapier-compatible out of the box, which means any of the thousands of apps Zapier connects — HubSpot, Salesforce, Google Sheets, Slack, Mailchimp — can receive your leads within seconds of capture.
Set up with Zapier
- 1
Create the Zap
In Zapier, create a new Zap with the trigger Webhooks by Zapier → Catch Hook. Zapier gives you a hook URL like
https://hooks.zapier.com/hooks/catch/…— copy it. - 2
Add the endpoint in Perch
Go to Settings → Integrations, paste the hook URL, and optionally add a label (e.g. “HubSpot via Zapier”). By default the endpoint receives leads from all companies; pick a company in the scope dropdown if this Zap should only get one client’s leads.
- 3
Store the signing secret
Perch shows the endpoint’s signing secret (
whsec_…) exactly once, right after you create it. Zapier doesn’t verify signatures, so you can dismiss it — but if you might ever point this endpoint at your own server, save it somewhere safe now. It can’t be shown again. - 4
Send a test event
Use the endpoint’s Send test action in Perch, then click Test trigger in Zapier. Zapier pulls in a sample lead payload (marked
"test": true) so you can see every field. - 5
Map fields and turn it on
Add your action step (create a CRM contact, append a spreadsheet row, post to Slack…) and map fields from the sample —
lead.name,lead.email,page.title,lead.utmCampaign, and so on. Turn the Zap on, and every new lead flows through automatically.
When webhooks fire
One event exists today: lead.created. It fires when a visitor submits the lead form on one of your published pages and that submission creates a genuinely new lead. Perch keeps one contact per email address per workspace, so a repeat submission from the same email updates the existing lead and does not fire a webhook — your CRM never gets duplicates. Delivery happens right after the visitor’s submission completes and never slows the page down.
Company-scoped endpoints only receive leads captured by that company’s pages. Manual status changes (e.g. marking a lead Qualified) don’t fire webhooks.
The payload
Every delivery is a POST with a JSON body shaped like this:
POST — application/json
{
"id": "evt_9f2c81d4a7b3",
"event": "lead.created",
"version": 1,
"createdAt": "2026-07-09T17:24:31.812Z",
"company": { "id": "cmp_4k1…", "name": "Acme Roofing" },
"page": {
"id": "pag_8s3…",
"slug": "acme-roofing-spring-x7k2q1",
"title": "Spring Roofing Special"
},
"lead": {
"id": "cnt_2m9…",
"name": "Jamie Rivera",
"email": "jamie@example.com",
"phone": "+1 555 0100",
"status": "NEW",
"capturedAt": "2026-07-09T17:24:31.640Z",
"utmSource": "google",
"utmMedium": "cpc",
"utmCampaign": "spring-roofing",
"utmTerm": null,
"utmContent": null,
"referrer": "https://www.google.com/",
"formData": { "message": "Need a quote for a roof replacement." }
}
}formData carries any custom form answers (like the message field) that aren’t part of the standard lead fields. Test deliveries include an extra "test": true flag so you can filter them out downstream.
Each request also carries these headers:
| Header | Contents |
|---|---|
X-Perch-Event | The event type, e.g. lead.created |
X-Perch-Delivery | Unique delivery ID — use it to deduplicate if you ever receive a retry twice |
X-Perch-Timestamp | Unix time (seconds) when the delivery was signed |
X-Perch-Signature | HMAC signature of the payload (see below) |
User-Agent | Perch-Webhooks/1 |
Verifying signatures (your own server)
If you point an endpoint at your own server instead of Zapier, verify each request before trusting it. The signature is an HMAC-SHA256 of <timestamp>.<raw body> keyed with your endpoint’s whsec_… secret:
verify.js
import crypto from "node:crypto";
function verifyPerchSignature(rawBody, headers, secret) {
const timestamp = headers["x-perch-timestamp"];
const signature = headers["x-perch-signature"]; // "v1=<hex>"
const expected =
"v1=" +
crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return (
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
);
}Zapier users can skip this
Delivery, retries, and failures
- A failed delivery is retried a couple of times within seconds, then automatically retried for up to 24 hours.
- After 20 consecutive failures an endpoint is auto-disabled so a dead URL doesn’t churn forever. Fix the receiver, then re-enable it from Settings → Integrations.
- The Recent deliveries log on the same page shows each attempt with its HTTP status or error message.
- Endpoint URLs must be public HTTPS addresses — local or private network hosts are rejected.
Troubleshooting
- Zapier isn’t receiving anything: confirm the Zap is turned on, and check Recent deliveries in Perch — a “Delivered (HTTP 200)” row means the lead reached Zapier and the issue is in the Zap’s action step.
- Endpoint shows “Auto-disabled”: the URL failed 20 times in a row. Verify the receiver is up, then click Enable — the failure counter resets.
- A form submission didn’t fire: most often the email already existed as a lead in your workspace, so it was an update, not a new lead.
Still need a hand?
We read every message during the beta. Email hello@perchpages.com or browse the rest of the help center.