Skip to content
<- Guides
Agents · Decision rules

The Data-Mode Router That Stops Bad ACoS Math

Amazon's raw exports don't tag ads vs. organic. A reusable Skill applies that decision, grounded by Amazon Agent Atlas, before a single ACoS number gets computed.

Your SQP export has no cost column. Atlas blocks ACoS there and routes to Conversion Performance Index instead, the fix for nonsense report numbers.

Kuudo
Reviewed by Kuudo Engineering
ChatGPT running the data-mode-router Skill: it detects which columns a report has, resolves data_mode to ads, organic, or mixed, blocks ACoS/ROAS as an invalid computation (priority 1) when the data is organic, and routes to Conversion Performance Index instead, ending in a routed decision plan.
A Skill decides what a signal means before any metric gets computed, applying logic Amazon's raw reports never apply themselves.
TL;DR
  • Amazon's own reports never tag data_mode themselves, a reusable data-mode-router Skill applies that logic every run, grounded by Amazon Agent Atlas, and tags the dataset ads, organic, or mixed from the columns present before any metric runs.
  • ACoS and ROAS compute only in ads mode, or for mixed records backed by real ad cost and revenue, never on organic totals.
  • Computing ACoS in organic mode is ranked the single highest-priority rule in the decision-precedence ladder, above thin-data holds and safety negatives.
  • Mixed datasets emit two separate records, one ads-backed and one organic-backed, instead of one blended and wrong number.
  • Organic mode routes to Conversion Performance Index instead: (your purchase rate / market purchase rate) x 100, with under 80 underperforming, 80 to 120 competitive, over 120 outperforming.

The nonsense ACoS numbers show up because your SQP export has no cost or revenue columns in it, and Amazon's own export doesn't flag that gap for you, nothing in the raw Ads or Selling Partner APIs tags a dataset as ads-backed or organic. Any report that hands you ACoS anyway skipped a check our data-mode-router Skill runs before every metric: a reusable, structured process, grounded by Amazon Agent Atlas's Keyword Analysis Decision Framework, that inspects the columns actually present in a dataset before it lets ACoS, ROAS (return on ad spend), or any spend-based number get computed. I asked our agent why a batch of n-gram reports kept returning ACoS on exports that never carried a spend column, and the answer was structural, not a bug. The Skill tags every dataset ads, organic, or mixed from its column set and hard-blocks ACoS/ROAS outside ads-backed data. Search Query Performance exports, pulled through the Selling Partner MCP, are organic by construction. There's no spend column in there to blend.

Why not just paste that export into ChatGPT or Claude and ask for ACoS? A plain chat hits the same three walls on any Amazon job. It has no access to your data, so it only sees the columns you pasted and can't check whether total_median_click_price is real spend or a market proxy. It has no way to take action, so the best it can do is hedge in prose instead of blocking the computation outright. And it runs on generic knowledge, not Amazon's, so it has no idea ACoS in organic mode is a codified Amazon violation. What you get is disconnected, generic, manual work that ships silent mistakes. Amazon Ads MCP and the Selling Partner MCP give an agent your data and the tools to act, Atlas supplies the rule book, Skills packages the workflow.

The router tags every dataset ads, organic, or mixed before a metric runs

Before our agent computes anything, the data-mode-router Skill runs its Quick Router logic to look at which columns a dataset actually has. This step doesn't exist in the raw Amazon data, it's the Skill applying Atlas's codified rules as a structured process, the same way every time. Ad signals are cost or spend, campaign_id, ad_group_id, keyword, match type, placement, the columns that exist because the Amazon Ads MCP mirrors those fields straight out of your live account, not because someone typed labels into a spreadsheet. Organic signals are search_query, total_impressions, asin_impression_share, and the rest of the Search Query Performance schema. If a dataset has ad signals and no organic ones, it's tagged ads. If it has organic signals and no ad ones, like every SQP export, it's tagged organic. Both together, it's mixed.

That tag isn't a note in a log somewhere, it's the first field on every record the Skill emits. Here's what an ads-mode row looks like coming out of the n-gram rollup:

{
  "data_mode": "ads",
  "ngram": "wireless headset",
  "n": 2,
  "imp": 12450,
  "clk": 386,
  "cost": 782.14,
  "orders": 41,
  "revenue": 4312.0,
  "metrics": {
    "ctr": 0.031,
    "cvr": 0.1061,
    "cpc": 2.027,
    "roas": 5.514,
    "acos": 0.181
  }
}

Notice data_mode comes before the metrics that depend on it, not after. This is also the point where the router's job ends and a different rulebook picks up. Once a keyword resolves to ads, whether to actually change a bid on it is a separate question, governed by a different Quick Router keyed on bidding_state, not data_mode, over in the Sponsored Ads Bidding Configuration Decision Framework. The data_mode router decides what a signal even means. That framework decides whether to act on it.

ACoS in organic mode isn't a style nitpick, it's priority #1 forbidden

Our data-mode-router Skill doesn't treat "don't compute ACoS on organic data" as a soft preference, it enforces it as code, every run. It's rule one of seven in the decision-precedence ladder the Skill applies, grounded by Atlas, ranked above thin-data holds, safety negatives, pull-back actions, scale moves, and mining or hygiene work. Invalid computations get dropped and repaired before any other business rule even gets evaluated. Nothing outranks it, and nothing in Amazon's own reporting enforces this ranking for you.

The Skill's config defaults reinforce the same rule structurally, not just procedurally: target_acos, target_roas, and break_even_acos only exist under the ads branch of the targets config. There's no organic branch for an ACoS target to live in, because organic data was never going to have spend to target against. Those same three constants (0.25, 4.0, 0.30 in our defaults) are actually anchored at the ad-group level by the Sponsored Ads Bidding Configuration Decision Framework mentioned above, the data-mode-router Skill just reads them, it doesn't own them.

precedence:
  1: invalid_computation   # drop/repair, e.g. ACoS computed in organic mode
  2: hold_thin_data
  3: safety_negative
  4: pull_back
  5: scale_unlock
  6: mining_hygiene
  7: creative_ops
targets:
  ads:
    target_acos: 0.25
    target_roas: 4.0
    break_even_acos: 0.30
  # organic has no target_acos / target_roas key at all

Mixed data doesn't get blended into one number, it gets split into two

Some exports carry both column families at once, a keyword report joined against a campaign report, say. When that happens, the Skill's mixed-dataset guardrail doesn't average the two into one blended figure. It computes ads metrics only where ads-backed cost and revenue actually exist, applies organic rules only to signals backed by SQP, and emits two separate records for the same keyword, each carrying its own data_mode tag, with no cross-mixing of numerators and denominators between them.

For "wireless headset," that looks like this:

[
  {
    "data_mode": "ads",
    "ngram": "wireless headset",
    "n": 2,
    "imp": 12450,
    "clk": 386,
    "cost": 782.14,
    "orders": 41,
    "revenue": 4312.0,
    "metrics": { "ctr": 0.031, "cvr": 0.1061, "cpc": 2.027, "roas": 5.514, "acos": 0.181 }
  },
  {
    "data_mode": "organic",
    "ngram": "wireless headset",
    "search_query": "wireless headset",
    "total_impressions": 58210,
    "total_clicks": 1904,
    "conversion_performance_index": 96,
    "share_funnel_gaps": { "impression_to_click_gap_pp": -0.4, "click_to_purchase_gap_pp": 0.6 }
  }
]

Two records, two tags, nothing shared between their numerators and denominators. The alternative, one blended row with an ACoS computed against a mix of real spend and organic volume, is exactly the invalid computation rule one exists to catch.

Organic mode routes to Conversion Performance Index, not ACoS

Blocking ACoS on organic data is only half the rule, the Skill also has to route to something valid, and for organic that's Conversion Performance Index and the impression-to-click-to-purchase share funnel. Neither is a field Amazon's Search Query Performance report gives you directly, the Skill calculates both from the shares Amazon does report. CPI is (your_purchase_rate / market_purchase_rate) x 100, banded under 80 as underperforming, 80 to 120 as competitive, and above 120 as outperforming. Alongside it, impression_to_click_gap_pp and click_to_purchase_gap_pp show exactly where your ASIN is losing share against the market baseline for that query.

Below the CPI sufficiency floor (asin_clicks >= 20 and total_clicks >= 100), the Skill holds the decision instead of guessing on thin data. The ASIN-level diagnostic layer downstream adds its own floor (total_impressions >= 200) before it turns a share gap into a strong claim, and cooldowns run 7 to 14 days per query and ASIN so the same finding doesn't refire every day. Here's a full routed decision for one query, including the guardrail check that blocked ACoS on the way in:

{
  "report_id": "sqp-ngram-2026-08-07",
  "routing": {
    "columns_detected": ["search_query", "total_impressions", "total_clicks",
      "asin_impression_share", "asin_click_share", "asin_purchase_share",
      "total_median_click_price"],
    "ads_signals_present": false,
    "organic_signals_present": true,
    "resolved_data_mode": "organic"
  },
  "guardrail_checks": [
    {
      "rule": "invalid_computation",
      "precedence_rank": 1,
      "trigger": "ACoS/ROAS requested but data_mode=organic (no cost/spend columns)",
      "result": "blocked",
      "action": "drop_and_repair",
      "repair": "substitute Conversion Performance Index + share funnel gaps"
    }
  ],
  "decisions": [
    {
      "data_mode": "organic",
      "search_query": "dog bed large",
      "asin": "B00XYZ...",
      "market": { "total_impressions": 123456, "total_clicks": 3456, "purchase_rate": 0.018 },
      "asin_metrics": { "impression_share": 0.024, "click_share": 0.031, "purchase_share": 0.020 },
      "conversion_performance_index": 64.5,
      "share_funnel_gaps": { "impression_to_click_gap_pp": 0.7, "click_to_purchase_gap_pp": -1.1 },
      "flags": { "thin_data": false },
      "recommendations": ["pdp_update"]
    }
  ]
}

A CPI of 64.5 lands below 80, underperforming the market, which is exactly why recommendations queues pdp_update rather than a bid change. Behind that field sits the ASIN-level diagnostic layer, running its own IF-THEN rules on top of the share funnel: high query volume with low impression share routes to seo_update plus pdp_update, purchase share trailing click share alongside slow shipping routes to shipping_speed_fix. ACoS was never going to point you at any of that.

What happens next

When I run this against a fresh batch of reports, our agent doesn't just resolve data_mode, fire the guardrail check, and hand back a JSON blob to stop there. It pushes the routed decision into Amazon Agent Flow, the data layer that holds routed decisions alongside the raw SP-API (Selling Partner API) and Ads API pulls they came from, so next week's report starts from the same schema instead of guessing again. The recommendation it queued, pdp_update, seo_update, shipping_speed_fix, becomes a scheduled run of the same Skill that reruns on the 7-to-14-day cooldown instead of a one-off answer you have to remember to ask for again.

If the resolved mode had come back ads instead, the same decision hands off to a different ladder entirely, applied by a different Skill: the bid-thrash precedence covered in the bidding rulebook guide, which decides whether to actually move a bid once the signal underneath it is already confirmed valid. The data-mode-router Skill decides what a number means. The bidding rulebook's Skill decides what to do about it once it does.

That's the pattern underneath the whole rulebook: validate the signal before you ever act on it, structured Skill logic doing work Amazon's raw reports never do on their own, and never let the two ladders answer each other's questions.

Next in the series: how the ASIN-level diagnostic decision object turns a Conversion Performance Index gap into a queued PDP or SEO fix.

Private beta

Stop shipping reports with the wrong metric on the wrong data

Bring us the report-routing workflow you want your agent to run. We'll map the data-mode-router Skill, Amazon MCP pulls, Atlas grounding, and private-beta setup with you.

Run this workflow in beta

What you need to run this

MCP
Amazon Ads MCP for ads-mode signals; Amazon Selling Partner MCP for the Search Query Performance pulls behind organic mode
Skill
data-mode-router Skill, run against any n-gram or ASIN-level rollup before a metric is trusted
Atlas collection
amazon_rules (playbooks: Keyword Analysis Decision Framework (N-Grams); Sponsored Ads Bidding Configuration Decision Framework)
Required subscriptions
None beyond an Amazon Ads MCP or Selling Partner MCP connection; Search Query Performance data ships free through Brand Analytics for brand-registered sellers
Last verified
"2026-08-07T00:00:00.000Z"

What success and failure look like

scenariosuccessfailure
SQP export requested as ACoSRouter tags data_mode=organic, blocks ACoS, and returns Conversion Performance Index insteadA report shows ACoS computed against an export that never carried a cost column
Mixed export with both ads and organic columnsTwo tagged records emitted, one ads-backed with ACoS/ROAS, one organic-backed with Conversion Performance IndexOne blended number mixing ad spend with organic volume
Thin organic sample below the CPI sufficiency floorhold_thin_data emitted, no Conversion Performance Index claim assertedA CPI computed on a handful of clicks and presented as a confident claim
Related reading

Keep exploring this topic

Use these companion guides to understand the inputs, follow-on analysis, and adjacent workflows behind this playbook.

Next step

FAQ

Why does my SQP report show an ACoS number when there's no cost column in the data?

It shouldn't, and Amazon's own export won't stop it since the platform doesn't tag data_mode itself. Our data-mode-router Skill applies that check, grounded by Amazon Agent Atlas, and hard-blocks ACoS/ROAS computation on organic data. A report showing ACoS from SQP data skipped the Skill, most often a manual export pasted into a spreadsheet or a chat prompt rather than run through it.

What is data_mode, and is it something Amazon provides?

No, it's a tag our data-mode-router Skill applies, ads, organic, or mixed, based on which columns a dataset actually has, grounded by rules from Amazon Agent Atlas. Amazon's Ads and Selling Partner APIs don't include this tag natively. Ad signals are cost, campaign_id, and ad_group_id; organic signals are search_query and the share fields like asin_impression_share. The tag is the first field on every decision record the Skill emits.

What's the difference between data_mode and bidding_state?

data_mode (applied by our data-mode-router Skill, grounded in the Keyword Analysis Decision Framework) decides what a signal means: whether it's backed by ad spend, organic search-visibility data, or both. bidding_state (a separate Skill grounded in the Sponsored Ads Bidding Configuration Decision Framework) decides whether an ad group's automated bidder is active. data_mode runs first; bidding_state only matters once a signal has already resolved to ads mode.

How is Conversion Performance Index calculated?

CPI equals your purchase rate divided by the market purchase rate, times 100, where your purchase rate is asin_purchases divided by asin_clicks and the market rate is total_purchases divided by total_clicks. Under 80 is underperforming the market, 80 to 120 is competitive, and above 120 is outperforming.

What happens when a dataset has both ads and organic columns?

The router treats it as mixed and never blends the two into a single number. It computes ads metrics only where ads-backed cost and revenue exist, applies organic rules only to SQP-backed signals, and emits two separate records, one per data_mode, so nothing shares a numerator or denominator across the two.

What is total_median_click_price, and why can't I use it to compute ACoS?

It's a market CPC proxy from Search Query Performance exports, an estimate of typical click price across all sellers on a query, not your actual ad spend. The router excludes it from every ACoS/ROAS gating decision by name, because treating a market proxy as real spend produces exactly the invalid computation the router exists to block.

My report shows one blended ACoS or CPI number across ads and organic. What does that mean?

It means the mixed-dataset guardrail didn't run. The correct output for a mixed dataset is always two tagged records, never one averaged figure. A single blended number is the specific failure mode the decision-precedence ladder's rule one, invalid computations: drop/repair, exists to catch.

Sources