pixellint

Identity · engineers · marketers

_fbp is the browser. _fbc is the click.

The pixel writes these cookies on your domain; CAPI cannot see document.cookie. If the server event omits them, you are matching on email and IP alone, which is how hybrid setups underperform the pixel.

The shapes

_fbp looks like fb.1.timestamp.random. _fbc looks like fb.1.timestamp.fbclid. Meta documents fb.${subdomain_index}.${creation_time}.${value}. Send the cookie value verbatim as user_data.fbp and user_data.fbc. Do not hash them. Do not send only the fbclid when you have _fbc.

subdomainIndex is typically 1 for example.com (the domain itself). creationTime is a Unix timestamp in milliseconds, the moment the cookie was minted. The last segment is a random number for _fbp, or the fbclid for _fbc. Pixellint checks ^fb\.[0-9]\.[0-9]+\..+$ on both fields: vendor.meta-conversions-api.body.user_data.fbp.invalid and .fbc.invalid.

A truncated value, a missing fb. prefix, or the raw fbclid in the fbc field fails that shape. fbclid=IwAR... pasted into user_data.fbc is the usual miss. Rebuild fbc as fb.1.{ms}.{fbclid} if the cookie never existed, using the landing timestamp you actually have.

Do not mint a fake _fbp

If the pixel never ran, you can still build _fbc from the landing fbclid in that format. You cannot invent an honest _fbp. That random is the browser id the pixel minted. A server-generated random that changes per event is a new person on every Purchase. A reused constant is one fake browser for the whole shop.

Omit fbp when you do not have the cookie. Do not generate fb.1.Date.now().Math.random() to satisfy a linter you wrote. Pixellint will accept any last segment that matches the regex. Matching the regex is not the same as matching a real browser Meta already saw.

Where they die

ITP, consent wipes, subdomain mismatches, and HttpOnly cookies the tag cannot read. A checkout host that is not the landing host will not have _fbp unless you copied it. That is a cross-domain problem that shows up as empty fbp on Purchase.

Safari will expire JS-set tracker-like cookies in days, not in the two-year max-age the pixel set. Returning Safari users look like new _fbp values and still look like themselves if you have email or fbc from a stored fbclid. Design for the cookie to be missing. Persist fbclid server-side so you can rebuild fbc after the cookie dies.

Pixel plus CAPI

The pixel already sent _fbp. The server should send the same strings plus the hashed email the page never had. Dedup on event_id. Do not skip fbp on the server because you assume the pixel covered it. The users who blocked the pixel are the ones CAPI is for.

Advanced matching on the browser tag is not a substitute for sending fbc on the server event. Send both pipes the cookies you have. If the server has fbc and the pixel does not, the blocked-pixel user still joins. If neither has fbc and the click id was stripped at redirect, no cookie format will save you.

Read them from the request you stored

CAPI workers do not see document.cookie. The page must POST _fbp and _fbc with the event, or the edge must copy Cookie headers from the browser hit into the queue. sGTM can read the first-party cookies on the collector host if Domain was set so the collector actually receives them.

Pixellint is not affiliated with Meta. The fb.N.timestamp.value shape is Meta's documented cookie format. Run pixellint validate json on a payload whose fbc is the raw fbclid and you should see user_data.fbc.invalid.

// WRONG: raw fbclid in fbc
user_data.fbc = 'IwAR0abc';
user_data.fbp = sha256hex(cookie); // hashed, also wrong

// WRONG: minted fbp
user_data.fbp = 'fb.1.' + Date.now() + '.' + Math.random();

// RIGHT: cookie verbatim, or rebuild fbc only
user_data.fbp = cookies._fbp; // omit if missing
user_data.fbc = cookies._fbc || ('fb.1.' + landedAtMs + '.' + fbclid);

Check the artifact

Paste the pixel URL or JSON body into the playground. Same engine as pixellint validate. Nothing leaves the tab.