Skip to content

How LeadTap.me webhooks work

A LeadTap.me webhook sends lead data, as JSON, to a URL the customer chooses. There are two kinds: the CRM webhook sends every lead in the account, whatever page they came from, and a page webhook sends the submissions of one lead page. Webhooks are a Plus feature, and a webhook sends nothing until the account owner turns it on in the portal.

CRM webhook or page webhook: which one to use

Section titled “CRM webhook or page webhook: which one to use”

Use the CRM webhook to get people into a CRM, an AI agent workflow or in front of a sales team; use a page webhook for one page’s data.

CRM webhook Page webhook
Scope The whole account: every lead page One lead page
What it sends A person (the lead) A submission of that page
Events lead.created, lead.verified, lead.submission complete, partial
Use it for A CRM, an AI agent workflow, Zapier, Make, n8n, a sales team Statistics, a spreadsheet, a record of what the page collected
Turned on in the portal at Integrations The lead page editor’s Connect tab
MCP tools list_crm_webhooks, create_crm_webhook, update_crm_webhook list_page_webhooks, create_page_webhook, update_page_webhook

Both kinds share test_webhook, list_webhook_deliveries and retry_webhook_deliveries.

Connecting LeadTap.me to HubSpot, Google Sheets and other tools

Section titled “Connecting LeadTap.me to HubSpot, Google Sheets and other tools”

LeadTap.me sends leads to other tools only through webhooks; it has no direct HubSpot or Google Sheets integration. To get every lead into HubSpot or another CRM, point the CRM webhook at Zapier, Make or n8n and let it pass each person on. To fill a spreadsheet with one page’s answers, point that page’s webhook at the sheet’s webhook URL or at Zapier, Make or n8n.

A LeadTap.me webhook of either kind starts off and only the account owner turns it on.

  1. The webhook is added in the portal, or by Claude with create_crm_webhook or create_page_webhook.
  2. It starts off. The account owner gets an email that says what it is for (the CRM, meaning every lead in the account, or the lead page), the webhook’s host (never the full URL) and its events, with a button to turn it on and a link to revoke the connected app’s access if the owner did not ask for the webhook.
  3. The owner turns it on in the portal: under Integrations for the CRM webhook, in the lead page editor’s Connect tab for a page webhook. Only then does it send lead data.

Claude can create a webhook, change its events, point it at a new URL, turn it off, test it and read its delivery log, but it cannot turn a webhook on or delete it. Pointing a webhook at a new URL turns it off again and emails the owner, like creating one.

A LeadTap.me webhook URL must be https, on a public host, with no credentials in it. Claude asks the customer for the URL and never guesses it. After the webhook is created, LeadTap.me shows only its host, never the full URL or the signing secret.

A LeadTap.me CRM webhook fires on the moments in a person’s life in the account.

Event When it fires
lead.created A person appears in the account for the first time, from any lead page (the default)
lead.verified The person confirms their email
lead.submission A person the account already knows answers again; what they answered enriches them

A LeadTap.me page webhook fires on one or both of two events of its lead page.

Event When it fires
complete Someone finished the lead page (the default)
partial Someone left the lead page halfway. Partial submissions must be on for the page

Every LeadTap.me webhook request, of either kind, is an HTTP POST with a JSON body and these headers.

Header Value
X-Leadtap-Event The event: lead.created, lead.verified, lead.submission, complete or partial
X-Leadtap-Idempotency-Key The same key on every retry of one delivery. Use it to drop duplicates
X-Leadtap-Signature t=<unix seconds>,v1=<hex HMAC-SHA256>, only when the webhook has a secret
X-Leadtap-Test 1, only on test sends

The body of a LeadTap.me CRM webhook is about a person: who they are in lead, and where they came from and what they answered in custom_data. Answers are keyed by the step key of each question, not by its wording, so an integration does not break when a question is reworded.

Field What it holds
event lead.created, lead.verified or lead.submission
test true on test sends; absent otherwise
occurred_at When it happened, ISO 8601
lead email, first_name, last_name, phone, verified (whether they confirmed their email)
custom_data form_id, form_name, form_slug, submission_id, session_id, score, score_max, outcome, and the answers by step key
custom_data_labels The question wording, by step key
utm The UTM parameters of the visit

The body of a LeadTap.me page webhook is one submission of its lead page. Answers are keyed by step key; labels carries the wording for people to read.

Field What it holds
event complete or partial
test true on test sends; absent otherwise
occurred_at When it happened, ISO 8601
submission_id, session_id Ids of the submission and the visit
form id, name, slug of the lead page
contact email, first_name, last_name, phone, verified
score value, max and outcome
answers The answers, by step key
labels The question wording, by step key
utm The UTM parameters of the visit

A LeadTap.me webhook with a secret is signed with HMAC-SHA256 over <t>.<raw body>, where t is the Unix timestamp in the header. To verify it, recompute the HMAC with your secret over the exact bytes received, compare it to v1 in constant time, and reject a t more than 300 seconds (5 minutes) from your clock. It works the same for both kinds of webhook.

import crypto from "node:crypto";
// rawBody: the request body exactly as received (a string), not re-serialized JSON.
export function isFromLeadTap(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.trim().split("=")));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) 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));
}

The secret is the customer’s own; LeadTap.me never returns it once it is set.

LeadTap.me waits up to 10 seconds for the receiver, and the answer decides what happens next.

Receiver answers Result
2xx Delivered
408, 429, 5xx, a network error or a timeout Retried later
Any other 4xx Failed, not retried: the receiver rejected the request

A delivery can arrive more than once, so receivers should drop repeats by X-Leadtap-Idempotency-Key. Every delivery is recorded in the webhook’s delivery log with its status (pending, delivered or failed), attempts, HTTP status, the receiver’s answer and the last error. Failed and stuck deliveries can be retried from the portal or with retry_webhook_deliveries.

A test send posts one sample event right away, even if the webhook is off: a sample lead.created for a CRM webhook, or a sample submission built from the page’s real questions for a page webhook. It carries "test": true and X-Leadtap-Test: 1, and it leaves no trace in the delivery log. Test after creating a webhook or changing its URL, before the owner turns it on.

Through the MCP server, Claude manages webhooks with nine tools. Full descriptions and parameters are in the tool catalog.

Tool What it does
list_crm_webhooks The account’s CRM webhooks, their events and whether each one is on
create_crm_webhook Adds a CRM webhook to the account; it starts off
update_crm_webhook Turns a CRM webhook off, changes its events or points it at a new URL
list_page_webhooks One lead page’s webhooks, their events and whether each one is on
create_page_webhook Adds a webhook to one lead page; it starts off
update_page_webhook Turns a page webhook off, changes its events or points it at a new URL
test_webhook Sends one sample event to a webhook of either kind, now
list_webhook_deliveries A webhook’s delivery log, newest first
retry_webhook_deliveries Re-queues a webhook’s failed and stuck deliveries and delivers them now

In the MCP tools, a webhook’s id is its destinationId: the list tools return it, and the other webhook tools take it.