Webhooks
The moment LeadPass decides a lead's verdict, it can POST the full result — verdict, summary, and the evidence per criterion — to a URL you choose. That means qualified leads land in your CRM, Slack, or automation platform seconds after they finish talking, with everything your team needs to act. Every request is signed so you can trust what arrives.
The webhook contract is v1 and additive-only: fields may be added over time, but nothing documented here will be renamed, retyped, or removed without a new, explicitly announced version.
Quick start
- Open Settings → Workspace → Webhook (
/app/settings/workspace). Enable the workspace webhook and pick which verdicts you want delivered. - Paste your endpoint URL. A Zapier "Catch Raw Hook", a Make "Webhook", or an n8n "Webhook" trigger URL works out of the box — no native integration needed.
- Click Send test event. LeadPass sends a sample
lead.passpayload with"test": trueso you can see the exact shape. - Map the fields you need (
verdict,lead.email,summary,criteria, …) in your receiving tool. Filter onevent,verdict, ortestas needed. - Copy the signing secret from the same Settings tab and verify signatures in your receiver (see "Verifying the signature" below). Recommended for anything beyond a no-code catch hook.
Events
| Event | When it fires |
|---|---|
lead.pass |
The lead's first decided verdict is Pass (revision: 1). |
lead.review |
The lead's first decided verdict is Review (revision: 1). |
lead.no_pass |
The lead's first decided verdict is No pass (revision: 1). |
lead.verdict_revised |
Any later decided revision on the same lead (revision > 1): a team-member decision or a re-analysis, even when the verdict value stays the same. |
The verdict vocabulary in payloads is pass | review | no_pass. A fourth value, incomplete, only ever appears as previous_verdict or as a re-analysis replacement — it is never subscribable and never a first event.
Subscription filter
The verdict checkboxes in Workspace settings decide what gets delivered:
- A first event (
revision: 1) is sent when its resulting verdict is selected. - A later revision (
lead.verdict_revised) is sent when either the previous or current verdict is selected. A Pass-only endpoint still hears a Pass being revoked, but never a Review-to-No pass revision for a lead it never saw. Re-analysis can also publish Pass-to-Pass with new evidence, summary, or suggested action.
Payload
Every delivery is a JSON POST body shaped like this:
{
"id": "evt_01J8ZKQ2V5X6C7...",
"event": "lead.review",
"version": 1,
"created_at": "2026-07-10T12:34:56+00:00",
"test": false,
"flow": { "public_id": "fl_8k2m9x", "name": "Before a demo", "language": "es" },
"lead": {
"uuid": "9b2f6c9e-...",
"name": "María García",
"email": "[email protected]",
"phone": "+34 600 000 000",
"company": "Example SL",
"utm": { "utm_source": "meta" },
"started_at": "2026-07-10T12:30:00+00:00",
"completed_at": "2026-07-10T12:34:55+00:00"
},
"verdict": "review",
"previous_verdict": null,
"decided_by": "system",
"revision": 1,
"summary": "Budget named, timing unclear...",
"criteria": [
{ "key": "budget", "label": "Budget", "polarity": "positive", "band": "high", "evidence": "\"around €2,000 a month\"" }
],
"failed_gates": [],
"missing_criteria": [],
"links": { "lead": "https://useleadpass.com/app/leads/9b2f6c9e-..." }
}
Field notes:
id— unique event id, stable across retries of the same event. Use it to deduplicate.event— one oflead.pass,lead.review,lead.no_pass,lead.verdict_revised.test—trueonly for test events sent from Workspace settings; they never describe a real person.flow.public_id— the flow's opaque public handle; filter on it when several flows share one endpoint.flow.languageis the flow's language at decision time (the language its leads answer in) — use it to follow up in the right language.lead— fields the flow doesn't collect arrive asnull.started_atis when the person began the flow;completed_atis when the qualification pipeline reached its final result.verdict—pass | review | no_pass; on a re-analysis replacement (revision > 1) it can also beincomplete, meaning the previous verdict no longer stands and no new read exists yet. It is never a first event.previous_verdict— the same values includingincomplete, ornullon a first verdict.decided_by—systemwhen the AI pipeline decided (first completion or a re-analysis),team_memberwhen a human did (an override or a decision from the review queue).revision— 1 for the first decided verdict, incremented by 1 for every later decision on the same lead. Test events userevision: 0.criteria— the flow's own criteria, not fixed signals. Each entry carrieskey,label,polarity,band, andevidence.failed_gates— criterionkeys whose hard rule knocked the lead out. This is how you explain ano_passwhose bands look decent: a rule fired.missing_criteria— criterionkeys that required evidence the lead never gave; the cause behind anincompletereplacement verdict.links.lead— opens the lead in LeadPass for any signed-in teammate with access, whatever workspace they had active.
Reading criteria bands through polarity
Each criterion's band is one of no_data | very_low | low | medium | high | very_high. Always read the band through the criterion's polarity:
| Polarity | High band means | Low band means |
|---|---|---|
positive |
Favourable | Unfavourable |
risk |
Adverse | Favourable |
no_data means the lead gave no evidence for that criterion.
Ordering: last write wins by revision
revision increases by 1 for every decided verdict of a lead. Deliveries can retry out of order, so keep the highest revision you have seen per lead.uuid and ignore anything lower. LeadPass also cancels superseded queued deliveries on its side, but the revision rule is the guarantee. A later revision can legitimately carry the same verdict after re-analysis.
Deduplicate by id: it is stable across retries of the same event.
Verifying the signature
Every request carries these headers:
X-LeadPass-Signature: t=1760096096,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e163747325f37d9fa8c2fe...
X-LeadPass-Event: lead.review
X-LeadPass-Delivery: evt_01J8ZKQ2V5X6C7...
v1 is HMAC-SHA256(t + "." + rawBody, secret), computed with your workspace's signing secret (Settings → Workspace → Webhook). To verify:
- Parse
t(a Unix timestamp) andv1from theX-LeadPass-Signatureheader. - Compute
HMAC-SHA256over the stringt + "." + rawBodyusing the raw, unparsed request body. - Compare against
v1with a constant-time comparison. - Reject stale timestamps — ±5 minutes is a sane window.
PHP
[$t, $v1] = [null, null];
foreach (explode(',', $request->header('X-LeadPass-Signature')) as $part) {
[$k, $v] = explode('=', $part, 2);
if ($k === 't') { $t = $v; }
if ($k === 'v1') { $v1 = $v; }
}
$expected = hash_hmac('sha256', $t.'.'.$request->getContent(), $secret);
abort_unless(hash_equals($expected, (string) $v1) && abs(time() - (int) $t) < 300, 403);
Node.js
const crypto = require('crypto');
function verify(signatureHeader, rawBody, secret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((p) => p.split('=', 2))
);
const expected = crypto
.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`)
.digest('hex');
const valid =
expected.length === (parts.v1 || '').length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return valid && fresh;
}
Make sure your framework gives you the raw request body for the HMAC — a re-serialized JSON object will not match.
Regenerating the secret (same Settings tab) applies to the very next delivery attempt — update your receiver first.
Delivery and retries
- HTTPS only. Plain
http://URLs are not accepted. - Your endpoint has 10 seconds to respond. Any
2xxstatus counts as delivered. Redirects are not followed. - Retryable failures — network errors,
408,425,429, and any5xx— are retried up to 5 attempts over roughly 2.5 hours with backoff and jitter. A numericRetry-Afterheader is honoured, capped at 1 hour. - Other
4xxresponses fail immediately with no retry. - Disabling the webhook cancels queued deliveries. Changing the URL applies to the next attempt of anything still queued.
- Delivery records (payload included) are kept for 30 days. Deleting a lead deletes its delivery records immediately.
Auto-disable after repeated failures
After 10 consecutive failed real deliveries with no success in between, LeadPass switches the webhook off, cancels anything still queued, and emails the Workspace creator if still an Account member, otherwise the Account owner. Verdicts decided while the webhook is off are not re-sent later. To recover: fix your receiver, re-enable the webhook in Workspace settings, and confirm with Send test event. Test pings never count toward the failure counter.
Test events
The Send test event button in Settings → Workspace → Webhook sends a sample payload with:
"test": true"event": "lead.pass""revision": 0
Test events never describe a real person and never count toward the auto-disable failure counter. Use them to wire up field mapping and to confirm a receiver is healthy after re-enabling.
They keep the exact real-event shape, including a synthetic links.lead string, so no-code tools can discover every field without exposing a stored record.