Integrations

What Should a Lead Webhook Payload Contain?

A lead webhook payload needs six field groups: identity, attribution, journey, context, qualification and a delivery envelope. Here is each one.

Quick answer

Six field groups: identity (normalised email and phone), attribution (UTMs, click IDs, landing page, referrer), journey (session count, first and last touch, time to convert), context (page, device, geo, timestamps), qualification (lead status, engagement, fit verdict) and a delivery envelope (event ID, event type, schema version, signature, timestamp). Most webhooks ship group one and skip the rest, which is why leads arrive in the CRM as a name nobody can route, score or attribute.

Recover the leads you're already earning

Tell us what you're trying to track or fix. We'll show you which visitors your forms miss — and how PartialLeads recovers and qualifies them.

Six groups of fields: identity, attribution, journey, context, qualification, and a delivery envelope. Identity is the only one most webhooks send. The other five are what turn a row in your CRM into a lead somebody can route, price and attribute — and they are all knowable at the moment the lead is created.

The reason they go missing is structural, not lazy. Your form tool posts the form’s own fields, because the form is all it knows about.

Why does your CRM get a name and an email and nothing else?

Because the thing sending the webhook is the form, and the form never saw the ad click. A form knows its own inputs. The campaign, the referrer, the click ID, the landing page, the three earlier visits — all of that lives in the tracking layer of the browser session, and nothing wires the two together by default.

So the payload that arrives is an echo of the fields on screen: name, email, maybe phone, maybe a dropdown. Then the sales rep asks where the lead came from and nobody can answer, and marketing reports on lead counts because lead counts are the only thing the data supports.

This is the same root cause behind conversions that show up as Direct in your ad platform’s reports. The attribution data existed. It was on the landing URL, in the referrer, in a cookie. Nothing carried it forward to the moment that mattered.

The fix is not a bigger form. Asking the visitor “how did you hear about us?” trades a measurable fact for an unreliable memory and costs you conversion rate to do it. The fix is a payload that carries what the session already knew.

What are the six field groups every lead webhook needs?

Identity, attribution, journey, context, qualification, and the delivery envelope. Each group answers a different question, and each has a different consumer inside your business — which is why cutting any of them produces a specific, predictable complaint two weeks later.

Field group What it carries Who needs it
Identity Normalised email, phone in E.164, name, a stable person ID, hashed copies for ad platforms Sales, dedupe logic, conversion APIs
Attribution utm_source/medium/campaign/content/term, click IDs, landing page, referrer Marketing reporting, budget decisions
Journey Session count, first touch, last touch, time to convert, pages before the form Attribution modelling, sales context
Context Form page URL, form ID, device, browser, country/region/city, timestamps Routing, troubleshooting, segmentation
Qualification Lead status (partial or completed), fields completed, engagement signals, fit verdict Prioritisation, SLA rules
Envelope Event ID, event type, schema version, signature, sent-at timestamp Your receiving endpoint

Two of these deserve more than a table row.

Identity is where normalisation happens, not where it gets deferred. Send the email lowercased and trimmed, and the phone in E.164 with the country code resolved — not as the visitor typed it. Every downstream system that will ever match this person expects a canonical form: Meta’s Conversions API requires email lowercased with whitespace trimmed and phone as digits only with country code before hashing (Meta, customer information parameters). If your webhook ships " Ava.Chen@Example.com " and (0412) 345 678, you have handed every consumer the same normalisation job and guaranteed they will do it differently.

Attribution is where you decide whether reporting is possible at all. Send the raw UTM parameters and the click IDs separately, never one merged “source” string. A resolved channel name is an opinion; gclid, fbclid, msclkid and ttclid are facts, and they are the only things that will still join to platform data next quarter. If the difference between those identifiers is fuzzy, gclid, fbclid and UTM parameters each answer a different question — and a payload should carry all three kinds.

Diagram of a lead webhook payload split into six labelled field groups — identity, attribution, journey, context, qualification and envelope — with example keys and values in each group, styled as a dark dashboard panel

What does a complete lead webhook payload look like?

Like this — about sixty lines of JSON, flat enough to map onto CRM fields without a transformation script, grouped enough that a human can read it.

{
  "event_id": "evt_9f2c1a7b4e",
  "event_type": "lead.captured",
  "schema_version": "1.2",
  "sent_at": "2026-09-20T10:42:18Z",
  "occurred_at": "2026-09-20T10:41:52Z",
  "identity": {
    "person_id": "ppl_8321fd",
    "email": "ava.chen@example.com",
    "phone": "+61412345678",
    "first_name": "Ava",
    "last_name": "Chen",
    "email_sha256": "a3f1…",
    "phone_sha256": "7c02…"
  },
  "attribution": {
    "utm_source": "facebook",
    "utm_medium": "paid_social",
    "utm_campaign": "spring-retarget",
    "utm_content": "video-15s-b",
    "click_ids": { "fbclid": "IwAR2…", "gclid": null },
    "landing_page": "https://example.com/demo?utm_source=facebook",
    "referrer": "https://l.facebook.com/"
  },
  "journey": {
    "sessions": 3,
    "first_touch": { "source": "google", "medium": "organic", "at": "2026-09-14T08:12:03Z" },
    "last_touch": { "source": "facebook", "medium": "paid_social", "at": "2026-09-20T10:39:41Z" },
    "time_to_lead_seconds": 528495
  },
  "context": {
    "form_page": "https://example.com/demo",
    "form_id": "demo-request",
    "device": "mobile",
    "browser": "Safari",
    "country": "AU",
    "region": "NSW",
    "city": "Sydney"
  },
  "qualification": {
    "status": "partial",
    "fields_completed": ["email", "phone", "company"],
    "last_field": "company",
    "dwell_seconds": 74
  }
}

Three things about this shape are worth copying. Timestamps are separate: occurred_at is when the lead happened, sent_at is when you posted it, and a retry changes only the second one. Nulls are explicit rather than omitted, so the receiver can tell “no gclid” from “field not implemented yet”. And the hashed identity copies sit beside the plaintext ones, so a downstream conversion API can use them without re-implementing the hashing rules.

Which fields does sales use, and which are for reporting?

Sales uses identity, qualification and a two-line journey summary. Reporting uses attribution and context. Conversion APIs use the hashed identity fields and the click IDs. They are genuinely different audiences, and the common failure is designing the payload for one of them.

A rep opening a lead record wants to know who this is, how warm they are, and what they were looking at. Campaign taxonomy is noise at that moment — but “third visit, came back from a Meta ad, filled everything except company” changes how the call opens.

Marketing wants the opposite: the identity fields are irrelevant in aggregate, and the UTM and click ID fields are the entire report. If those arrive inconsistently — sometimes in a merged source string, sometimes missing on mobile — every source-level number is quietly wrong.

The qualification group is the one that serves both. A lead status of partial versus completed is an operational fact, and a partial lead is a real, contactable lead rather than a failed submission — but only if the status travels with the record. Send it as its own field, never as a magic value inside a notes string.

How do you make lead webhook delivery reliable?

Four things: a stable event ID, at-least-once delivery with retries, a signature, and a receiver that is idempotent. Delivery is the half of webhook design that nobody specifies until the day the CRM has three copies of the same person.

Give every event a deterministic ID. Derive it from the record it describes — not a random UUID per attempt — so the same lead produces the same event_id on the first send and on the retry an hour later. This is the same pattern conversion APIs use to collapse duplicate events: Meta deduplicates browser and server events that share an event ID and event name (Meta, deduplicate pixel and server events). Your CRM deserves the same protection.

Retry, with backoff, and keep the failures visible. A webhook endpoint returning a 502 for ten minutes should not cost you ten minutes of leads. Retry on connection errors, timeouts and 5xx responses; do not retry a 400, because the payload will still be malformed next time. Whatever exhausts its retries needs to land somewhere a human looks, otherwise “we send a webhook” quietly becomes “we send most leads”.

Sign the payload. An HMAC signature over the raw body, computed with a shared secret and sent as a header alongside the timestamp, lets the receiver verify both origin and integrity. A webhook endpoint that accepts anything posted to a guessable URL is an open door into your CRM.

Make the receiver idempotent anyway. At-least-once delivery means duplicates are normal, not exceptional. Store the event_id on receipt and drop anything you have already processed. This is cheap to build on day one and painful to retrofit after a bad week.

What should you never put in a lead webhook payload?

Secrets, sensitive-category data, and anything you cannot explain the lawful basis for. A webhook body travels to a system you do not control and lands in logs you will never audit, so the contents deserve the same scrutiny as a public API response.

Never put an API key, session token or password in the body. Credentials belong in headers, rotated separately from the schema. Never include the raw values of sensitive form fields — health details, financial specifics, government identifiers — just because they happen to be on the form.

Do not send fields you have no use for on the receiving end. The honest inventory of what a tracking tag actually collects is broader than most teams assume, and forwarding all of it to a CRM turns a marketing integration into a data-retention problem nobody scoped.

And do not send unbounded free text. A comments field with no length cap is how one visitor’s paste breaks an integration for everyone.

How does PartialLeads fill in a lead webhook payload?

By owning the layer the form never sees. PartialLeads captures email and phone as they are typed — before the visitor hits submit — and keeps them on a session record that already carries the UTMs, click IDs, landing page, referrer and geo from the visit that started it. That is partial lead capture: the field-level capture and terminal flush that produce a contactable record even when the submit button is never pressed.

Identity arrives normalised, not raw. Email is lowercased and trimmed; phone is converted to E.164, with the country code resolved from the session’s geo when the visitor typed a local-format number. The same canonical builder feeds the hashed copies used by the server-side conversion APIs, so the value your CRM sees and the value Meta scores are derived from one normalisation, not two.

The journey group comes from the identity cluster rather than the current session. PartialLeads unions a person’s sessions across six tiers — visitor ID, email, phone, IP and user-agent, device fingerprint, and shared click ID — which is how visitor identity resolution works in practice, and it is why the record can say “three sessions, first touch Google organic, last touch Meta” instead of treating each visit as a stranger.

PartialLeads Leads list showing four leads with a Journey column of session touch badges, UTM source and campaign, location and device, a Partial or Completed status badge, and an API column counting the conversion APIs each lead was dispatched to

Qualification is a field, not an inference. Every lead carries a Partial or Completed status, and where AI qualification is switched on, a match verdict and confidence from scoring the lead against your ideal customer sit alongside it — so routing rules can act on fit, not just on arrival order.

When a purchase later matches back to that person, the same pipeline fans the conversion out server-side to Meta, Pinterest, TikTok and Google Ads, plus your own CRM webhook, from one deterministic event ID — so a redelivered webhook cannot double-count the sale.

The honest constraint: the payload is only as complete as the visit. A lead who arrives with no UTMs, no click ID and a blank referrer has an attribution group full of nulls, and no system invents what the browser never sent. The journey and identity groups are what recover that case — not a guessed source.

What breaks The mechanism Where you see it in the dashboard
CRM receives a name and email with no source Session-level capture of UTMs, click IDs, landing page and referrer, carried onto the lead record Leads list — UTM column beside each lead
The lead abandoned before submit, so nothing fires Field capture on input with a terminal flush before the page unloads Leads list — Partial status badge
Each visit looks like a different person Six-tier identity cluster unions sessions into one person Leads list — Journey column, “N sessions” label
Email and phone arrive in inconsistent formats Email lowercased and trimmed, phone normalised to E.164 with geo country fallback Lead detail — normalised contact fields
Sales cannot tell which leads to call first Partial or Completed status plus AI match verdict and confidence Lead Intelligence — Customer Match card
A redelivered event double-counts a conversion Deterministic event ID with a unique dedup table per config CAPI activity log — one row per event

Recover the leads you're already earning

Tell us what you're trying to track or fix. We'll show you which visitors your forms miss — and how PartialLeads recovers and qualifies them.

Sources

https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events https://developers.facebook.com/docs/marketing-api/conversions-api/using-the-api


Frequently asked questions

QWhat is the minimum a lead webhook payload should contain?
Email or phone in a normalised form, a stable event ID, a timestamp for when the lead occurred, and the attribution fields from the landing URL — UTMs and any click ID. That is the floor. Without the event ID you cannot deduplicate, without the occurred-at timestamp you cannot order events, and without attribution the record is unreportable the moment it lands.
QShould a lead webhook send hashed or plaintext email?
Both, in separate fields. Your CRM needs the plaintext value to contact the person; conversion APIs need the SHA-256 hash of the normalised value. Sending only the hash makes the lead useless to sales, and sending only plaintext forces every downstream consumer to re-implement the platform's normalisation rules, which is exactly where match quality gets lost.
QHow do you stop a webhook creating duplicate leads in the CRM?
Put a deterministic event ID on every event — derived from the lead record, not random per attempt — and make the receiver store it and drop repeats. Retries and redeliveries are normal in at-least-once delivery, so the receiving end has to be idempotent. Matching on email alone is not enough: the same person can submit twice with two addresses.
QShould the payload include UTM parameters or a resolved channel name?
Include the raw UTM parameters and click IDs. A resolved channel name is a derived opinion that depends on whoever wrote the classification rules, and it cannot be re-derived once the raw values are dropped. Send both if your CRM wants a friendly label, but the raw fields are what will still join to ad platform data in six months.
QWhat timestamp format should a lead webhook use?
UTC, in an unambiguous machine format such as an ISO 8601 / RFC 3339 string, with separate fields for when the lead occurred and when the event was sent. Local times without offsets are the usual cause of conversion imports being rejected for happening before the click that produced them.
QDoes a partial lead need a different webhook payload from a completed one?
No — same schema, different status field. Use one event shape with a status of partial or completed and a list of which fields were filled, so your CRM rules can act on the difference. Two separate schemas means two integrations to maintain and a guaranteed drift between them the first time a field is added.
QHow should the receiving endpoint verify a lead webhook is genuine?
Verify an HMAC signature computed over the raw request body with a shared secret, sent as a header alongside a timestamp, and reject anything outside a short time window to prevent replays. Verify before parsing. A guessable endpoint URL with no signature check is an open write path into your CRM.
QWhat happens to attribution fields when a visitor arrives with no UTMs?
They come through null, and the honest payload says so rather than filling in "direct". The referrer and landing page are often still present, and a person-level identity join can supply the original touch from an earlier session — but a system that guesses a source when the browser sent none is manufacturing data your reports will later treat as fact.

Find the qualified leads your forms are currently throwing away.

Install PartialLeads on one landing page, send traffic, and compare what your CRM captured against what PartialLeads recovered and qualified.