pixellint

CAPI · marketers · PMs · engineers

Do not fire the whole funnel on thank-you

ViewContent on the product page, AddToCart on add, InitiateCheckout on checkout start, Purchase on success. Content ids should match your catalog feed or dynamic ads will show the wrong SKU.

One step per page, not a bundle

ViewContent on the product page. AddToCart when the cart actually changes. InitiateCheckout when checkout starts. Purchase when money is captured (or when you have a hard policy that authorize is enough, and then you refund voids). Firing all four from the thank-you tag because the GTM recipe included them teaches the model that every converter also just viewed and added in the same millisecond.

Firing only Purchase starves upper-funnel learning. Prospecting campaigns that optimize for ViewContent or AddToCart need those events to be true. A Purchase-only account can still retarget buyers; it cannot easily find the next ones from on-site behavior it never sent.

Content ids match the feed

The same id the product feed uses, not the URL slug, not the Shopify numeric id unless that is what the feed uses. A mismatch is a creative problem that looks like a pixel problem: dynamic ads show the wrong SKU or none.

Send the same ids on ViewContent, AddToCart, and Purchase. A ViewContent of sku-9 and a Purchase of 123456789 is two products as far as the catalog is concerned. TikTok and Pinterest catalog ads punish this as hard as Meta.

Value along the funnel

AddToCart value is the cart, not 1. InitiateCheckout is the cart about to pay. Purchase is what was paid. Using 1 as a placeholder trains value optimization to bid toward $1 orders. Meta Purchase without value and currency fails vendor.meta-conversions-api.body.purchase_requires_value_and_currency. AddToCart without value is often legal and still a bad bid signal if you optimize for it.

GA4 add_to_cart and begin_checkout need currency, value, and items (vendor.google-analytics.body.cart_requires_ecommerce_fields). purchase adds transaction_id. Do not send GA4 purchase on the product page because it is easier than wiring the funnel.

Names on each vendor

Meta: ViewContent, AddToCart, InitiateCheckout, Purchase. Snap: VIEW_CONTENT, ADD_CART, START_CHECKOUT, PURCHASE. TikTok: ViewContent, AddToCart, InitiateCheckout, CompletePayment (not Purchase). GA4: view_item, add_to_cart, begin_checkout, purchase.

A shared dataLayer event checkout_start has to map. If sGTM sends InitiateCheckout to Meta and begin_checkout to GA4, good. If it sends Purchase to both because the trigger is Thank You Page, you bundled the funnel again.

Leads are a funnel too

Lead on form submit. CompleteRegistration if they confirmed email. Do not fire Lead on page load of /contact. PMs who want volume will ask. Volume is not a lead. A Lead pixel on every blog view is how CPL looks cheap and sales says the leads are junk.

If CRM closed-won is the optimization, the form Lead and the offline conversion are two events on purpose. Do not also fire Purchase on the form thank-you with value 1.

Thank-you is Purchase only

The product page already had ViewContent. Do not replay the funnel at the end.

// Wrong: thank-you fires the whole Meta funnel
fbq('track', 'ViewContent');
fbq('track', 'AddToCart');
fbq('track', 'InitiateCheckout');
fbq('track', 'Purchase', { value: 84.50, currency: 'USD' }, { eventID: 'order-1842' });

// Right: Purchase on success, same eventID on CAPI
fbq('track', 'Purchase', { value: 84.50, currency: 'USD', contents: [{ id: 'sku-9', quantity: 1 }], content_type: 'product' }, { eventID: 'order-1842' });

// Snap names are not Meta names
{ "event_name": "ADD_CART", "action_source": "WEB", "event_source_url": "https://shop.example/cart" }

// GA4 cart events need items
{ "name": "add_to_cart", "params": { "currency": "USD", "value": 84.50, "items": [{ "item_id": "sku-9", "quantity": 1 }] } }

Check the artifact

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