Tracking & Attribution

Why Don't My WooCommerce Purchase Conversions Include Shopping Cart Data?

Meta says your WooCommerce purchases have no shopping cart data? Why content_ids and contents go missing, and how to send them from the order's line items.

Quick answer

Because whatever sends your Purchase to Meta isn't reading the order's line items. A tag-manager tag, a theme snippet or a thank-you-page script set up with only value and currency sends a sale with no products in it. The fix is to build Purchase from the WooCommerce order itself — content_ids, contents, num_items and content_type from its line items — and to keep one source for the event.

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Your WooCommerce purchases arrive in Meta without shopping cart data because the tool sending the Purchase isn’t reading the order’s line items. It sends a value and a currency — enough to count a sale — but no content_ids, no contents and no num_items. Meta knows you made money. It doesn’t know which products you sold.

That gap matters more than it looks. Catalog ads decide which products to show by matching the ids in your events to the ids in your catalog. A Purchase with no ids can’t tell them what sold, and product-level reporting in Meta has nothing to group by.

Here’s where cart data comes from, the five ways it goes missing on WooCommerce, and how to send it from the order instead of the page.

What counts as “shopping cart data” in a Meta Purchase?

Shopping cart data is the product detail inside the event’s custom_data: content_ids (the product ids), contents (each id with its quantity and optionally its price), num_items and content_type. Meta documents all four as standard custom data parameters for both the Meta Pixel and the Conversions API. Value and currency tell Meta how much; these tell it what.

A complete server-side Purchase looks like this:

{
  "event_name": "Purchase",
  "event_id": "order-1234",
  "action_source": "website",
  "custom_data": {
    "currency": "USD",
    "value": 128.40,
    "content_type": "product",
    "content_ids": ["4417", "4420"],
    "contents": [
      { "id": "4417", "quantity": 2, "item_price": 39.00 },
      { "id": "4420", "quantity": 1, "item_price": 42.00 }
    ],
    "num_items": 3
  }
}

The version that triggers the complaint has the same first four lines and a custom_data block with only currency and value. Meta accepts it, counts it and reports the revenue. It just can’t connect the sale to a single product.

If value and currency are also wrong or missing, fix that first — sending purchase value and currency to Meta covers it. Cart data is the next layer down.

Where does the cart data in a Purchase event come from?

It comes from whoever builds the event, and that builder has to read the order. On WooCommerce the order object holds every line item with its product, variation and quantity. A sender that reads the order can fill contents exactly. A sender that only reads the thank-you page sees whatever the theme prints, which is often just a total.

That’s the whole problem in one line: cart data is only as good as the data source the sender reads.

Meta’s own plugin shows the right pattern. In the Meta for WooCommerce source, the Purchase handler loops over the order’s line items, adds each product’s id to content_ids, writes an id-and-quantity entry into contents, and switches content_type to product_group when a variable product is involved. The ids are the WooCommerce product or variation ids, unless a filter changes them.

Mechanism diagram: a WooCommerce order #1234 card with two line items — a variation id 4417 with quantity 2 and a product id 4420 with quantity 1 — flows into a Meta Purchase payload card where each line becomes an entry in content_ids and contents, num_items reads 3 and the value is the order total, next to a greyed page-scraped Purchase card that carries only value and currency

Why is cart data missing from my WooCommerce purchases?

Almost always because a Purchase that doesn’t read the order is being sent — either instead of a complete one, or alongside it. The five causes below cover nearly every case. The first two account for most of them, and all five leave the same symptom: a Purchase that carries a value but no products.

1. A tag-manager or theme Purchase with value only

Someone added a Google Tag Manager tag, a theme “tracking code” box or a snippet in functions.php that fires fbq('track', 'Purchase', {value, currency}) on the thank-you page. It works, so nobody revisits it. It never had product ids to send.

2. A second tool is sending Purchase

The official plugin sends a complete Purchase, but a pixel plugin, a tag-manager container or a point-and-click event from Meta’s event setup tool also sends one with no products. Two sources, two payloads, and the thin one is the one you see.

3. Order lines whose product no longer loads

A sender that reads the order can still skip lines. In Meta for WooCommerce, a line whose product can’t be loaded — deleted, or trashed after the sale — is left out of content_ids and contents. If every line in the order is like that, the Purchase goes out with empty cart fields.

4. A server-side sender that only maps totals

Some server-side setups and middleware forward the order total and customer data but never map line items into custom_data. The event is server-side and well matched, and still has no products.

5. Cart data that’s present but doesn’t match the catalog

This is a different failure that looks similar. The ids are there, but they’re SKUs while your catalog uses product ids, or parent ids while the catalog lists variations. Meta then reports no match rather than no data. Why dynamic product ads show zero catalog match walks through that case.

How do you check which Purchase is missing its products?

Open Test Events in Events Manager, place a small test order, and expand every Purchase that arrives. Check for content_ids and contents on each, and note whether it came from the browser or the server. The copy without products tells you which sender to fix or switch off.

  1. Run one test order. Use Test Events for your pixel — the same view you’d use to check your Meta CAPI is working. Buy a product with a variation and one without, so both id types show up.
  2. Count the Purchases. One is correct. Two or more means more than one tool sends Purchase, and cause 2 is live.
  3. Open the parameters. On each Purchase, look for content_ids, contents, num_items and content_type. Note the source — browser or server.
  4. Match ids to the order. Compare the ids against the product and variation ids in WooCommerce → Orders. Missing ids point to causes 1–4. Present but unfamiliar ids point to cause 5.
  5. Test a server payload by hand. Meta’s Payload Helper builds a sample Conversions API payload with the custom data fields, so you can see what a complete event should look like next to the one you’re sending.
  6. Switch off one sender at a time. Disable Purchase in each tracking tool in turn and repeat the test. When the thin Purchase disappears, you’ve found it.

How do you get cart data back into the Purchase event?

Build Purchase from the WooCommerce order, not from the page, and send it from one place. Read each line item, send its product or variation id with its quantity, set num_items and content_type, and use the same ids your catalog uses. Then remove every other Purchase sender so the complete one is the only one Meta sees.

In rough order of impact:

  • Send Purchase server-side from the order. A server event fired when the order is paid reads the line items directly. The guide to tracking WooCommerce purchases server-side to Meta walks through that setup, which uses the Conversions API.
  • Keep one Purchase source. Turn off the value-only tag or snippet, or turn off the plugin’s Purchase if another tool owns it. One event, one payload.
  • Use variation ids for variable products. Send the id of the variation that was bought — the same id your catalog lists — so a size-M-blue sale doesn’t read as the parent product.
  • Fix the catalog to match, not the event. If your catalog uses different ids, align the catalog with the ids in your events. A product feed that stays in sync with your ad platforms makes that stick.
  • Don’t delete products that just sold. Draft or hide them instead, so a sender that loads the product at send time can still read it.

The PartialLeads Meta CAPI page with a Purchase row for order 1234 expanded to show its payload — content_ids 4417 and 4420, a contents list with quantities, num_items 3, content_type product and the order total as value — above the buyer's per-lead revenue block listing the same two line items, quantities and order total

How does PartialLeads send cart data with every WooCommerce Purchase?

PartialLeads builds the server-side Purchase from the WooCommerce order itself. When a paid order arrives by webhook, its line items become the event’s content_ids, contents and num_items. They’re taken from the order, not scraped from the thank-you page, so a theme that prints only a total can’t strip the products out.

Where the ids come from. Each line sends the variant id — the WooCommerce variation that was bought — and falls back to the product id for simple products. If your catalog comes from the PartialLeads feed, its item ids are those same variant ids, grouped by product, so events and feed match. If your catalog comes from somewhere else, check that it uses the same ids.

What the rest of the event carries. The value is the full order total, including tax and shipping after discounts, with the order’s currency. Only paid orders are sent — Processing or Completed. Pending, on-hold, failed and cancelled orders aren’t, and a cash-on-delivery or bank-transfer order goes out when it’s marked paid, inside Meta’s 7-day event-time window.

Where you see it working. The Meta CAPI page shows each server Purchase with its status, and its payload shows the content_ids and contents that went out. Each buyer’s per-lead revenue block lists the order’s line items, so you can check the products Meta received against the products the customer bought.

The honest constraints. An order is recorded once, so a post-purchase upsell added to the same order later doesn’t update the products or re-send the event. PartialLeads’ event ids are its own. It never double-sends, but it doesn’t deduplicate against another plugin’s browser Purchase. If Meta for WooCommerce or a tag-manager tag still sends a value-only Purchase, switch that one off and keep one source per event. And PartialLeads controls what’s sent; how Meta matches it to your catalog and your ads is Meta’s half.

What breaks The mechanism Where you see it in the dashboard
Purchase arrives with value only, no products Purchase built from the order’s line items: content_ids, contents, num_items Meta CAPI page, event payload
Thank-you page prints only a total Event comes from the paid-order webhook, not the page Meta CAPI page, one Purchase per order
Variable products sent as the parent Variant id per line, product id for simple products Meta CAPI page payload vs per-lead line items
Events and catalog use different ids PartialLeads feed uses the same variant ids Product Catalog, feed URL
A second tool sends a value-only Purchase No cross-tool dedup — switch the other sender off Leads-list API column vs Events Manager sources

Tell us what's broken. We'll fix your tracking — free.

Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.

Sources


Frequently asked questions

QWhat does "most recent conversions don't have shopping cart data" mean in Meta?
It means the Purchase events Meta received carry a value but no product detail — no content_ids or contents saying which items were bought. The sale still counts, but Meta can't tie it to specific products, so catalog ads and product-level reporting lose that signal. Find the sender that builds Purchase without reading the order and fix or remove it.
QDo I need content_ids if I'm not running catalog ads?
It's not required to count a sale, and value and currency are what Meta uses for revenue. But without product ids you can't start catalog ads later with a useful purchase history, and you can't see which products your ads sell. Sending them costs nothing extra once Purchase is built from the order.
QShould content_ids be the product id or the variation id?
Use whatever id your catalog uses for the item that was bought. For variable products that's usually the variation id, since catalogs list each size or colour as its own item. The ids in your events and the ids in your catalog must be the same strings, or Meta reports no catalog match even when cart data is present.
QWhy does my WooCommerce Purchase have cart data in the browser event but not the server event?
Because two different builders make them. The browser copy is made on the thank-you page by one script; the server copy is made by another tool that may only map the order total. Check each copy in Test Events, then fix the server sender to read line items — or keep one sender for Purchase so there's only one payload.
QWill PartialLeads include products that were deleted after the order?
PartialLeads builds the Purchase from the paid order when its webhook arrives, which is normally right after checkout, so the order's line items are what get sent. Don't rely on that for products you plan to delete: draft or hide them instead, so every tool that reads your store — including your catalog — still sees them.

Find the qualified leads your forms are currently throwing away.

Install PartialLeads on one landing page, send traffic, and compare what your CRM captured against what PartialLeads recovered and qualified.