Agency Reporting: Defending ROAS When Client COGS Data Is Stale

A client-conversation playbook for agency leads shipping ROI dashboards on top of stale Shopify COGS: what to ask for, what to model, and how to caveat the report until real costs land.
Quick answer
If the client's Shopify `cost` field hasn't been updated in 90+ days, stop shipping ROAS as if it were fact. Switch the headline metric to MER, model a proxy gross margin by product category, and footnote every ROI figure with the freshness date. Reconcile the back-catalogue once finance delivers real costs — and put a COGS freshness clause into the next retainer.
Agency Reporting: Defending ROAS When Client COGS Data Is Stale
The playbook agencies use to keep client ROI reporting honest when the Shopify cost field is months out of date.
Stale COGS is the silent kill switch on agency ROI reporting. When a client's Shopify `cost per item` field hasn't been touched since the last supplier price change, every ROAS, contribution-margin, and payback number in your dashboard drifts further from reality each week — and you're the one signing the deck.
This page is the client-conversation playbook: how to detect the drift before the QBR, how to request fresh data without triggering a finance escalation, how to model a defensible proxy when real numbers won't arrive in time, and how to caveat the report so nobody mistakes a modelled figure for a measured one.
The scenario is depressingly common. You inherit a Shopify Plus apparel account, pull the last twelve months into your reporting stack, and the blended ROAS looks strong. Three weeks in, the client mentions their fabric costs went up 22% in March. It's now November.
Every margin-based number you've shipped since onboarding is wrong in the same direction — too optimistic — and the paid-social scale-up you recommended was underwritten by a cost basis that no longer exists. The question isn't whether to fix it. It's how to fix it without torching client trust.
Why client COGS goes stale in the first place
Shopify's `cost per item` field is optional, invisible on the storefront, and lives on the variant — not the product. That means updating it correctly requires a bulk CSV export, a finance sign-off on landed cost, and a re-import that touches thousands of SKUs. Most operators do it once, at store setup, and never again.
The second failure mode is scope: `cost per item` is meant to be fully-loaded landed cost (goods + freight + duty + inbound handling), but in practice it captures the supplier invoice and nothing else. When freight rates doubled in 2021 and again in 2024, brands that never revisited the field silently overstated gross margin by 6-15 points.
The 90-day rule
If the last-modified timestamp on the client's `cost` field is older than 90 days — or older than the last supplier price change, whichever is sooner — treat every downstream margin number as modelled, not measured. See the companion page on detecting stale Shopify COGS before the QBR for the exact SQL to run.
The client conversation: what to ask for, in what order
Do not open with "your data is bad." Open with a specific ask. The COGS data request email every agency should have on file is a three-line template: current landed cost per SKU (or per category), effective date, and confirmation of what's included (freight, duty, packaging). That's it — no lecture on why it matters.
Expect a two-to-six week turnaround. Client finance teams do not drop everything for a marketing agency's dashboard. Plan the interim: what you'll report on, what you'll caveat, and what you'll refuse to report at all until the file arrives.
If the client offers you a spreadsheet from their ERP instead of a Shopify re-import, take it. You can join it in your warehouse layer without touching their store. This also avoids the political landmine of your agency editing product records in their live Shopify admin.
What to model while you wait
Typical gross margin ranges by DTC vertical — use as proxy COGS bands when client data is unavailable
| Vertical | Gross margin (low) | Gross margin (typical) | Gross margin (high) |
|---|---|---|---|
| Beauty & skincare | 62% | 72% | 82% |
| Apparel & accessories | 48% | 58% | 68% |
| Supplements & wellness | 58% | 68% | 78% |
| Home goods & décor | 42% | 52% | 62% |
| Consumer electronics | 22% | 32% | 42% |
| Food & beverage (shelf-stable) | 38% | 48% | 58% |
| Jewellery (fashion tier) | 55% | 65% | 75% |
Modeling proxy COGS by product category is the workhorse move. Pick the typical column, apply it at the SKU-category level, and flag every derived metric as "modelled — awaiting client confirmation." This is defensible in a QBR because you can name the assumption and the source; a blank field is not.
Change what you report, not just how you caveat it
The single biggest lever is switching the headline metric. Reporting MER instead of ROAS when client COGS is unreliable removes the margin dependency entirely — MER (marketing efficiency ratio) is total revenue over total ad spend, which needs no cost basis. You lose channel-level profitability but you gain a number the client can't argue with.
Keep contribution-margin ROAS in the appendix, footnoted with the proxy assumption. And for high-stakes decisions — a channel shutoff, a budget doubling — there are cases where the right answer is to refuse to ship a ROAS number at all until real COGS arrives. That refusal, delivered with a written rationale, is what a senior agency lead does.
Caveat, reconcile, and prevent the next occurrence
Every modelled figure gets a confidence-band footnote — a two-line note under the chart that names the assumption, the date it was made, and the sensitivity. Confidence-band footnotes that protect agency deliverables are the difference between "the agency was wrong" and "the agency flagged the uncertainty." One of those ends the retainer.
When real costs finally land, reconcile the back-catalogue: re-run the last twelve months of margin reporting against actual COGS, publish a diff, and update the historical narrative. Then add a COGS freshness clause to the next retainer — quarterly cost updates from the client, in a named format, or the reporting scope drops to MER-only. That single clause prevents the entire scenario from recurring.
Frequently asked questions
90 days is the operational ceiling for most DTC verticals — sooner if the client has had a recent supplier price change, freight-rate shift, or currency swing. Beyond 90 days, treat every margin-derived metric (contribution margin, contribution-margin ROAS, payback period) as modelled and footnote accordingly.
For a headline MER-adjacent view, yes — a single blended margin from the trailing quarter's P&L is better than nothing. For channel-level ROI it's dangerous, because product mix by channel is rarely uniform: paid social often skews to higher-margin hero SKUs, while Google Shopping catches the long tail. Use blended margin only as a sanity check.
Report MER as the headline and drop contribution-margin ROAS from the deck entirely. Document the refusal in writing so the reporting scope is defensible later. If the retainer depends on ROI reporting, escalate to whoever signs the invoice — they usually override the refusal within a week.
Use a vertical-typical gross margin band (see the table above), apply it at the category level rather than SKU level, and label every downstream metric with a "modelled" tag in the dashboard itself — not just the footnote. The visual tag is what stops the client screenshotting a single chart out of context.
It's supposed to represent fully-loaded landed cost, but in practice most stores populate it with the supplier invoice only. Before you trust any margin number, ask the client explicitly: what's included in that field? The answer changes your proxy by 5-15 margin points.
Show the last-modified date on their `cost` field, then show the sensitivity: at a 10-point margin swing, contribution-margin ROAS moves by X. Most clients concede immediately because they know the field is stale — they just hadn't done the math on what it costs them. The confidence-band footnote pattern makes this conversation shorter.
No. Even with admin access, editing product records in a live store is a scope creep landmine and a source of finance-reconciliation disputes. Model costs in your warehouse layer, keep the Shopify record untouched, and hand the client a CSV they can import themselves when finance signs off.
If you're feeding cost data into Meta or Google via the products feed for value-based bidding, stale COGS silently distorts the bid signal — the algorithm optimises toward SKUs whose true margin is lower than reported. Pause value-based bidding on affected campaigns until the cost file is refreshed, or switch to conversion-value optimisation on revenue only.
Query the `updated_at` timestamp on the Shopify product variant, filtered to `cost` field changes if you have access to the audit log. Cross-reference against the last known supplier price change. If more than 30% of SKUs by revenue haven't been touched in 90 days, you have a stale-COGS problem. There's a dedicated companion page on detecting stale Shopify COGS before the QBR with the exact query.
Yes — and proactively. Re-run the trailing twelve months against actual costs, publish a one-page diff showing the delta, and update the narrative in the next QBR. The reconciling-backdated-COGS workflow is what turns a reporting embarrassment into a trust-building moment: you flagged the uncertainty, you fixed it, and the numbers now match reality.
See Metricuno on your data
Bring your stack — Google Analytics, Stripe, a CRM, anything — and we'll walk through the metric tree that turns your funnel into one number.