pixellint

Identity · PMs · engineers · marketers

The platform's graph is not your CDP

Segment Personas, your identity table, and Meta's matching graph agree only on the keys you sent, after each side's own rules. Treating them as one person-id is how ROAS and product analytics tell different stories.

What you actually send

Hashed email and phone, click ids, first-party cookies, IP, user-agent, external_id. The platform attaches the event to accounts it already has for those keys. You never see the join. Event Match Quality is a score of that join, not a proof of truth. An 8/10 EMQ with the wrong email still optimizes toward the wrong people.

Your CDP can stitch anonymousId to userId on a login. That stitch does not update Meta's graph unless you send the new keys on the next CAPI event. There is no sync. There is no bulk 'here is our identity table' API that rewrites last week's unmatched Purchases. Send the keys on the event that needs them.

Without a device graph

Third-party cookies are gone or partitioned. You do not have a cross-site device id to send. Click ids and first-party hashed PII are the remaining join. If checkout never collected an email and the click id dropped, the event is a dark conversion no graph can rescue.

Do not fake a device id. Do not reuse GA4 client_id as Meta external_id and expect a shared person. Map keys in a dictionary: which field, which vendor, hashed or not. A UUID you mint per page view is not a device graph. It is noise the platform cannot link to an ads account.

CDP as a pipe, not as the graph

Segment track with userId plus a Meta destination is a translator. It still has to emit the Meta field names, the hash, and the cookies the destination template mapped. A missing mapping looks like a Meta outage and is a CDP config. Personas unifying profiles in your warehouse does not fill user_data.em on the wire.

Warehouse identity is useful for your reports. Ads optimization trains on the platform graph. Reconcile the two in finance, not by forcing one id down every API. Pixellint will check the Meta body the destination emitted. It will not check that Personas merged the right profiles. Pixellint is not affiliated with Segment or Meta.

Match quality is not incrementality

Raising EMQ by sending more hashed fields means more events attach to accounts the platform already knows. That can improve optimization. It is not proof those accounts would not have converted. Do not treat a matching project as a causal study. Treat it as plumbing: the keys the vendor documented, normalized the way the vendor documented.

A CDP 'identity resolution' vendor that promises a shared graph across walled gardens is selling a warehouse join. The ads platforms still match on what you POSTed. If the POST omitted fbc, their graph never saw the click.

What to put in the brief

List the keys you will send per event name: PageView (fbp, fbc, IP, UA), Lead (those plus hashed email), Purchase (those plus hashed phone and external_id). List the CDP mappings. List what you will not invent (fake fbp, hashed IP, GA4 client_id as external_id).

QA the JSON, not the Personas UI. pixellint validate json on the CAPI body after the destination mapping is the check that matches the graph the bidder uses.

// WRONG: assume the CDP join is Meta's join
meta.user_data.external_id = segment.anonymousId;
meta.user_data.em = segment.userId; // raw, and the wrong key

// RIGHT: vendor fields, vendor hashing
meta.user_data.em = sha256hex(email.trim().toLowerCase());
meta.user_data.external_id = sha256hex(crmId);
meta.user_data.fbp = cookies._fbp;
meta.user_data.fbc = cookies._fbc;

Check the artifact

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