The translation problem
Every ad platform has its own vocabulary for ecommerce data. Meta wants an array called contents with objects containing id, quantity, and item_price. GA4 wants an array called items with item_id, item_name, price, and quantity. TikTok wants contents with content_id and content_name. Reddit wants products with id, name, and category.
Without server-side translation, your developers would have to pass the same product data three different ways depending on which platform you're sending to, or maintain three different tag configurations in GTM. That's painful, error-prone, and scales badly when you add a fourth or fifth platform.
Echo solves this by accepting a single canonical format from your SDK and translating it per destination automatically. Your developers write one track() call. Every destination receives the event with its items properly shaped. Adding a new destination doesn't require any code changes on your site.
How to fire an ecommerce event
Use the Echo SDK the same way you'd use GA4's gtag or dataLayer push. Call tph.echo.track with a standard event name and a params object that includes an items array:
tph.echo.track('add_to_cart', {"{"} items: [{"{"} id: 'SKU1', name: 'Widget', price: 29.99, quantity: 1, category: 'Tools' {"}"}], value: 29.99, currency: 'USD' {"}"})
Each item object supports id, name (or item_name), price (or item_price), quantity, category, brand, variant, and coupon. You don't have to include every field for every item, just what's relevant.
If your SDK or dataLayer setup produces items in GA4's flat format (pr1id, pr1nm, pr1pr, pr1qt, pr2id, ...), Echo reads that too automatically as a fallback. Either format works without configuration.
What each destination receives
When your add_to_cart event with items fires, Echo translates the event name per destination and reshapes the items array accordingly. Here's what happens for that same single track() call:
Meta CAPI
Event name becomes AddToCart. The items array is transformed into custom_data.contents: [{"{"} id: 'SKU1', quantity: 1, item_price: 29.99 {"}"}]. Echo also auto-derives custom_data.content_ids: ['SKU1'], which is the array of strings Meta needs for Dynamic Product Ads retargeting.
Snapchat CAPI
Event name becomes ADD_CART (Snap uses UPPER_SNAKE for standard events). Items become custom_data.contents: [{"{"} id: 'SKU1', quantity: 1, item_price: 29.99 {"}"}] with content_ids derived as with Meta.
Reddit CAPI
Event name becomes AddToCart under event_type.tracking_type. Items become event_metadata.products: [{"{"} id: 'SKU1', name: 'Widget', category: 'Tools' {"}"}]. Echo also derives event_metadata.item_count from the sum of quantities.
TikTok Events API
Event name becomes AddToCart. Items become properties.contents: [{"{"} content_id: 'SKU1', content_name: 'Widget', price: 29.99, quantity: 1, content_category: 'Tools' {"}"}].
Pinterest CAPI
Event name becomes AddToCart. Items become custom_data.contents: [{"{"} id: 'SKU1', item_price: 29.99, quantity: 1 {"}"}] plus custom_data.product_ids: ['SKU1'].
Google Analytics 4
Event name stays add_to_cart. Items become params.items in GA4's native shape with item_id, item_name, price, and quantity.
Meta's Dynamic Product Ads rely on content_ids being an array of strings, not a comma-separated string. Echo derives the correct array shape from your items automatically. Before the translation existed, passing content_ids as a string meant DPA retargeting was subtly broken. Echo fixes this.
Standard ecommerce event names
Along with items translation, Echo maps standard ecommerce event names per destination. If you fire purchase, Meta receives Purchase, Snap receives PURCHASE, Reddit receives Purchase as a tracking_type, and so on. The full list of mapped events includes view_item, view_item_list, select_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_payment_info, add_shipping_info, purchase, refund, plus engagement events like lead, sign_up, search, and subscribe.
If you fire a custom event name that isn't in the standard list (like SaveForLater), Meta, Snap, and Reddit accept it as a custom event with the name preserved. The items translation still applies for the custom event. So your custom event still gets proper ecommerce data shaping.
Destinations without items support
LinkedIn Conversions API, Google Ads Enhanced Conversions, Microsoft Ads, and Floodlight don't have a standard items concept server-side. Their APIs focus on single conversions with value and click identifiers rather than product-level detail. For these destinations, value and currency flow through but the items array is skipped. If you need product-level attribution on these platforms, you typically rely on different mechanisms like Google Ads' conversion value rules or LinkedIn's enhanced offline conversions.
Tips
- Developer writes one SDK call. Marketing and engineering teams do not need to maintain separate destination-specific code paths.
- Nested items array is preferred. GA4 flat format is supported as a fallback for legacy dataLayer setups.
- Always pass at least
id,name,price, andquantityper item. Other fields are optional but useful for catalog matching. - For Meta Dynamic Product Ads, your
idvalues need to match theidfield in your Meta product catalog exactly. Use the same SKU format in your SDK calls. - Currency must be a valid ISO 4217 code (USD, EUR, GBP, CAD, etc.) for all destinations. Echo passes it through without reformatting.
Troubleshooting
Items show up in some destinations but not others
If items appear in Meta but not GA4, or vice versa, confirm both destinations are enabled and the event filter includes the event name. If filters are fine, your destination mapping may be pinned to an older version that predates items support. Re-save the destination to refresh its mapping config.
Items array appears but fields are missing
Each destination only includes fields it cares about. Meta contents don't include content_name, only id, quantity, and item_price. If you expected content_name on Meta, use the ep.content_name standard mapping (which flows from the event-level content_name parameter) rather than expecting it inside individual items.
DPA retargeting not working on Meta
DPA requires content_ids to be an array of strings matching your catalog IDs exactly. Check Events Manager and confirm custom_data.content_ids appears as an array. If it appears as a single string, the old mapping is still in effect. Re-save the Meta destination and verify the embedded mapping file was deployed. Also confirm the IDs in your SDK call match your Meta catalog's id field precisely (case-sensitive).