Webhooks
Webhooks post a JSON event to a URL you choose whenever something happens in your assistant. They’re available on the Startup plan and above, and work with any tool that accepts an incoming webhook:
Two events are available. Each webhook subscribes to one; add the same URL twice to receive both.
| Event | Sent when |
|---|---|
new_lead |
A visitor submits the lead form or leaves their email for a human handoff |
escalation_requested |
A visitor asks for a person and leaves their email. Includes the assistant’s summary |
booking_created |
A visitor books through Cal.com in the chat. Includes booking with uid, start (UTC, the first time), starts (every time booked, for multi-day bookings), event_type, notes and paid (true when paid via a Cal.com payment link) |
Handoffs and bookings also count as leads, so they send new_lead too. If you use Freshdesk, handoffs can also open a ticket directly, without a webhook. Its new_lead payload has "source": "escalate_to_human" in data, so you can filter it out if you subscribe to both.
Add a webhook
Section titled “Add a webhook”- Open your chatbot and go to Webhooks.
- Paste a public
httpsURL, pick the event, and click Add webhook. - Click Send test to post a sample event, then check that it arrived.
Each webhook gets its own signing secret, shown on its card. You can rotate it at any time.
Payload
Section titled “Payload”{ "id": "evt_4f0c9a2e7b1d4c3a9e8f6b5a4d3c2b1a", "event": "new_lead", "chatbotId": "k57abc123...", "leadId": "j97def456...", "conversationId": "conv_8f2d...", "data": { "name": "Maya Chen", "email": "maya@example.com", "phone": "+1 555 0100" }, "tags": { "intent": "booking", "intent_confidence": 0.92, "urgency": 0.67, "urgency_label": "Wants it within the next few days", "qualified": 0.88 }, "timestamp": 1760281200000}-
datacontains whichever fields the visitor filled in, including answers to any extra questions on your lead form. -
tagsis PageClerk’s read of the conversation, when available:intent: one ofbooking,quote,purchase,support,complaint,question,other, withintent_confidence(0–1)urgency: 0 (no time pressure) to 1 (today or an emergency), with a readableurgency_labelqualified: 0–1, how likely this is a real prospect worth following up
Use them to route in your automation, for example complaints to a manager or
qualifiedabove 0.7 to a sales pipeline. If tagging isn’t available,tagsis left out and the event is still sent. -
escalation_requestedevents addsummary: the assistant’s description of what the visitor needs (empty if none was produced). -
timestampis in milliseconds. -
Test events include
"test": trueand sample data.
Every request also carries these headers:
| Header | Value |
|---|---|
X-PageClerk-Event |
The event name, e.g. new_lead |
X-PageClerk-Delivery |
The event id. Use it to drop duplicates |
X-PageClerk-Signature |
t=<unix seconds>,v1=<hex> |
Extra questions
Section titled “Extra questions”In Settings → Lead capture → Extra questions, add questions such as “What do you need?” with follow-ups that only appear for some answers. Each answer is sent in data under the question’s key, for example "project_type": "A full project".
Retries
Section titled “Retries”Return any 2xx status to acknowledge the event. Network errors, timeouts (10 seconds), 408, 429, and 5xx responses are retried after 1 minute and again after 10 minutes. Other 4xx responses aren’t retried. The webhook card shows the result of the last delivery.
Because an event can arrive more than once, use X-PageClerk-Delivery to ignore repeats.
Verify the signature
Section titled “Verify the signature”The signature is an HMAC-SHA256 of <t>.<raw request body>, keyed with your signing secret and hex-encoded. Compute it over the raw body exactly as received, before parsing the JSON.
import crypto from "node:crypto";
export function verifyPageClerk(rawBody, header, secret, toleranceSeconds = 300) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); const t = Number(parts.t); if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return ( expected.length === parts.v1?.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1)) );}Checking the timestamp blocks replays of old requests.
- Add a Webhook trigger node. Set the method to
POSTand copy its Production URL. - Paste the URL into PageClerk and click Send test. Use the Test URL instead while the workflow is in “Listen for test event” mode.
- Map fields from
body.data(for example{{ $json.body.data.email }}) into the next node, such as Google Sheets, HubSpot, or Slack.
To verify the signature in n8n, add a Crypto node (HMAC, SHA256, hex) over {{ $json.headers["x-pageclerk-signature"].split(",")[0].slice(2) }}.{{ JSON.stringify($json.body) }} and compare it with the v1 value. n8n re-serializes the body, so for strict verification turn on Raw Body in the Webhook node’s options and sign that instead.
Zapier
Section titled “Zapier”- Create a Zap with Webhooks by Zapier → Catch Hook as the trigger and copy the hook URL.
- Paste it into PageClerk and click Send test.
- In Zapier, click Test trigger. The sample lead appears with
data__email,data__name, and the other fields ready to map.
- Create a scenario starting with Webhooks → Custom webhook and copy the address.
- Paste it into PageClerk, click Run once in Make, then click Send test so Make learns the data structure.
HighLevel (GoHighLevel)
Section titled “HighLevel (GoHighLevel)”Send leads straight into your HighLevel contacts and pipelines, in your own account or a client’s sub-account.
- In the sub-account, go to Automation → Workflows and create a workflow from scratch.
- Add the Inbound Webhook trigger and copy its URL.
- Paste the URL into PageClerk and click Send test. Back in HighLevel, click Fetch Sample Request on the trigger, select the test event, and save the trigger.
- Add a Create Contact action. For each field, open the custom values menu, choose Inbound Webhook Trigger, and pick the matching value:
data→name,emailandphone. - Optionally add Create/Update Opportunity to place the lead in a pipeline stage, Add Tag (for example
pageclerk, or thetags→intentvalue), and Internal Notification to alert your team. Use an If/Else on thetags→qualifiedorintentvalue to route qualified leads and complaints differently. For anescalation_requestedwebhook, add thesummaryvalue to a note or notification so your team sees what the visitor needs. - Save and publish the workflow.
Inbound Webhook is a premium workflow trigger, so HighLevel may bill each run depending on your plan. HighLevel workflows can’t verify the signature header, so keep the webhook URL private. If it leaks, add a new Inbound Webhook trigger and replace the URL in PageClerk.