What Meta sees when the lead has no cookie: external_id, fbc and fbp explained
Ad blocker, Safari, private tab, two devices: Meta’s cookie is missing in almost half of visits. What remains to match the event to the person are three fields almost nobody fills correctly. This article says what each is, where it comes from and why external_id should be your lead’s id.

The search terms that bring the most people to this blog are “_fbp cookie”, “fbc fbp” and “fbclid decode”. It is no coincidence: they are the three things Meta uses to match an event to a person when there is no e-mail, and the three that vanish when the browser blocks the pixel script. This article is the long answer.
The three identifiers, one sentence each
| Field | What it is | Where it comes from | When it is missing |
|---|---|---|---|
| fbc | The ad click: fb.1.{ms}.{fbclid} | From the fbclid URL parameter when the person arrives from the ad | Organic visit, copied link, fbclid dropped by a redirect |
| fbp | The browser: fb.1.{ms}.{random} | Generated by Meta’s script and stored in a cookie | Blocker, Safari with cookie limits, script did not load |
| external_id | The person, in YOUR system | You define it; hashed with SHA-256 | Almost always — nobody fills it |
The point that changes everything: fbc and fbp can be built on YOUR side, on your domain, without depending on Meta’s script. The fbc is derived from the fbclid and persisted for 90 days; the fbp can be born with the same format Meta’s script would use — and the script, when it loads, respects the cookie that already exists. We told the measurement in first-party fbc and fbp: fbp went from 44% to close to 100% of events.
external_id: the field nobody fills
The external_id is the person’s identifier in your system. Meta uses it to link events from the same person across devices and over time — and to match profiles when you have already sent that same id before along with an e-mail. Most integrations leave it empty, or send the session id (which changes every visit and links nothing).
The right value is the lead’s id in your CDP: stable, unique per person, the same on today’s pageView and tomorrow’s purchase. In a centralized base it exists for 100% of events — including anonymous ones, because the anonymous visitor is also a lead with an id. That is why, in an account we measured, external_id was the only field with full coverage on pageView, while e-mail covered 11%.
When two people are merged in your CDP, the external_id that goes to Meta has to be the canonical person’s. Otherwise Meta keeps seeing two.
Measured coverage, by event type
| Field | pageView (site) | Purchase (Hotmart) |
|---|---|---|
| external_id | 100% | 100% |
| fbc | 92% | 97% |
| fbp | 53% (before first-party) | 98% |
| e-mail / phone | 11% / 10% | 100% / 100% |
| city / state / zip (by IP) | 97% | ~11% (declared, no IP) |
The important read: on the site event, what sustains matching is IP geolocation plus cookies; on the purchase, it is e-mail and phone. The event that leaves the server has to pick the right source for each field — the city declared at checkout when it exists, the one from the visit’s IP when not. That chain is what makes match quality rise.
Mistakes that silently zero the field
- Sending the raw fbclid as fbc. The format is fb.1.{timestamp in ms}.{fbclid}; without the prefix Meta discards it.
- Generating fbp on every pageView. It identifies the BROWSER; if it changes, every visit is a new browser.
- Sending external_id without hashing, or hashed with different normalization between events. The hash has to be equal today and tomorrow.
- Leaving the server IP in client_ip_address. The IP has to be the person’s browser — and it may be IPv6.
- Treating “no cookie” as “send nothing”. Without a cookie there is still e-mail, phone, external_id, IP and user agent — and that is where the server beats the pixel.
In CrazyLeads the pixel builds fbc and fbp first-party, the resolver stores both as lead attributes (so the purchase coming from Hotmart, which has no cookie of its own, inherits the person’s last known), and the external_id of every event is the canonical lead id — the same one shown on the profile.