Lead Capture & Forms

What Is Partial Lead Capture, and How Does It Work?

Partial lead capture saves form data as it is typed, not on submit — so a visitor who abandons halfway still becomes a contactable, attributed lead.

Quick answer

Partial lead capture records what a visitor types into your form — usually email and phone — as they type it, instead of waiting for the submit button. A script listens to each field's input and blur events, waits for a short pause, and posts the value to your server, then flushes anything still pending when the page is hidden or closed. Someone who fills two fields and leaves becomes a contactable lead with a traffic source attached, rather than an anonymous session that ends in nothing.

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.

Partial lead capture is the practice of recording form field values as a visitor types them, rather than waiting for the submit button. A script on the page listens for input on each field, waits for a short pause, and posts the value to your server. When the visitor leaves, anything still pending is flushed before the page goes away.

The result is that the person who typed an email address and then closed the tab is a lead you can contact, not a bounce you can only count.

Nothing about the form itself changes. No extra step, no email-first gate, no popup. The difference is entirely in when the data leaves the browser.


Why does the submit button lose so many of your leads?

Because submit is the only moment most tracking is listening for, and it’s the last moment in the sequence. Everything before it — the email typed into field one, the phone number half-entered in field three — exists in the browser and nowhere else. If the visitor leaves, the browser discards it and no system you own ever knew it existed.

This is a collection problem wearing a tracking problem’s clothes: analytics can tell you the form was viewed and not submitted, but never who, because the who was in the fields.

The reasons people stop are mostly boring: the form asks for a phone number, the next field is a dropdown they can’t answer, a call comes in. What they share is timing. Abandonment happens after the visitor has identified themselves and before your systems are told — which is why form abandonment is a revenue question rather than a UX footnote.

A visitor who types an email has done the hardest thing you will ever ask of them. Treating that as a non-event because they didn’t finish the last field is a choice, not a technical necessity.


How does partial lead capture actually work?

Four steps: listen to each field, debounce the values, send them to your own endpoint, and flush anything outstanding before the page unloads. The engineering difficulty is not in the capture — it’s in the leaving.

What triggers a capture?

The input event on each field you care about, plus blur as a backstop. input fires on every keystroke, which is far too often to transmit, so captures are debounced: the script waits a few hundred milliseconds after typing stops, then sends the current value once. A 500 ms debounce turns a fifteen-character email into one request instead of fifteen.

blur matters because the visitor moved on from that field deliberately — the strongest available signal that the value is final rather than half-typed.

What happens when the visitor leaves?

This is the part naive implementations get wrong. If someone types their phone number and immediately closes the tab, the debounced value is sitting in a timer that will never fire.

A correct implementation keeps a terminal flush on pagehide and on visibilitychange when the page becomes hidden, and sends it with navigator.sendBeacon() — a request the browser completes after the page is gone — with fetch(..., {keepalive: true}) as the fallback. The old unload event is not a reliable place for this on mobile; pagehide is the one that fires.

Get the flush wrong and you lose exactly the leads you were trying to save: the ones who left abruptly.

Where does the data go?

To your own endpoint, on your own domain, and it should be normalised the moment it arrives — not when you later try to use it. Email lowercased and trimmed. Phone converted to E.164 with a country code inferred from the session when the visitor didn’t type one.

The payload should also carry the context that makes the lead worth anything: page URL, landing page, referrer, UTM parameters, and any ad click identifiers on the session. A captured email with no source tells your sales team who to call. A captured email with a source tells your media buyer which campaign to scale.

Dashboard mockup of the capture path: a form field being typed, a 500 ms debounce window, the queued field payload, and the terminal flush fired on pagehide via sendBeacon


What does a partial lead record actually contain?

More than the contact details, if it’s built properly. A useful partial lead record has four layers:

Contact fields. Whatever was typed — email, phone, first name, company. Email is the minimum viable version: without one, follow-up is impossible.

Field order and timestamps. Which fields were completed, in what order, and when the last one was touched. This is the layer most tools throw away, and it’s the one that tells you where the form is losing people. If the last completed field is always the one before “annual revenue”, you have a diagnosis, not a hunch.

Session attribution. UTM parameters, referrer, landing page, and click identifiers like gclid or fbclid. Without this, a partial lead is a name in a spreadsheet. With it, it’s a name attached to a campaign.

Signal quality. Time on page, interaction depth, whether the browser looked automated, whether the email domain is disposable. Partial leads arrive in higher volume than completed ones, so sorting the serious from the noise is not optional.


Can you build partial lead capture yourself?

Yes. The core is about thirty lines of JavaScript and an endpoint that writes a row. Here’s the shape of it:

// Capture email/phone as they're typed, flush before the page goes away.
const pending = new Map();
let timer;

function queue(name, value) {
  pending.set(name, value);
  clearTimeout(timer);
  timer = setTimeout(flush, 500); // one post per pause, not per keystroke
}

function flush() {
  if (!pending.size) return;
  const payload = JSON.stringify({
    session_id: sessionId,          // your own first-party id
    page_url: location.href,
    fields: Object.fromEntries(pending),
  });
  const blob = new Blob([payload], { type: 'application/json' });
  // sendBeacon survives navigation; keepalive fetch is the fallback
  if (!navigator.sendBeacon('/capture/field', blob)) {
    fetch('/capture/field', { method: 'POST', body: payload, keepalive: true });
  }
  pending.clear();
}

document.querySelectorAll('input[type=email], input[type=tel]').forEach((el) => {
  el.addEventListener('input', () => queue(el.name, el.value));
  el.addEventListener('blur', flush);
});

// The terminal flush. pagehide fires where unload does not.
addEventListener('pagehide', flush);
document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') flush();
});

That gets you captures. What it doesn’t get you is everything after them, which is where self-built versions stall:

  • The same person captured five times. One visitor, one session, several field revisions, two devices. Without identity resolution you have five “leads” and one human being.
  • Iframe forms. If your form is a Typeform, Jotform or GoHighLevel embed, the script above runs inside an iframe whose URL has no UTM parameters. Attribution arrives empty unless the parent page relays it in.
  • Multi-step forms. Fields that don’t exist yet when the page loads need delegated listeners, or step two is invisible.
  • Bot noise. An open capture endpoint collects whatever automated traffic types into your form, and partial capture has no submit-time gate to filter it.
  • Getting the lead somewhere useful. A row in your database is not a lead in your CRM, an event in Meta’s Conversions API, or a task for a salesperson.

None of this is exotic. It’s just considerably more work than the capture itself, which is the part everyone estimates.


Is it legal to capture data before someone hits submit?

Treat it as what it is: collecting personal data. The same rules that apply to a submitted form apply to a partially completed one — the difference in your database is not a difference in the law. This isn’t legal advice, but the defensible position is consistent everywhere.

Disclose it in your privacy policy in plain words. Where your jurisdiction requires consent before non-essential tracking, gate the capture behind that consent rather than firing first and asking later. Capture only fields you have a reason to hold, and never sensitive ones — no passwords, no payment fields. Honour deletion requests against partial records exactly as you would against submitted ones.

The practical test: if you’d be uncomfortable telling the visitor you have their email, the problem is the follow-up you were planning, not the mechanism.


How does PartialLeads capture partial leads?

By capturing fields on input with a terminal flush, then resolving every capture into a person and an attributed lead rather than leaving it as a stray row.

Capture as they type. The tag records fields on input and blur with a 500 ms debounce, and drains any pending field on submit, pagehide or visibilitychange using sendBeacon with a keepalive-fetch fallback. The last field typed is the one most likely to be lost, and it’s the one this exists to protect.

One script, no tag manager. A single tag — around 10 KB gzipped — on the surfaces where lead forms live: ordinary sites, Shopify, WooCommerce, GoHighLevel, Typeform, Jotform, Unbounce, ClickFunnels.

Iframe attribution relay. The tag listens for postMessage attribution from parent frames and broadcasts UTM parameters down into iframes, which is what keeps a Typeform or GHL embed from reporting every lead as Direct.

Normalisation on arrival. Email lowercased and trimmed, phone normalised to E.164 with a country-code fallback derived from the session’s geography. Consistent formatting is what makes later matching possible.

Identity, not rows. Captures resolve into a person across six ranked tiers — visitor ID, email, phone, IP plus user agent, device fingerprint, click ID — anchored by a visitor ID whose first-party cookie backup is server-set, so Safari’s cap on script-written cookies doesn’t shorten it. One person who came back three times is one lead with three touches.

The captured contact is a conversion payload. A partial lead carries a real email and often a phone, so it can be dispatched server-side to the ad platforms as a Lead event through the Conversions API — hashed, deduplicated on a deterministic event ID, retried on transient failures. Which is why form design matters more than the pixel: with email and phone as required fields, PartialLeads consistently delivers event match quality of 9 or above, observed across customer accounts. Email-only forms structurally cap lower, because you can only transmit what you collect.

Qualification before the call list. Two-step AI enrichment scores each lead against your ideal-customer persona and surfaces a match verdict, so follow-up starts with the strong matches rather than with row one.

Honest constraints. A visitor who types nothing identifying cannot be captured — there is no lead in an empty field. Ad-platform match quality still depends partly on the platform’s own graph and on your client-side base tag being present; PartialLeads reconstructs _fbc from an fbclid in the URL, but it does not invent an _fbp cookie that was never set. And partial leads are a different contact experience from inbound submissions — they didn’t ask you to call, which is a follow-up design problem, not a data problem.

Where you see it. Captured-but-unsubmitted people appear on the Leads page with an amber Partial badge, each carrying their captured fields, journey and source, and — once they buy or book — a flip to Completed with revenue attached. A partial lead stops being a hypothetical the first time you open that list and recognise a company name.

Dashboard mockup of the Leads list: rows with source badges, amber Partial badges, a journey ribbon of touches, and one row flipped to Completed with revenue

What breaks The mechanism Where you see it in the dashboard
Data only leaves the browser on submit Field capture on input/blur with a 500 ms debounce Leads page, Partial badges
The last field typed is lost when the tab closes Terminal flush on pagehide/visibilitychange via sendBeacon Captured fields in order on the lead
Nobody knows which field people quit on Per-field capture timestamps kept in sequence Lead detail, field entries
An iframe form reports every lead as Direct postMessage UTM relay plus referrer fallback Correct source label on embedded-form leads
One person looks like several leads Email lowercased and trimmed, phone normalised to E.164, then a six-tier identity cluster resolves sessions to a person Journey ribbon, unique contacts
The ad platform never learns about the lead Server-side Conversions API dispatch with deterministic event IDs CAPI activity log
Volume buries the leads worth calling AI qualification against your ideal-customer persona Customer Match verdicts, priority inbox

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

  1. Meta for Developers — Conversions API, customer information parameters: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/customer-information-parameters
  2. Meta for Developers — Conversions API overview: https://developers.facebook.com/docs/marketing-api/conversions-api
  3. WebKit — Tracking Prevention in WebKit: https://webkit.org/tracking-prevention/

Frequently asked questions

QIs partial lead capture the same as exit-intent popups?
No. An exit-intent popup interrupts the visitor and asks them to give you something before they go. Partial lead capture takes nothing extra — it saves what they already typed. The two solve the same problem from opposite directions, and they combine: the popup tries to prevent the exit, the capture makes the exit survivable if it happens anyway.
QWhat is the minimum a partial lead needs to be useful?
An email address. Without at least one contact field, a partial lead is a session record, not a lead. Email plus phone is materially more valuable — not only because it doubles your chances of reaching the person, but because both are high-weight match parameters when sending the lead server-side to an ad platform.
QDoes partial lead capture slow down my form?
It shouldn't. The capture is a debounced background request of a few hundred bytes, sent asynchronously; the form's own submit path is untouched. The one thing to avoid is transmitting on every keystroke — that's the implementation that generates hundreds of requests per visitor and gets noticed.
QCan it capture passwords or credit card fields?
It should be built so that it cannot. Restrict the capture to the fields you actually need — typically email, phone and name — and exclude password, payment and any sensitive category by default. A capture layer that takes everything on the page is a liability, not a feature.
QHow do I follow up on a partial lead without being creepy?
Acknowledge the context rather than pretending it didn't happen. A short message that refers to the thing they were looking at, sent reasonably soon, from a real person, with an easy way to opt out, reads as helpful. A cold call three weeks later that opens with details they never submitted does not. The data is legitimate; the tone is what people react to.
QWill partial leads mess up my conversion rate reporting?
Only if you mix them into the same metric. Keep completed submissions as your conversion rate and report partial leads as their own line — captured, contactable, and separately trackable through to whether they converted later. Blending the two produces a number that looks better and means less.
QDoes this work if my form is a Typeform or GoHighLevel embed?
Yes, with one caveat that applies to every tool: the form lives in an iframe whose URL carries no UTM parameters, so attribution has to be relayed in from the parent page. Capture works inside the embed; correct source labelling depends on that relay existing. If a vendor doesn't mention iframes at all, assume embedded leads will arrive as Direct.

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.