Analytics · PMs · engineers · marketers
A CDP is a pipe. A pixel is a bid.
Marketers turn on the Meta destination in Segment and assume Event Match Quality. Engineers assume the destination mapping hashes email. Someone should open the actual POST.
Three schemas, one dictionary
Your product event name, GA4 recommended events, Meta standard events. A map with tests. Without it you ship Purchse to one and purchase to another. Segment track event is a string you chose. GA4 purchase is a recommended name with ecommerce params. Meta Purchase is PascalCase with event_id. The CDP will not translate unless a mapping says so.
Product analytics (PostHog, Mixpanel, Amplitude) wants funnels and retention. Ads CAPI wants matchable conversions for bidding. GA4 MP wants a G- stream and 16-digit micros. One track() is not automatically all three jobs done well. Write two columns on the dictionary: product and ads.
Bare bodies are not interchangeable
Segment and PostHog both post a root event. A bare body has no URL. Pixellint tells them apart by writeKey, userId, anonymousId versus api_key, distinct_id. Mixpanel is a bare array with properties.token. Amplitude is api_key plus events[].event_type. Shipping the wrong envelope still 200s at the wrong host, or matches no pack.
Validate the source call and the destination body. pixellint validate json --rulepack vendor/segment on the batch, then vendor/meta-conversions-api on the POST the Meta destination emitted. HTTP 200 from Segment is not Event Match Quality.
pixellint validate json @segment-batch.json --rulepack vendor/segment
pixellint validate json @meta-capi.json --rulepack vendor/meta-conversions-api
PII is a legal multiplier
A CDP makes it easy to fan out raw email to five vendors. Hash at the destination or strip in the mapping. Do not rely on the CDP probably hashed it. Ads tools want SHA-256 of a normalized address. Product tools often keep email in the clear for support. Running the ads hasher on the analytics destination by accident is how people search dies.
IP and user-agent stay plaintext on CAPI. Hashing them is hashed_plaintext_field on Meta, TikTok, Snap, Pinterest. A generic hash all strings mapping is the incident. See the engineer PII checklist.
Klaviyo and Braze are not Segment
Klaviyo Create Event is JSON:API. data.type must be event. The metric name lives at data.attributes.metric.data.attributes.name. A profile identifier is required: id, email, phone number, or external id. Pixellint: vendor.klaviyo.body.data.type.invalid, metric name missing, profile_needs_an_identifier. Copying a Segment track here fails the resource type before anything else runs.
Braze POST /users/track wants events[].name, ISO 8601 time, and one of external_id, user_alias, braze_id, email, or phone. Purchases need product_id, ISO 4217 currency, and price. Epoch timestamps fail the time contract. A dollar sign is not a currency code. These are messaging and lifecycle pipes. They still need a person. They are not Meta CAPI.
{
"data": {
"type": "event",
"attributes": {
"metric": {"data": {"type": "metric", "attributes": {"name": "Placed Order"}}},
"profile": {"data": {"type": "profile", "attributes": {"email": "buyer@example.com"}}}
}
}
}
When to go direct
Purchase CAPI from the shop backend, product events through the CDP, is a common split. Dedup still needs a shared event_id when both touch Meta (fbq eventID on the pixel, event_id on CAPI, same string). A CDP destination that mints its own UUID and a backend that mints another is two purchases.
Pixellint is not affiliated with Segment, Meta, Klaviyo, or Braze. Pack pages under /packs/ are the field tables. This article is the routing decision. Open the actual POST.
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.