Why this happens
GA4's ecommerce schema and Meta's CAPI schema were designed independently for two different reporting systems. Both model "a purchase has a list of products," but neither the field names nor the array nesting line up. A team that already collects GA4-shaped item data naturally wants to reuse that same array for CAPI rather than maintaining a second parallel structure.
Passing GA4's items array directly into Meta's contents field fails quietly: Meta ignores fields it does not recognize rather than erroring, so the purchase event still sends successfully with content_ids empty or missing. The top-level value and currency fields, which do overlap in meaning, still report correctly - which is exactly why the missing product-level detail is easy to miss until dynamic-ads catalog matching turns out not to work.
Fix it
- Keep GA4's
itemsarray as the source of truth if that is where product data already lives - do not restructure GA4 tracking to accommodate Meta. - At the point the CAPI payload is built (client-side before sending, or in server-side GTM if that is where the call is assembled), map each GA4 item to a Meta
contentsentry:item_idtoid,quantitytoquantity,pricetoitem_price. - Populate
content_idsas a flat array of just the product IDs pulled from the same items array - Meta requires this as a sibling field tocontents, not something it derives automatically from it. - Set
contents_typetoproduct(or the catalog content type your Meta catalog uses) so Meta knows how to interpretcontent_idsagainst your Product Catalog for dynamic-ad matching. - Set the top-level
valuefield to the full order total, not a per-item sum, in the same currency-scale convention (major units, for example49.99rather than cents) Meta's Purchase event expects, andcurrencyas the ISO 4217 code. - In server-side GTM, do this mapping inside a Custom JavaScript Variable that transforms the incoming items array, referenced by the CAPI tag's Contents field, rather than hand-writing the transform once per tag, so it stays maintainable if the item schema changes upstream.
How to verify it worked
- Send a test purchase and inspect the event payload Meta received, via the Test Events tool's event detail view, for a populated
contentsarray with correctid,quantity, anditem_priceper line item, and a non-emptycontent_idsarray. - In Meta's Catalog Manager, if using dynamic ads, confirm the product IDs in
content_idsmatch IDs that actually exist in the connected Product Catalog - a mapping that sends the wrong ID field (for example a GA4 SKU instead of the catalog's product ID) will pass CAPI validation but fail to match any catalog item. - Cross-check the top-level
valuereported for the test purchase against the actual order total in your own system - a mismatch usually means the value was summed fromitem_pricefields with a rounding or currency-scale error rather than taken from the authoritative order total.