# Kuudo - Full Knowledge Base Kuudo connects Amazon Ads, AMC, SP-API, and Vendor Central to ChatGPT, Claude, Cursor, and any MCP client so teams can run governed Amazon workflows from chat. Those tools know nothing about your business out of the box. Your competitors use them too. Kuudo gives those same tools your edge: your data, your rules, and the way your business operates. This document contains the complete text of our guides and documentation for AI crawlers. ## Guides ### Validate AMC SQL in the Sandbox First URL: https://www.kuudo.com/guides/amc-sql-sandbox-validation/ Validate [Amazon Marketing Cloud](/features/amc/) (AMC) SQL with a three-gate Skill: check the query against current AMC guidance, run the same SQL on sandbox synthetic signals, then confirm it on a short advertiser-instance window. The [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies the authorized AMC context and execution tools; [Amazon Agent Atlas](/features/agent-atlas/) grounds the checks in Amazon's current sandbox rules. A sandbox pass removes structural uncertainty. It does not predict your brand's results or production-scale runtime. The operator question was specific: *“Can we test an AMC query before it runs against production and we wait an hour to learn it was wrong?”* I wanted the agent to catch a missing column, bad grouping choice, or invalid join before the expensive run, without turning synthetic output into a media recommendation. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. | Gate | Proves | Cannot prove | | --- | --- | --- | | Atlas and schema | Names, rules, intended grain | Runtime or results | | AMC sandbox | Syntax, joins, output shape | Real IDs or scale | | Short production run | Real data and runtime | Long-window capacity | ## A sandbox pass proves structure, not the business result The Skill starts with the exact SQL intended for production. It does not rewrite a simplified “test query” and hope the two versions behave alike. Atlas retrieved both **Introduction to AMC Sandbox** and **Working with AMC sandbox**, which document that the sandbox uses synthetic signals while providing the same AMC functionality and table structure needed for query iteration. This query keeps the grouping from the reconstructed **Create and execute an ad hoc workflow** source, then fixes its cost-unit boundary before using the result for spend reporting. It establishes one row per advertiser and campaign from `dsp_impressions`: ```sql SELECT advertiser, campaign, SUM(total_cost / 100000.0) AS spend, SUM(impressions) AS impressions, COUNT(DISTINCT user_id) AS reach FROM dsp_impressions GROUP BY advertiser, campaign ``` `total_cost` is stored in 1/100,000 currency units, so the query converts it to currency before labeling the result as `spend`. The sandbox can accept a query with the raw sum; execution success alone would not catch that unit error. The sandbox run answers structural questions: Does the SQL compile? Do the table and columns exist? Does the grouping produce the intended grain? Do the joins and aggregations return the expected shape? If one of those fails, the Skill returns the error with the failed gate instead of spending the production run on a preventable mistake. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see which AMC instance, table, or execution your team is using. You must paste the SQL and error into chat, and your data is now exposed. The MCP reads the authorized context directly. > - **Your call, before anything changes.** A chat can suggest edited SQL, but you must submit it to the sandbox and retrieve the result yourself. The Skill runs the exact saved query and records the outcome. > - **Your judgment, running every time.** Generic SQL advice cannot establish whether `dsp_impressions` and its fields match current AMC guidance. Atlas grounds that check in Amazon's playbooks. ## Aggregation controls must match the question being tested The sandbox offers two distinct views of synthetic data. With aggregation controls on, the result behaves like advertiser-instance reporting: aggregated and anonymous. With controls off, the sandbox can expose event-level synthetic rows so I can inspect how fields and tables relate. Amazon says that event-level view is not available in advertiser instances. I keep controls on for the production-like validation pass. I turn them off only to investigate record shape or use table preview. The sandbox query editor's preview can inspect up to 100 rows and automatically runs that preview with aggregation controls off. Those rows explain the schema; they are not the report I plan to ship. The Skill run record makes the mode explicit: ```yaml skill: amc-sql-sandbox-validation query: campaign-reach-and-spend.sql sandbox: synthetic_data: required aggregation_controls: on checks: - query_compiles - output_grain_is_advertiser_campaign - expected_metrics_are_present production_confirmation: initial_window: "1 day" on_empty_or_suppressed: "widen to 7 days or reduce output granularity" reuse_exact_query: true ``` For API-driven sandbox executions, Amazon's documented control is `requireSyntheticData: true`. The related `disableAggregationControls` setting determines whether the synthetic run keeps normal aggregation protections or exposes event-level records. The Skill owns that distinction so the operator does not accidentally compare two different output modes. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see which aggregation mode was used for the submitted execution. The MCP reads the execution context and the Skill stores the mode with the result. > - **Your call, before anything changes.** A chat cannot rerun the saved query with controls on and off. You would have to coordinate both executions and compare their output by hand. > - **Your judgment, running every time.** A generic answer may treat event-level output as a production option. Atlas retrieves Amazon's sandbox-specific rule: event inspection is synthetic and unavailable in advertiser instances. ## Synthetic signals create a hard validation boundary Sandbox signals are programmatically generated to mimic the structure, statistical properties, and logical cross-table relationships of AMC signals. They are not tied to a real advertiser campaign, customer ID, or ASIN. Amazon explicitly says not to use sandbox results to inform media strategy or implementation. That boundary rules out four conclusions: | Sandbox finding | What it does not establish | | --- | --- | | Query returns rows | Your campaign IDs exist | | Join produces output | Your distribution is representative | | ASIN field is populated | Your ASINs are covered | | Query finishes quickly | Production will avoid timeout | Amazon also documents cases the sandbox may not reproduce well: filters for specific campaign or ASIN identifiers, extremely granular output, varied distributions or outliers, and compute failures caused by a particular time window. A successful sandbox result is evidence about query structure, never evidence about campaign performance. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot compare synthetic output with your real campaign and ASIN coverage. The first authorized advertiser-instance run supplies that evidence. > - **Your call, before anything changes.** A chat cannot test whether a real identifier resolves. You must copy the suggestion into AMC and inspect the result yourself. > - **Your judgment, running every time.** “It worked in test” is too broad. Atlas gives the Skill Amazon's named sandbox limitations, so it blocks business conclusions that the synthetic result cannot support. ## A short advertiser run closes the remaining gap After the sandbox passes, the Amazon Ads MCP submits the unchanged query to the advertiser instance for the smallest useful window, starting with one day. That first production run has a narrow job: confirm real identifiers resolve, validate the output grain on real signals, and establish that the workload completes. An empty one-day result is not automatically a failure. AMC removes output that does not clear its aggregation thresholds, so a low-volume advertiser or granular campaign grouping can produce no usable rows even when the identifiers and SQL are valid. When threshold suppression is plausible, the Skill marks the result **inconclusive**, widens the window to seven days, broadens a restrictive filter, or groups less granularly, then retries. Invalid identifiers and execution errors fail the gate; a threshold-limited result changes the test window. This is where sandbox validation and [AMC query-performance review](/guides/amc-sql-query-performance/) meet. If the query is structurally valid but the short run is still slow, the next action is to filter earlier, reduce intermediate rows, inspect join grain, or test a smaller scope. The agent no longer wastes that investigation on a syntax error the sandbox could have caught. The run record keeps the query text, sandbox aggregation mode, structural checks, short production window, execution identifiers, and result shape together. That evidence makes “validated” a reproducible state instead of a comment in Slack. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see the advertiser-instance result or compare its grain with the sandbox result. The MCP retrieves both under the authorized connection. > - **Your call, before anything changes.** A chat cannot submit the short confirmation, watch its status, or widen the window when the first result is inconclusive. The Skill performs each gate in order. > - **Your judgment, running every time.** A chat may call sandbox success production validation or mistake threshold suppression for an invalid query. Atlas keeps both boundaries explicit, and the Skill requires a conclusive real-data confirmation before the full run. ## What happens next Once the short advertiser run returns a conclusive result, I save the validation as a recurring Skill and attach its evidence to the [Amazon Agent Data layer](/features/amazon-agent-flow/). The Amazon Ads MCP handles AMC execution, Atlas grounds the checks, and Skills preserve the gate order. If an ads workflow needs catalog or ASIN context, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can supply that upstream without changing what the sandbox is allowed to prove. The useful pattern is simple: use synthetic data to remove structural doubt, then spend the first production run testing reality. *Next: [turn the validated AMC query into an agent Skill](/guides/amc-agent-workflows/) with repeatable execution and run evidence.* ### Fix AMC SQL Query Performance Before Timeout URL: https://www.kuudo.com/guides/amc-sql-query-performance/ Run the slow [Amazon Marketing Cloud](/features/amc/) (AMC) query through a query-performance Skill that reads the live query context with the [Amazon Ads MCP](/features/amazon-ads-mcp/), checks each change against [Amazon Agent Atlas](/features/agent-atlas/), and returns a smaller query you can test before another long run. The answer is not one clever SQL trick. It is a fixed sequence: filter earlier, reduce rows before joins, sample exploratory work correctly, and shrink the test window until you know whether the problem is the SQL or the workload. The question that kicked this off was blunt: *"Our AMC queries either time out or run for an hour. What do we actually change?"* I gave our agent the SQL, the AMC instance, and the date range. It came back with the exact expensive operations, a rewritten query, and a validation plan instead of a generic list of database tips. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## Early, sargable filters remove work before joins begin AMC reads sources, applies `WHERE` filters, performs joins and aggregations, then applies `HAVING`. That makes early filtering the first place I look. A filter that wraps a field in a function can force the engine to evaluate far more records than a narrow, sargable predicate. Atlas retrieved this documented before-and-after pair from **Optimize AMC SQL queries** and **How to optimize AMC SQL queries**. The first query is functionally valid, but `ARRAY_CONTAINS` makes the filter non-sargable: ```sql SELECT campaign_id_string, COUNT(DISTINCT user_id) AS users FROM amazon_attributed_events_by_traffic_time WHERE ARRAY_CONTAINS(ARRAY [ '1234567', '2345678' ], campaign_id_string) AND user_id IS NOT NULL GROUP BY 1 ``` The documented correction defines the campaign scope once and joins to it: ```sql WITH campaigns (campaign_id_string) AS ( VALUES ('1234567'), ('2345678') ) SELECT a.campaign_id_string, COUNT(DISTINCT user_id) AS users FROM amazon_attributed_events_by_traffic_time a INNER JOIN campaigns c ON (a.campaign_id_string = c.campaign_id_string) WHERE user_id IS NOT NULL GROUP BY 1 ``` The Skill does not declare victory because the SQL looks cleaner. The Amazon Ads MCP runs the corrected block, the Skill verifies that the output grain is still campaign-level, and the run record shows whether it completed on the same test window. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see the live query or date range. You have to paste both into the chat, and your data is now exposed. The MCP reads the authorized query context directly. > - **Your call, before anything changes.** The shared AI can return edited text, but you must paste it into AMC and run it yourself. The Skill validates the actual correction against the live instance. > - **Your judgment, running every time.** A chat may call any CTE an optimization. Atlas grounds the change in Amazon's specific sargable-filter guidance. ## Smaller CTEs and stricter joins prevent row multiplication CTEs are useful structure, not a performance guarantee. The gain comes from reducing each intermediate result to the smallest useful grain before the next join. An unaggregated CTE can carry duplicate rows forward, and a later join can turn that extra work into wrong metrics. The agent's audit uses four checks: | Finding | Why it costs | Correction | | --- | --- | --- | | Unfiltered CTE | Carries irrelevant rows | Add early `WHERE` | | Duplicate join grain | Multiplies later metrics | Aggregate before joining | | Unneeded outer join | Preserves unused rows | Use `INNER JOIN` | | Filter-only joined table | Returns unused columns | Use `EXISTS` | Amazon's example is a useful warning: joining 10 impressions to two purchases at the wrong grain can report 20 conversions and 20 impressions. That is not merely slow SQL. It is a fast route to a confident, wrong decision. The Skill checks selected columns, grouping grain, join predicates, and whether compatible metric streams can use `UNION ALL` before it proposes a rewrite. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot compare the proposed grain with the rows your live query returns. The MCP lets the Skill test the output shape instead of guessing. > - **Your call, before anything changes.** A chat cannot run the revised joins in AMC. You remain the person copying, executing, and comparing results by hand. > - **Your judgment, running every time.** Generic SQL advice can miss AMC's documented `INNER JOIN`, `UNION ALL`, `EXISTS`, and early-aggregation checks. Atlas retrieves the relevant AMC playbooks for the audit. ## Random sampling speeds exploration only after deduplication Random sampling belongs in exploratory analysis, before you spend hours testing a full population. The order matters: define the eligible population, deduplicate to one row per user, and only then apply `random()`. Sampling event rows first gives frequently active users more chances to enter the sample. This complete Atlas-reconstructed query samples 10% of unique users, then applies the same user filter to impressions and clicks: ```sql -- Instructional Query: Join Impressions and Clicks using UNION ALL with random sampling-- WITH user_filter AS ( SELECT user_id FROM ( SELECT user_id FROM dsp_impressions WHERE campaign_id IN (111111111111) AND impressions > 0 AND user_id IS NOT NULL GROUP BY 1 ) WHERE random() <= 0.1 ), imp AS ( SELECT campaign_id, campaign, SUM(impressions) AS impressions, 0 AS clicks FROM dsp_impressions WHERE campaign_id IN (111111111111) AND user_id IN ( SELECT user_id FROM user_filter ) GROUP BY 1, 2 ), clicks AS ( SELECT campaign_id, campaign, 0 AS impressions, SUM(clicks) AS clicks FROM dsp_clicks WHERE campaign_id IN (111111111111) AND user_id IN ( SELECT user_id FROM user_filter ) GROUP BY 1, 2 ), combined AS ( SELECT campaign_id, campaign, impressions, clicks FROM imp UNION ALL SELECT campaign_id, campaign, impressions, clicks FROM clicks ) SELECT campaign_id, campaign, SUM(impressions) AS impressions, SUM(clicks) AS clicks, /* ------- Customization Instructions ------- Adding CTR calculation */ (SUM(clicks) / SUM(impressions)) AS CTR FROM combined GROUP BY 1, 2 ``` The validation rule is practical. On a 10% sample, counts and sums should land near 10% of the full run, while averages and rates should remain similar. I run both versions over the same small window first. Sampling is a diagnostic accelerator; the final decision still gets the full-population run. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot measure whether your realized sample is representative. The MCP returns both runs so the Skill can compare counts and rates. > - **Your call, before anything changes.** A chat cannot execute the sampled and full queries on the same AMC window. You must coordinate that comparison manually. > - **Your judgment, running every time.** A generic suggestion to add `random()` can sample duplicate events. Atlas supplies Amazon's rule to deduplicate users before sampling. ## A shorter window separates SQL problems from capacity limits I test a compute-heavy query on one day before asking it to scan weeks of data. If it still struggles, the Skill narrows the advertiser, campaign set, ad product, or geography. Amazon's optimization guide then recommends reducing a one-month run to two weeks or one week when the query continues to time out. That sequence produces a useful escalation instead of "AMC is slow." If the reduced query still fails, the Skill assembles the SQL ID, Instance ID, screenshots, and the query for AMC support. One documented compute-heavy use case calls out a six-hour timeout window, but the workflow treats that as context for that example, not a promise that every AMC workload gets the same limit. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot tell which slice of your workload still fails. The MCP reruns controlled windows and preserves the result. > - **Your call, before anything changes.** A chat can draft a support note, but it cannot collect the SQL ID, Instance ID, run evidence, or screenshots from AMC. > - **Your judgment, running every time.** "Try fewer rows" is not a troubleshooting plan. Atlas provides the ordered reductions and the evidence Amazon asks for when escalation is necessary. ## What happens next The output is a query-performance review: the expensive operations, the exact rewrite, the test window, the validation result, and the escalation packet if the problem remains. I run the corrected SQL through the Amazon Ads MCP, compare its grain and core rates with the original, then widen the date range one step at a time. Once the query holds, I save the audit as a recurring Skill and attach its run evidence to the [Amazon Agent Data layer](/features/amazon-agent-flow/). That turns a late-night timeout hunt into the same review every time. It also fits the broader platform: the Amazon Ads MCP handles AMC, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can supply adjacent retail context, Skills preserve the workflow, and Atlas keeps the rules current. If you are still building the query itself, start with [the first AMC SQL queries](/guides/amc-sql-first-queries/) before optimizing it. A slow AMC query becomes manageable when the agent can show what is expensive, prove the correction on live data, and preserve the reasoning for the next run. *Next in the series: validate the corrected query safely in the AMC SQL sandbox before you widen the window.* ### Automate Vendor Listing Quality at Catalog Scale URL: https://www.kuudo.com/guides/vendor-listing-quality-automation/ Yes. A [Vendor Central MCP](/features/amazon-vendor-central-mcp/) Skill can rank catalog gaps by page views, resolve each product type schema with [Amazon Agent Atlas](/features/agent-atlas/), generate compliant imagery through [Amazon Agent Iris](/features/amazon-agent-iris/) when needed, and hold validated writes for approval. Amazon already gives you three ways to change a listing. The hard part is deciding which attributes matter, what a correct value looks like, and which SKUs deserve attention first. The ranked audit appears before a write is proposed: | Priority signal | Agent finds | Agent returns | | --- | --- | --- | | Page views | Missing attribute | Ranked content fix | | Listing Quality | Image or A+ gap | Multimedia fix | | Listings issue | New SKU problem | Approval-ready plan | That distinction matters because a bulk spreadsheet solves only the upload. Someone still has to identify the right product type, resolve its recommended attributes, and provide a valid value for every row. The mechanism is not the bottleneck. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**, expressed as **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies current catalog and listing-quality context, [Skills](/features/skills/) preserve your content and brand standards, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep the write with you. ## The Listing Quality Indicator grades two pillars, and both vary by product type Amazon's own definition is precise. The Listing Quality Indicator measures how complete and correct a detail page is across **content quality**, which evaluates product data such as titles, bullet points and key attributes for correctness, completeness and policy compliance, and **multimedia quality**, which reviews image and A+ content quality and completeness. Two things follow that most catalog projects miss. First, **requirements vary by product type**, so there is no single checklist to apply across a catalog. Amazon publishes a per-product-type recommended attribute list and updates it monthly. Second, the score is explicitly a directional guide: it compares your listing to the strongest listings in similar categories and does not account for dynamic factors like offer price, fulfilment or seasonality. The `Improve listing quality` and `Enhance listings` experiences surface these opportunities per ASIN, with page views available as a prioritisation signal. They are related but not identical: `Improve listing quality` changes can take up to one and a half hours to appear, while `Enhance listings` refreshes its quality indicator within 15 minutes after you publish updates. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot open your Catalog page, so it cannot tell you which ASINs have opportunities or which have the page views to justify going first. > - **Your judgment, running every time.** The shared AI may describe common listing attributes, but it does not automatically retrieve the current recommended list for each of your product types when that list changes. Atlas and the Skill make that schema check part of every run. > - **Your call, before anything changes.** The shared AI cannot read the Listing Quality Indicator, so it cannot tell you whether a change moved the score. > - The Vendor Central MCP supplies the live indicator and ASIN priorities; Atlas and the Skill apply the product-type guidance consistently. ## Amazon gives you three ways to write the change, and none of them decide what to write This is the claim worth internalising before buying anything. The three documented paths are all mechanisms: | Path | What it costs | What it still needs from you | | --- | --- | --- | | Improve listing quality | Up to 90 minutes | The values, one product at a time | | Bulk edit and upload | Upload status polling | The values, for every row | | Listings APIs | A five-step build | The values, plus an application to maintain | The API path is the one people underestimate. Amazon states it plainly: register a developer profile, create the required AWS resources and Selling Partner API (SP-API) application, develop the functionality, test the application, then release and maintain. So the honest options are click forever or fund an engineering project. An agent on the Vendor Central MCP is a third: the same APIs, your account, no application of your own to maintain. Kuudo also exposes shared SP-API operations through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) for workflows that span vendor and seller accounts. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI cannot submit a feed, call an API or upload a spreadsheet. Whichever of the three paths you pick, you are still the one operating it. > - **Your account, as it is right now.** The shared AI cannot check whether an upload reached Completed, so it cannot tell you a change actually landed. > - **Your judgment, running every time.** Ask the shared AI to automate this and it will describe building the SP-API integration, because that is the public answer, not the cheapest one for you. ## Product Type Definitions turns "recommended" into a schema the agent can satisfy The foundational API is the one nobody markets. Product Type Definitions lets you search for available product types by marketplace and keyword, then retrieve the definitions and schemas describing the attributes and data requirements for that type. The important property is reuse: those same schemas back the Listings Items API, the Catalog Items API and the bulk `JSON_LISTINGS_FEED`. Validating against the schema catches attribute and data-requirement errors before submission. It does not guarantee publication, because Amazon can still surface processing, contribution, or policy issues afterward. That is the difference between an audit that merely proposes edits and one that reduces preventable submission failures. The read side pairs with it. The Catalog Items API returns reconciled information for an ASIN, equivalent to what the detail page displays, including images, product type and item identifiers. Read the current state, resolve the schema, then compute the gap. The cold-open table is the artifact. It ranks the gaps using the signal Amazon itself offers for prioritisation, and every content recommendation is tied to the product type schema that produced it. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI cannot retrieve your product type definitions, so it guesses attribute names that can fail validation. > - **Your account, as it is right now.** The shared AI cannot read the current detail page state, so it cannot compute a gap. It can only describe what good listings generally contain. > - **Your call, before anything changes.** Pasting a catalog export into a general chat to work around that can also expose product and cost data outside your approved catalog workflow. > - The Vendor Central MCP retrieves the current state and definitions, and the Skill validates the proposed gap before any write. ## Image Manager moves files; it does not know which of Amazon's rules apply to this SKU Multimedia is one of the Listing Quality Indicator's two pillars, so a catalog project that only fills text attributes leaves image and A+ gaps untouched. But `Manage Images` is a tool for a manual process. Image Manager lets you upload individual images, delete them, copy a media set to sibling products, and preview whether they are live. It moves and organises files. Everything that decides whether the file was the right one happens somewhere else. Amazon is direct about the consequence: uploading an image does not guarantee it will be displayed, because Amazon selects and arranges images from multiple sellers, and the way to improve your odds is to comply with the product image requirements. Compliance is evaluated after you upload, not before. So the manual loop is produce the file elsewhere, upload it, wait **up to 24 hours** for display, and if it was non-compliant, learn that from `Image suppression` and start over. The troubleshooting path even depends on which submission method you used. The harder problem is that there is no single rulebook to learn. Image requirements in the `amazon_vendors` corpus are category-specific: `Multipack imaging standards` requires the main image to show the total quantity delivered and the product in its packaging, with the title carrying that count as well. `Technical image file requirements` accept RGB and mark CMYK as not preferred, with instructions to convert it to RGB. Replacing a suppressed main image means uploading with the **MAIN variant code** rather than deleting the old one, because every ASIN requires a main image. The sharpest example is the newest one. `How to tag media that contains an AI-generated person` exists because some jurisdictions require disclosure, and Amazon requires the media to be tagged **before upload**. Its documented procedure is to open the file in Preview on macOS, press Cmd+I, and add a keyword in the Keywords tab, or on Windows to use the Tags field in File Explorer, with an explicit warning that Subject and Tags write to different metadata locations. That is the state of the art for compliance on AI-generated imagery: a per-file manual metadata edit, done correctly, every time, across a catalog. An agent inverts the order of all of this. It reads the current image state through the Catalog Items API, resolves which of those rule sets apply to this product type from Atlas *before* anything is produced, generates through [Amazon Agent Iris](/features/amazon-agent-iris/) against live product data rather than a stock template, applies the disclosure metadata where the rule is triggered, and writes through the Listings Items API with the correct variant code, validated against the product type schema. The multipack case shows why that ordering matters: the fix is an image and a title together, and doing one without the other leaves the listing non-compliant in a new way. The part that is yours rather than Amazon's matters just as much. Amazon's requirements are public and identical for every vendor. Your brand rules are not. Atlas grounds the Amazon rules; your private style guide supplies the palette, framing, voice, and tone. The Skill enforces both in the same pass, so compliant and on-brand stop being separate review cycles. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI cannot open Image Manager, tag IPTC metadata, or submit with the MAIN variant code. It describes the image you should make and leaves you to produce, tag and upload it. > - **Your judgment, running every time.** The shared AI can repeat public image guidance when prompted, but it does not reliably apply the current multipack, color-mode, suppression, and AI-disclosure rules alongside your private brand guide on every asset. Iris, Atlas, and the Skill run those checks together. > - **Your account, as it is right now.** The shared AI cannot see your current main image or whether it was suppressed, so it cannot tell you the image is the problem rather than the copy. Pasting a catalog export into a general chat can also move product data outside your approved catalog workflow. ## What happens next The audit is worth running once. The loop is worth keeping. The Notifications API sends `LISTINGS_ITEM_ISSUES_CHANGE` whenever the issues attached to a SKU you own change, and those issues are exactly the ones that can push a listing to inactive or search suppressed. Subscribed through the [Amazon Agent Data layer](/features/amazon-agent-flow/), that notification is the trigger: a SKU develops a problem, the agent re-resolves the schema, proposes the fix, and waits for approval before writing. Saved as a reusable [Skill](/features/skills/), the same loop runs against the whole catalog on a schedule and composes with the vendor reporting work you already do, such as the [ARA report and metric glossary](/guides/vendor-ara-reports-metric-glossary/). Read and written through the Vendor Central MCP, it sits alongside the [Amazon Ads MCP](/features/amazon-ads-mcp/) when the question turns to what the traffic did once the detail page was complete. One [Amazon Agent Data layer](/features/amazon-agent-flow/), grounded by Atlas throughout. The mechanism was never the hard part. Deciding what to write, per product type, across a catalog nobody has time to read, is the hard part, and it is the part that finally has an operator. *Next in this series: the same catalog-scale review loop applied to A+ content.* ### Fix Catalog Identity Before You Write A+ Content URL: https://www.kuudo.com/guides/vendor-catalog-data-preflight/ When an A+ submission bounces in Vendor Central and the error names the ASIN rather than the module, you are not looking at a content problem. You are looking at an identity problem: the brand string, the product ID, or the product type. [Our agent](/features/ai-clients/) audits all three across the catalog through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/) on the `amazon_vendors` corpus, and it runs before a single module is written. The ordering is the whole point. `A+ content FAQ & troubleshooting` traces the most common A+ error back to inaccurate listing data on the ASIN. So a team that writes content first spends seven business days finding out the listing was wrong, fixes the listing, and spends another seven finding out whether the content was. Two review cycles for one problem, and the first one told you the wrong thing. [ChatGPT, Claude, Perplexity, and Copilot](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: your data, your rules, and the way your business operates. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies current catalog and Brand Registry context, [Skills](/features/skills/) preserve your preflight rules, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep catalog changes with you. ## Brand verification fails on characters you cannot see `Brand guidelines` is precise about what brand verification checks. When a brand name is updated, all attributes are verified, and the discrepancies to remove are exactly the ones that survive a visual review: the brand name is **case sensitive and must be entered exactly as it appears in the Brand Registry application**, and special characters such as the trademark and registered symbols have to be removed. That is why this failure mode is so durable. `Acme™` and `Acme` look like the same brand to the person checking the spreadsheet, and a trailing space is invisible in every tool anyone uses to look at it. Across six hundred ASINs, a hand check is not a plan. The correction path matters just as much, and it is the reason to be honest about what automation does here. Once a brand has been submitted, you can request an update or deletion only by contacting Amazon: Contact us, then **Manage my catalog**, then **Item detail edit**, selecting **Other product information** and supplying the ASIN and vendor code. For multiple ASINs, Amazon points you at the **Item maintenance form**. That is a request workflow, not a write. So the agent's job on this claim is not to silently repair a brand string. It is to find every ASIN whose brand attribute deviates from the Brand Registry name, character by character, and produce the populated request with the affected ASIN list attached. Detection at catalog scale, submission by a human. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot read your ASIN's brand attribute or your Brand Registry record, so it cannot compare them at all, let alone character by character. > - **Your judgment, running every time.** It does not know that the trademark symbol has to be stripped, because that rule lives in Amazon's brand guidelines rather than in general branding advice. > - **Your call, before anything changes.** It cannot open the Item detail edit request or fill the Item maintenance form, so even a correct list of ASINs leaves the work undone. ## Product IDs are unique, so most catalog errors are identity collisions The barcode errors all come from one rule: a product ID, whether you call it a GTIN, a UPC or an EAN, can only be linked to one ASIN at a time. What differs is who else is holding it and why. | Error | What it means | Why it matters | | --- | --- | --- | | 100873 | The barcode is already assigned to a different ASIN | Needs a Support review if the matched product is genuinely different | | 100980 | The barcode is linked to an ASIN that seems to be a different product | Dispute, or delete and recreate with the correct barcode | | 100992 | The barcode does not belong to your brand | The message carries a removal date for the listing | `Error 100992` deserves separate treatment because it is the only one with a clock on it. The message tells you the barcode on the listing is incorrect and states the date by which Amazon will remove the listing, so it belongs at the top of a triage queue rather than in a catalog-hygiene backlog. Resolving it means supplying Selling Partner Support with the brand name, the current 14-digit barcode, and the replacement GTIN if one is needed. That list is the useful part for automation. The evidence Support asks for is structured and knowable, which means an agent can assemble it for every affected ASIN at once rather than a person rebuilding it per ticket. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see which of your barcodes collide with another ASIN, so it cannot tell you the scope of the problem. > - **Your call, before anything changes.** It cannot raise the dispute or supply the 14-digit barcode and replacement GTIN, so the ticket is still assembled by hand, one at a time. > - **Your judgment, running every time.** Ask it about a numeric Amazon error and it will improvise, because these codes and their remedies are not in general training data. ## Product type has to agree with the product, and everything downstream depends on it `Error 101067` is the one that looks cosmetic and is not. It fires when the product type attribute disagrees with the rest of the details you supplied. Amazon's own example is entering `Shirt` as the product type on a listing whose title describes sunglasses. The error itself is easy to read. The consequence is the part worth planning around: product type is what determines which attributes Amazon recommends for a listing, and the schema that a valid write has to satisfy. A listing filed under the wrong product type will therefore be audited against the wrong attribute set, pass that audit, and still be wrong. Fixing product type after an attribute audit means running the attribute audit again. The bulk path has its own version of this. `Troubleshoot errors in products uploaded by bulk spreadsheet` documents that Product Type column values are pre-populated when the spreadsheet is generated, and editing them produces an error telling you to use the Vendor Central website instead. Other rows in that table follow the same shape: a value that was not taken from the provided drop-down is rejected, and a field marked non-editable has to be returned to its original value. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** It has no way to know your product type taxonomy, so it cannot tell you that a title and a product type disagree. > - **Your account, as it is right now.** It cannot read the spreadsheet Amazon generated for you, so it cannot see which columns are pre-populated or non-editable. > - **Your call, before anything changes.** It cannot regenerate the spreadsheet or resubmit it, so a diagnosis is where its contribution ends. > - The Selling Partner MCP supplies the catalog and spreadsheet state, while the Skill preserves the product-type dependency before downstream work. ## One loop, in order: identity, attributes, then content Put together, the three surfaces have a strict dependency order, and running them out of order is what turns one problem into three review cycles. **Identity first.** Brand string against Brand Registry, product IDs against collisions and brand ownership, product type against the product. Nothing downstream is trustworthy until these pass, and `Error 100992` in particular is time-bound. **Attributes second.** Only once product type is correct does the recommended-attribute set mean anything, because product type is what selects it. **Content last.** A+ modules, imagery, brand voice. This is the only stage where the seven-business-day review clock applies, which is precisely why it should be the stage with the fewest unknowns left in it. An agent runs that as one chained workflow rather than three projects owned by different people. It reads reconciled ASIN state through the Catalog Items API, resolves schemas through Product Type Definitions, writes what is writable through the Listings Items API, and splits out the cases that need a Support request with the evidence already assembled. Findings that are time-bound get ranked ahead of findings that are merely untidy. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** It cannot chain anything. Each stage is a separate conversation, and carrying state between them is your job. > - **Your account, as it is right now.** It cannot tell you whether stage one is actually clean, so you cannot know when it is safe to start stage two. > - **Your judgment, running every time.** It has no reason to know the ordering matters at all, and will happily help you write A+ copy for an ASIN that is about to be removed. ## What happens next The artifact is a ranked identity report: every ASIN whose brand string deviates from Brand Registry, every barcode collision with the error code that will fire, every product type that disagrees with its own listing, split into what the agent can write and what needs a Support request with the evidence attached. Saved as a reusable [Skill](/features/skills/), it runs ahead of the A+ pre-flight and the listing quality audit rather than alongside them, and re-runs on the listings-issue notifications the [Amazon Agent Data layer](/features/amazon-agent-flow/) subscribes to, so a barcode collision introduced next month surfaces the same week rather than at the next content push. Imagery regenerated through [Amazon Agent Iris](/features/amazon-agent-iris/) and reporting through the [Vendor Central MCP](/features/amazon-vendor-central-mcp/) sit on the same layer, alongside the [Amazon Ads MCP](/features/amazon-ads-mcp/) when the question becomes what the fixed listings did to traffic. The reporting side of that picture is in the [ARA report and metric glossary](/guides/vendor-ara-reports-metric-glossary/). Seven business days is a reasonable price for a review of your content. It is an unreasonable price for finding out a barcode was wrong. *Next in this series: once identity is clean, deciding which attributes actually matter for each product type, and why the answer changes per type.* ### Your First AMC Queries: Tables, Filters, Dates URL: https://www.kuudo.com/guides/amc-sql-first-queries/ Useful Amazon Marketing Cloud (AMC) SQL query examples begin with four choices: the event table, campaign identifier, ad product type, and date window. Get any one wrong and a syntactically valid query can return zero rows or answer a different question. This guide turns those choices into two small first-query patterns you can adapt. Every [Amazon Marketing Cloud](/features/amc/) (AMC) query starts with the same four decisions: which table holds the event you are asking about, which of the three campaign identifiers scopes it to your campaigns, which `ad_product_type` you actually mean, and which date window applies. Get those four right and the query runs on the first submit. [Our agent](/features/ai-clients/) resolves all four through the [Amazon Ads MCP](/features/amazon-ads-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/), before it writes a line. The reason this is a guide and not a footnote is that all four fail quietly. A colleague sent me a query last month that ran clean and returned nothing. She had pasted campaign IDs out of the ads console and matched them against `campaign_id`. The console shows `campaign_id_string`. Same campaigns, different column, zero rows, no error. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## AMC SQL query examples start with the event table The instinct is to look for a table with the number you need. AMC is organized the other way: tables hold **events**, and your metric is an aggregate over them. `sponsored_ads_traffic` contains traffic events for all sponsored ads products, so Sponsored Products, Sponsored Brands, Sponsored Display and Sponsored Television all land there together. Amazon DSP (demand-side platform) traffic is separate, in `dsp_impressions`. Ad-attributed conversions live in the attributed events tables, `amazon_attributed_events_by_traffic_time` and `amazon_attributed_events_by_conversion_time`, which differ in whether a conversion is dated to the traffic event or to the conversion itself. The `conversions` table holds AMC conversion events more broadly, and counts a conversion as ad-attributed when a traffic event was served in the 28-day period before it. One rule travels with the attributed tables and costs people real numbers: wait two weeks past the end of a campaign before trusting the totals. The 14-day attribution window is still open before that, so conversions are still being attributed while you are reading your report. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot list the tables in your instance, so it cannot tell you whether the events you want are in `sponsored_ads_traffic` or `dsp_impressions`. > - **Your judgment, running every time.** It has no reason to know about the 14-day attribution wait, so it will happily hand you a query to run the morning after a campaign ends. > - **Your call, before anything changes.** It cannot run the query to see that your conversion totals are still climbing day over day, which is the symptom that would have told you. > - The Amazon Ads MCP supplies the instance tables and results, while Atlas and the Skill preserve the attribution wait. ## Three campaign identifiers, and the wrong one returns zero rows Atlas retrieves the `How to identify your campaigns and campaign IDs` playbook for this, and the first thing it settles is that AMC exposes the same campaign three ways, which are not interchangeable: | Column | What it is | Where you see it | | --- | --- | --- | | `campaign` | Campaign name | Ads console; Order name in DSP | | `campaign_id_string` | Campaign ID, STRING | Ads console; Order ID in DSP | | `campaign_id` | Campaign ID, LONG | Sponsored ads API responses | Use `campaign` when you have the name, `campaign_id_string` when you copied an ID out of the console, and `campaign_id` when the ID came from an API. For Amazon DSP campaigns `campaign_id` and `campaign_id_string` carry the same value, which is exactly why the mistake survives a spot check against a DSP campaign and then fails on sponsored ads. A first query worth running is the one that just lists what you have: ```sql SELECT campaign, campaign_id_string, ad_product_type FROM amazon_attributed_events_by_traffic_time GROUP BY 1, 2, 3 ``` > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI reaches for `campaign_id` because that is the conventional name for an ID column. In AMC that is the API-format ID, and console IDs will not match it. > - **Your account, as it is right now.** It cannot check whether the IDs you are holding came from the console or an API, so it cannot catch the mismatch. > - **Your call, before anything changes.** The query returns zero rows when you run it by hand, which reads like "no data" rather than "wrong column." ## `ad_product_type` separates the sponsored ads families, but not the formats Because `sponsored_ads_traffic` pools every sponsored ads product, `ad_product_type` is how you narrow it. The `How to filter by ad product type` playbook lists the four accepted values: `sponsored_products`, `sponsored_brands`, `sponsored_display` and `sponsored_television`. ```sql -- Instructional Query: Ad Product Type - Sponsored Products SELECT campaign, campaign_id_string, ad_product_type, SUM(impressions) AS impressions FROM sponsored_ads_traffic WHERE ad_product_type = 'sponsored_products' GROUP BY 1, 2, 3 ``` The limit is worth knowing before it bites: `ad_product_type` distinguishes product families, not ad formats. Sponsored Brands covers both retail formats and Sponsored Brands Video, and they share one value. To isolate SBV you use the video viewership metrics on the same table, the columns beginning `video_` plus `five_sec_views`, which populate only for video ads and read NULL for everything else. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI for Sponsored Brands Video and it will invent a filter value like `'sponsored_brands_video'`. There is no such value, and the query fails or silently returns the whole Sponsored Brands set. > - **Your account, as it is right now.** It cannot look at your rows to notice that `video_` columns are NULL on most of them, which is the signal that separates the formats. > - **Your call, before anything changes.** You find out when you paste the query in and the numbers look too big. > - Atlas supplies the accepted product values, and the Amazon Ads MCP lets the Skill test them against the live rows. ## Dates are a filter you write, not a setting you pick There is no date picker in the query. The window is part of the SQL, and it takes one of two shapes depending on what you are asking. For a calendar comparison, cast both sides so you are comparing dates rather than strings: ```sql SELECT DISTINCT cast(campaign_start_date AS Date) FROM amazon_attributed_events_by_conversion_time WHERE cast(campaign_start_date AS Date) > cast('2026-01-31' AS Date) ``` For a custom conversion window, measure the gap between the traffic event and the conversion event directly. This one counts purchases that landed within nine days of the ad exposure, which is how you ask a question the standard attribution windows do not answer: ```sql -- Instructional Query: How to Filter by Custom Date -- SELECT campaign, sum(total_purchases) AS total_orders_9d FROM amazon_attributed_events_by_traffic_time WHERE SECONDS_BETWEEN (traffic_event_dt_utc, conversion_event_dt_utc) <= 60 * 60 * 24 * 9 GROUP BY campaign ``` The arithmetic is deliberate. Writing the window as `60 * 60 * 24 * 9` rather than `777600` keeps the intent legible to the next person, and to you in three months. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** It reaches for `GETDATE()` or `CURRENT_DATE` to build a relative window. `GETDATE()` is unsupported in AMC, so the query is rejected on submit. > - **Your account, as it is right now.** It cannot see your campaign start dates, so a window it invents may not overlap your traffic at all. > - **Your call, before anything changes.** And if you paste a sample of your result rows back into the chat to check the window looks right, your campaign data is now sitting in a place it should not be. > - The Amazon Ads MCP supplies the campaign dates, while the Skill applies an Atlas-grounded window and validates it before execution. ## What happens next Once these four decisions are resolved, the query is mechanical, which is exactly why it is worth handing over. The agent reads the table list and the identifier rules from Atlas, matches the IDs you actually have to the column that holds them, applies the `ad_product_type` filter for the family you named, and writes the date window in the shape your question needs. It runs the workflow through the Amazon Ads MCP and returns the result rather than the SQL. Saved as a reusable [Skill](/features/skills/), that becomes the front door for every ad-hoc AMC question your team asks, and it composes with the workflows you already run through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) and the rest of the [Amazon Agent Data layer](/features/amazon-agent-flow/). The dialect rules from [part one](/guides/amc-sql-introduction/) still apply on top: no `SELECT *`, no `ORDER BY`, and thresholds that quietly drop thin rows. Four decisions, made in order, and the blank editor stops being intimidating. The queries that fail after this point fail for a different reason: they are too expensive to finish. *Next in this series: why AMC queries time out, and the two levers that fix it.* ### AMC SQL: Where It Stops Behaving Like SQL URL: https://www.kuudo.com/guides/amc-sql-introduction/ Amazon Marketing Cloud (AMC) SQL is a documented, privacy-constrained dialect rather than unrestricted warehouse SQL. Queries cannot rely on patterns such as SELECT *, ORDER BY, or LIMIT, and valid-looking output can disappear below aggregation thresholds. This guide explains the checks an agent should perform before it writes or runs the first query. [Amazon Marketing Cloud](/docs/guides/amazon-marketing-cloud/) (AMC) SQL is a restricted dialect running inside a privacy clean room, so two things are true at once: the query you already know how to write will be rejected, and the query that gets accepted can still return nothing. `SELECT *`, `ORDER BY`, `LIMIT`, `RIGHT JOIN` and `GETDATE()` are all unsupported, and any output row describing fewer than 100 distinct shoppers gets dropped before you see it. [Our agent](/features/ai-clients/) checks both rule sets through the [Amazon Ads MCP](/features/amazon-ads-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/), before it writes a line. The [Amazon Marketing Cloud software](/features/amc/) keeps those dialect checks, privacy rules, and run artifacts together when the query becomes a repeatable operating workflow. I learned the second half the hard way. An analyst pinged me with a query and a shrug: *"It runs. It returns four rows. There should be ninety."* Nothing was broken. She had grouped purchases by `postal_code`, and AMC had silently withheld every postal code with fewer than 100 shoppers behind it. The SQL was correct, the result was censored, and nothing in the output said so. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## AMC SQL is a documented dialect, not the SQL you already know The first thing the agent does is stop treating this as generic SQL. Atlas retrieves the `Introduction to AMC SQL` playbook and the `AMC SQL functions` catalog from the `amazon_ads` corpus, which together define the grammar, the data types, the logical operators, and the exact function list AMC accepts. What is supported is narrower than it looks: `SELECT FROM `, `WHERE`, `GROUP BY`, aggregates like `SUM`, `AVG`, `COUNT` and `COUNT DISTINCT`, conditionals like `CASE`, `IF`, `IN` and `NOT IN`. What is not supported is documented just as precisely in `Limitations and unsupported functions`: no `SELECT *`, no `LIMIT`, no `RIGHT JOIN`, no `GETDATE()`. So a working query names its columns and nothing more: ```sql SELECT campaign_id, campaign, campaign_start_date, campaign_end_date, SUM(impressions) AS impressions FROM dsp_impressions GROUP BY 1, 2, 3, 4 ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot list the columns in your `dsp_impressions` table, so it guesses column names from public documentation and hands you ones your instance may not expose. > - **Your judgment, running every time.** It writes the SQL it was trained on. `SELECT *` is the single most natural way to explore an unfamiliar table, and it is rejected by AMC every time. > - **Your call, before anything changes.** It cannot submit the query to find out. You paste it into the query editor, it fails, and you bring the error back to the chat to guess again. ## A valid query can return nothing, and AMC will not tell you This is the rule that costs people days. AMC assigns every column an aggregation threshold, and rows that fall below it are removed from your output rather than flagged. The classifications run NONE, LOW, MEDIUM, HIGH, VERY_HIGH and INTERNAL. In practice you need **2 distinct users** behind a row for a LOW column and **100 distinct users** for a MEDIUM or HIGH one. `postal_code` is a HIGH column, which makes it the clearest demonstration. This is the query my analyst ran: ```sql SELECT postal_code, SUM(purchases) AS purchases, COUNT(DISTINCT user_id) AS customers FROM amazon_attributed_events_by_conversion_time GROUP BY 1 ``` In Amazon's own worked example of this pattern, 91 rows come back complete because each postal code had at least 100 users behind it. Four more rows come back with the `postal_code` value blank and the purchases still showing, because those postal codes had fewer than 100 shoppers. Nothing errors. The rows just quietly stop identifying themselves. The agent knows the fix before you hit it, because the `Data aggregation thresholds in AMC` playbook lists the remedies: extend the time window, broaden a restrictive filter, or group by a less granular dimension. A week instead of a day. A region instead of a postal code. More distinct users per row, more rows that clear the floor. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot know how many shoppers sit behind your postal codes, so it cannot tell you this query will come back mostly blank for your account. > - **Your call, before anything changes.** It cannot run the query and inspect the result, so the filtering is invisible to it. It will describe your four rows as your answer. > - **Your judgment, running every time.** Threshold suppression has no analogue in normal SQL. And if you paste the returned rows back into the chat for interpretation, your customer data is now exposed in a place it should not be. ## `user_id` is the column you build on and never select `user_id` is classified VERY_HIGH, the strictest level. Values from a VERY_HIGH column can never appear in workflow output, and no threshold will reveal them, because a single row would expose one shopper's events. That does not make the column unusable. You can join on it, filter on it relationally, and aggregate it, which is why `COUNT(DISTINCT user_id)` appears in almost every AMC query worth writing, including the threshold example above. What you cannot do is put it in the final `SELECT`, and you cannot filter it against literal values. `WHERE user_id IN (111,222)` is rejected, because picking specific IDs out of a clean room is precisely what the classification exists to prevent. The distinction is small in syntax and total in effect: `user_id` is how you count customers, never how you list them. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** In every other SQL dialect the shared AI has seen, an ID column is selectable. It will hand you `SELECT user_id` without hesitation, and AMC will refuse it. > - **Your account, as it is right now.** It cannot check a column's classification, so it cannot warn you which of your dimensions are LOW, HIGH or VERY_HIGH before you build a query on one. > - **Your call, before anything changes.** The rejection arrives when you submit it by hand, at the end of your work, not while you are writing. > - Atlas supplies the classification rule, and the Amazon Ads MCP lets the Skill validate the query before it becomes a manual failure. ## Sorting and row limits happen after the query, not inside it `ORDER BY` is not a supported top-level clause in AMC, and neither is `LIMIT`. This surprises people more than the threshold rules, because sorting feels like part of asking the question rather than part of reading the answer. There is exactly one place `ORDER BY` is legal: inside a `PARTITION BY` clause, where it orders rows within a window function rather than the result set. For everything else, you sort the downloaded output file. Amazon's own instructional queries carry that note inline as a pro tip, which is a fair signal of how often it trips people up. The practical consequence for an agent workflow is that ranking is a post-processing step, not a query clause. The query returns the aggregate; the [Amazon Agent Data layer](/features/amazon-agent-flow/) sorts, formats and routes it. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** "Top 10 campaigns by impressions" is a sentence that produces `ORDER BY ... LIMIT 10` from any chat assistant. Both clauses are unsupported, so the query fails on submit. > - **Your call, before anything changes.** It cannot sort the result for you either, because it never receives one. You export the file and sort it yourself. > - **Your account, as it is right now.** It cannot tell you whether your ranking question needs a window function or a post-export sort, because it cannot see the shape of what comes back. ## What happens next Once the dialect rules and the threshold rules are both loaded from Atlas, writing AMC SQL stops being trial and error and becomes a check-then-write loop. The agent reads the function catalog, checks the classification of every column you want to group by, warns you when a grain will not clear 100 users, and only then composes the query. It runs the workflow through the Amazon Ads MCP and returns the result rather than the SQL, so the failure modes above never reach you. That loop is worth saving. Turned into a reusable [Skill](/features/skills/) it becomes the front door to every AMC question your team asks, and it composes with the workflows you already run through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) and the rest of the Amazon Agent Data layer. The same pattern is what turns a one-off analysis into something scheduled, as in the [AMC agent workflows guide](/guides/amc-agent-workflows/). The dialect is small enough to learn in an afternoon. The thresholds are what quietly cost you a week, and they are invisible in the output. *Next in this series: your first real AMC queries, where the tables live and how to scope one to your own campaigns.* ### Launch a DSP Campaign Without Half-Built Entities URL: https://www.kuudo.com/guides/amazon-ads-create-dsp-campaign/ Launching a DSP (demand-side platform) Performance+ or Brand+ campaign end to end is one ordered programmatic write sequence: every write is read back before the next step depends on it, every entity is created paused, and activation runs parent-first. Our **create-DSP-campaign Skill** runs that sequence through the [Amazon Ads MCP](/features/amazon-ads-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/) where a retrieved rule decides what a step is even allowed to send, and it confirms real delivery in [Amazon Marketing Cloud](/features/amc/)'s DSP traffic tables rather than trusting a create response. The ask arrived as a Slack message on a Thursday: *"Can you spin up a Performance+ campaign for the Q4 ASINs? Display and streaming TV, five thousand dollars, starts Monday."* Straightforward request. The last time someone ran it by hand we ended up with a campaign that existed, ad groups that did not, and an activation call returning NOT_FOUND on an ad group the API had confirmed creating seconds earlier. Half-built and live is worse than not built at all, because the spend starts before anyone notices the gap. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies live campaign state and authorized actions, [Skills](/features/skills/) enforce your decision rules on every run, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep material changes in your hands. ## Create every entity paused and read each write back before anything depends on it Nothing in this workflow is born live. The campaign is created with `state: "PAUSED"`, every ad group is created paused, and activation is a deliberate final step. That ordering is what makes a failure halfway through recoverable instead of expensive. The budget lives on the flight, not the campaign. There is no campaign-level budget field, so the single budget, start date, and end date the operator gave us map to one flight inside the campaign: ```json { "Amazon-Ads-AccountId": "", "campaigns": [ { "adProduct": "AMAZON_DSP", "name": "DSP|PB|Campaign 2026-08-20_14-02-11", "countries": ["US"], "state": "PAUSED", "flights": [ { "startDateTime": "2026-08-24T00:00:00Z", "endDateTime": "2027-08-24T00:00:00Z", "budget": { "budgetType": "MONETARY", "budgetValue": { "monetaryBudgetValue": { "monetaryBudget": { "value": "5000" } } } } } ], "optimizations": { "bidSettings": { "bidStrategy": "SPEND_BUDGET_IN_FULL" }, "goalSettings": { "kpi": "ROAS" }, "primaryInventoryTypes": ["DISPLAY", "VIDEO_STV"] } } ] } ``` Every datetime carries the `Z` suffix because the API rejects naive datetimes outright. Then the part that turns this from racy into deterministic: the Skill does not move on. It waits 100 milliseconds, queries the campaign it just created, and retries up to three times at one-second intervals if the record is not visible yet. The Amazon Ads API v1 makes no read-after-write guarantee, so a create can return successfully and the very next call can fail to find what it created. Worst case this adds about 3.1 seconds per step, which is a fair trade against a half-built campaign. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot query your account to find the DSP advertiser ID that every later call depends on. It will confidently leave you a `` placeholder, and you get to go find the real value yourself. > - **Your call, before anything changes.** The shared AI cannot create the campaign or read it back afterwards. If you paste its payload into an API client by hand, you also inherit the verification step, which means you are the one retrying on an eventually consistent API at the exact moment you thought you were done. > - **Your judgment, running every time.** Ask where the budget goes and you will likely be told to put it on the campaign. There is no campaign-level budget field, so Amazon rejects that shape and you debug a payload that was wrong before you sent it. ## Zero eligible tactics is a goal-KPI mismatch, not an outage Once the campaign exists, ASIN conversion tracking gets attached: between 1 and 2,000 products per call, with a campaign ceiling of 500,000 products. Each entry carries the product ID, a domain derived as `AMAZON_` plus the country code, and a product association of `FEATURED`. That step has to land before the next one is meaningful, and it gets its own verification read for a specific reason. Eligible tactics are computed asynchronously, and the recomputation has its own propagation lag on top of the products landing. So the Skill verifies the products are present, then re-queries the campaign for `eligibleAutomatedTargetingTactics`, and retries three times at one-second intervals if the list comes back empty. When the list is still empty after retries, the cause is almost never an outage. It is that the campaign's goal KPI cannot produce the tactic you asked for: | Tactic | Compatible KPIs | Goal | |---|---|---| | CUSTOMER_ACQUISITION (P+) | ROAS | Conversions | | REMARKETING (P+) | ROAS | Conversions | | RETENTION (P+) | ROAS | Conversions | | MAXIMIZE_PERFORMANCE (P+) | COST_PER_DETAIL_PAGE_VIEW, DETAIL_PAGE_VIEW_RATE | Conversions | | PROSPECTING (B+) | REACH, FREQUENCY_AVERAGE, COST_PER_VIDEO_COMPLETION, VIDEO_COMPLETION_RATE | Awareness | ROAS there is return on ad spend, and the KPI is set back at campaign creation, which is why this failure surfaces two steps after the decision that caused it. Ask for Brand+ prospecting on a campaign built with a ROAS goal and the eligible list is empty, correctly, forever. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI why your eligible tactics list is empty and you get generic API troubleshooting: check your credentials, check your permissions, try again later. The actual answer is a compatibility table it has never seen. > - **Your account, as it is right now.** The shared AI cannot see that your campaign was created with a ROAS goal while you are asking for PROSPECTING, because it cannot read the campaign. You would have to know to paste that detail in, which means knowing the answer already. > - **Your call, before anything changes.** A chat cannot retry the eligibility query for you, so it cannot tell the difference between propagation lag and a permanent mismatch. Those two look identical in a single response and need opposite fixes. ## The ad group renames the inventory type the campaign just used This is the single most common rejection in the whole workflow, and it is pure API naming inconsistency rather than anything conceptual. The same inventory type has one spelling at the campaign level and another at the ad-group level: | Intent | Campaign field | Ad group field | |---|---|---| | Display | DISPLAY | DISPLAY | | Streaming TV | VIDEO_STV | STREAMING_TV | | Online video | VIDEO_OLV | ONLINE_VIDEO | | Live events | LIVE_EVENTS | LIVE_EVENTS | | Audio | AUDIO | AUDIO | The eligible-tactics response hands back campaign-level names, so the value you receive is not the value you send one call later. The Skill translates on the way through, which is exactly the kind of mechanical rule that should live in a workflow rather than in someone's memory. The second trap is the field allowlist. A Performance+ or Brand+ tactic ad group accepts six fields and nothing else: ```json { "Amazon-Ads-AccountId": "", "adGroups": [ { "adProduct": "AMAZON_DSP", "campaignId": "", "name": "DSP|PB|STREAMING_TV|CUSTOMER_ACQUISITION 2026-08-20_14-02-11", "state": "PAUSED", "inventoryType": "STREAMING_TV", "targetingSettings": { "automatedTargetingTactic": "CUSTOMER_ACQUISITION" } } ] } ``` Bid, budgets, pacing, optimization, start and end times, creative rotation, viewability: all of them are auto-managed for tactic ad groups, and all of them are rejected if you send them. When this returns `INVALID_ARGUMENT`, the fix is to strip fields, not to correct values. That distinction saves a long debugging session, because the error text reads like a validation failure on the values you did send. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI to write the ad-group payload and it will helpfully include a bid and a budget, because that is what ad groups take everywhere else in advertising. Send that by hand and Amazon rejects the whole call. > - **Your account, as it is right now.** The shared AI cannot read the eligible-tactics response, so it cannot know which inventory types came back for your campaign or that they need translating before you send them onward. > - **Your call, before anything changes.** A chat cannot create the ad group, so it cannot see the rejection and correct itself. You paste, you get INVALID_ARGUMENT, you paste the error back, and you iterate by hand against an API that is telling you something specific. ## Activate parent-first, or the ad groups fail against a campaign Amazon cannot see Activation is two moves in a fixed order: the campaign flips to `ENABLED` first, then each ad group flips to `ENABLED`. Never the reverse, and never in parallel. If the campaign fails to activate, the Skill stops and does not touch the ad groups at all. Activating children under a parent that is still paused produces either an outright failure or, worse, a confusing half-state where some entities are live and the thing that governs them is not. Reporting what did succeed is more useful than pushing on and leaving someone to reconstruct the order afterwards. This is also where the verification reads from every earlier step pay off. Activation immediately follows creation, and an update against a record the API cannot see yet is precisely how NOT_FOUND appears on an entity you watched get created. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI cannot flip anything to ENABLED. The activation sequence is something you execute manually, in order, while remembering which ad group IDs came back from which create call. > - **Your account, as it is right now.** A chat cannot check whether the campaign actually reached ENABLED before you activate the ad groups, so it cannot stop you from creating the half-state it just advised you to avoid. > - **Your account, as it is right now.** To get a chat this far you have pasted advertiser IDs, campaign IDs, ASINs, and budget figures into a conversation history you do not control. The Skill never copies your account into a prompt; the MCP reads and writes over an authorized connection. ## Confirm delivery in the DSP tables, not in the create response A successful activation call means the entities are enabled. It does not mean a single impression has served. Those are different claims, and only one of them is what the operator actually asked for. Amazon Marketing Cloud carries three DSP traffic tables that answer the delivery question: `dsp_impressions`, `dsp_views`, and `dsp_clicks`. They cover all Amazon DSP campaigns across ad product types including display, online video, streaming TV, and audio. `dsp_clicks` is a subset of `dsp_impressions` that captures the impressions that were clicked, so a campaign with rows in impressions and none in clicks is delivering and not being clicked, which is a very different problem from not delivering. The dimension that makes this readable per tactic is `line_item`, which holds the name of the DSP line item responsible for the event, in the shape `'Widgets - DISPLAY - O&O - RETARGETING'`. Amazon's own example encodes the inventory type and the tactic directly in that name, which is the same grain the Skill created against: one entity per inventory type and tactic pair. Read delivery at that grain and a tactic that never served shows up immediately instead of hiding inside a campaign total. Check how your own line item names resolve the first time you run it, because the create surface and the reporting surface are different systems and nothing guarantees the strings match. Attributed conversions are a separate question again, and come from `amazon_attributed_events` rather than the traffic tables. One caveat worth knowing before you panic at an empty result. The DSP traffic tables contain inputs only from the DSP advertising accounts that have been added to that Marketing Cloud instance. A campaign can be delivering perfectly and still return nothing if its advertiser was never added. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot query your Marketing Cloud instance, so it cannot tell you whether your new campaign delivered. It can only describe what DSP reporting generally looks like. > - **Your judgment, running every time.** Ask the shared AI why your instance returns no DSP rows and it will suggest a date range or a typo. The real answer, that the advertiser account was never added to the instance, is instance configuration it cannot see and has no reason to guess. > - **Your call, before anything changes.** A chat cannot run the check on a schedule, so nobody finds out that a tactic never delivered until someone thinks to look. ## What happens next The launch is the easy half. What makes it durable is that the whole sequence is a [Skill](/features/skills/) rather than a runbook, so it re-runs the same way for the next campaign, and the delivery check re-runs on a schedule instead of when someone remembers. The [Amazon Agent Data layer](/features/amazon-agent-flow/) is what turns that check into something standing: the DSP tables land next to the rest of your Amazon data, alongside the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) pulls behind your catalog and order history, so a per-tactic delivery question is a query rather than a project. The decision rule we run on is simple. Give a tactic a defined window after activation, then check `line_item` delivery. A tactic with no impressions in that window is not a slow start, it is a signal to look at eligibility and inventory rather than to wait longer. A tactic delivering impressions with no clicks is a creative problem, not a targeting one, and those two findings route to different people. Once the campaign has enough delivery to compare against everything else running, the next question is usually how much of it is incremental rather than overlapping what your sponsored ads already reached. *Next: the [four-way sponsored ads and DSP overlap audit](/guides/amc-sponsored-ads-dsp-overlap-4way/), which measures exactly that.* ### The Data-Mode Router That Stops Bad ACoS Math URL: https://www.kuudo.com/guides/rules-data-mode-router/ Your SQP report produces nonsense ACoS because the export has no cost or revenue fields, yet the raw Amazon report does not label the dataset for downstream calculations. The **data-mode-router Skill** fixes that before any metric runs. Grounded in [Amazon Agent Atlas](/features/agent-atlas/)'s Keyword Analysis Decision Framework, it inspects the columns, assigns `data_mode` as `ads`, `organic`, or `mixed`, and blocks ACoS or return on ad spend (ROAS) unless a record contains ad-backed cost and revenue. An SQP export pulled through the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) therefore resolves to the framework's `organic` mode and routes to Conversion Performance Index (CPI) plus share-funnel diagnostics instead of spend-based metrics. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) and [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supply live campaign and retail signals, [Skills](/features/skills/) enforce your routing rules on every run, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep material changes in your hands. | Mode | Required evidence | Allowed output | |---|---|---| | `ads` | Cost and revenue | ACoS and ROAS | | `organic` | SQP fields, no spend | CPI and share gaps | | `mixed` | Both column families | Two tagged records | ## 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. SQP 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 SQP ones, it's tagged `ads`. If it has SQP signals and no ad ones, it's tagged `organic` by the Skill. Both together, it's `mixed`. That tag isn't a note in a log somewhere, it's required on every record the Skill emits. Here's what an ads-mode row looks like coming out of the n-gram rollup: ```json { "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. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** You paste one export into the shared AI, and that is the only schema it will ever see. It has no standing check that would catch you accidentally blending two different report exports into a single paste. > - **Your call, before anything changes.** Even when the shared AI correctly guesses "this looks like SQP," you get a sentence back, not a machine-readable `data_mode` field your dashboard or next automation step can route on. > - **Your judgment, running every time.** Ask the shared AI why a column belongs to "ads" versus "organic" and you get a plausible guess built from header names, not the actual Amazon report taxonomy. ## ACoS in organic mode triggers the Skill's priority-one guardrail 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. ```yaml 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 ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot verify whether the "cost" or "revenue" figure you pasted is real ad spend or a market estimate. It has no route into your account data to check its provenance. > - **Your call, before anything changes.** The shared AI can warn you in a sentence, but it cannot drop the computation and substitute a repair action the way a rule ranked above every other business rule does. > - **Your judgment, running every time.** The shared AI has no concept of "ACoS in organic mode is an invalid computation, priority one." That is a severity ranking our Skill enforces, grounded in real Amazon operational patterns, and it cannot derive it from the ACoS formula alone. ## 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: ```json [ { "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. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** You'd have to manually label which rows in a combined export are ads-backed and which are organic-backed, the shared AI has no cross-reference to your actual campaign IDs to do it for you. > - **Your call, before anything changes.** Ask the shared AI to "split this out" and you get a one-time answer for that message, next week's paste starts from zero with no consistency guarantee across reports. > - **Your judgment, running every time.** The generic instinct is to hand you one clean, helpful number per keyword. Without the Atlas-grounded workflow, the shared AI has no standing rule that treats a single blended number as the invalid output, not the goal. ## 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 framework also requires at least 100 total impressions and 10 total clicks for organic claims, rising to 200 impressions for 2-grams. Organic actions cool down for 7 days and operations actions for 14, 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: ```json { "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": 1000, "total_purchases": 20, "purchase_rate": 0.020 }, "asin_metrics": { "clicks": 155, "purchases": 2, "purchase_rate": 0.0129, "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. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** CPI needs a market denominator, total purchases over total clicks across every seller on that query. The shared AI has only what you pasted, and it cannot hold that baseline steady from one report to the next. > - **Your call, before anything changes.** Even if the shared AI computes CPI once in chat, it cannot enforce your data-sufficiency floor or turn a share-gap into a queued `pdp_update`, you're back to opening a ticket by hand. > - **Your judgment, running every time.** Without the Atlas rule, the shared AI has no standing instruction to prefer CPI for this schema. You must supply the formula, bands, and sufficiency thresholds yourself. ## 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 the [Amazon Agent Data layer](/features/amazon-agent-flow/), where routed decisions sit 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](/guides/rules-agent-bidding-rulebook/), 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.* ### Auditing FBA Reimbursements: What Amazon Owes You URL: https://www.kuudo.com/guides/seller-fba-reimbursement-audit/ Grounded by [Amazon Agent Atlas](/features/agent-atlas/), the [fba-reimbursement-audit Skill](/features/skills/) is what actually answers "how much in FBA (Fulfillment by Amazon) loss and damage reimbursements has Amazon already paid us, and how much are we missing," because Amazon's own Inventory Defect and Reimbursement portal can't: it lists individual defect events across its Eligible, In Progress, and Resolved tabs, but it never classifies an event into a claim type, checks it against that type's own window, or applies the correct valuation rule. I went looking for this number after our reconciliation spreadsheet fell three months behind the portal. Once I asked our agent to pull every eligible event through [Selling Partner MCP](/features/amazon-selling-partner-mcp/), classify it, and check it against Amazon's actual policy, the picture changed: real money sitting unclaimed, and real deadlines closing on it. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads current defect, reimbursement, and account state, [Skills](/features/skills/) preserve how your team classifies and values claims, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep disputes with you. ## A loss or damage event isn't one claim, it's three claim types with three different windows Every lost or damaged unit Amazon owes us for falls into one of three claim types, and each type carries its own deadline, not a shared one. Shipment to Amazon claims (units lost or damaged in transit to a fulfillment center or third-party facility) have to be filed no later than nine months after the verified delivery date. Fulfillment Center Operations claims (units lost or damaged inside Amazon's own operations) have to be filed no later than sixty days after the item was reported lost or damaged in the Inventory Defect and Reimbursement portal or the Inventory Ledger report. Customer Return claims are the one most sellers get wrong: file no sooner than sixty days and no later than 120 days after the refund or replacement, a window with a floor as well as a ceiling. Classifying comes first. The Skill sorts every raw defect and return event by type, then computes each one's window status, open, closing soon, or expired, as a flagged field in the audit report. That's the step the portal skips: it'll show an event happened, but it won't say which of the three clocks is running on it, let alone how many days are left. ```json { "event_id": "EVT-00113", "classified_claim_type": "fulfillment_center_operations", "reported_date": "2026-06-12", "window": { "end": "2026-08-11", "status": "closing_soon", "days_remaining": 4 } } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Ask the shared AI which of your events is approaching deadline and you're the one pasting them in, and you'd already need to know which of the three windows applies before it can check the math. > - **Your call, before anything changes.** Even a correct window calculation from the shared AI doesn't file anything for you. You still have to go into Seller Central and submit the claim before it closes. > - **Your judgment, running every time.** Ask either one for "the reimbursement deadline" and you're as likely to get one universal answer as three separate ones, with no particular reason to know the customer-return window has a sixty-day floor, not just a limit. ## Eligibility isn't a spectrum, it's seven gates that all have to hold Amazon doesn't reimburse partial eligibility. A unit has to be FBA-registered at the time of loss, compliant with FBA restrictions, shipped in the exact quantities on the shipping plan, part of a shipment that wasn't canceled or deleted, not pending or actioned for disposal, not customer-damaged or defective, and tied to an account that stays in normal status all the way through the claim and any appeal. Seven gates, and every single one has to be true. Fail one and the claim is void, no matter what the item was worth. The Skill checks all seven gates automatically before an event counts toward the "recoverable" total, splitting the result into eligible-and-unclaimed versus ineligible, and naming the specific gate that failed on each ineligible event instead of just marking it "no." That distinction matters when we're deciding whether to spend appeal time on a claim that was never going to qualify. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI can recite the seven rules from memory, but the shared AI can't check your actual account status, your shipping-plan quantities, or whether a given shipment was canceled. > - **Your call, before anything changes.** If your account drops out of normal status while a claim is under review, the claim voids, and the shared AI has no visibility into your account health to warn you. > - **Your judgment, running every time.** It's easy to assume "lost is lost." Ask the shared AI and it's unlikely to flag that a disposal-pending or customer-damaged item is excluded categorically, no matter how clearly the loss was Amazon's fault otherwise. ## Valuation depends on when the loss happened, not what the item is worth Pre-order events pay differently than post-order ones, and that split runs through every lost unit on the sheet. Shipment-to-Amazon losses, removals, and fulfillment center operations events pay our sourcing cost: what we paid to source the unit, not what we'd have sold it for. Customer return events pay the refund or replacement amount minus applicable fees instead. Either way, $5,000 per unit is the ceiling: Amazon caps a single unit's reimbursement at that amount regardless of which of the two rules set the value. Most sellers, including me before I looked closely, mentally price every lost unit at retail. The policy doesn't work that way, and the gap between the two numbers is where the missed money hides. Inside the Skill, the valuation engine applies the correct rule per event based on its classified claim type, and for every pre-order event it checks whether we've actually submitted our own sourcing cost or whether Amazon is still defaulting to its own estimate. The audit report shows both numbers side by side, Amazon's likely valuation against our sourcing-cost-corrected valuation, with the $5,000 cap applied wherever it binds. ```json { "artifact_type": "audit_report", "skill": "fba-reimbursement-audit", "grounded_by": "amazon-agent-atlas", "summary": { "events_audited": 214, "reimbursed_total_usd": 8420.15, "eligible_unclaimed_total_usd": 1180.40, "at_risk_window_closing_usd": 340.00, "disputable_valuation_total_usd": 265.75 }, "events": [ { "event_id": "EVT-00113", "classified_claim_type": "fulfillment_center_operations", "order_relationship": "pre_order", "valuation": { "rule_applied": "sourcing_cost", "sourcing_cost_submitted": false, "amazon_estimate_usd": 14.20, "unit_cap_usd": 5000 }, "status": "eligible_not_filed", "flags": ["window_closes_in_4_days", "sourcing_cost_not_submitted"] }, { "event_id": "EVT-00087", "classified_claim_type": "customer_return", "order_relationship": "post_order", "valuation": { "rule_applied": "refund_minus_fees", "refund_amount_usd": 42.00, "fees_deducted_usd": 6.30 }, "reimbursement_actual": { "issued": true, "amount_usd": 30.00, "variance_vs_expected_usd": -5.70 }, "status": "disputable", "flags": ["eligible_for_valuation_dispute"] } ] } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI doesn't know your actual sourcing cost, your actual refund amounts, or which events were pre-order versus post-order. > - **Your call, before anything changes.** Even when the shared AI correctly says "this should be sourcing cost, not retail," you still have to go submit the figure on the Manage Your Sourcing Cost page yourself and validate it if Amazon asks. > - **Your judgment, running every time.** The shared AI's training-data instinct says reimbursement equals what you'd have made on the sale, and without the policy text right in front of the shared AI, it will confidently give you the wrong valuation logic. ## Reimbursement isn't a one-shot event, it's a loop most sellers never run Getting paid once isn't the end of it. There's a recovery loop layered on top of the three windows and seven gates: submitting our own sourcing cost instead of accepting Amazon's estimate, respecting the thirty-day cooldown before resubmitting without new information, disputing a valuation within sixty days of an issued reimbursement, and handling the customer-return special case correctly. If a customer is refunded but never returns the item to a fulfillment center within sixty days, Amazon typically charges the customer and reimburses us. If the item does come back within sixty days and it's sellable, it goes back into inventory and we get no reimbursement. If it comes back unsellable and Amazon caused the damage, we get reimbursed and the item isn't restocked. Tracking doesn't stop at filing. The Skill follows every event's reimbursement status across its full lifecycle, not just at the moment a claim gets filed, flagging "sourcing cost not yet submitted," "eligible for valuation dispute, window closes in nine days," and "resubmission blocked until August 20th, no new information supplied," so the backlog of things we could still do doesn't quietly age out. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI does not know which of your reimbursements were already issued, on what date, at what amount, or which ones are still inside their sixty-day dispute window. > - **Your call, before anything changes.** Filing the dispute, submitting sourcing-cost documentation, or waiting out the thirty-day cooldown are all Seller Central actions. The shared AI can draft the dispute language, but it can't track the clock or click submit for you. > - **Your judgment, running every time.** The sellable-versus-unsellable branching logic on customer returns is easy to get backwards from memory. Ask the shared AI and it's as likely to say "you get reimbursed either way" as to correctly explain that a sellable return within sixty days forfeits reimbursement entirely. ## What happens next Once the audit report is built, it doesn't sit still. We feed it into [Amazon Agent Flow](/features/amazon-agent-flow/), the same data layer that holds [Amazon Ads MCP](/features/amazon-ads-mcp/) and Selling Partner MCP pulls side by side, so recovered reimbursement dollars sit next to the ad spend they help offset. The Skill schedules the same pass weekly and hands off anything newly flagged: a window closing inside seven days, a sourcing cost still unsubmitted, a dispute that just opened, so it shows up as a task instead of a line buried in a spreadsheet. Recovery is only half the ledger. The other half, what we pay out rather than what Amazon owes back, is covered in the [FBA inventory health guide](/guides/seller-fba-inventory-health-post-2024/): storage fees, capacity limits, aged-inventory surcharges. Running both audits is how we see the full ledger instead of half of it. Same pattern every time: Amazon publishes the rule, leaves the classifying and window-checking to the seller, and pays out only what gets claimed correctly and on time. *Next in the series: what happens when Amazon denies a valid claim outright, and the appeal window most sellers let expire.* ### The ARA Report and Metric Glossary for Vendors URL: https://www.kuudo.com/guides/vendor-ara-reports-metric-glossary/ The ARA report router [Skill](/features/skills/), grounded by [Amazon Agent Atlas](/features/agent-atlas/), is what actually answers which ARA report answers a given question, and what the metric inside it actually measures. Amazon's Vendor Central UI doesn't do that: it hands you a dashboard tab. Raw SP-API (Selling Partner API) reports don't do it either: they hand you a payload full of fields, not an interpretation of which field you needed or what its number means. I run into this constantly. Someone on my team asks why sell-through dropped, or what "Conversion" means in the export we're staring at, and the honest answer depends on which report, which account view, and which section of Amazon's own help docs you're reading, not on which tab happened to be open when you logged in. [ChatGPT, Claude, Perplexity, and Copilot](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: your data, your rules, and the way your business operates. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies current report and entitlement context, [Skills](/features/skills/) preserve the metric definitions your team uses, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep downstream decisions with you. ## Every ARA dashboard has a mirrored SP-API report type, so naming the dashboard is only half the job Sales maps to `GET_VENDOR_SALES_REPORT`. Inventory maps to `GET_VENDOR_INVENTORY_REPORT`. Forecasting maps to `GET_VENDOR_FORECASTING_REPORT`. Traffic maps to `GET_VENDOR_TRAFFIC_REPORT`. Net PPM maps to `GET_VENDOR_NET_PURE_PRODUCT_MARGIN_REPORT`. Knowing the dashboard name gets you nowhere near an API call, and knowing a report type string with no dashboard context doesn't tell you which metric definition governs it. The ARA report router Skill resolves both in a single lookup, calling through [Selling Partner MCP](/features/amazon-selling-partner-mcp/) to fetch the report once it knows which one you actually need. When I asked our agent why sell-through dropped on a set of ASINs, the Skill didn't guess. It resolved the question to the Inventory dashboard, the `GET_VENDOR_INVENTORY_REPORT` report type, and the `sellThroughRate` field inside it, then pulled the report through the same call path. It's also the reason the Skill catches a retirement most operators don't think to check: the Sales and Inventory EDI transactions (X12 852, EDIFACT SLSRPT) and the Forecast EDI transaction (X12 830, EDIFACT DELFOR) were retired after June 30, 2022, so API is now required to pull them. Traffic and Net PPM never had an EDI form at all; they've been API-only from day one. If the question resolves to Net PPM specifically, that's where [the margin-leakage guide](/guides/vendor-net-ppm-margin-leakage/) picks up. This guide is upstream of it: once the router names the dashboard and the report type, that guide is where you go hunting for the actual leak. ```json { "operator_question": "Which report do I pull to find products dragging my margin?", "resolution": { "dashboard": "Net PPM", "sp_api_report_type": "GET_VENDOR_NET_PURE_PRODUCT_MARGIN_REPORT", "view_required": "manufacturing", "metric": { "name": "Net pure product margin (Net PPM)", "definitions": [ { "formula": "(shipped revenue - shipped PCOGS + CCOGS - sales discounts) / shipped revenue", "cited_section": "Net PPM dashboard" } ], "conflicting_definition": false } }, "caveat_flags": ["excludes_warehouse_deals", "manufacturer_only_dashboard"] } ``` > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI which SP-API report matches a dashboard and you'll often get retired EDI-era vocabulary back, because that's what dominates older training data, not the current API-first framing. > - **Your call, before anything changes.** The shared AI can describe a report type name in prose, but it cannot verify the literal enum string against Amazon's schema or confirm it against a live call on your account. > - **Your account, as it is right now.** The shared AI doesn't know whether your integration is still EDI-based or already migrated to API, so it cannot tell you if the 2022 retirement even applies to your account. ## ARA and ABA are different products gated by different rules, so "I can't find my dashboard" means two different problems Every vendor gets ARA. No Brand Registry requirement, no enrollment gate, it's part of the vendor relationship. ABA, Amazon Brand Analytics, is a different product entirely: it requires Brand Registry enrollment and being the brand-selling party on the ASINs in question. Before the Skill routes an operator's question to a dashboard, it classifies the question against those access rules first, so it doesn't send you looking for a dashboard your account structurally cannot have. Beyond the access gate, the two products measure different things entirely. ABA's Search feature covers search popularity, click share, and conversion share for a search term. Its market basket analysis shows co-purchased products. Its repeat purchase behavior tracks order counts and unique customers over time. None of that lives in ARA, which is built around sales and operational dashboards, not search or purchase-pattern analytics. Conflating the two doesn't just point you at the wrong tab, it points you at a feature set that doesn't exist in the product you're looking at. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see your account's Brand Registry status, so it cannot tell you which of the two products you should even expect to see. > - **Your judgment, running every time.** "Brand Analytics" and "Retail Analytics" sound like variants of the same phrase, so if you ask the shared AI to tell them apart, expect ABA's market basket analysis attributed to ARA. > - **Your call, before anything changes.** Even when the shared AI correctly names Brand Registry as the gate, checking your actual enrollment status is an account action on Amazon's side. You have to go look yourself. ## Amazon's own ARA docs define "Conversion" two different ways on the same overview page This is the one that made me stop trusting my own memory of ARA definitions. The Traffic dashboard section of Amazon's ARA overview defines Conversion as ordered revenue divided by glance views. The General dashboard information section, on the same overview page, defines Conversion as ordered units divided by glance views. Not a typo in one section and a fix in the other, both are live, current documentation. Amazon's own help content disagrees with itself two sections apart, and neither section says so. Rather than pick a side, the Skill attaches both formulas to the resolution whenever it resolves a question to Conversion, citing each one's exact source section and flagging the metric as having a conflicting definition instead of silently returning one number as "the" answer. That flag matters more than it sounds: if your internal reporting was built against one formula and a teammate pulled a number using the other, you'd see two different Conversion rates and no obvious reason why, unless something told you to check. ROOS and Rep OOS get the same treatment, for a related reason: they sound interchangeable and aren't. ROOS, procurable product out-of-stock, considers procurable ASINs, a broader cohort than Rep OOS's replenishable-ASINs-only scope. Because the ROOS cohort is larger, ROOS usually reads as a lower percentage than Rep OOS on the same account, which is exactly the kind of thing that looks like an error until you know it's a definitional difference. Sourceable Product OOS is a third, related but distinct glossary entry, not a synonym for either: OOS glance views on sourceable ASINs divided by total glance views, also Manufacturing view only. The Skill keeps all three as separate glossary entries so it never treats one as a stand-in for another. Sell Through Rate gets its own definition too: shipped units minus customer returns, divided by on-hand units plus received units, a formula with no ambiguity but plenty of adjacent metrics it gets confused with. ```json { "operator_question": "What does Conversion mean in my ARA dashboard, and which report is it in?", "resolution": { "dashboard": "Traffic", "sp_api_report_type": "GET_VENDOR_TRAFFIC_REPORT", "view_required": "manufacturing", "metric": { "name": "Conversion", "definitions": [ { "formula": "ordered revenue / glance views", "cited_section": "Traffic dashboard" }, { "formula": "ordered units / glance views", "cited_section": "General dashboard information" } ], "conflicting_definition": true, "resolution_note": "Amazon's ARA overview defines Conversion two different ways in two sections of the same page. Both sections are current, confirm which formula your internal reporting matches before comparing numbers across teams." } }, "caveat_flags": ["manufacturer_only_dashboard", "conflicting_definition"] } ``` > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI what the Conversion formula in ARA is and you'll get one formula stated with full confidence. It cannot notice that Amazon's own help page disagrees with itself two sections apart. > - **Your account, as it is right now.** Two people on your team asking the shared AI the same question a week apart could get genuinely different formulas back and never know to reconcile them. > - **Your call, before anything changes.** If the shared AI conflates ROOS, Rep OOS, and Sourceable Product OOS, it cannot pull your actual account data and check which cohort your number is scoped to, that's a live-data check only you can run. ## Four recurring distortions make an ARA number look complete when it isn't Warehouse Deals sales are excluded from the Sales and Net PPM dashboards, so if your internal reporting reconciles against ARA and includes Warehouse Deals in its own totals, the two will never match, by design, not by error. Sourcing view versus Manufacturing view changes which ASINs and even which metrics appear at all: Traffic, Net PPM, Forecasting, Sourceable Product OOS, and ROOS are Manufacturing-view-only, so a Sourcing-view login won't show them regardless of what the operator asked for. Customer returns and cancellations get booked against the original date of the sale, not the date the return happened, which means a return processed this month can quietly revise a number from three months ago. And ASIN mapping issues, once corrected, can take up to fourteen days to actually show up in the dashboards, so a "fixed" product can look missing for two more weeks. Net PPM itself is (shipped revenue minus shipped PCOGS plus CCOGS minus sales discounts) divided by shipped revenue, and the Warehouse Deals exclusion applies to it the same way it applies to Sales. The decision_plan the Skill produces carries these as caveat flags attached to the resolution itself, not buried in a footnote: `excludes_warehouse_deals: true` on Sales and Net PPM, `view_required: manufacturing` on Traffic, Net PPM, Forecasting, Sourceable OOS, and ROOS. The flag travels with the number, so whoever reads the resolution sees the caveat before they build a decision on the metric, not after. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see which view your Vendor Central login has. You have to go check. > - **Your call, before anything changes.** If your internal reporting reconciles against ARA and includes Warehouse Deals, the shared AI cannot pull both numbers and compare them. That is an action against live data, not a description of one. > - **Your judgment, running every time.** The return-date attribution rule is easy to get backwards, so if you ask the shared AI, you're as likely to get the intuitive-but-wrong version, booked to the return date, as the correct one. ## What happens next A decision_plan isn't a one-off answer, it's structured output the Skill can hand off downstream. Once the Skill has resolved a question to a dashboard, a report type, and a set of caveat flags, that resolution feeds into [Amazon Agent Flow](/features/amazon-agent-flow/), the same data layer that holds [Amazon Ads MCP](/features/amazon-ads-mcp/) and Selling Partner MCP pulls side by side, so a routed ARA answer sits next to the ad-spend numbers it eventually gets compared against. The Skill can schedule the same lookup as a recurring check instead of a one-time query, useful for anything with a mapping delay attached, since a "missing" ASIN today might just need another look in two weeks. For questions that resolve to Net PPM, the handoff goes straight to the margin-leakage guide, because routing tells you where to look and that guide is where you actually go digging for the leak itself. The pattern underneath all four sections is the same: get the question routed to a cited, current answer before you build anything on top of it, not after you've already shipped a number that was wrong in a way nobody flagged. *Next in the series: ARA vs. ABA, which dashboard actually answers which question.* ### Listing Suppression Diagnosis: Stranded or Buyable URL: https://www.kuudo.com/guides/seller-listing-suppression-diagnosis/ Create this workflow as a reusable Skill called `silent-asin-diagnosis`. It reads live `BUYABLE` and `DISCOVERABLE` status through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), uses [Amazon Agent Atlas](/features/agent-atlas/) to interpret the exact suppression or stranded-inventory rule, and returns the reason-specific fix plus its deadline before anyone edits the listing. I start there when a top ASIN suddenly has no orders or impressions. Search suppression, a non-buyable offer, a block, a deletion, and stranded Fulfillment by Amazon (FBA) stock can look identical from the sales graph, but each demands a different repair. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads the current listing, offer, and inventory state, [Skills](/features/skills/) preserve your diagnostic order, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep the repair with you. | Live state | Diagnosis | Next evidence | |---|---|---| | `BUYABLE` + `DISCOVERABLE` | Listing is live | Diagnose traffic and demand | | `BUYABLE`, not `DISCOVERABLE` | Search suppressed | Pull reason and issues | | Not `BUYABLE`; FBA units | Possible stranded stock | Confirm no active offer | | Not `BUYABLE`; no stranded units | Offer-side failure | Check quantity, window, restrictions | ## `BUYABLE` and `DISCOVERABLE` separate suppression from stranding When a listing is search suppressed, Amazon removes discoverability but can leave the offer buyable; stranded inventory is FBA stock in a fulfillment center that does not have an active offer. That distinction is the first branch, not a semantic detail. The Skill reads `ListingsItemStatus`, whose relevant values are `BUYABLE`, `DISCOVERABLE`, and `DELETED`. `BUYABLE` without `DISCOVERABLE` points to search suppression. An absent `BUYABLE` status establishes that the offer is not buyable, but it does not establish stranding until the agent also finds FBA units and no active offer. If both status flags are healthy, I move the investigation away from catalog repair. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot read the live status, offer, or FBA unit count. You can paste a search result, but that does not prove buyability or inventory state. > - **Your call, before anything changes.** The shared AI can give you a checklist. You must retrieve each status and compare the records by hand; the Selling Partner MCP performs the connected read. > - **Your judgment, running every time.** The shared AI can collapse “suppressed” and “stranded” into one diagnosis. Atlas keeps the branch tied to Amazon's status definitions. ## The suppression reason chooses the repair `GET_MERCHANTS_LISTINGS_FYP_REPORT` contains only suppressed listings, the reason for each suppression, and instructions for removing it. It can be requested for a current investigation or scheduled for continuous monitoring. I use the report to identify the affected SKU, then add its live listing issues to the evidence. The **Suppressed Listings Management** playbook routes the result through Fix Your Products: image failures go to image requirements, missing attributes expose the absent value, and newly required compliance attributes get patched directly. Rewriting unrelated copy or adding stock does not repair those causes. The bulk report is not real-time proof. Later report calls can be 1 to 6 hours old, and the stock-data freshness commitment is three hours, so the Skill pairs that evidence with the live listing read before proposing a patch. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot request the suppressed-listings report or inspect the SKU's current issues. You would have to paste a manual snapshot into the chat. > - **Your call, before anything changes.** The shared AI can draft replacement text only. You must find the named field and apply the correction yourself; the Selling Partner MCP can execute an approved, scoped patch. > - **Your judgment, running every time.** The shared AI may suggest a broad rewrite. Atlas distinguishes image, attribute, and compliance causes, including the exact missing value. ## The stranded reason chooses relist, restore, or removal `Stranded reason` determines whether the operator should relist the SKU, restore an offer, correct price or condition, fix the fulfillment channel, or remove the units. The Fix stranded inventory page is the most accurate source when Manage Inventory and stranded status disagree. Until the active offer returns, those FBA units cannot sell and continue to accumulate storage fees. The **Resolve stranded inventory issues** playbook reads `Stranded reason` and the accompanying `Additional information` or Recommendations. Common evidence includes a missing listing, a missing price or condition, or a merchant-fulfilled listing attached to FBA units. For a catalog-wide repair, the same playbook uses the Bulk Fix Stranded Inventory workflow. The clock matters. If the seller does not create a listing or order removal within 30 days of Amazon's notice, Amazon designates the inventory unsellable and it must be removed. The deadline begins with Amazon's notice, not with the day I diagnose it. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see your FBA units, active-offer state, `Stranded reason`, or recommended action. You must collect and paste those facts. > - **Your call, before anything changes.** The shared AI can describe relisting or removal. You must complete the selected Seller Central step yourself; the connected workflow runs supported MCP actions and keeps any Seller Central-only step explicit for you. > - **Your judgment, running every time.** The shared AI may tell you to create a second listing when price, condition, offer state, or fulfillment channel is the actual fault. Atlas supplies the reason-specific path and 30-day rule. ## A repair is complete only when status returns A submitted change is not a completed repair. Suppression closes when discoverability returns; stranded stock closes when buyability and an active offer return. The Skill watches `LISTINGS_ITEM_STATUS_CHANGE`, schedules `GET_MERCHANTS_LISTINGS_FYP_REPORT`, and re-reads the affected SKU after Amazon's processing window. It records the branch, approved action, evidence, and stranded deadline in the run log. If `BUYABLE` and `DISCOVERABLE` are both healthy but demand is still absent, the workflow hands the ASIN to the [Amazon Ads MCP](/features/amazon-ads-mcp/) instead of misclassifying a traffic problem as a listing problem. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot observe a later status change or verify that the active offer returned. You must collect another snapshot. > - **Your call, before anything changes.** The shared AI can remind you to check. You must schedule and perform every recheck; the Skill monitors the expected status and preserves the deadline. > - **Your judgment, running every time.** The shared AI can stop at “submitted.” Atlas keeps the completion test tied to discoverability, buyability, and the active offer. ## What happens next I route the completed diagnosis to one owner. Product-data issues go to the team approving an image, attribute, or compliance patch. Offer and FBA issues go to the inventory team with the reason-specific action packet. A healthy listing with no demand moves to the advertising team with its catalog state already cleared. That handoff can run as one recurring Skill through [Amazon Agent Flow](/features/amazon-agent-flow/), so each new silent-ASIN alert arrives with its evidence and owner instead of becoming another unclassified ticket. The pattern is status, reason, scoped action, and verified return. That keeps a silent ASIN from turning into an expensive guess. *Next, use the [listing audit-to-patch guide](/guides/seller-listing-agentic-audit-to-patch/) to turn a verified attribute issue into the smallest safe catalog change.* ### The Agent Bidding Rulebook That Prevents Bid Thrash URL: https://www.kuudo.com/guides/rules-agent-bidding-rulebook/ A bidding rulebook stops thrashing when a bid can only move after three independent gates agree: the n-gram clears data sufficiency, its ACoS sits outside a deadband around target, and the resulting change fits inside a hard daily clamp. That is the whole answer. I handed the question to [our agent](/features/ai-clients/), which runs it as a [Skill](/features/skills/) over the [Amazon Ads MCP](/features/amazon-ads-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/) in two corpus playbooks: the Keyword Analysis Decision Framework (N-Grams) and the Sponsored Ads Bidding Configuration Decision Framework. The question that sent me looking came from our PPC lead, in Slack, on a Tuesday: every tool she had used either overcorrected or undercorrected, and she wanted to know what a sane rulebook actually looks like underneath. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies live campaign state and authorized actions, [Skills](/features/skills/) enforce your decision rules on every run, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep material changes in your hands. ## Thin data gets a hold, not a bid change Most thrash is not a bad threshold, it is a threshold applied to a sample too small to mean anything. The rulebook gates on sufficiency before any rule is allowed to evaluate. For 1-grams that means `IMP_g >= 50` and either `CLK_g >= 10` or `ORD_g >= 2`. Longer n-grams need proportionally more: 2-grams require `IMP_g >= 100`, 3-grams `IMP_g >= 150`, with the same click-or-order condition. There is a second gate one level up, at the ad group. The starvation guard in the Sponsored Ads Bidding Configuration Decision Framework holds everything when `CLK_l30d < 50` and `ORD_l30d < 5`. The prescribed action is `hold_thin_data` with no destructive change, and the advice is to consolidate or extend the observation window rather than act early. An agent that respects both gates spends most of its first run reporting that it is not going to do anything yet. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** It cannot see impression counts per n-gram, so it cannot tell a 4,000-impression term from a 40-impression one. Paste a top-50 rows sample and it will confidently rank terms that have no statistical business being ranked. And your search-term data is now sitting in someone else's chat log. > - **Your call, before anything changes.** Even when it correctly says "this needs more data," it cannot set a hold, record a cooldown, or stop a downstream job. Nothing enforces the wait except you remembering. > - **Your judgment, running every time.** Ask for a minimum impression threshold and you get a plausible round number. The real gates are versioned per n-gram length in the corpus playbook, and they are not the numbers a public model guesses. ## Scale and pull-back fire at fixed distances from target This is the part that actually prevents thrash. The scale rule and the pull-back rule do not trigger at target ACoS, they trigger at fixed distances on either side of it, leaving a gap where nothing happens at all. A scale decision needs `ORD_g >= 10` **and** `ACOS_g <= 0.9 x TARGET_ACOS`, and produces `increase_bid` at +5 to 10% on keywords containing that n-gram. A pull-back needs `ORD_g >= 10` **and** `ACOS_g > 1.1 x TARGET_ACOS`, and produces `decrease_bid` at -10%, plus a 10% reduction to the Top-of-Search multiplier if one is in use. Both re-evaluate in 5 days. Between 0.9x and 1.1x of target, no bid rule fires. That deadband is the single most load-bearing number in the rulebook. A bidder without one sits exactly at target and oscillates forever, correcting every run in whichever direction last week's noise pointed. | Condition | Action | Re-eval | |---|---|---| | `ORD_g >= 10`, `ACOS_g <= 0.9x target` | `increase_bid` +5 to 10% | 5 days | | `ORD_g >= 10`, `ACOS_g > 1.1x target` | `decrease_bid` -10% | 5 days | | ACoS inside the deadband | none | next run | | `ROAS_g >= target`, impressions bottom quartile | `increase_bid` +5 to 8%, TOS +10% | 5 days | > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** It does not know your `TARGET_ACOS`, so it cannot compute a deadband around it. Tell it your target and it will still evaluate against the single number rather than a band, which is the behavior that thrashes. > - **Your call, before anything changes.** It can describe a +8% bid increase. It cannot apply one. If you retype that change into Campaign Manager yourself, nothing records that the entity is now in a 5-day review window, so next week you may well move it again. > - **Your judgment, running every time.** The Skill preserves the playbook's 0.9 and 1.1 multipliers, +5 to 10% magnitudes, and 5-day re-evaluation. The shared AI alone has no reason to produce those particular numbers consistently. ## Every lever is clamped before it ships Gates decide whether a rule fires. Clamps decide how far it can go when it does. The global controls are short enough to memorize: a daily bid change clamp of plus or minus 20%, a Top-of-Search placement cap of 1.50, and a standard review window of 5 days for Ads and 7 to 14 days for Organic and Ops work. The clamp matters most in the case where the rulebook is most confident. An n-gram at half of target ACoS with 200 orders is a genuinely strong signal, and the temptation is to move the bid a long way at once. The clamp refuses. Twenty percent per day, then look again in five days. Every decision record the agent emits carries its own `max_change_pct` and `cooldown_days`, the latter defaulting to 5 to 14 depending on the action type, so the bounds travel with the decision rather than living in a settings page nobody re-reads. ```json { "decision_category": "scale_winner", "action_id": "increase_bid", "action_scope": ["ADS"], "selector": { "ngram": "stainless steel", "n": 2, "match_type": "phrase" }, "magnitude_pct": 8, "max_change_pct": 20, "cooldown_days": 5, "confidence": "high", "reason": "ORD_g=31, ACOS_g=0.19 <= 0.9 x TARGET_ACOS(0.25); sufficiency met", "badges": { "data_sufficient": true, "cooldown_ok": true, "thin_data_reason": null } } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** It cannot see what the bid was yesterday, so it cannot tell you whether a proposed change is inside the daily clamp. Apply its suggestion by hand two days running and you can move a bid 40% without noticing. > - **Your call, before anything changes.** Placement multipliers are a good example: it can tell you to raise Top-of-Search by 10%, but it cannot read the current multiplier, so neither of you knows whether that crosses the 1.50 cap until you are in the console. > - **Your judgment, running every time.** Ask for safe bid-change bounds and you get advice, not a contract. The clamp here is a field on the decision record, which means a downstream job can reject anything that violates it. ## Precedence decides which rule wins Real accounts produce conflicts. A keyword can look wasteful on one metric and efficient on another, and two rules will match the same entity in the same run. Rather than letting whichever rule evaluated last take the entity, the framework fixes an order: invalid computations first, then thin data, then safety negatives, then pull back, then scale, then mining and hygiene, and creative or ops work last. The tiebreak is explicit and conservative. When a rule suggests both scale and pull-back, prefer pull-back, unless credible intervals support scaling with high confidence. That is where the reliability layer earns its place: the recommended signals are Bayesian credible intervals at 95%, Beta for CTR and CVR, Gamma for spend and revenue ratios, alongside recency weighting with a 14-day half-life and peer medians computed with trimming. The interval, not the point estimate, is what lets a scale decision beat a pull-back decision. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** With no click and order counts it cannot compute a credible interval at all, so the one mechanism that resolves a scale-versus-pull-back tie is unavailable to it. > - **Your call, before anything changes.** Ask it to arbitrate two conflicting recommendations and it will pick one in prose. Nothing stops both from reaching the account if you work through its list by hand and apply each item as you read it. > - **Your judgment, running every time.** The Skill enforces the seven-level precedence ladder and documented tiebreak on every run. The shared AI alone can produce a reasonable-sounding ordering that differs from the one your rulebook actually uses, which is worse than no ordering because it looks authoritative. ## What happens next The output is a decision plan, not an applied change. Each record names the entity, the bounded action, the clamp, the cooldown, and the reason it fired, which makes it reviewable before anything reaches the account. That review step is the same pattern as a [human approval gate](/guides/human-approval-for-amc-activation/) on [Amazon Marketing Cloud](/features/amc/) activation: the agent assembles the artifact, a person signs it, the platform ships it. Once a plan is approved, the review windows do the pacing. Ads actions re-evaluate in 5 days, Organic and Ops in 7 to 14, and each record's `cooldown_days` prevents the next run from touching an entity still inside its window. Wired as a recurring Skill over the [Amazon Agent Data layer](/features/amazon-agent-flow/), the agent reads a fresh rollup, skips everything on cooldown, and emits a much shorter plan the second week. A rulebook that is working produces fewer decisions over time, not more. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) covers the catalog side of the same account when a decision turns out to be a listing problem rather than a bidding one. The pattern is worth stating plainly: bounded actions with explicit gates beat continuous optimization because they are auditable, and an agent that can explain why it did nothing is more trustworthy than one that always has a change to make. *Next: the data-mode router, and why computing ACoS on organic search-query data is the fastest way to poison a rulebook.* ### Recover Featured Offer Eligibility Without Guessing URL: https://www.kuudo.com/guides/seller-buy-box-eligibility-recovery/ The fastest way to recover a missing Featured Offer is to identify the failed gate first: account eligibility, price health, or competition among eligible offers. The agent reads the live Fulfillment by Amazon (FBA) inventory offer for the Amazon Standard Identification Number (ASIN) through the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/), applies a reusable [Skill](/features/skills/), and uses [Amazon Agent Atlas](/features/agent-atlas/) to ground the diagnosis in Amazon's Featured Offer rules. The same evidence can flow through [Amazon Agent Flow](/features/amazon-agent-flow/) and the [Amazon Agent Data layer](/features/amazon-agent-flow/) when the recovery check spans selling and advertising signals. That distinction matters because “we lost the Buy Box” describes two different situations. An offer can be ineligible before Amazon ranks it, or it can be eligible and still lose the placement to another offer. Amazon also announced a gradual July 2026 rollout removing the seller-eligibility step, so a historical rule cannot be treated as universal across every store yet. If the offer's demand context matters, the [Amazon Ads MCP](/features/amazon-ads-mcp/) can add campaign-side evidence without turning a plain chat into a live data connection. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads the current offer and account state, [Skills](/features/skills/) preserve how your team separates eligibility from competition, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep price or offer changes with you. ## Eligibility is a gate, not proof of placement The first check is whether Amazon currently allows the offer to compete. The Atlas-grounded Featured Offer Eligibility playbook says a Professional selling account and performance-based requirements have historically been part of that gate, while Amazon's July 2026 announcement says the seller-eligibility step is being removed gradually. The agent should therefore read the current account state and store rollout status instead of returning a fixed yes/no from old rules. ```json { "asin": "B0EXAMPLE12", "account_type": "Professional", "store": "US", "eligibility_gate": "verify_current_policy", "performance_evidence": { "status": "read_live_account_metrics", "source": "Seller Central account health" }, "rollout_note": "Eligibility rules are changing during the July 2026 rollout" } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot read your current account type, performance state, or store-level rollout. It can only reason from what you paste. > - **Your judgment, running every time.** A chat may repeat the Professional-account rule as if it were permanent. The agent checks the current policy context and labels uncertainty instead of treating a historical gate as universal. > - **Your call, before anything changes.** The chat cannot inspect the account or confirm the offer's current eligibility. You still have to verify the state in Seller Central yourself. ## `PRICING_HEALTH` isolates a price-driven ineligibility When the agent receives `PRICING_HEALTH`, the immediate question is not “did a competitor undercut me?” Amazon says the notification means the offer is ineligible at its current price, independent of competitor changes. Compare the offer's total price, including shipping, with the Competitive External Price and the `referencePrices` values such as `averageSellingPrice`, `msrpPrice`, and `competitivePriceThreshold`. ```json { "status": "price_ineligible", "notification": "PRICING_HEALTH", "total_price": 32.98, "competitive_external_price": 29.99, "reference_prices": { "averageSellingPrice": 30.49, "msrpPrice": 34.99, "competitivePriceThreshold": 29.99 }, "recommended_action": "review price and shipping; approval required" } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** A chat cannot inspect the notification payload or the live shipping charge, so it may compare the wrong number. > - **Your judgment, running every time.** It may treat `ANY_OFFER_CHANGED` as the same signal. `PRICING_HEALTH` means Amazon considers this offer ineligible; `ANY_OFFER_CHANGED` reports offer changes and can include different context. > - **Your call, before anything changes.** The shared AI cannot validate a proposed price against Amazon. You must check the evidence and apply any change yourself. ## Eligible offers still compete on price, shipping, and experience Passing the gate does not win the placement. The Atlas playbooks describe the next stage as a competition among eligible offers; Amazon evaluates competitive price and compelling shipping options, and the detail page can show one New and one Used Featured Offer where applicable. The **Get to Know the Product Detail Page** playbook is the reference for how those offers appear and compete. The recovery artifact should label this state `eligible_not_featured`, not `eligibility_failure`. ```yaml # Skill: featured-offer-recovery read: - offer: live price, shipping, condition, fulfillment - account: current eligibility and performance state - notifications: PRICING_HEALTH, ANY_OFFER_CHANGED compare: - total_price_plus_shipping - competitive_external_price - reference_prices - delivery_and_availability classify: - account_gate - price_ineligible - eligible_not_featured - insufficient_evidence ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** A chat cannot see the competing offers, shipping promises, or availability that shape the ranking. > - **Your judgment, running every time.** It may promise that matching one price guarantees the placement. Amazon says eligibility does not guarantee being featured. > - **Your call, before anything changes.** The chat cannot monitor the next offer change or confirm that a price adjustment changed the placement. ## The recovery artifact should recommend, not silently reprice The Skill turns the evidence into a small decision plan. It names the failed gate, cites the notification or account evidence, proposes the narrowest next step, and keeps approval on any price or fulfillment change. If evidence is incomplete, it returns `insufficient_evidence` instead of guessing. For related operational context, compare the same evidence-first pattern in the [FBA inventory health guide](/guides/seller-fba-inventory-health-post-2024/) and use [Agent Crawl](/features/agent-crawl/) only when the workflow explicitly needs current external-market evidence. ```json { "asin": "B0EXAMPLE12", "status": "price_ineligible", "evidence": [ "PRICING_HEALTH", "total price $32.98 > competitive threshold $29.99" ], "recommended_action": "review price and shipping options", "requires_approval": true, "monitor": ["PRICING_HEALTH", "ANY_OFFER_CHANGED"] } ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** It cannot produce a trustworthy evidence trail from the live offer, so its “fix” is usually a generic repricing suggestion. > - **Your judgment, running every time.** It cannot distinguish a price-health failure from an eligible offer that simply lost the ranking. > - **Your call, before anything changes.** You remain the operator applying and monitoring the change by hand. The Skill keeps the approval step explicit and watches the relevant notification stream. ## What happens next Once the artifact identifies the gate, the team can approve one narrow change, continue monitoring, or route an account issue to Seller Central support. The Skill rechecks `PRICING_HEALTH` and `ANY_OFFER_CHANGED` after the decision so a recovered offer is measured instead of assumed. Featured Offer recovery is a diagnosis problem before it is a pricing problem: verify the gate, read the evidence, then approve the smallest action that addresses it. *Next in the series: diagnosing a listing that is suppressed or stranded before its inventory keeps accruing storage cost.* ### FBA Inventory Health After the 2024 Fee Change URL: https://www.kuudo.com/guides/seller-fba-inventory-health-post-2024/ The post-2024 Fulfillment by Amazon (FBA) inventory playbook is not “stop watching capacity.” It is to separate three signals before the next fee cycle: cubic-foot capacity usage, monthly storage cost, and units approaching the aged-inventory threshold. An agent can run that review through the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) as a reusable [Skill](/features/skills/), grounded by [Amazon Agent Atlas](/features/agent-atlas/) so the policy window and report fields are not guessed. Amazon ended FBA inventory storage overage fees effective July 1, 2024 in the United States, Europe, United Kingdom, and Canada stores. That change does not remove monthly inventory storage fees or the aged inventory surcharge. The operator question is therefore narrower and more useful: which inventory is consuming capacity, which inventory is accumulating storage cost, and which inventory is close to the next age threshold? The same review can join Amazon Standard Identification Number (ASIN) inventory context to the campaign and demand signals available through the [Amazon Ads MCP](/features/amazon-ads-mcp/). [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads current FBA inventory while the [Amazon Ads MCP](/features/amazon-ads-mcp/) can add demand context, [Skills](/features/skills/) preserve how your team ranks risk, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep inventory decisions with you. ## Separate the fee policy from the inventory decision The first move is a policy check, not a removal recommendation. The FBA Inventory Storage Overage Fees playbook says the overage fee stopped being charged in the named stores on July 1, 2024. The same source still describes monthly storage fees and an aged inventory surcharge as separate charges. Atlas returns that distinction with the retrieval context, so the agent does not collapse “overage fee removed” into “storage is free.” > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI can repeat a fee announcement, but it cannot verify which store, date, or fee family applies to your account. The agent reads the current seller context and Atlas-grounded policy before it labels an inventory issue. The output should be a short policy record: store scope, effective date, fee families still in play, and the source playbooks used. That record becomes the explanation for every downstream recommendation. ## Measure capacity in cubic feet, not units Capacity is a volume problem. The report exposes `capacity_usage_volume`, `capacity_limit`, `overage_volume`, and `volume_unit`; the unit is cubic feet. A unit count can rise while volume stays flat, or a small number of bulky units can create the larger constraint. The agent should read the Capacity Monitor or Inventory Performance view, then reconcile the report fields before ranking action. ```yaml inventory_health_snapshot: storage_type: oversize capacity_usage_volume: 600 capacity_limit: 500 overage_volume: 100 volume_unit: cubic feet decision: investigate slow-moving bulky inventory ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI usually turns “too much inventory” into a unit count when that is all a seller pasted into the prompt. The MCP reads the capacity fields and preserves the volume unit, while the Skill keeps a bulky ASIN from being hidden inside an account-wide average. The useful artifact is not a generic “reduce stock” list. It is a ranked queue by storage type, cubic-foot contribution, velocity, and removal or replenishment option. Keep `volume_unit` attached to every number so a later operator does not mistake volume for units. ## Review monthly storage separately Monthly storage fees remain a distinct operating signal after the overage-fee change. Review the Monthly Storage Fee report by ASIN, then identify the SKUs whose storage cost is persistent without a matching sell-through or replenishment need. Atlas retrieval also surfaces storage-utilization surcharge and AWD waiver context, so the agent can mark a fee as a policy exception rather than treating every charge as a capacity breach. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI can suggest liquidation, but it cannot tell whether a SKU is driving base storage, a utilization surcharge, or a fee covered by a program benefit. The agent reads the relevant report and explains which cost layer moved before anyone chooses a removal order. This is where the operator separates diagnosis from action. A high monthly storage charge may justify a sell-through, liquidation, return, or replenishment change, but the action belongs after the evidence review. The Skill should present the report rows, the decision rule, and the expected trade-off together. ## Catch aged inventory before the fifteenth-day snapshot The aged inventory surcharge applies to units stored 181 days or longer and is assessed using an inventory snapshot on the fifteenth day of each month. The FBA Inventory tool can show inventory that is already subject or will become subject within 60 days. The review should therefore sort by days-to-threshold, cubic-foot exposure, and available action window. ```text next_snapshot: 2026-08-15 asin: B0EXAMPLE12 age_days: 176 days_to_181_day_threshold: 5 per_unit_volume: 0.102 cubic feet recommended_review: removal, sell-through, or return economics ``` > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI can calculate “181 days” from a pasted date, but it cannot see the current age distribution or the next snapshot. The agent reads live FBA inventory, grounds the threshold in Atlas, and gives the team time to approve an action before the charge is assessed. The Aged Inventory Surcharge report is the evidence layer after the decision. It provides itemized SKU quantities, per-unit volume, surcharge tier, and amount charged. Keep the pre-snapshot queue and the post-charge report together so the team can compare the avoided cost with the action it chose. For a related report-driven workflow, see [FBA order and report review](/guides/seller-fbm-orders-reports/). ## What happens next Run the review on a schedule: policy check first, capacity volume second, monthly storage third, and aged-inventory countdown last. The agent should return a compact report with source playbooks, report fields, affected ASINs, and proposed actions. A human then approves removals, returns, liquidation, or replenishment changes through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/). The [Amazon Agent Data layer](/features/agent-atlas/) keeps the policy context attached to the decision, and the [Amazon Agent Flow](/features/amazon-agent-flow/) can carry the approved workflow forward. If your FBA review still asks only whether the overage fee exists, it is looking at one policy line instead of inventory health. Separate volume, storage, and age, then decide with the evidence in front of you. *Next in the series: turning the same age and volume queue into a controlled removal recommendation without losing the audit trail.* ### Find ASIN Margin Leakage with Net PPM URL: https://www.kuudo.com/guides/vendor-net-ppm-margin-leakage/ Run an Amazon Standard Identification Number (ASIN)-level Net PPM comparison, rank each negative rate contribution, then decompose the largest drags into shipped COGS/PCOGS, CCOGS, and sales-discount pressure where the connected report exposes those fields. The ranking is Kuudo-derived, not an Amazon metric. Amazon's dashboard identifies products driving profitability up or down after costs, funding, and discounts. That answers my question: *"Our Vendor Central revenue looks fine but margin is sliding. Which ASINs are actually dragging down our pure product margin?"* Revenue cannot show which equation term changed. The report must answer materiality first, then cause where the component fields support it. | Question | Test | Report output | | --- | --- | --- | | Biggest drag? | Rate-leakage proxy | Ranked ASINs | | Cost pressure? | PCOGS/revenue up | Cost flag | | Funding pressure? | CCOGS/revenue down | Funding flag | | Discount pressure? | Discounts/revenue up | Discount flag | The rate-leakage proxy and driver tests are Kuudo-derived from the sourced equation. Amazon does not name these fields. [ChatGPT, Claude, Perplexity, and Copilot](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: your data, your rules, and the way your business operates. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies live profitability context, [Skills](/features/skills/) preserve how your team ranks and explains margin leakage, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep decisions with you. ## The Net PPM dashboard identifies products driving profitability up or down The Net PPM dashboard identifies products that raise or lower profitability. It lives in Amazon Retail Analytics, which Amazon's reports index separates from Brand Analytics. An ABA customer or search dashboard is not the source. The retrieved **Amazon Retail Analytics and Reports Overview** defines Net PPM around cost of goods, vendor funding, and sales discounts. That keeps the Skill focused on profitability instead of treating revenue as a margin answer. | Run field | Bound value | | --- | --- | | Skill | `vendor-net-ppm-margin-leakage` | | Input | Account and two periods | | Scope | Selected ASIN set | | Connector | Selling Partner MCP | | Grounding | Atlas `amazon_vendors` | | Output | `analysis_report` | | Report header | Recorded value | | --- | --- | | Report | ASIN Net PPM analysis | | Vendor scope | Connected account or group | | Marketplace | Connected marketplace | | View | Manufacturing | | Current period | Selected closed period | | Comparison period | Selected comparable period | | Data as of | Close and refresh status | | Warehouse Deals | Excluded | | Material move | `{{material_ratio_move_pp}}` pp | | Residual tolerance | `{{residual_tolerance_pp}}` pp | The report renders its executive answer instead of hiding it in a notebook: | Executive result | Bound result | | --- | --- | | ASINs reviewed | `{{asins_reviewed}}` | | Negative PPM changes | `{{negative_change_count}}` | | Top-five leakage share | `{{top_five_leakage_share}}` | | Largest flagged driver | `{{largest_driver}}` | | Boundary warnings | `{{boundary_warnings}}` | Only the connected run populates those values. When a component field is unavailable, the executive answer keeps the ASIN ranking and marks the driver analysis incomplete. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Without a connected account, you must export and paste the dashboard data. The shared AI can discuss margin, but it cannot inspect the current ASIN rows or determine whether the selected account exposes the Net PPM dashboard. The Selling Partner MCP supplies that live vendor context. ## Net PPM separates revenue, PCOGS, CCOGS, and sales-discount pressure Net PPM separates four signed components, so each available ratio's direction supports a first-pass driver flag. The normalized equation is: ```text Net PPM = (shipped revenue - shipped COGS [PCOGS] + CCOGS - sales discounts) / shipped revenue ``` The **Amazon Retail Analytics Metric Glossary** defines Shipped COGS as Amazon's item procurement price, also called Product Cost of Goods Sold (PCOGS). Contra-COGS (CCOGS) is vendor funding collected for agreements tied to purchases, customer sales, or marketing. CCOGS includes Vendor Allowance, Quick Pay Discounts, and Discretionary COOP, but excludes display-ads Contra-COGS. One glossary rendering says "shipped CCOGS" in the subtraction term. The report normalizes it to shipped COGS/PCOGS because CCOGS is separately defined as the added funding term. | Type | Report field | Meaning | | --- | --- | --- | | Observed | Shipped revenue | Shipment-item price | | Observed | Shipped COGS/PCOGS | Amazon procurement cost | | Observed | CCOGS | Collected vendor funding | | Observed | Sales discounts | Discount formula term | | Derived | PCOGS ratio | PCOGS divided by revenue | | Derived | CCOGS ratio | CCOGS divided by revenue | | Derived | Discount ratio | Discounts divided by revenue | The run records a material-move threshold and a residual tolerance. Those are operator inputs, not Amazon thresholds. | Driver flag | Derived rule | Interpretation | | --- | --- | --- | | PCOGS pressure | Cost magnitude clears threshold | Procurement cost drag | | Funding pressure | Funding magnitude clears threshold | Less vendor funding | | Discount pressure | Discount magnitude clears threshold | More sales discount | | Mixed | Two magnitudes clear threshold | Shared cause | | Unclassified | No clear or complete cause | Check scope or rounding | Each component effect is signed: a negative value reduces Net PPM. Its pressure magnitude is `max(-component_effect_pp, 0)`. A single-driver label requires exactly one pressure magnitude greater than or equal to `{{material_ratio_move_pp}}` percentage points, and that magnitude must be the largest. **Mixed** requires two or more pressure magnitudes to clear the same threshold. **Unclassified** covers a missing component, no magnitude that clears the threshold, or an absolute residual greater than `{{residual_tolerance_pp}}` percentage points. The output records both inputs beside every label so another run can reproduce the classification. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** A pasted topline-revenue prompt can produce a plausible but unverified formula. You still have to supply current PCOGS, CCOGS, discounts, and revenue, and the shared AI can conflate Product Cost of Goods Sold with Contra-COGS. Atlas supplies the definitions the Skill applies on every run. ## A rate-leakage proxy ranks material ASIN deterioration before investigation Holding current revenue constant and multiplying it by a negative Net PPM rate change prioritizes ASINs with material sales and worsening margin. A large swing on a tiny ASIN cannot automatically outrank a larger drag. **Kuudo-derived calculations, not Amazon metrics** ```text net_ppm_delta_pp = current_net_ppm_pct - prior_net_ppm_pct current_margin_dollars_proxy = current_shipped_revenue * current_net_ppm_pct / 100 rate_leakage_dollars_proxy = current_shipped_revenue * max(prior_net_ppm_pct - current_net_ppm_pct, 0) / 100 pcogs_ratio_delta_pp = current_pcogs_ratio_pct - prior_pcogs_ratio_pct ccogs_ratio_delta_pp = current_ccogs_ratio_pct - prior_ccogs_ratio_pct discount_ratio_delta_pp = current_discount_ratio_pct - prior_discount_ratio_pct pcogs_effect_pp = -pcogs_ratio_delta_pp ccogs_effect_pp = ccogs_ratio_delta_pp discount_effect_pp = -discount_ratio_delta_pp component_pressure_magnitude_pp = max(-component_effect_pp, 0) explained_delta_pp = pcogs_effect_pp + ccogs_effect_pp + discount_effect_pp residual_delta_pp = net_ppm_delta_pp - explained_delta_pp ``` Holding revenue constant isolates rate deterioration from volume. The dollar fields are prioritization proxies, not booked loss, reimbursement value, or Amazon-calculated fields. Rounding and unavailable components can prevent reconciliation. The report sorts `rate_leakage_dollars_proxy` descending and fills five rows from the connected periods. The bindings contain no sample ASINs or percentages. | Rank | ASIN | Current revenue | Prior revenue | Prior PPM | Current PPM | Delta | Leakage | | --- | --- | --- | --- | --- | --- | --- | --- | | 1 | `{{rank_1_asin}}` | `{{rank_1_revenue}}` | `{{rank_1_prior_revenue}}` | `{{rank_1_prior_ppm}}` | `{{rank_1_current_ppm}}` | `{{rank_1_delta_pp}}` | `{{rank_1_leakage}}` | | 2 | `{{rank_2_asin}}` | `{{rank_2_revenue}}` | `{{rank_2_prior_revenue}}` | `{{rank_2_prior_ppm}}` | `{{rank_2_current_ppm}}` | `{{rank_2_delta_pp}}` | `{{rank_2_leakage}}` | | 3 | `{{rank_3_asin}}` | `{{rank_3_revenue}}` | `{{rank_3_prior_revenue}}` | `{{rank_3_prior_ppm}}` | `{{rank_3_current_ppm}}` | `{{rank_3_delta_pp}}` | `{{rank_3_leakage}}` | | 4 | `{{rank_4_asin}}` | `{{rank_4_revenue}}` | `{{rank_4_prior_revenue}}` | `{{rank_4_prior_ppm}}` | `{{rank_4_current_ppm}}` | `{{rank_4_delta_pp}}` | `{{rank_4_leakage}}` | | 5 | `{{rank_5_asin}}` | `{{rank_5_revenue}}` | `{{rank_5_prior_revenue}}` | `{{rank_5_prior_ppm}}` | `{{rank_5_current_ppm}}` | `{{rank_5_delta_pp}}` | `{{rank_5_leakage}}` | I would not hand operations a rank-only table. The same rows continue into the causal review and ownership queue: | ASIN | Cost effect | Funding effect | Discount effect | Residual | Driver | | --- | --- | --- | --- | --- | --- | | `{{rank_1_asin}}` | `{{rank_1_pcogs_effect}}` | `{{rank_1_ccogs_effect}}` | `{{rank_1_discount_effect}}` | `{{rank_1_residual}}` | `{{rank_1_driver}}` | | `{{rank_2_asin}}` | `{{rank_2_pcogs_effect}}` | `{{rank_2_ccogs_effect}}` | `{{rank_2_discount_effect}}` | `{{rank_2_residual}}` | `{{rank_2_driver}}` | | `{{rank_3_asin}}` | `{{rank_3_pcogs_effect}}` | `{{rank_3_ccogs_effect}}` | `{{rank_3_discount_effect}}` | `{{rank_3_residual}}` | `{{rank_3_driver}}` | | `{{rank_4_asin}}` | `{{rank_4_pcogs_effect}}` | `{{rank_4_ccogs_effect}}` | `{{rank_4_discount_effect}}` | `{{rank_4_residual}}` | `{{rank_4_driver}}` | | `{{rank_5_asin}}` | `{{rank_5_pcogs_effect}}` | `{{rank_5_ccogs_effect}}` | `{{rank_5_discount_effect}}` | `{{rank_5_residual}}` | `{{rank_5_driver}}` | | ASIN | Investigation note | Owner | Evidence request | | --- | --- | --- | --- | | `{{rank_1_asin}}` | `{{rank_1_note}}` | `{{rank_1_owner}}` | `{{rank_1_evidence}}` | | `{{rank_2_asin}}` | `{{rank_2_note}}` | `{{rank_2_owner}}` | `{{rank_2_evidence}}` | | `{{rank_3_asin}}` | `{{rank_3_note}}` | `{{rank_3_owner}}` | `{{rank_3_evidence}}` | | `{{rank_4_asin}}` | `{{rank_4_note}}` | `{{rank_4_owner}}` | `{{rank_4_evidence}}` | | `{{rank_5_asin}}` | `{{rank_5_note}}` | `{{rank_5_owner}}` | `{{rank_5_evidence}}` | If components are unavailable, the rank still ships, the driver reads **Unclassified**, and the queue requests the missing evidence rather than fabricating a cause. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI can suggest ranking logic after you paste data. You must still assemble comparable periods, normalize percentages, run the calculations, and carry the ranked result into a separate work queue. The Skill preserves the ranking logic and produces the queue from each connected period. ## Manufacturing-view access and Warehouse Deals exclusions define the report boundary ARA access does not guarantee Net PPM access. All vendors have Sourcing views; manufacturers also receive a Manufacturing view for ASINs they manufacture. Amazon displays Traffic, Net PPM, and Forecasting only to manufacturers. If an eligible manufacturer cannot see Net PPM, contact retail partners or open a Contact Us case. Amazon's wording says Brand Analytics, including ARA, excludes Warehouse Deals because vendors do not collect the proceeds. Retail-partner tools may include those sales, so a vendor-facing and internal number can legitimately differ. | Boundary check | Report status | Next step | | --- | --- | --- | | View | Manufacturing required | Check entitlement | | Period | Closed and populated | Rerun after refresh | | Data as of | Status recorded | Avoid instant-data claim | | Warehouse Deals | Excluded consistently | Reconcile internal scope | | Metric labels | PCOGS differs from CCOGS | Re-map before ranking | | Derived fields | Explicitly labeled | Keep separate from Amazon | Amazon aims to update weekly reports within 72 hours, but reports can take up to a week after period close. The report records its period and as-of status instead of calling the data instantaneous. The provenance footer records exact evidence instead of counts: | Evidence type | Identifier | | --- | --- | | Source ref | `3dff9404cf730721` | | Source ref | `42c4d79b31a1eefd` | | Source ref | `285ce9ce068d666c` | | Content hash | `f334dd577810b7ca` | | Content hash | `647fba2b2a4cc729` | | Content hash | `927f70d802757798` | | Content hash | `2d8af27996d3f246` | | Content hash | `8e9bd73e5f629c0a` | | Content hash | `33460c203219fb4b` | | Content hash | `5dde3db908638908` | It also records Atlas collection `amazon_vendors`, corpus version `794a9c3de8380dfa`, connected periods, run timestamp, and the exact titles **Amazon Retail Analytics and Reports Overview**, **Amazon Retail Analytics Metric Glossary**, and **Vendor Reports and Analytics Overview**. Derived prioritization fields remain labeled as non-native Amazon metrics. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot inspect your view entitlements or reconcile a vendor export with an internal retail-partner number. The Selling Partner MCP supplies the authorized scope, while Atlas keeps the Warehouse Deals boundary attached to the comparison. ## What happens next Turn the ranked report into an investigation queue, not an automatic commercial decision. Review the top ASINs and, where PCOGS, CCOGS, and sales-discount fields are exposed, confirm the largest component effect. When they are not, preserve the rank, mark the driver unclassified, and request the missing evidence. Then assign an owner. Run the workflow on closed, comparable periods and preserve the as-of status. If Warehouse Deals or an internal retail-partner scope explains a mismatch, flag the row for reconciliation instead of rewriting the vendor-facing metric. Amazon did not supply remediation thresholds or an automatic action policy in the retrieved material, so the operator reviews the evidence before changing terms or escalating. The [Amazon Agent Data layer](/features/amazon-agent-flow/) combines [Amazon Ads MCP](/features/amazon-ads-mcp/), [Selling Partner MCP](/features/amazon-selling-partner-mcp/), [Amazon Agent Atlas](/features/agent-atlas/), and [Skills](/features/skills/) so the report can be grounded, rerun, reviewed, and handed to an operator as one workflow. Revenue can stay stable while ASIN economics deteriorate; a sourced Net PPM decomposition turns that ambiguity into a ranked investigation queue. *Next, use the same evidence-first pattern when [disputing PO on-time accuracy chargebacks](/guides/vendor-po-chargeback-disputes/).* ### Brand Registry: Fix 'Trademark Already Enrolled' URL: https://www.kuudo.com/guides/seller-brand-registry-trademark-conflict/ The "trademark is already enrolled" error is a role-request problem, not an enrollment problem: someone already holds the Administrator role for your brand, and the fix is getting added as an additional user, not filing again from scratch. [Our agent](/features/ai-clients/) produced that answer, and the resolution plan behind it, by running a [Skill](/features/skills/) on the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), grounded in the Brand Registry rules [Amazon Agent Atlas](/features/agent-atlas/) retrieves. That plan exists because of a message from our brand manager: *"We're trying to enroll our brand in Amazon Brand Registry and getting the 'trademark is already enrolled' error. What do we do?"* Nobody on the team had enrolled anything; the person who set the brand up left last year. So I asked the agent before anyone opened a support case blind. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads the current brand and account state, [Skills](/features/skills/) preserve how your team investigates and resolves conflicts, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep sensitive submissions with you. ## "Trademark is already enrolled" means an Administrator already exists, and the fix is becoming an additional user The first thing the Skill pulled was the rule the error actually points at. Atlas retrieves it from the **Brand Registry FAQ** playbook in the `amazon_sellers` corpus: if a trademark is already enrolled in Brand Registry, you contact the brand's Administrator and request to be added as an **additional user**. An Administrator always exists: the **Brand Registry Protection Roles** playbook says the user who enrolls a brand is automatically assigned the Administrator and Rights Owner roles, and only Administrators can assign roles to others. Three protection roles exist in total, Administrator, Rights Owner, and Registered Agent, and the last two are mutually exclusive. Amazon's own routing for this message is its Brand Registry Access: Troubleshooting Guide; the fix mechanics live in the FAQ and role playbooks the agent retrieved. The diagnosis: not a trademark dispute, not a second enrollment. A role request, aimed at whoever enrolled first. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI can't see whether your trademark number is already enrolled in Brand Registry or under whose account; you only discover the conflict when the enrollment flow throws the error. The MCP-connected agent reads the live state before you burn an application. > - **Your judgment, running every time.** Ask the shared AI about the error and it reaches for trademark-office advice: dispute the mark, call your attorney. The grounded answer is a role request to the existing Administrator, a rule that lives in Amazon's documentation, not public training data. > - **Your account, as it is right now.** To get even the wrong answer, you pasted your trademark number and account details into a chat history you don't control. The MCP reads them over an authorized connection, and they stay inside your account. ## Request an invitation from a known Administrator, or continue to apply and share your contact information Branch one is the shortcut: if you know the identity of a current Administrator, reach out to them directly and request an invitation to the brand. On their side the flow is **Invite a user to your brand**, then **Send invitation**, and you have to accept the invitation before anything changes. We had no name to call, which is the branch the Brand Registry FAQ covers next: if you're unsure who to contact, or the current Administrator is inactive, you can **continue to apply** for Brand Registry with the same trademark. If you're an eligible user, the application gives you a step to **share your contact information** with the Administrator. Approval is discretionary by design: an active Administrator will use that information to add your Brand Registry account to the brand, *if they choose to do so*. The Brand Registry Protection Roles playbook shows where the request lands: in the Administrator's **User Permissions** tool under **Access requests**, where they approve or decline each request at their discretion. The Skill assembled all of it into the decision plan, every branch traced to a retrieved rule: ```yaml decision_plan: trigger: error_message: "Trademark is already enrolled" surface: "Brand Registry enrollment flow" facts_to_gather: - administrator_known: "Do you know the identity of a current Administrator?" - administrator_active: "Is that Administrator active and reachable?" - other_brand_enrolled: "Do you have another brand enrolled in Brand Registry?" branches: - id: administrator-known-active condition: "You know a current Administrator and they are active" action: "Reach out directly and request an invitation as an additional user" admin_side: "Invite a user to your brand > Send invitation; you accept it" outcome: "Added to the brand with the assigned protection role" - id: administrator-unknown condition: "Unsure who the Administrator is, or cannot reach them" action: "Continue to apply with the same trademark" during_flow: "Share your contact information (step shown to eligible users)" admin_side: "Request lands in User Permissions > Access requests" decision_rule: "Approved or declined at their discretion (if they choose to do so)" outcome_if_approved: "Your Brand Registry account is added to the brand" outcome_if_no_response: "Fall through to administrator-inactive" - id: administrator-inactive condition: "No active Administrator (inactive, or left the company)" paths: - id: appeal-form entry: "Same flow: a warning links to the Share Contact Information form" action: "Click 'use this form'; the bottom link opens Appeal Submission" gate: "Appeal Submission link is visible only to eligible users" outcome: "Success adds you with Administrator and Rights Owner roles" - id: brand-registry-support gate: "One other enrolled brand plus Brand Registry site access" action: "Open a case; select 'Update brand ownership' as the topic" outcome: "Request reviewed; response follows" while_waiting: review_sla: "Average 10 business days; identity checks can extend it" deadlines: - "Complete the application within three days or it shows Expired" - "Reply to the case within 10 days with the verification code and case ID" watch: "Brand applications page for status changes" statuses: [Approved, Rejected, Pending submission, Expired, Ineligible, Pending review, Withdrawn, Removed from Brand Registry] if_rejected: "Rejected can't be edited: click Copy, correct, resubmit" ``` > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI can't submit the Brand Registry application or complete the share-contact step for you; the most it produces is a draft message. The Skill scripts the exact click path, continue to apply and then share contact information, so you execute it correctly the first time. > - **Your account, as it is right now.** The shared AI can't see the Administrator's **Access requests** queue, so it can't tell you whether your request is pending, approved, or declined. Silence from Amazon reads like rejection when it may just be an Administrator who hasn't opened **User Permissions** yet. ## An inactive Administrator opens two escalation paths: the "use this form" appeal and an "Update brand ownership" support case If the Administrator is inactive, the FAQ's instruction is literal: click **use this form** to submit your request form. The label undersells what's behind it. You re-enter the already-registered trademark number during the application, a warning appears offering a link to a form for sharing your contact information, and that link opens the **Share Contact Information** form. At the bottom of that form sits one more link, available only to eligible users, leading to an **Appeal Submission** form. Neither playbook defines who counts as eligible, so if you've hunted for the appeal form and never found it, that gate is why. When an appeal succeeds, you're added to the brand with the Administrator and Rights Owner roles, which makes you the brand's new front door. There's a parallel escalation path with its own gate. If you have at least one other brand already enrolled in Brand Registry and you can access the Brand Registry site, you can submit the request through Brand Registry Support and select **Update brand ownership** as the topic. Amazon asks that you identify the nature of the error before contacting support, and that's exactly the triage the plan encodes: it checks the gates and routes you to the path your account qualifies for, instead of letting you file a case that bounces. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI does not know the **use this form** fallback exists, that it hides at the bottom of the **Share Contact Information** form, or that the **Appeal Submission** link is eligibility-gated. You get routed to generic Seller Support and never see the appeal path. Atlas carries the exact rule. > - **Your account, as it is right now.** The support path is gated on having at least one other brand already enrolled under your account. A chat can't read your Brand Registry account, so it can't tell you which of the two escalation paths you qualify for. One MCP read settles it. ## Once you reapply, the clock is Amazon's: 10 business days of review, a 3-day completion deadline, a 10-day reply window Reapplying starts a review that takes an average of **10 business days**, and Amazon may need additional information to verify your identity, which can extend the review period. Two more deadlines ride along, both pulled from the **Brand Registry Application Process** playbook and both easy to miss. An application that isn't completed within three days flips to **Expired**. And when Amazon sends a verification code, you reply to the case within 10 days with the code and the case ID. Those are two different tens: ten business days is how long Amazon takes on average; ten days is how long you get to reply. Every application surfaces on the **Brand applications** page with one of eight statuses: **Approved**, **Rejected**, **Pending submission**, **Expired**, **Ineligible**, **Pending review**, **Withdrawn**, or **Removed from Brand Registry**. A rejected application can't be edited. You locate it on the **Brand applications** page, click **Copy**, correct the details, and resubmit. Before that resubmission, the Skill runs the decline-reason pre-checks from the **Manage Brand Registry Application Issues** playbook: expired trademarks aren't eligible, supplemental trademarks aren't accepted (the Principal Register is required), and Amazon currently accepts only text-based marks or image-based marks with words, letters, or numbers. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI can't check the **Brand applications** page, so it can't tell **Pending review** from **Expired**, and it can't warn you that an incomplete application dies after three days. The MCP-connected agent watches the page for you. > - **Your judgment, running every time.** The shared AI doesn't know the verification-code reply window is 10 days against the case ID. Miss it and the enrollment silently stalls, with nobody telling you why. ## What happens next The plan isn't a document the agent hands over and forgets; it's a watch list. The agent monitors the **Brand applications** page through the Selling Partner MCP and reports status changes instead of letting the team infer them from silence. It flags the three-day completion deadline the moment an application sits in **Pending submission**, and it counts down the 10-day verification-code window against the case ID so the reply goes out with days to spare. If the outreach branch stalls with no Administrator response, the plan falls through to the appeal branch instead of waiting for someone to remember. The complete workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): the Selling Partner MCP supplies the live Brand Registry and application state, [Amazon Ads MCP](/features/amazon-ads-mcp/) covers the advertising side of the same account when it's needed, and Atlas grounds every branch of the plan. Scheduled as a recurring Skill, the same triage re-runs until the application lands on **Approved**, and every Amazon-facing step, the outreach message, the appeal submission, waits for a person to say yes, the gate pattern from [human approval for agent activation](/guides/human-approval-for-amc-activation/). That's the whole pattern: read the live state first, walk Amazon's real decision tree, and put the deadlines on a clock the agent owns instead of a person's memory. *Next in the series: [letting the agent fix a failing listing without editing it blind](/guides/seller-listing-agentic-audit-to-patch/), the same read-first, approve-before-ship discipline pointed at your live catalog.* ### Dispute a PO On-Time Accuracy Chargeback URL: https://www.kuudo.com/guides/vendor-po-chargeback-disputes/ Dispute a PO on-time accuracy chargeback with evidence that directly contradicts Amazon's recorded data, and file it through Operational Performance within 30 days of the notification. The strongest submission is not a long explanation. It names the wrong field, supplies the matching PO or carrier record, and asks Amazon to reverse the specific chargeback. | Situation | Evidence | Route | Deadline | | --- | --- | --- | --- | | Wrong CRDD or PRO | Appointment or PRO record | Dispute by data | 30 days | | External event | Event evidence | External exception | 30 days | | Duplicate amount | Chargeback IDs | Raise dispute | 30 days | | First refusal | New contrary evidence | Second dispute | 30 days | That is the answer to the message that landed in our queue: *"Vendor Central just hit us with a PO on-time accuracy chargeback we believe is wrong. How do we dispute it before the 30-day window closes?"* I gave the chargeback record and PO context to our agent through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), with [Amazon Agent Atlas](/features/agent-atlas/) grounding the dispute rules. [ChatGPT, Claude, Perplexity, and Copilot](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: your data, your rules, and the way your business operates. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies current PO and dispute evidence, [Skills](/features/skills/) preserve how your team triages and resolves exceptions, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep submissions with you. ## The first dispute is a 30-day evidence deadline, not a support case Amazon's **How to Dispute a Chargeback in Vendor Operational Performance** says the initial dispute is due within 30 days of the notification date. If Amazon rejects it, the second dispute is due within 30 days of the first refusal notification. Those are separate clocks, so the agent records both dates instead of treating the first deadline as the whole lifecycle. Start in **Reports > Operational Performance**, open the full chargeback list, locate the chargeback ID, and confirm that **Dispute this chargeback** is available. The same help content limits a chargeback to two disputes. After two rejections, it is no longer eligible, and a Contact Us case cannot substitute for those two dashboard submissions. The practical rule is simple: preserve the first attempt. Submit the cleanest contrary record you have, keep the dispute ID and attachments together, and reserve the second attempt for new evidence or a precise correction to Amazon's refusal reason. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Chat cannot see whether the button is available, whether the charge is pending, or whether a prior dispute already used one attempt. > - **Your judgment, running every time.** A generic answer may tell you to open support immediately, even though Amazon's policy requires two dashboard disputes first. > - **Your call, before anything changes.** Plain chat cannot create the deadline, attach the evidence, or submit through Operational Performance. ## The evidence must contradict the exact PO on-time data point Amazon's **Vendor Central Chargebacks Support and Policies** says PO on-time accuracy covers confirmed products that miss the PO window, including items placed on backorder. The indexed policy measures the prepaid case against the carrier requested delivery date (CRDD). A statement that the shipment was "on time" is weak; an appointment record showing the CRDD inside the delivery window directly contests Amazon's recorded value. Smart Aggregation gives the agent four routes, and choosing the wrong one can end the review before the evidence is considered. | Problem | Correct route | Core input | | --- | --- | --- | | Wrong Amazon field | Dispute by data | PRO or contrary value | | Severe external event | External exception | Event evidence | | Other non-data concern | Non-data problem | Issue explanation | | Not in Smart Aggregation | Legacy dispute | Chargeback record | The **Vendor Central Chargebacks - Smart Aggregation Dashboard** says Dispute by data can validate a core attribute such as a PRO and return a near-real-time indication of whether the evidence is sufficient. External-events exceptions are for circumstances such as severe weather; an unrelated accuracy complaint submitted there is automatically denied. Smart Aggregation is also limited to supported vendors and chargeback types, so the agent falls back to the legacy dashboard when the record is not present. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Chat cannot compare Amazon's CRDD or PRO with the appointment record in your account. > - **Your judgment, running every time.** The shared AI may recommend a persuasive narrative when the workflow expects one contrary data point. > - **Your call, before anything changes.** Chat cannot choose the live dispute route or verify that Smart Aggregation is available for the record. ## A useful dispute letter names one error and one contrary record The agent produced the following dispute letter after matching the chargeback ID, PO, defect field, and attachment names. Keep the merge fields intact until the corresponding record has been verified. Do not submit a field whose evidence you cannot attach. ```text Subject: PO on-time accuracy dispute for chargeback {{CHARGEBACK_ID}} We dispute PO on-time accuracy chargeback {{CHARGEBACK_ID}} for PO {{PO_NUMBER}}, notified on {{NOTIFICATION_DATE}}, in the amount of {{DISPUTED_AMOUNT}}. Amazon's chargeback record shows: - Defect type: {{DEFECT_TYPE}} - Recorded value: {{AMAZON_RECORDED_VALUE}} Our source record shows: - Correct value: {{VENDOR_SOURCE_VALUE}} - Source: {{SOURCE_SYSTEM_OR_DOCUMENT}} - Record date: {{SOURCE_RECORD_DATE}} The attached {{ATTACHMENT_NAME}} ties the PO and shipment to the correct {{CONTESTED_FIELD}}. It contradicts the value shown in the chargeback record. Please review the attached evidence and reverse chargeback {{CHARGEBACK_ID}}. If another field is controlling the decision, please name that field and its recorded value in the resolution. ``` For a duplicate, replace the source-record paragraph with both chargeback IDs and state which amount is duplicated. Amazon's Smart Aggregation guidance explicitly says to raise a dispute when duplicate chargebacks appear so the duplicate can be reversed. For a second dispute, lead with the first dispute ID and Amazon's refusal reason, then identify the new evidence that answers that reason. The letter stays short because the evidence carries the claim. Every sentence either identifies the disputed record, links the contrary evidence to it, or requests a correction. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Chat can fill the template only with values a person pastes into the conversation. > - **Your judgment, running every time.** A generic letter often argues fairness without naming the controlling field or dashboard route. > - **Your call, before anything changes.** Plain chat cannot attach the source record, preserve the dispute ID, or route a refusal into a second review. ## Smart Aggregation separates previews, confirmed charges, and reversals The **Vendor Central Chargebacks - Smart Aggregation Dashboard** surfaces unconfirmed chargebacks in the **Processing** tab every seven days. Those rows are projections, not invoices. Once confirmed, a chargeback moves to **Confirmed**. Starting April 30, 2024, Amazon's indexed guidance gives 30 days' notice before invoicing PO on-time accuracy chargebacks. That preview creates useful working time. The agent can open an evidence task while the item is still processing, pull the PO and carrier references, and flag a likely duplicate before the confirmed amount reaches the dispute queue. Smart Aggregation groups chargebacks at the most granular available attribute, shows the final confirmed amount, and removes duplicates from that view. If a duplicate still appears, the guidance says to dispute it. The status is part of the workflow. **Under review** means wait for the dispute team. **Reversed** means the payment returns in the next payment cycle. **Waived** means Amazon did not charge it. **Charged** means the amount was enforced and remains eligible for the dispute process if the button is available. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** Chat cannot distinguish a Processing preview from a confirmed or already reversed charge in the account. > - **Your judgment, running every time.** An ungrounded answer can treat every row as money already deducted and start the dispute too late. > - **Your call, before anything changes.** Plain chat cannot monitor the seven-day preview cycle or reconcile a reversal with the next payment cycle. ## What happens next Turn the dispute into a reviewed recurring [Skill](/features/skills/), not an auto-submit rule. On each seven-day Processing refresh, the Skill identifies new PO on-time records, collects the controlling PO and carrier fields through the Selling Partner MCP, checks the deadline and prior-attempt count, then prepares the letter and attachments for human approval. The complete workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): the Selling Partner MCP supplies live PO and catalog context, [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies the advertising side of the same business when needed, Atlas grounds the chargeback rules, and Skills preserve the evidence and approval trail. The final submission remains a deliberate operator action. The letter is the last step; the winning work is matching one chargeback data point to one contrary record before the clock expires. *Next, trace what the chargebacks you did not win are costing you downstream, with [ASIN margin leakage in Net PPM](/guides/vendor-net-ppm-margin-leakage/).* ### Measure Off-Amazon Conversions in AMC URL: https://www.kuudo.com/guides/amc-off-amazon-conversion-events-manager/ Amazon Marketing Cloud (AMC) measures off-Amazon conversions only after Events Manager signals are configured, associated with the relevant DSP order, and available in the analysis instance. This workflow joins delivery to web, app, store, or offline outcomes, keeps purchase sales separate from non-purchase value, and returns a grounded total-impact query for repeatable reporting. Yes, [Amazon Marketing Cloud](/features/amc/) (AMC) can measure DTC web, app, store, and offline conversions once Events Manager receives them through Amazon Ad Tag (AAT), Conversions API (CAPI), or a Mobile Measurement Partner (MMP), the event is defined in the Amazon demand-side platform (DSP), and it is associated with a DSP order. The practical difference from Amazon conversions is the setup and taxonomy: off-Amazon subtypes arrive as numeric codes, purchase sales and non-purchase value mean different things, and the complete history starts on October 20, 2023. I handed the question to our agent through the [Amazon Ads MCP](/features/amazon-ads-mcp/), grounded by [Amazon Agent Atlas](/features/agent-atlas/), and asked it to prove each gate before running AMC SQL. | Question | Answer | | --- | --- | | Can AMC read DTC events? | Yes, through Events Manager | | Required ingestion | AAT, CAPI, or MMP | | Required DSP setup | Define event; link DSP order | | Purchase revenue | `off_amazon_product_sales` | | Non-purchase value | `off_amazon_conversion_value` | | Total product sales | `combined_sales` | | Complete history | `10/20/2023` onward | [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## Events Manager data reaches AMC only after the event is linked to a DSP order Sending an event to Amazon DSP is necessary but not sufficient. The **Introduction to Events Manager** instructional query names three prerequisites: set up AAT, CAPI, or MMP; define the event in Amazon DSP; and associate that event with an Amazon DSP order. If the order association is missing, the data does not flow into AMC. The empty result can look like zero customer activity even though the failure is configuration. The agent therefore runs a preflight before SQL. It checks the ingestion source, event definition, order association, AMC instance, and requested start date, then returns a pass or a named setup failure. That sequence also keeps the claim precise: Events Manager covers off-Amazon web, offline, and app events used for attribution, reporting, optimization, and targeting, but the event must pass the DSP setup gate first. All Events Manager signals are available in AMC whether ad-exposed or not, and the playbook says the paid `conversions_all` subscription is not required to query non-ad-exposed Events Manager signals. That makes non-ad-exposed analysis possible without making it causal proof. It is a comparison and audience-discovery input, not evidence that advertising created an outcome in a user who was never exposed. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see whether the event definition is actually linked to the order. You would have to inspect DSP yourself and paste the result back into chat. > - **Your judgment, running every time.** The shared AI can write plausible AMC SQL while omitting the order-association gate. The query then returns no rows, and the setup failure is mistaken for zero conversions. > - **Your call, before anything changes.** Plain chat cannot run the preflight or execute the AMC workflow. The Amazon Ads MCP can perform those account-bound checks through an authorized connection. ## Purchase sales and non-purchase conversion value are different measurements `off_amazon_product_sales` is monetary sales from off-Amazon purchases. `off_amazon_conversion_value` is an advertiser-defined, unitless value for non-purchase events such as a page view, app install, lead, or form submission. Do not add the second field to revenue or use it in return on ad spend (ROAS) unless the advertiser has explicitly documented a monetary scoring convention outside the schema. Events Manager also represents off-Amazon `conversion_event_subtype` values as numeric codes. The retrieved mapping is: | Event | Code | Monetary | | --- | ---: | --- | | Subscribe | `5` | No | | Application | `7` | No | | Sign-up | `21` | No | | App first start | `37` | No | | Add to cart | `53` | No | | Off-Amazon purchase | `54` | Yes | | Other | `133` | No | | Page view | `134` | No | | Search | `135` | No | | Contact | `136` | No | | Checkout | `140` | No | | Lead | `141` | No | The event formerly labeled **Product purchased** is now **Off-Amazon purchase**. The Skill keeps the mapping versioned and leaves an unknown code visible instead of coercing it to a familiar label. That is safer than silently converting an unrecognized future subtype into revenue. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI may compare the subtype to `purchase` or `add_to_cart`, but Events Manager sends the numeric codes `54` and `53` for those off-Amazon events. > - **Your judgment, running every time.** The shared AI may call every configured value revenue or reuse the old Product purchased label. That inflates ROAS and hides a renamed event. > - **Your account, as it is right now.** Plain chat cannot see the advertiser-defined event names or determine whether an unknown subtype has entered the instance. The agent checks the returned taxonomy before labeling the output. ## The canonical Total Impact query joins campaign delivery to conversion outcomes Amazon's canonical **Total Impact Analysis** query from **Introduction to Events Manager** joins campaign cost, impressions, and unique reach from `dsp_impressions` to Amazon and off-Amazon outcomes from `amazon_attributed_events_by_traffic_time`. It keeps purchase sales, non-purchase value, combined sales, and conversion counts separate, then calculates Amazon and off-Amazon ROAS from the corresponding monetary sales fields. Set the workflow's date range in the AMC query editor to October 20, 2023 or later before running it. ```sql -- Instructional Query: How to query Events Manager Signals - Total Impact Analysis -- -- AD PRODUCTS: Amazon DSP /* ------- Customization Instructions ------- 1) Set the "Date range" in the Query Editor to define the time range. 1a) It is recommended to query events after 10/20/2023, as events before this date may be missing dimensions and metrics. */ -- Gather campaign cost and impression data WITH traffic AS ( SELECT campaign_id_string, campaign, SUM(impressions) AS impressions, SUM(total_cost / 100000) AS total_cost, COUNT(DISTINCT user_id) AS unique_reach FROM dsp_impressions WHERE user_id IS NOT NULL GROUP BY 1, 2 ), -- Gather Amazon and off-Amazon (Events Manager events) conversion data conversions AS ( SELECT campaign_id_string, conversion_event_source_name, conversion_event_name, conversion_event_category, CASE conversion_event_subtype WHEN 53 THEN 'Add to shopping cart' WHEN 7 THEN 'Application' WHEN 140 THEN 'Checkout' WHEN 136 THEN 'Contact' WHEN 141 THEN 'Lead' WHEN 54 THEN 'Off-Amazon purchase' WHEN 133 THEN 'Other' WHEN 134 THEN 'Page View' WHEN 135 THEN 'Search' WHEN 21 THEN 'Sign-up' WHEN 5 THEN 'Subscribe' ELSE conversion_event_subtype END amazon_conversion_event, SUM(off_amazon_conversion_value) AS off_amazon_conversion_value, SUM(off_amazon_product_sales) AS off_amazon_product_sales, SUM(conversions) AS conversions, SUM(total_product_sales) AS total_product_sales, SUM(total_purchases) AS total_purchases, SUM(combined_sales) AS combined_sales FROM amazon_attributed_events_by_traffic_time GROUP BY 1, 2, 3, 4, 5 ) -- Join cost and impressions with conversions SELECT t.campaign, c.conversion_event_category, c.conversion_event_source_name, c.conversion_event_name, c.amazon_conversion_event, SUM(t.impressions) AS impressions, SUM(t.total_cost) AS total_cost, SUM(t.unique_reach) AS unique_reach, SUM(COALESCE(c.off_amazon_conversion_value, 0)) AS off_amazon_conversion_value, SUM(COALESCE(c.off_amazon_product_sales, 0)) AS off_amazon_product_sales, SUM(COALESCE(c.conversions, 0)) AS conversions, SUM(COALESCE(c.total_product_sales, 0)) AS total_product_sales, SUM(COALESCE(c.total_purchases, 0)) AS total_purchases, SUM(COALESCE(c.combined_sales, 0)) AS combined_sales, CASE WHEN SUM(t.total_cost) > 0 THEN SUM(COALESCE(c.total_product_sales, 0)) / SUM(t.total_cost) ELSE 0 END AS amazon_roas, CASE WHEN SUM(t.total_cost) > 0 THEN SUM(COALESCE(c.off_amazon_product_sales, 0)) / SUM(t.total_cost) ELSE 0 END AS off_amazon_roas FROM traffic t LEFT JOIN conversions c ON t.campaign_id_string = c.campaign_id_string GROUP BY 1, 2, 3, 4, 5 ``` The query stays faithful to the retrieved instructional query, including Amazon's `total_cost / 100000` normalization and campaign-ID join. Inspect the returned source names and event definitions before operationalizing it; unknown subtype codes stay visible through the `ELSE` branch instead of being silently relabeled. `combined_sales` is Amazon `total_product_sales` plus `off_amazon_product_sales`; it does not include the unitless value assigned to non-purchase conversions. The query reports Amazon and off-Amazon ROAS separately, so leads and page views never enter either monetary numerator. A combined-sales return can be derived downstream only after the output grain and cost allocation have been reviewed. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot discover the source names and custom event names in your AMC instance. You would have to run the query, export the result, and paste it back. > - **Your judgment, running every time.** The shared AI may calculate total impact from Amazon sales alone or add `off_amazon_conversion_value` to the numerator. Both change what ROAS means. > - **Your call, before anything changes.** Plain chat cannot execute or schedule the AMC workflow. The Amazon Ads MCP runs the canonical query, while the Skill validates returned codes before downstream calculation. ## Events Manager history is complete only from October 20, 2023 onward Treat October 20, 2023 as the completeness boundary for the named Events Manager dimensions and metrics in `amazon_attributed_events_by_*` and `conversions*` tables. Earlier periods can be missing those fields. A range before the boundary can also omit 30-day ad-unexposed events from `conversions` and `conversions_with_relevance`, so a lower historical count is not safely interpreted as lower customer activity. The **Off-Amazon Conversions Playbook** carries the result beyond reporting into campaign measurement, segmentation, and separately reviewed audience creation. Examples include users who added to cart off Amazon but did not purchase, converters who were not ad-exposed, and non-ad-exposed purchasers of product A who may be candidates for product B. Those are useful hypotheses. They do not prove incrementality, and measurement SQL does not activate an audience by itself. AMC only returns aggregated, pseudonymized outputs that meet its aggregation thresholds. The agent keeps the output at campaign and event grain, avoids `SELECT *`, rejects unsafe start dates, and reports suppressed or unknown results instead of turning them into zeros. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI may query the advertiser's full history and interpret missing pre-boundary data as zero. The agent applies the `10/20/2023` guard before execution. > - **Your account, as it is right now.** The shared AI cannot see whether sparse event groups were suppressed by AMC thresholds or whether the requested window crosses the completeness boundary. > - **Your call, before anything changes.** Plain chat cannot schedule a corrected run or route a reviewed segment into a separate audience workflow. The Skill can stop the run, explain the guard, and continue only with an approved window. ## What happens next After the preflight and subtype checks pass, schedule the total-impact analysis as a recurring Skill. Keep Amazon product sales, DTC purchase sales, and non-purchase conversion value in separate output columns; use `combined_sales` for the Amazon-plus-DTC product-sales view; and review any unknown subtype before the run reaches a dashboard or optimization rule. The complete workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): the [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies live AMC access, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) adds catalog or retail context when the decision needs it, [Skills](/features/skills/) make the checks recurring, and Amazon Agent Atlas grounds the taxonomy, date rule, and setup gates. If a segment is worth activating, hand it to a separate reviewed audience workflow rather than making activation an invisible side effect of measurement. Events Manager turns DTC outcomes into dependable AMC measurement only when ingestion, DSP association, event taxonomy, and revenue semantics are all correct. *Next, place those captured outcomes inside [the full AMC path across DSP and sponsored ads](/guides/amc-path-to-conversion-sankey/).* ### Rebuild Your Listing Images From Your Own Photos URL: https://www.kuudo.com/guides/seller-listing-image-regeneration-seeded/ Rebuilding a thin listing gallery is one conversation: the [Amazon Agent Iris](/features/amazon-agent-iris/) Skill seeds new images from your own product photos, fills factual details from the listing's own catalog data, grades every image against Amazon's rules through [Amazon Agent Atlas](/features/agent-atlas/), and waits for you to approve before it publishes a single one through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/). One photo in, a full compliant set out, nothing live until you say yes. That sequence exists because of a Slack message I get some version of every month: *"Can the agent use our existing product photos as seeds to make better listing images that still pass Amazon's image rules, and let me approve before anything publishes?"* The pain underneath is the long tail. Most catalogs stay underbuilt because fixing every laggard meant a photographer, a studio, a designer, and a round of manual uploads, and that was never worth it for a SKU doing twelve units a month. Good imagery used to mean picking two of fast, cheap, and safe. So I handed a thin water-bottle listing to [our agent](/features/ai-clients/) with one rule: seed from our real photos, check before you publish, and stop at my approval. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies live listing and product context, the [Amazon Agent Iris](/features/amazon-agent-iris/) Skill applies your product and brand standards, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific image guidance, and approval controls keep the final call yours. ## Seeding from your own product photos turns a photographer-studio-designer-upload chain into one conversation The benefit first: you get studio-quality images without a studio, and the time and cost of that whole chain collapse into a single run. The Amazon Agent Iris Skill on the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads the listing and its existing images, then generates new images seeded from the seller's own product photos, so the real product identity, logos, and printed text stay intact rather than becoming generic stock. It fills factual details, like size-guide measurements, from the listing's own catalog data, so the specs are exact and not invented. The whole loop runs in one conversation with no manual Seller Central uploads. Here is what it looked like. The water-bottle listing started with one image and ended with four candidates in a single run: a sharpened white-background main shot, a true lifestyle scene of the bottle in use, a precise size guide whose measurements came straight from the listing's own data, and a detail shot the validator bounced for falling under the zoom threshold. That gate is the point, and the surviving set still cleared the four-image target shape, which is not arbitrary. It follows the recommended set in Amazon's image guidance: one image of the product on a white background, one in an environment, and one with product information such as dimensions. The Skill seeded all of it from photos we already owned. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot see your listing or your existing photos, so it cannot seed from your real product. You would have to describe it or paste images, and it invents a generic-looking product that is not quite yours. The Skill reads your real photos through the MCP and seeds from them. > - **Your call, before anything changes.** Even if the shared AI drafts an image, it cannot get it onto Amazon. You export it and upload it by hand in Seller Central, where it can take up to 24 hours to appear and may not display at all. The Skill publishes through the MCP and confirms it. > - **Your account, as it is right now.** To get this far you pasted product photos and account context into a chat history you do not control. The Skill never copies your data into a prompt; the MCP reads it over an authorized connection and it stays in your account. ## When cost-per-image and effort-per-listing crater, the math flips: every listing gets brought up to standard, not just the heroes The benefit here is catalog-wide quality that was never economically possible before. Because the loop is a reusable [Skill](/features/skills/), the same seed, check, approve, publish run scales across the catalog. The long tail of thin and single-image listings was never worth a studio shoot, so it stayed underbuilt and under-converting. When the per-listing cost collapses, the recovered conversion from that long tail becomes reachable, and the recommended set (white-background main, an environment image, a product-information image, ideally six or more images per the **Product Image Requirements for Amazon Listings** guidance) is now within reach for a SKU doing twelve units a month, not just the flagship. The standard each listing is brought up to is Amazon's, not a house style. I run it as a recurring Skill across the catalog, often kicked off when the [audit-to-patch workflow](/guides/seller-listing-agentic-audit-to-patch/) flags an image laggard, so a thin gallery gets re-merchandised the same week it is found instead of next quarter. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** A chat cannot run the same fix across 400 ASINs (Amazon Standard Identification Numbers). You would repeat the paste, export, upload dance by hand for each one, which is exactly why the long tail never gets fixed. The Skill re-runs the loop unattended up to the approval gate. > - **Your judgment, running every time.** Ask a chat to batch-improve listings and it gives the same generic advice for every product type, missing that a Fashion main image follows different rules from a shoe or a kids' item. The Skill grades each against the per-category rule (next section). > - **Your account, as it is right now.** A chat cannot tell which of your listings are actually underbuilt, because it cannot see your catalog. The MCP reads which listings are thin and worth upgrading. ## Every generated image is graded against Amazon's image rules before it can publish, and a human approves the final set This is the part that makes the speed safe: nothing publishes that breaks Amazon's rules or that you did not approve. The Skill checks each generated image against Amazon's image policy before anything publishes, a policy validator that returns a per-image verdict, grounded by Atlas against the **Product Image Requirements for Amazon Listings**, **Suppressed Listings Management**, and the per-category style guides such as the **Fashion Category Style Guide** (the `listing_categories_style_guide` set). That verdict set is the audit report the Skill produces: ```json { "asin": "B0EXAMPLE34", "sku": "BOTTLE-32OZ", "seeded_from": "existing main product photo + 1 detail photo", "starting_images": 1, "proposed_images": [ { "slot": "main", "kind": "white-background product shot", "verdict": "pass", "checks": ["pure white background (255,255,255)", "product ~85% of frame", "no text/logos", ">1,000 px longest side"], "grounded_in": "Product Image Requirements for Amazon Listings" }, { "slot": "secondary-1", "kind": "lifestyle / environment scene", "verdict": "pass", "checks": ["represents real product", "no added accessories not included"], "grounded_in": "Product Image Requirements for Amazon Listings" }, { "slot": "secondary-2", "kind": "size guide (measurements from catalog data)", "verdict": "needs changes", "fix": "specs pulled from listing's own catalog data; confirm dimensions before approve", "grounded_in": "Product Image Requirements for Amazon Listings (product-information image)" }, { "slot": "secondary-3", "kind": "detail / texture shot", "verdict": "reject", "fix": "longest side under 1,000 px; regenerate at higher resolution for zoom", "grounded_in": "Product Image Requirements for Amazon Listings (1,000 pixels)" } ], "suppression_risk_if_published_uncorrected": true, "awaiting": "human approval before publish" } ``` Read the verdicts left to right. **Pass** means compliant and ready. **Needs changes** is a human judgment call, like confirming the size-guide dimensions the Skill pulled from the catalog. **Reject plus fix** is a hard rule failure with the exact remedy attached. The rules behind those verdicts are Amazon's, retrieved through Atlas, not paraphrased from training data. The main image must have a pure **white background** (RGB 255,255,255), the product must fill about 85 percent of the frame, and there can be no text, logos, or watermarks. Images over **1,000 pixels** on the longest side enable the zoom Amazon prioritizes, with all images between 500 and 10,000 pixels. The image must accurately represent the product's real scale, quantity, and color. And the reason the check runs before publish rather than after: a non-compliant image will suppress the listing from search until compliant images are provided, the rule grounded in **Suppressed Listings Management**. Which rule applies to the main image depends on the category, so the validator grades a Fashion item against the **Fashion Category Style Guide** and a different product type against its own guide. The agent never makes the judgment call of whether an image truly represents the product; a human signs off, and only then does the Skill publish through the MCP and verify ingestion. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** A chat cannot test an image against Amazon's validator, so nothing catches a bad image before it is live. You only learn it failed when the listing gets suppressed from search and stops converting. The Skill grades each image before publish and ships only what passed. > - **Your judgment, running every time.** The shared AI can repeat public image rules, but it does not guarantee that the current, category-specific rule set is applied to every generated asset. Atlas grounds the Skill in Amazon's image guidance, including the 1,000-pixel zoom threshold, pure-white main-image background, and category-specific requirements, before anything is approved. > - **Your judgment, running every time.** A chat will happily invent measurements for a size-guide image. The Skill pulls them from your listing's own catalog data, and a human approves that the image truly represents the product before it ships, the call a machine should not make alone. ## What happens next Once the verdicts are clean and the human approves the set, the Skill publishes the approved images through the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) and then confirms ingestion. It verifies the images actually went live rather than assuming success, the opposite of the manual path where images can take up to 24 hours and may never display. Anything that cannot clear forks off: an image marked reject goes back through the seed-and-check loop at higher resolution, and an underlying suppression that no image can fix is the audit-to-patch concern. I run the whole thing as a recurring [Skill](/features/skills/) across the catalog so newly thin or newly suppressed listings get re-merchandised, with the same approval gate in front of every run. Run this way, the loop is one lane of the [Amazon Agent Data layer](/features/amazon-agent-flow/): the same Amazon Agent Flow fabric that wires the Selling Partner MCP, the [Amazon Ads MCP](/features/amazon-ads-mcp/), and Atlas into automation your team approves rather than babysits. This is the image side of the [audit-to-patch workflow](/guides/seller-listing-agentic-audit-to-patch/), which forks an image-suppression finding straight into this loop. The pattern is the same one that makes any of this safe: seed from your own truth, check before publish, approve before live. That is not model cleverness. It is the product surface doing its job. *Next in the series: bringing a suppressed FBA (Fulfillment by Amazon) listing back into search when the problem is not the image but the inventory state behind it.* ### Amazon FBM Orders Reports: What to Request and Why URL: https://www.kuudo.com/guides/seller-fbm-orders-reports/ Use Seller Central FBM Order Reports for fulfillment data, and use the [Amazon Selling Partner API (SP-API) MCP](/features/amazon-selling-partner-mcp/) to request SP-API All Orders reports when the job is tracking, support, reconciliation, or a finance period cut. Those are different files with different risk profiles, even though operators often call both "FBM orders." | Operator need | Request | Why | | --- | --- | --- | | Fulfill seller-fulfilled packages | Seller Central `FBM Order Reports` | Includes buyer information needed to fulfill FBM orders. | | Catch changed orders since the last sync | `GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL` | Returns all Merchant Fulfilled Network (MFN) and Fulfillment by Amazon (FBA) orders updated in the period. | | Cut orders by purchase period | `GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_GENERAL` | Returns all MFN and FBA orders placed in the period. | | Backfill older placed orders | `GET_FLAT_FILE_ARCHIVED_ORDERS_DATA_BY_ORDER_DATE` | Retrieves archived orders by order date. | The practical question came from an ops lead: *"We need a daily FBM order file for the warehouse and a support file for changed orders. Can the agent just pull the FBM report through SP-API?"* The answer is yes for the support file, not by that name for the warehouse file. [Amazon Agent Atlas](/features/agent-atlas/) retrieved the Seller Central **FBM Order Reports** article and Amazon's **SP-API order report type values** reference, and the distinction matters: one is a fulfillment file with buyer data; the other is an order-tracking report family that Amazon explicitly says is not for driving seller fulfillment. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads the current order and account state, [Skills](/features/skills/) preserve how your team reviews and resolves exceptions, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep consequential actions with you. ## Start with the fulfillment question, not the API endpoint Amazon's Seller Central **FBM Order Reports** article says the FBM order report is a tab-delimited text file for seller-fulfilled products sold during a selected period. It includes the buyer information needed to fulfill orders, but not confidential billing or credit-card information. It can be generated manually for the past 1, 2, 7, 15, or 30 days, and it can be scheduled. Because Seller Central does not generate those reports beyond 30 days, the operational pattern is simple: archive the daily file if it is your warehouse record. The SP-API All Orders report family answers a different question. In Amazon's **order report type values** reference, Amazon describes the All Orders reports as order-tracking reports available in all regions and for all sellers. They return all orders regardless of fulfillment channel or shipment status, but they are intended for tracking, not fulfillment, because they do not include customer-identifying information and scheduling is not supported. For a warehouse, that distinction is not academic. A warehouse dispatch queue needs shipment-ready buyer data. A support or operations queue often needs "what changed since yesterday" and can safely operate from order ID, SKU, status, channel, quantities, city/state/postal/country, and prices. The agent should branch on that intent before it asks the SP-API MCP for anything. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask for an "FBM orders report" and it may invent one endpoint or hand you the All Orders report for fulfillment. Atlas surfaces the Seller Central FBM report and the SP-API All Orders report family as separate sources, so the agent does not confuse dispatch data with tracking data. > - **Your account, as it is right now.** The chat cannot inspect your marketplaces, fulfillment mix, or last archive date. The Selling Partner MCP reads the seller context through an authorized connection before choosing the report. > - **Your call, before anything changes.** It cannot schedule or request anything. It can only tell an operator to click around, which is exactly where daily report archives get missed. ## Request all-orders by last update when operations need changed orders When support asks "what changed since the last sync," the MCP should request `GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL`. Amazon's reference says this report contains all orders updated in the specified period, covers MFN and FBA, is available to sellers, and returns a tab-delimited flat file. It is a requested report, not a scheduled one, and the date range is limited to 30 days. For self-fulfilled pending orders, Amazon also notes that item price is not shown, so do not use this report as a pending-order revenue ledger. This is the right operational feed for support queues, warehouse exception review, cancellations, shipment-status changes, and downstream systems that reconcile on `last-updated-date`. It also avoids a common off-by-one mistake: if a customer changes or cancels an older order today, an order-date report for today's placed orders will miss it, but a last-updated report will catch it. ```json { "mcp": "amazon-spapi", "tool": "reports.createReport", "arguments": { "reportType": "GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_GENERAL", "marketplaceIds": ["ATVPDKIKX0DER"], "dataStartTime": "2026-06-21T00:00:00Z", "dataEndTime": "2026-06-22T00:00:00Z" } } ``` The agent should store the successful window boundary with the run log, then make the next request from the prior `dataEndTime`. That is a [Skill](/features/skills/) responsibility, not something an operator should remember in a prompt. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** A chat cannot know your last completed report window or whether yesterday's run failed halfway through download. The MCP-backed Skill can read the run log and request only the missing window. > - **Your judgment, running every time.** It often chooses order date because that sounds natural. Amazon's reference says last update is the report type for orders updated in the period, which is the support and exception-management question. > - **Your call, before anything changes.** Even with the right report type, a chat cannot submit `createReport` or watch processing status. The agent can. ## Request all-orders by order date when finance needs a period cut When finance or planning asks "what orders were placed in this period," request `GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_GENERAL`. The same SP-API order type values reference says this report contains all orders placed in the specified period, with MFN and FBA rows in a tab-delimited file. Use it for daily sales cuts, month-to-date order extracts, reconciliation against BI tables, and date-based audits. For FBM-only analysis, do not request a different report just because the business says "FBM." Request the All Orders period cut, then filter the rows where the fulfillment channel is merchant fulfilled. The report's value is that it holds both MFN and FBA orders, so the same extract can support FBM exception analysis and whole-account reporting. ```json { "mcp": "amazon-spapi", "tool": "reports.createReport", "arguments": { "reportType": "GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_GENERAL", "marketplaceIds": ["ATVPDKIKX0DER"], "dataStartTime": "2026-06-01T00:00:00Z", "dataEndTime": "2026-06-22T00:00:00Z" } } ``` If the request is a historical backfill by order date, Amazon's archived order report reference identifies `GET_FLAT_FILE_ARCHIVED_ORDERS_DATA_BY_ORDER_DATE` as the archived-orders report type. Keep that as a deliberate backfill path, not the default daily workflow. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** It may tell you "order date" and "last update" are interchangeable. They are not. The report names encode different selection rules, so the wrong one silently drops the orders you meant to include. > - **Your account, as it is right now.** It cannot see whether the finance period crosses marketplaces, whether a run was already completed, or whether the extract should be filtered to MFN after download. > - **Your call, before anything changes.** It cannot request the period cut or retrieve the tab-delimited file. The SP-API MCP can submit the request and pass the result into the next workflow. ## Let the SP-API MCP handle polling, retrieval, and storage discipline Atlas also pulled Amazon's Reports API tutorials for requesting a report, verifying report processing, and retrieving a report document. The sequence is mechanical, which is exactly why it belongs in a Skill. 1. Call `createReport` with `reportType`, `marketplaceIds`, and optional `dataStartTime` / `dataEndTime`. 2. Poll `getReport` with the returned `reportId` until `processingStatus` is `DONE`, `CANCELLED`, or `FATAL`. 3. If `DONE` includes `reportDocumentId`, call `getReportDocument`. 4. Download the returned pre-signed `url` before it expires, applying `compressionAlgorithm` if present. 5. Keep encryption at rest; Amazon's retrieve tutorial warns against storing unencrypted report content on disk, even temporarily. ```json { "mcp": "amazon-spapi", "tool": "reports.getReport", "arguments": { "reportId": "amzn1.spapi-report.example" } } ``` ```json { "mcp": "amazon-spapi", "tool": "reports.getReportDocument", "arguments": { "reportDocumentId": "amzn1.spdoc.example" } } ``` The failure states matter. `CANCELLED` can mean Amazon found no eligible data. `FATAL` can still include a document that explains the failure. `IN_QUEUE` and `IN_PROGRESS` are not terminal, so the agent should keep polling instead of telling the operator the report is missing. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** A chat cannot poll `getReport`, retrieve a pre-signed URL, or download before expiry. It will leave the operator with a checklist. > - **Your judgment, running every time.** It often misses the terminal states and treats any non-DONE status as an error. Amazon distinguishes queued, in-progress, cancelled, done, and fatal states. > - **Your account, as it is right now.** It cannot apply the seller's storage policy or write the artifact to the approved encrypted location. The MCP workflow can route the document through the account's governed path. ## What happens next In production, the [Amazon Ads MCP](/features/amazon-ads-mcp/), the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), [Skills](/features/skills/), and the [Amazon Agent Data layer](/features/amazon-agent-flow/) sit behind one agent surface, so a support request can pull the right SP-API order report, archive it, and hand downstream teams the same audited artifact. The same pattern powers the approval loop in [Preview Listing Patches Before They Go Live](/guides/seller-listing-agentic-audit-to-patch/): retrieve the governed source, perform the narrow action, write the run log, and keep the operator out of manual repeat work. For this guide, the narrow action is report selection. A warehouse dispatch workflow needs the Seller Central FBM fulfillment file or a restricted fulfillment path. A support, finance, or reconciliation workflow should ask the SP-API MCP for the All Orders report type that matches the date logic. *Next in the series: using SP-API order status changes to decide when an agent should notify support, warehouse operations, or finance without turning every report row into an alert.* ### Find Your Optimal DSP Frequency Cap in AMC URL: https://www.kuudo.com/guides/amc-optimal-frequency-cap/ Amazon Marketing Cloud (AMC) optimal-frequency analysis buckets users by impression count and compares cumulative cost with cumulative return. The useful cap is the last reliable bucket before incremental return turns negative, not the bucket with the highest observed purchase rate; thin high-frequency tails and campaign-mix differences must be reviewed before changing DSP settings. Your optimal Amazon demand-side platform (DSP) frequency cap is not the bucket with the highest purchase rate. That rate climbs forever (0.48% at one impression, 1.10% at two, 3.86% at three, 5.10% at four), so it always says never cap. The cap is the frequency bucket where cumulative return % stops outrunning cumulative cost %. The [Amazon Marketing Cloud](/features/amc/) (AMC) Optimal Frequency Analysis method finds it, the agent runs it on your `amazon_attributed_events_by_traffic_time` conversions through the [Amazon Ads MCP](/features/amazon-ads-mcp/), and hands you a single number to set in DSP, grounded by [Amazon Agent Atlas](/features/agent-atlas/). That sequence came out of one Slack question: *"What's our optimal DSP frequency cap, and at what impression count are we paying for ad fatigue instead of conversions?"* It is easy to ask and easy to answer wrong. I trusted the best-converting bucket until the cost column flipped it. So I handed it to [our agent](/features/ai-clients/) with one instruction: don't read the cap off the rate column, run the cumulative cost-and-return method and show me where it crosses. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## Optimal frequency analysis starts by bucketing every user by impression count, not by reading one average The raw shape every later step reads from is a per-bucket table, not a single average frequency. The Amazon Ads MCP runs the bucketing query and the agent grades the columns against the rules Atlas retrieves. Frequencies group into buckets `frequency_01` through `frequency_25+`, where `25+` means exposed 25 or more times. The output schema is the AMC convention verbatim: `frequency_bucket`, `users_in_bucket`, `impressions_in_bucket`, `purchases`, and purchase rate. Conversions attach through `amazon_attributed_events_by_traffic_time`, the corpus-backed source for attributed purchases and product sales. This step is the **"Measuring Optimal Impression Frequency for Amazon DSP Campaigns"** playbook joined to the bucketing schema, with the reach distribution from **"Calculating Reach and Impression Frequency in AMC."** The data pull below is an illustrative scaffold, not a query retrieved from Atlas. The corpus guarantees the output schema and the method, not these exact table and column names, so it carries the skip marker and ships unverified by design (the canonical runnable query is the linked Amazon instructional query). ```sql WITH impressions AS ( SELECT user_id, SUM(impressions) AS impressions, SUM(total_cost) AS cost FROM dsp_impressions_by_user_segments GROUP BY 1 ), conversions AS ( SELECT user_id, SUM(purchases) AS purchases, SUM(total_product_sales) AS product_sales FROM amazon_attributed_events_by_traffic_time GROUP BY 1 ), by_user AS ( SELECT i.user_id, i.impressions, i.cost, LEAST(i.impressions, 25) AS frequency, COALESCE(c.purchases, 0) AS purchases, COALESCE(c.product_sales, 0) AS product_sales FROM impressions i LEFT JOIN conversions c USING (user_id) ) SELECT CONCAT('frequency_', LPAD(CAST(frequency AS VARCHAR), 2, '0'), IF(frequency = 25, '+', '')) AS frequency_bucket, COUNT(user_id) AS users_in_bucket, SUM(impressions) AS impressions_in_bucket, SUM(cost) AS cost, SUM(purchases) AS purchases, SUM(product_sales) AS product_sales FROM by_user GROUP BY 1 ORDER BY 1 ``` > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI can't read your impression logs, so it can't build a single frequency bucket. You'd have to hand-export and paste a table, and your account data would land in a chat history you don't control. The Amazon Ads MCP reads it over an authorized connection and the data stays inside your account. > - **Your judgment, running every time.** Ask the shared AI how to bucket and it invents a column. AMC's convention is `frequency_01` through `frequency_25+`, and conversions attach through `amazon_attributed_events_by_traffic_time`. Get the table or the bucket scheme wrong and the query returns nothing. The agent reads the real schema first. ## The highest purchase-rate bucket is the wrong cap, because it always tells you to never cap Purchase rate rises monotonically with frequency: 0.48% at one impression, 1.10% at two, 3.86% at three, 5.10% at four across the first four buckets. Cap at the highest-rate bucket and you would cap at the top, which is the same as never capping at all. Atlas surfaces the **"Measuring Optimal Impression Frequency for Amazon DSP Campaigns"** result table that shows the climb, and the playbook's own warning that optimal frequency on Amazon may not be optimal across all advertising channels. The agent flags the trap unprompted rather than chasing the best-looking number. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI "which frequency converts best?" and it points at the top bucket, the trap. Rate never stops rising, so that answer is "never cap," which is how you keep paying for fatigue. The agent knows the optimum lives on the cost side, not the rate column. > - **Your call, before anything changes.** Even if you spotted the trap, the shared AI can't compute the cost-side correction. It has no cost-per-bucket and no way to run the cumulative math. The Amazon Ads MCP has both, run over your real campaign window. ## The cap is the last bucket where Percent Change is still above zero Set the cap at the last frequency bucket where Percent Change (cumulative return % minus cumulative cost %, differenced bucket-over-bucket) is still above zero. Past that bucket, each added impression costs more than it returns. This is the **"Optimal Frequency Analysis in Amazon Marketing Cloud"** method, and it is the analysis report the agent produces. Per bucket it computes cumulative cost, cumulative return, cumulative cost %, cumulative return %, Percent Difference (cumulative return % minus cumulative cost %), and Percent Change (this bucket's Percent Difference minus the prior bucket's). The cap is the last bucket where Percent Change is above zero. You can gloss it as the inflection point where the curve of added value goes flat, but the mechanical rule is the zero-crossing, not a fitted curve. The post-processing table is the artifact. Here is the playbook's worked example, with the cap read off where Percent Change first goes negative: | frequency_bucket | conversions | cumul. conversions | cost | cumul. cost | cumul. return % | cumul. cost % | Pct Difference | Pct Change | |---|---|---|---|---|---|---|---|---| | 1 | 9,203 | 9,203 | $7,801 | $7,801 | 19% | 9% | 9.90% | n/a | | 2 | 6,532 | 15,735 | $7,933 | $15,734 | 32% | 18% | 14.18% | 4.28% | | 3 | 5,024 | 20,759 | $7,492 | $23,226 | 43% | 27% | 15.87% | 1.69% | | 4 | 4,157 | 24,916 | $7,060 | $30,286 | 51% | 35% | 16.28% | 0.41% (last >0) | | 5 | 3,569 | 28,485 | $6,621 | $36,907 | 58% | 42% | 15.99% | -0.29% (crosses) | Bucket 5's -0.29% is the first negative, so the cap is frequency 4. The ROAS (return on ad spend) sanity check confirms it: at this cap cumulative ROAS is about $1.17, and a looser cap of 9 dropped cumulative ROAS to $0.95, where overall spend exceeded sales. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI doesn't know this method exists. No cumulative cost %, no Percent Change column, no zero-crossing rule. It guesses a round number like "cap at 3." The agent runs the exact AMC Optimal Frequency Analysis bookkeeping and the table is auditable. > - **Your call, before anything changes.** The whole method needs per-bucket cost and conversion volume run over your real campaign window. The shared AI can't query it and can't carry the cumulative sums across buckets. The Amazon Ads MCP runs it and the result is reproducible. ## Your high-frequency tail is too thin to trust Roughly 90% of users are exposed three times or fewer, so the high-frequency buckets thin out fast and fall below AMC's aggregation thresholds. Read the cap off where the data is dense, not off a noisy tail. This is where the **"Calculating Reach and Impression Frequency in AMC"** distribution and the prerequisites from **"Optimal Frequency Analysis in Amazon Marketing Cloud"** land. The prerequisites are an AMC instance, at least 7 days of backfilled data, and 4 or more active campaigns, plus the AMC concept of data aggregation thresholds. Atlas carries both, and the agent surfaces them unprompted along with the caveat that optimal frequency is not one size fits all: it depends on the goal (awareness versus conversion), campaign length, audience size, and whether the campaign is a new launch or steady state. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI will happily compute a cap off your `frequency_18` bucket as if it were solid, never warning that about 90% of users sit at three or fewer and the tail is statistically empty, or that AMC won't even return rows below its aggregation threshold. The agent knows the distribution and the threshold and reads the cap off dense buckets. > - **Your account, as it is right now.** The shared AI can't see that your campaign only ran 5 days or has 2 active campaigns, below the playbook's 7-day, 4-campaign floor, so it can't tell you the answer isn't ready yet. The Amazon Ads MCP knows your real backfill and campaign count. ## What happens next The artifact is a decision: one cap number. You set it in Amazon DSP at the line-item or campaign level, the lever the playbook names as the actionable output. Then schedule the analysis as a recurring [Skill](/features/skills/) so the cap re-checks itself as spend shifts and campaigns evolve, rather than going stale after one run. Amazon's point-and-click Optimal Frequency solution is a fine quick read, but the AMC SQL path wins on campaign-level control and custom KPIs (key performance indicators). The whole thing rides the [Amazon Agent Data layer / Agent Flow](/features/amazon-agent-flow/) that unifies the Amazon Ads MCP and the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) under one grounded surface, so the conversions you join here come from the same place the rest of your Amazon work reads from. A frequency cap is one number, but it only means anything when it sits on the cost side of the math instead of the rate side. Set it there and you stop paying for impressions past the point where added cost outruns added return. *Next in the series: where that capped DSP impression actually sits in the full conversion path, mapped end to end in [the path-to-conversion Sankey](/guides/amc-path-to-conversion-sankey/).* ### Map AMC Paths Across DSP and Sponsored Ads URL: https://www.kuudo.com/guides/amc-path-to-conversion-sankey/ Amazon Marketing Cloud (AMC) path-to-conversion analysis reconstructs ordered DSP and Sponsored Ads exposures, groups campaigns before privacy aggregation erases sparse steps, and returns source-to-destination rows ready for a Sankey chart. The workflow also preserves the conversion lookforward window, so an early visualization is not mistaken for a final performance read. Ask the agent in [your AI client](/features/ai-clients/) to map the customer journey, and the [Amazon Ads MCP](/features/amazon-ads-mcp/) runs the path-to-conversion workflow live in your [Amazon Marketing Cloud](/features/amc/) (AMC) instance: it joins `dsp_impressions`, `sponsored_ads_traffic`, and `amazon_attributed_events_by_traffic_time` on `user_id`, groups campaigns the way the current "Path to Conversion by Campaign Groups" pattern prescribes, and returns a Sankey-ready source-to-destination dataset. [Amazon Agent Atlas](/features/agent-atlas/) keeps it honest by surfacing that pattern from the **Customer Journey Analytics Playbook** instead of letting the agent guess. The answer exists because our media lead asked in Slack: *"What's the actual journey our customers take across demand-side platform (DSP), Sponsored Products, Sponsored Brands, and Sponsored Display before they convert?"* Everyone had an opinion about which campaign deserved credit. Nobody had the path. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## The whole journey lives in exactly three AMC tables, joined on user_id The Customer Journey Analytics Playbook's "Tables used" list has exactly three entries: `dsp_impressions`, `sponsored_ads_traffic`, and `amazon_attributed_events_by_traffic_time`. Not one table per ad product. `sponsored_ads_traffic` carries the traffic events, impressions and clicks, for Sponsored Products, Sponsored Brands, Sponsored Display, and Sponsored Television in one table; you split it by `ad_product_type` (`'sponsored_products'`, `'sponsored_brands'`, `'sponsored_display'`). The third table holds the conversion events: purchases, new_to_brand_purchases, detail page views. The join rule comes straight from Amazon's guidance: join on `user_id` when you are interested in understanding user behavior. Every branch of the query filters `user_id IS NOT NULL` so the paths stay user-level. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask the shared AI for the journey SQL and it invents per-product tables, an `sp_impressions` here, an `sb_traffic` there, that do not exist in AMC. All sponsored ads traffic lives in one table split by `ad_product_type`; the agent reads the real schema through the MCP before writing a line. > - **Your account, as it is right now.** Even with correct table names, the joined journey only exists inside your AMC instance after the query runs there. You cannot paste `user_id`-level impression rows into a chat, so the chat never sees a single path. > - **Your judgment, running every time.** The classic miss is joining the two traffic tables and stopping. Without `amazon_attributed_events_by_traffic_time` there is no conversion endpoint, only exposure. Atlas surfaces the exact three-table list, so the query lands complete. ## Group your campaigns, or aggregation thresholds swallow most of the path as NULL rows The old "Path to Conversion by Campaign" instructional query (IQ) is retired. Amazon's notice reads: "As of August 31, 2022, an improved version of this IQ has been made available in AMC. We recommend advertisers to use the new version: Path to Conversion by Campaign Groups." The improvements are operator-facing: grouping reduces the likelihood of violating aggregation thresholds so "users will see a smaller number of NULL rows," campaign and Amazon Standard Identification Number (ASIN) filters exist, and Sponsored Display conversions are included. The grouping mechanism is a small `VALUES` mapping: list each campaign with its group, then `COALESCE` everything ungrouped into `'DSP-Others'`, `'SP-Others'`, `'SD-Others'`, or `'SB-Others'` so no row falls below the thresholds. (The playbook also ships a campaign-category variant that maps campaign names to funnel stages with `CASE ... SIMILAR TO`, but those categories depend on each advertiser's naming conventions.) Here is the workflow the agent submitted through the Amazon Ads MCP: ```sql -- Path to conversion across DSP + sponsored ads, Sankey-ready output -- Pattern: "Path to Conversion by Campaign Groups" IQ (supersedes the -- "Path to Conversion by Campaign" IQ as of 2022-08-31) WITH campaign_group (campaign, campaign_group) AS ( VALUES -- placeholder campaign names/IDs; ungrouped rows fall back to -- '-Others' so aggregation thresholds aren't violated ('111111111111111111', 'group 1'), ('222222222222222222', 'group 1'), ('SP_campaign', 'group 2'), ('SD_campaign', 'group 3'), ('SB_campaign', 'group 4') ), impressions AS ( SELECT COALESCE(g.campaign_group, 'DSP-Others') AS campaign_group, 'DSP' AS product_type, i.user_id, MIN(i.impression_dt) AS impression_dt_first, MAX(i.impression_dt) AS impression_dt_last, SUM(i.impressions) AS impressions, SUM(i.total_cost) AS total_cost FROM dsp_impressions i LEFT JOIN campaign_group g ON g.campaign = i.campaign WHERE i.user_id IS NOT NULL GROUP BY 1, 2, 3 UNION ALL SELECT COALESCE(g.campaign_group, 'SP-Others') AS campaign_group, 'SP' AS product_type, a.user_id, MIN(a.event_dt) AS impression_dt_first, MAX(a.event_dt) AS impression_dt_last, SUM(a.impressions) AS impressions, SUM(a.spend) AS total_cost FROM sponsored_ads_traffic a LEFT JOIN campaign_group g ON g.campaign = a.campaign WHERE a.user_id IS NOT NULL AND a.ad_product_type = 'sponsored_products' GROUP BY 1, 2, 3 UNION ALL SELECT COALESCE(g.campaign_group, 'SD-Others') AS campaign_group, 'SD' AS product_type, a.user_id, MIN(a.event_dt) AS impression_dt_first, MAX(a.event_dt) AS impression_dt_last, SUM(a.impressions) AS impressions, SUM(a.spend) AS total_cost FROM sponsored_ads_traffic a LEFT JOIN campaign_group g ON g.campaign = a.campaign WHERE a.user_id IS NOT NULL AND a.ad_product_type = 'sponsored_display' GROUP BY 1, 2, 3 UNION ALL SELECT COALESCE(g.campaign_group, 'SB-Others') AS campaign_group, 'SB' AS product_type, a.user_id, MIN(a.event_dt) AS impression_dt_first, MAX(a.event_dt) AS impression_dt_last, SUM(a.impressions) AS impressions, SUM(a.spend) AS total_cost FROM sponsored_ads_traffic a LEFT JOIN campaign_group g ON g.campaign = a.campaign WHERE a.user_id IS NOT NULL AND a.ad_product_type = 'sponsored_brands' GROUP BY 1, 2, 3 ), converted AS ( -- conversions attributed to in-window traffic; events can land up to -- 30 days after the window, so totals move until attribution closes SELECT user_id, SUM(conversions) AS conversions FROM amazon_attributed_events_by_traffic_time WHERE user_id IS NOT NULL GROUP BY 1 ), ranked AS ( SELECT user_id, campaign_group, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY impression_dt_first ) AS path_rank FROM impressions ), steps AS ( -- explode the path into source -> destination pairs: QuickSight's -- Sankey visual needs one source and one destination dimension per row SELECT r1.user_id, r1.campaign_group AS path_step_source, r2.campaign_group AS path_step_destination FROM ranked r1 LEFT JOIN ranked r2 ON r2.user_id = r1.user_id AND r2.path_rank = r1.path_rank + 1 ) SELECT s.path_step_source, s.path_step_destination, COUNT(DISTINCT s.user_id) AS path_occurrences, SUM(c.conversions) AS conversions FROM steps s LEFT JOIN converted c ON c.user_id = s.user_id GROUP BY 1, 2 -- NO ORDER BY here: AMC rejects ORDER BY in the outer query of a -- workflow ("ORDER BY unexpected"). Sort downstream, post-export. ``` Swap the placeholder `VALUES` rows for your own campaign names or IDs and the rest runs as-is. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** A chat reproduces the deprecated by-campaign pattern from stale training data. Run campaign-level paths in an instance of any real size and the aggregation thresholds NULL the small rows, exactly what the 2022 notice says grouping fixes. > - **Your call, before anything changes.** The chat hands you SQL to carry by hand. The first hand-carried draft of this exact analysis died in AMC's parser with "ORDER BY unexpected" because it sorted in the outer query. The agent submits through the MCP, sees the parser's verdict immediately, and corrects the workflow; the version above sorts downstream, after export. ## Conversions keep landing for 30 days after your window, so day-zero numbers are not final `amazon_attributed_events_by_traffic_time` has a property that surprises people: the traffic events all sit inside your query window, but the attributed conversions "may have occurred up to 30 days after the time window." Amazon extends the conversion range automatically and says plainly that "the output of a workflow that uses this table may change over time." The lookback itself is fixed per campaign and cannot be changed in AMC: 14 days for Brands, 7 days for Sellers' Sponsored Products. I was skeptical when the agent flagged this, so we re-pulled the identical workflow later in the attribution window. The conversion column grew, and nothing was wrong; attribution was still filling in. Run the analysis the morning after your window closes and you under-count whatever is still inside that 14-day lookback, which is why the agent derives a safe re-pull date from the lookback rather than treating day-zero output as final. One eligibility wrinkle from the Custom Attribution Overview is worth knowing too: DSP conversions require a viewable impression and Sponsored Products require a click, so impression-only SP exposures never appear in the attributed datasets at all. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** The shared AI treats your query window as the whole story. It will not warn you that the same by_traffic_time workflow returns different numbers tomorrow, or that a path analysis run too early under-counts conversions still in flight. > - **Your call, before anything changes.** A chat cannot wait out the attribution window and re-pull. You rerun by hand, if you remember to. The agent schedules the re-run for after attribution closes and compares the two outputs side by side. ## Sankey-ready means source-to-destination rows, and the agent returns the dataset already in that shape Amazon's path output arrives as ranked arrays, paths like `[[1, DSP-Display], [2, DSP-Video], [3, BSI], [4, SB-Others]]` with `path_occurrences` and `impressions` alongside. QuickSight cannot chart that directly. The playbook is explicit: the "Sankey diagram is a visual type within Amazon QuickSight and requires a one dimension in source and one dimension in destination." A four-element path "will 'explode into' 3 separate steps/rows," metrics repeated on every exploded row; the playbook does this in a notebook ending with `df_final.to_csv('Sankey-Diagram_Input.csv', index=False)`. The query above skips the notebook step. The `ROW_NUMBER` self-join in the `steps` CTE emits the exploded pairs directly, one row per hop, ordered by first impression time. A user's terminal step carries a NULL destination (single-exposure users keep one row); label that terminal node from the `converted` join and the diagram shows where each journey ends. Read the `conversions` column as the conversion total of users who traversed that hop, not as credit attributed to the hop itself. > **The AI is shared. The moat is yours.** > - **Your judgment, running every time.** Ask a plain chat for "path to conversion" and you get a flat table, one row per campaign. No source/destination pairs means nothing QuickSight's Sankey visual can ingest. > - **Your call, before anything changes.** Even when the shared AI explains the explode correctly, the chat cannot apply it to results it never had. You re-shape rows by hand in a spreadsheet; the agent hands back rows that already are the chart's input. ## What happens next Reading the diagram is the fast part. The playbook's worked example charts the top 20 paths by purchase, with high-purchase users usually exposed to loyalty and consideration campaign categories and awareness overlap comparatively lower. The move: reuse what the high-overlap groups share (impression frequency, cost, line items) in the next campaign, and seed lookalikes from those audiences. One row to ignore: the "none" exposure group, purchasers with no impression recorded under their `user_id`; Amazon says to skip it. From there I have the agent re-run the workflow as a recurring [Skill](/features/skills/), timed past attribution close, so each month's diagram compares settled numbers to settled numbers. When the fixed 14-day window is the wrong lens, the custom attribution IQs (First Touch, Last Touch, Linear, Position Based) extend the lookback to 28 days and re-credit the same journey; that choice is its own guide: [choosing a custom attribution model in AMC](/guides/amc-custom-attribution-models/). The path query is one surface of [Amazon Agent Flow](/features/amazon-agent-flow/), the Amazon Agent Data layer connecting the Amazon Ads MCP, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/), Atlas, and Skills into end-to-end workflows. The pattern is the one this series keeps landing on: the agent runs Amazon's current query in your instance, grounded in Amazon's own playbook, and returns an artifact you can act on the same day. The journey was always in your data. The walls were in the chat. *Next up: how much your DSP and sponsored ads audiences actually overlap, and what that says about incrementality, in [the four-way sponsored ads and DSP overlap analysis](/guides/amc-sponsored-ads-dsp-overlap-4way/).* ### Preview Listing Patches Before They Go Live URL: https://www.kuudo.com/guides/seller-listing-agentic-audit-to-patch/ The Slack message came in right before a weekend push: *"Our luggage listing is converting badly, the bullets read like a wall of text, and one image got rejected last week. Can the agent just fix it? But I don't want it quietly editing a live listing without me seeing what changed first."* That last clause is the whole job. Fixing a listing is easy to ask for and dangerous to automate, because a single wrong attribute name can wipe a field you never meant to touch. So I handed it to [our agent](/features/ai-clients/), backed by [Amazon Agent Atlas](/features/agent-atlas/), with one rule: audit first, show me the diff, and do not write anything live until I say yes. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Selling Partner MCP](/features/amazon-selling-partner-mcp/) supplies the live listing and authorized tools, [Skills](/features/skills/) preserve the audit-and-patch rules, Atlas applies Amazon-specific guidance, and approval controls keep every proposed edit under your control. ## The AI is shared. Atlas changes the listing decision The shared AI can produce a plan that looks reasonable. Without the live listing, the safe-patch Skill, and Atlas grounding, that plan can still cause real damage. > **Four failure modes in a single un-grounded response** > 1. It proposed a full `putListingsItem` replace instead of a `patchListingsItem` partial update. A full replace requires every attribute to be resent; the omitted ones get wiped. The agent only wanted to change two bullets. > 2. It submitted the change directly, with no validation step. The Listings Items API offers `mode=VALIDATION_PREVIEW`, which runs the real validation and returns errors without committing. Skipping it means the first time you learn the patch is malformed is after the listing breaks. > 3. It rewrote the bullets with an emoji and the phrase "eco-friendly." Amazon removes bullets containing emojis, trademark symbols, and prohibited phrases like eco-friendly or anti-microbial. The model had no idea the copy would be silently stripped. > 4. It assumed a generic `bullets` attribute. Attribute names are product-type specific. The correct path is `/attributes/bullet_point`, and the model would have gotten a rejected patch for an unknown attribute. None of this is exotic. It is all in Amazon's own documentation, scattered across the Selling Partner API (SP-API) reference and a handful of Seller Central help pages that only surface when you already know the exact term to search. ## What Atlas retrieves When the agent gets the question, it runs a semantic search across the `amazon_sellers` collection and pulls the governing documents before it writes a single attribute: - The **Partially Update a Listing** reference, which establishes that `patchListingsItem` applies a JSON Patch to one or more attributes without disturbing the rest of the listing. - The **Preview Errors Before Partially Updating a Listing** tutorial, the source of the `mode=VALIDATION_PREVIEW` parameter that turns a blind submission into a dry run. - The **Catalog Items API** reference, whose `getCatalogItem` call returns the live `attributes`, `images`, and `summaries` for an ASIN (Amazon Standard Identification Number), keyed to the product type definition. - **Suppressed Listings Management**, which explains how to download the suppressed listings report and why a listing is hidden from search in the first place. - **Product Image Requirements for Amazon Listings**, which states plainly that non-compliant images suppress the listing from search until compliant images are provided. - **Suggest Changes to Your Product Detail Page**, Amazon's own surface for recommended detail-page improvements, which the agent reads as a second opinion on what to fix. Atlas does not write the patch. It surfaces the rules the patch has to obey, wired through [the Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/), and the agent adapts them to this specific listing. ## The agent's working output The agent did not start by editing anything. It pulled the live listing state through `getCatalogItem` and produced an audit report that names each finding, its severity, and the document it is grounded in: ```json { "asin": "B0EXAMPLE12", "sku": "LUG-HARDSIDE-28", "listing_status": "active", "findings": [ { "field": "bullet_point", "severity": "high", "finding": "Two bullets exceed 255 characters and one contains an emoji", "source": "Product Bullet Points Requirements" }, { "field": "main_image", "severity": "medium", "finding": "Main image background is not pure white; at risk of suppression", "source": "Product Image Requirements for Amazon Listings" }, { "field": "item_name", "severity": "low", "finding": "Title repeats the brand token three times", "source": "Suggest Changes to Your Product Detail Page" } ] } ``` From that audit, the agent built a JSON Patch that touches only the bullet points, leaving every other attribute untouched. This is the body it would send to `patchListingsItem`: ```json { "productType": "LUGGAGE", "patches": [ { "op": "replace", "path": "/attributes/bullet_point", "value": [ { "value": "Hardside shell resists scuffs and cracking on checked flights", "marketplace_id": "ATVPDKIKX0DER" }, { "value": "Spinner wheels roll in four directions for tight gate turns", "marketplace_id": "ATVPDKIKX0DER" }, { "value": "TSA-approved combination lock built into the side panel", "marketplace_id": "ATVPDKIKX0DER" } ] } ] } ``` Then the part the operator actually asked for. The agent submitted that body with `mode=VALIDATION_PREVIEW`, read the returned `issues` array, and refused to go live until a human approved: ```python import requests SPAPI = "https://sellingpartnerapi-na.amazon.com" MARKETPLACE = "ATVPDKIKX0DER" SELLER_ID = "A1EXAMPLESELLER" def preview_patch(sku, body, token): response = requests.patch( f"{SPAPI}/listings/2021-08-01/items/{SELLER_ID}/{sku}", params={"marketplaceIds": MARKETPLACE, "mode": "VALIDATION_PREVIEW"}, headers={"x-amz-access-token": token}, json=body, ) return response.json() def submit_patch(sku, body, token): response = requests.patch( f"{SPAPI}/listings/2021-08-01/items/{SELLER_ID}/{sku}", params={"marketplaceIds": MARKETPLACE}, headers={"x-amz-access-token": token}, json=body, ) response.raise_for_status() return response.json() def run(sku, patch_body, token, approver): preview = preview_patch(sku, patch_body, token) errors = [i for i in preview.get("issues", []) if i.get("severity") == "ERROR"] if errors: return {"status": "blocked", "errors": errors} if not approver.approves(sku, patch_body, preview): return {"status": "declined"} return submit_patch(sku, patch_body, token) ``` The design choice that matters is that the preview call and the live call send the *same* body. The only difference is the `mode` query parameter. That means the thing the human approves is byte-for-byte the thing that ships, with no second translation step where a new error can creep in. The agent also kept the patch scoped to `/attributes/bullet_point` rather than rewriting the whole listing, so an approval reviewer reads one diff instead of auditing the entire item. And because the audit flagged the main image as a suppression risk, the agent explicitly declined to claim the copy fix would lift a suppression. It cannot, and saying so is part of being honest about what a patch can do. ## The footnotes the agent surfaced unprompted This is where retrieval grounding pulls away from fluent guessing. Without being asked, the agent attached the caveats an operator needs but rarely thinks to request: > **Things Atlas surfaced that the operator didn't ask for** > - **Preview error codes are changing.** The `VALIDATION_PREVIEW` error codes are being revised; Amazon supports both old and new codes during the transition, so match on issue meaning, not just the literal code string. > - **Bullet rules are strict.** Each bullet must be 10 to 255 characters, carry no end punctuation, and exclude emojis, the registered or trademark symbols, and prohibited phrases like eco-friendly or anti-microbial. Include at least three. > - **An image-suppressed listing will not un-suppress from a copy edit.** Fix the non-compliant image first; a clean bullet patch on a suppressed listing changes nothing a shopper can see. > - **Throttle is five requests per second per operation.** Submit only items with material changes. Resubmitting unchanged attributes inflates processing backlogs and slows the whole queue. > - **Prefer notifications over polling.** Subscribe to `LISTINGS_ITEM_ISSUES_CHANGE` to get near-real-time SKU, severity, and enforcement-action updates instead of repeatedly calling the API to check whether the issue cleared. > - **An ASIN must represent one product.** Editing a detail page to describe a different product is a policy violation, not an optimization. The agent will refuse a patch that changes what the listing fundamentally is. Any one of these would have cost an afternoon to rediscover after a failed submission. ## What happens next Once `VALIDATION_PREVIEW` returns a clean `issues` array and the human approves, the agent resends the identical body without the `mode` parameter and the change goes live. It then watches the `LISTINGS_ITEM_ISSUES_CHANGE` notification stream to confirm the issue actually cleared, rather than assuming success. For the findings a text patch cannot solve, the path forks: the image flagged as a suppression risk is handed to the seeded image-regeneration workflow, and recurring audits are wired up as a scheduled [Skill](/features/skills/) so the listing is re-checked after every catalog change instead of once a quarter. The same approval gate applies every time, which is the pattern explored in [human approval for agent activation](/guides/human-approval-for-amc-activation/). The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Selling Partner MCP](/features/amazon-selling-partner-mcp/) reads and previews listing changes, the [Amazon Ads MCP](/features/amazon-ads-mcp/) can add [Amazon Marketing Cloud](/features/amc/) (AMC) campaign-side performance context, Atlas grounds the catalog rules, and Skills keep every recurring patch behind a reviewable approval gate. ## Why this matters A foundation model can draft a better bullet point. What it cannot reliably do is read the live product-type schema, scope the edit to a JSON Patch that leaves the rest of the listing intact, run it through Amazon's own validation before committing, and tell you that the real problem was the image all along. That sequence is not model cleverness. It is a corpus of Amazon's own listing rules, indexed and addressable by an agent at the moment it is about to act, with a human holding the final yes. If your agent can edit a live listing, it should be able to show you the diff first. --- *Next in the series: using your current product photos as seeds for OpenAI image generation to regenerate listing visuals that still pass Amazon's image requirements, with the same approve-before-publish gate sitting in front of every upload.* ### Count New-to-Brand Customers in AMC URL: https://www.kuudo.com/guides/amc-ntb-customers/ Amazon Marketing Cloud (AMC) new-to-brand analysis should count distinct customers from the conversion-time view, then separate that customer count from purchases, units, and revenue. This workflow identifies the correct NTB signal, prevents traffic-time attribution drift, and returns a number that can be compared across campaigns without pretending every conversion row represents a new person. ## What is the NTB customer question? A Slack from our paid team last Tuesday: *"Of all the customers our campaigns reached who actually bought, how many were buying our brand for the first time?"* The honest answer is: nobody at the agency knows, and the Amazon Ads console gives a partial number that excludes Amazon's demand-side platform (DSP). Broadly available AI can hand back a SQL query that compiles and counts the wrong table. The right answer lives in three [Amazon Marketing Cloud](/features/amc/) (AMC) instructional queries plus a schema document, joined to the authorized instance through the [Amazon Ads MCP](/features/amazon-ads-mcp/). [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The MCP supplies live account context and authorized tools, [Skills](/features/skills/) preserve the measurement method, [Amazon Agent Atlas](/features/agent-atlas/) applies Amazon-specific guidance, and approval controls keep the reporting decision yours. The matrix the agent grounded in before writing a line of SQL: | Source | Use it for | Don't use it for | |---|---|---| | `amazon_attributed_events_by_conversion_time` | Recurring NTB measurement, by promoted Amazon Standard Identification Number (ASIN) | Anything time-of-impression | | `amazon_attributed_events_by_traffic_time` | Same-day pacing only | Recurring workflows | | `amazon_retail_purchases` (NTB gateway IQ) | Custom NTB lookback (e.g. 1095 days) | Ad-attribution analysis | | `conversions` (custom attribution) | Pixel + off-Amazon NTB | Sponsored Ads NTB counts | The IQ behind the answer is **New to brand customers**. ## The AI is shared. Atlas changes the measurement decision > **Four failure modes I watched in a single un-grounded draft of this query** > 1. It picked `amazon_attributed_events_by_traffic_time` because the name sounds neutral. That table re-extends the conversion window by up to 30 days after the query, so the output changes after the workflow runs. A recurring NTB report pinned to it drifts. > 2. It forgot `purchases > 0` and counted every user the campaign reached, not just buyers. The denominator was wrong and so was the percentage. > 3. It put `SELECT user_id` in the final output and the query ran but the column came back blocked. `user_id` carries an aggregation threshold and can only live inside a CTE that aggregates it away. > 4. It confused the **New-to-brand customers** IQ with the **New-to-brand purchases** IQ. They use the same table but different denominators. The first counts distinct users; the second counts orders. The model swapped them and the answer to "how many first-time buyers did we acquire" came back as a purchase count inflated by repeat NTB orders. None of these failures throw an error. The query runs. The number is wrong. ## What Atlas retrieves The agent didn't write SQL from training data. It pulled five chunks from Atlas and grounded the query in them: - The **New to brand customers** instructional query: the canonical AMC IQ for this exact operator question, including the requirement language ("ASINs must be tracked to campaigns") and the policy window ("previous 365 day period"). - The **Amazon Attributed Events Overview** schema doc: defines the `new_to_brand` boolean column and the stability difference between `amazon_attributed_events_by_conversion_time` and the traffic-time variant. This is the source for the "use conversion_time for recurring workflows" rule. The table is wired through the [Amazon Ads MCP](/features/amazon-ads-mcp/), so the agent could resolve the column without resorting to a live web fetch via [Amazon Agent Crawl](/features/agent-crawl/). - The **New to brand purchases analysis** IQ, adjacent to the customer IQ and the one a model would otherwise confuse with this one. Atlas surfaced it so the agent could write Section 8 distinguishing the two. - The **Understanding New-to-Brand ASINs on Amazon** IQ, the natural follow-up. Once the operator knows the portfolio-level NTB%, the question becomes "which ASINs are the front door for NTB acquisition." - The **Data aggregation thresholds in AMC** reference: the privacy-safeguard doc that explains why `user_id` SELECTs get blocked at a final-output grain. ## How the agent's NTB SQL output works The SQL the agent returned, copy-pasteable into the AMC query editor: ```sql -- New-to-brand customers: count and percentage by campaign. -- Source IQ: New to brand customers (AMC instructional query). -- NTB lookback: 365 days (Amazon-managed; not parameterizable in this IQ). -- Run on the analytics table, NOT the for_audiences variant. WITH ad_buyers AS ( SELECT advertiser, campaign, user_id, MAX(new_to_brand) AS new_to_brand_flag FROM amazon_attributed_events_by_conversion_time WHERE purchases > 0 GROUP BY 1, 2, 3 ), ntb_only AS ( SELECT user_id, advertiser, campaign FROM ad_buyers WHERE new_to_brand_flag = TRUE ) SELECT b.advertiser, b.campaign, COUNT(DISTINCT b.user_id) AS users_that_purchased, COUNT(DISTINCT n.user_id) AS ntb_users_that_purchased, COUNT(DISTINCT n.user_id) * 1.0 / NULLIF(COUNT(DISTINCT b.user_id), 0) AS ntb_users_percentage FROM ad_buyers b LEFT JOIN ntb_only n ON b.user_id = n.user_id AND b.advertiser = n.advertiser AND b.campaign = n.campaign GROUP BY 1, 2 ORDER BY ntb_users_percentage DESC ``` Three decisions in this query would otherwise cost an operator an afternoon. The table choice is `amazon_attributed_events_by_conversion_time`, not the traffic-time variant. The Atlas chunk for `Amazon Attributed Events Overview` is explicit: traffic-time will re-extend the conversion window by up to 30 days *after* the query runs, so a recurring NTB report pinned to it produces a different answer each time. Conversion-time is the only safe table for a recurring measurement workflow. The `WITH` CTE exists because `user_id` carries an aggregation threshold. AMC will block any final SELECT that tries to expose `user_id` directly. The CTE collapses each user into a single row tagged with their NTB flag, and the outer SELECT operates only on COUNT DISTINCT. That is legal; it is also why the IQ template uses a CTE rather than a single-pass SELECT. The grouping is by `campaign`, not by `advertiser` alone. A portfolio-level NTB% hides the campaigns doing the actual acquisition work. If three campaigns are running and one is a loyalty-retargeting campaign with deliberately low NTB%, the aggregate percentage drops and looks like a problem. Grouping by campaign separates the loyalty workload from the acquisition workload so each campaign gets judged on its own goal. ## What NTB footnotes the agent surfaced > **Five things the agent surfaced unprompted, the ones an un-grounded model wouldn't include because it didn't know to** > 1. Amazon's docs phrase the NTB window two ways: the IQ chunk says "previous 365 day period" and the events table schema doc says "previous 12 months." Same definition, different wording. The IQ language is the authoritative one for this query; surface the inconsistency to the reader so nobody chases a ghost. > 2. The pixel-only path is a different query. Advertisers without a promoted ASIN that resulted in an ad-attributed purchase cannot run this IQ at all. The Atlas retrieval surfaced the `amazon_retail_purchases` NTB gateway IQ as the alternative, with a custom-lookback parameter (1095 days = 3 years is a common pick). > 3. The DSP NTB count includes only promoted-ASIN purchases. Sponsored Ads NTB also includes Brand Halo (related ASINs). A mixed-product portfolio measured by this IQ under-counts NTB on the DSP side relative to Sponsored Ads. The contrast with the [custom-attribution data sources](/guides/amc-custom-attribution-models/) is sharper here than the IQ doc admits. > 4. The column is `tracked_asin`. The IQ doc prose refers to "promoted ASIN." Same thing. Operators who grep the table schema for `promoted_asin` come up empty and assume the data isn't there. > 5. If a campaign was deliberately set up as existing-only (a Subscribe & Save win-back, for example), NTB% will be near zero. That is correct, not a bug. The IQ explicitly requires campaigns to "target both new and existing customers" for the result to be interpretable, and an existing-only campaign violates that requirement on purpose. ## What happens next The single-run query is the first step. The repeatable workflow is what the operator actually wanted. Activate this query as a recurring [Skill](/features/skills/) on a weekly cadence. Push the per-campaign NTB% to a BI dashboard so brand managers can read the trend without re-running SQL. Wire the underlying retrieval through the [Amazon Ads MCP](/features/amazon-ads-mcp/) so the agent surfaces the same context to a human reviewer mid-month that it had at compose time. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): Amazon Ads MCP brings campaign and AMC signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog context, Atlas grounds the table choice, and Skills turn the query into a reviewed recurring automation. The rule that fires off this measurement: campaigns below the brand's target NTB% get re-allocated toward upper-funnel placements (DSP awareness inventory, Sponsored Display NTB audiences). Campaigns above target hold steady. Campaigns dramatically below target with no upper-funnel exposure are the strongest signal that the budget is being spent on existing customers who would have purchased anyway. The natural follow-on is the per-ASIN NTB breakdown. The portfolio number tells you whether acquisition is happening. The ASIN-level number tells you which products are doing the work. ## Why this NTB count matters If your acquisition number is just "total buyers," you are not measuring acquisition. *Next up: the per-ASIN NTB gateway analysis, which ASINs are the front door for new-customer purchases, and how to find them in the [path-to-conversion data](/guides/amc-path-to-conversion-sankey/).* ### Sponsored Ads x DSP Overlap: The 4-Way AMC Analysis URL: https://www.kuudo.com/guides/amc-sponsored-ads-dsp-overlap-4way/ An Amazon Marketing Cloud (AMC) Sponsored Ads and DSP overlap analysis separates customers into four mutually exclusive exposure groups: Sponsored Ads only, DSP only, both, or neither as defined by the analysis. The workflow aligns campaign scope and attribution timing before comparing reach and outcomes, so overlapping impressions do not masquerade as incremental performance. The Slack question was blunt: *"When a shopper sees both our demand-side platform (DSP) ads and our Sponsored Products ads, does the combination actually move purchase rate, or are we paying twice for the same conversion?"* That is not a campaign-reporting question. Standard reports can show spend, clicks, impressions, and attributed sales by product line. They cannot tell you whether exposure to multiple ad products changes the purchase rate versus exposure to one. The answer lives in [Amazon Marketing Cloud](/features/amc/) (AMC), but the wrong query gives a very confident wrong answer. I handed the question to our agent, backed by [Amazon Agent Atlas](/features/agent-atlas/), and asked it to build the overlap analysis before we made a budget call. The decision matrix it used before writing SQL: | Question | Use this query | Why | |---|---|---| | DSP plus Sponsored Products only | Sponsored Products and DSP Display Overlap | Narrow legacy comparison | | Sponsored Display plus DSP only | Sponsored Display and DSP Overlap | Older two-way comparison | | DSP plus Sponsored Products, Brands, or Display | Sponsored Ads and DSP Overlap | Improved 2/3/4-way query | | Ordered touch sequence | Path-to-conversion sankey | Sequence, not overlap | For this operator question, the right artifact is **Analyzing Sponsored Ads and DSP Overlap**, run through the [Amazon Ads MCP](/features/amazon-ads-mcp/) so the agent can keep the table names, filters, and waiting rules intact. ## Why generic overlap SQL misstates Sponsored Ads and DSP incrementality > **Four silent failure modes showed up in the un-grounded version** > 1. It used the older Sponsored Display and DSP overlap query even though the newer Sponsored Ads and DSP overlap IQ supersedes it for broader full-funnel analysis. > 2. It treated Sponsored Products exposure as clicks. The IQ says exposure means impressions, including Sponsored Products impressions. > 3. It skipped the 14-day attribution-close rule and produced a report that would undercount conversions if run immediately after the media window. > 4. It compared campaigns that had not run for the same products in the same period. That makes the overlap group look like a performance difference when it is really a campaign-design difference. None of those mistakes has to fail at runtime. The SQL can compile. The chart can look clean. The budget call that follows can still be wrong. ## What the AMC overlap workflow must retrieve Atlas pulled the relevant chunks before the agent wrote the query: - **Analyzing Sponsored Ads and DSP Overlap**: the improved instructional query. It states that the IQ measures 2-way, 3-way, and 4-way overlap and is an improved version of the older Sponsored Display and DSP overlap query. - **Sponsored Products and DSP Display Overlap Analysis**: the narrower prior IQ. Atlas surfaced it as a contrast case so the agent could avoid using the two-product template for a broader Sponsored Ads question. - **Joining and Unioning Sponsored Ads Traffic with Conversions**: the join pattern that points overlap queries at `SPONSORED_ADS_TRAFFIC` and `AMAZON_ATTRIBUTED_EVENTS_BY_TRAFFIC_TIME` through `user_id`. - **Creating Audiences from Clicked Sponsored Ads Without Purchases**: the adjacent audience workflow. It reminded the agent that overlap measurement can become an activation workflow later, but measurement comes first. - **Analyzing the Overlap of Amazon DSP Display, Streaming TV, and Sponsored Products**: the three-way precedent. It has the same one-week co-running and 14-day waiting logic, which is useful when the operator expands from two products to a larger full-funnel plan. The important thing is not just that Atlas found a query. It found the older query, the improved query, the join pattern, and the timing constraints together. That bundle is what keeps an agent from solving the wrong version of the problem. ## Build the four-way Sponsored Ads and DSP overlap analysis The agent returned the AMC SQL as a measurement query, not as an audience. I kept the campaign filters in place but commented, matching the IQ style. Run it unfiltered first to validate the exposure groups, then narrow it to the campaign IDs you actually want to compare. ```sql -- Instructional Query: Sponsored Ads and DSP Overlap -- Use after all compared ad products ran for at least one week -- in the same period. Wait 14 full days after the query end date. WITH dsp_campaigns (campaign_id_string) AS ( VALUES ('1111111111111'), ('2222222222222') ), sa_campaigns (campaign_id_string) AS ( VALUES ('3333333333333'), ('4444444444444') ), impressions_cte AS ( SELECT user_id, ARRAY_SORT(COLLECT(DISTINCT ad_product_type)) AS exposure_group, MIN(impression_dt) AS min_impression_dt, SUM(impressions) AS impressions FROM ( SELECT i.user_id, 'DSP' AS ad_product_type, i.impressions, i.impression_dt FROM dsp_impressions i /* Optional DSP filter: WHERE i.campaign_id_string IN ( SELECT campaign_id_string FROM dsp_campaigns ) */ UNION ALL SELECT i.user_id, i.ad_product_type, i.impressions, i.event_dt AS impression_dt FROM sponsored_ads_traffic i /* Optional Sponsored Ads filter: WHERE i.campaign_id_string IN ( SELECT campaign_id_string FROM sa_campaigns ) */ ) GROUP BY 1 ), reach_by_group AS ( SELECT exposure_group, COUNT(DISTINCT user_id) AS ad_exposed_users FROM impressions_cte GROUP BY 1 ), purchases_by_group AS ( SELECT i.exposure_group, COUNT(DISTINCT p.user_id) AS users_purchased, SUM(p.total_purchases) AS total_purchases, SUM(p.total_product_sales) AS total_product_sales FROM amazon_attributed_events_by_traffic_time p INNER JOIN impressions_cte i ON i.user_id = p.user_id WHERE p.total_purchases > 0 AND p.conversion_event_dt > i.min_impression_dt /* Optional matched campaign filter: AND ( p.campaign_id_string IN (SELECT campaign_id_string FROM dsp_campaigns) OR p.campaign_id_string IN (SELECT campaign_id_string FROM sa_campaigns) ) */ GROUP BY 1 ) SELECT r.exposure_group, r.ad_exposed_users AS unique_reach, COALESCE(p.users_purchased, 0) AS users_that_purchased, COALESCE(p.total_purchases, 0) AS total_purchases, COALESCE(p.total_product_sales, 0) AS total_product_sales, COALESCE(p.users_purchased, 0) * 1.0 / NULLIF(r.ad_exposed_users, 0) AS purchase_rate FROM reach_by_group r LEFT JOIN purchases_by_group p ON r.exposure_group = p.exposure_group ORDER BY purchase_rate DESC; ``` The output is a table of exposure groups. A row might be `['DSP']`, `['sponsored_products']`, or `['DSP','sponsored_products']`, depending on what the shopper saw during the window. The comparison that matters is not total sales. It is purchase rate by exposure group after the same products, same period, and attribution-close rules are satisfied. The `ARRAY_SORT(COLLECT(DISTINCT ad_product_type))` choice matters. Without sorting and distinct collection, the same set of exposures can appear as different labels depending on event order. The agent grouped the exposures as a set, not as a sequence. If you need sequence, that is a different guide: the [path-to-conversion sankey](/guides/amc-path-to-conversion-sankey/). The conversion join uses `AMAZON_ATTRIBUTED_EVENTS_BY_TRAFFIC_TIME` because this is an exposure-window analysis. The query asks whether users who saw an ad product later purchased, so the conversion event has to occur after the user's minimum impression timestamp. That `conversion_event_dt > min_impression_dt` predicate is the line that turns a raw join into a causal sanity check. ## Which timing and grain caveats govern the overlap read > **Five caveats the agent brought back before I asked** > 1. The improved Sponsored Ads and DSP overlap IQ is not the same as the older Sponsored Display and DSP overlap IQ. Use the improved one when Sponsored Products or Sponsored Brands are part of the comparison. > 2. Sponsored Products exposure in this IQ means impressions. If the team expects click-only logic, say that before anyone interprets the result. > 3. Every ad type in the comparison should advertise the same products during the same period and run for at least one week. Otherwise the exposure groups are not comparable. > 4. Wait at least 14 full days after the query end date. Running earlier makes the multi-exposure group look weaker because conversions have not fully closed. > 5. The result is set overlap, not touch order. If the business question is "which touch came first," use the path-to-conversion workflow instead. Those are the guardrails that make the chart reviewable. A generic model can write a join. The hard part is knowing which join should not be trusted yet. ## What happens next Run the unfiltered query once to see whether the overlap groups exist at a usable volume. If the `['DSP','sponsored_products']` row is tiny, do not make a purchase-rate call yet. Extend the window, widen the campaign set, or accept that the campaigns did not create enough overlap to measure. If the overlap group is large enough, package the query as a recurring [Skill](/features/skills/) with an approval gate. The agent should check the three preconditions before each run: same products, at least one week of co-running media, and 14 full days since query end date. If one fails, the Skill should report "not ready" instead of sending a chart to Slack. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): Amazon Ads MCP brings the DSP, Sponsored Products, Sponsored Brands, and Sponsored Display signals; the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add Amazon Standard Identification Number (ASIN) and catalog context; Atlas grounds the timing rules; and Skills keep the recurring run reviewable. The budget action comes after review. If the combined exposure group has materially higher purchase rate than either single-exposure group, the DSP and Sponsored Ads plan is doing real full-funnel work. If the combined group is flat and reach is duplicated, move budget toward the ad product that drives incremental reach or test new audience definitions before increasing spend. ## Why mutually exclusive exposure groups matter Overlap analysis is only useful when the setup rules are as visible as the result. *Next up: use the same exposure groups as inputs to a [path-to-conversion sankey](/guides/amc-path-to-conversion-sankey/) so the agent can separate overlap from sequence.* ### Choose the Right AMC Attribution Source URL: https://www.kuudo.com/guides/amc-custom-attribution-models/ Amazon Marketing Cloud (AMC) attribution modeling starts by choosing the source table whose timing, conversion scope, and privacy behavior match the decision. This guide compares traffic-time, conversion-time, and audience variants before any custom weighting is applied, so a model does not silently mix incompatible grains or disagree with standard reports for avoidable reasons. The Slack message came in mid-afternoon, from our paid-media lead: *"Our attribution model defaults are leaving us blind. Can we build a custom attribution view in AMC, and which data source do we use?"* This is the kind of ask that sounds like one question and is really three. There is not "one" [Amazon Marketing Cloud](/features/amc/) (AMC) attribution table. There are several, they are not interchangeable, and the obvious moves get you the wrong one. Ask ChatGPT for the AMC attribution table and you get back a single name with no context. Search Amazon's docs and the Custom Attribution Overview instructional query (IQ) surfaces, but the data-source comparison is buried in section 3.6 where most readers never scroll. Ping the agency and they default to `amazon_attributed_events_by_conversion_time` because that is the standard reporting source used by the Amazon DSP (demand-side platform) UI. So I handed it to our agent, which has [Amazon Agent Atlas](/features/agent-atlas/) behind it. Atlas indexes the AMC documentation, the instructional queries, the schema reference for every Amazon Standard Identification Number (ASIN) the catalog tracks, and the privacy framework that governs which columns you can put in a SELECT. Here is what came back. ## Why an attribution model fails when it starts from the wrong AMC source I ran the same prompt through a frontier model with no retrieval. The output looked plausible. It was wrong in five specific, silent ways. > **Five failure modes I saw on a single un-grounded prompt** > - It treated `amazon_attributed_events_by_conversion_time` as a custom-attribution source. That table is the standard-reporting source: competition-aware, 14-day last-touch, de-duped across campaigns. You cannot build a custom model on it. > - It wrote `SELECT user_id` from `conversions_with_relevance` for an audience-building query. `user_id` carries the aggregation threshold "Very high" and can never appear in the final SELECT of an analytics table. The only data source that permits audience-building semantics is `conversions_with_relevance_for_audiences`. > - It filtered `WHERE engagement_scope = 'PROMOTED'` against `conversions`. `engagement_scope` lives on `conversions_with_relevance`; the plain `conversions` table excludes every campaign-dependent column. > - It assumed `conversions` and `conversions_with_relevance` have the same row counts. They do not. In `conversions_with_relevance`, a single conversion can appear on multiple rows, one per relevant campaign. The IQ example splits conversion_id 123456 across campaign_a and campaign_b. > - It quoted a 14-day pixel lookback to size pixel volume in custom attribution. That is the standard-reporting lookback. Inside custom attribution, the pixel lookback is bounded only by when the pixel was tracked to the campaign, or by the query time window. None of these failures throw an error. The query runs. The numbers look like attribution numbers. They are the wrong ones, and you only find out when the downstream model contradicts a sanity check. ## What the AMC attribution workflow must retrieve When the agent gets the question, it does a semantic search across the `amazon_ads` collection, which is the corpus that backs the [Amazon Ads MCP](/features/amazon-ads-mcp/). It pulls six chunks before it writes anything: - The **Custom Attribution Overview** (dataset `conversions`) defines the two custom-attribution data sources, the 28-day ASIN relevancy rules, and the section 3.6 comparison table that names all four AMC attribution sources. - The **Amazon Ads Conversions with Relevance** chunk (dataset `conversions_with_relevance`) names both physical variants: the Analytics table (`conversions_with_relevance`) and the Audience table (`conversions_with_relevance_for_audiences`). - The **Amazon Attributed Events Overview** (dataset `amazon_attributed_events`) is the schema reference for the standard-reporting attribution data, including the LOW-threshold dimension columns. - The **Data Aggregation Thresholds in AMC** chunk (dataset `amazon_attributed_events_by_conversion_time`) classifies every column as None, Low, Medium, High, or Very high, and explains why `user_id` cannot be SELECTed from a normal analytics table. - The **Custom Attribution Linear Model** IQ template shows how a chosen base table plugs into the standard-vs-custom comparison. - The **Joining and Unioning Sponsored Ads Traffic with Conversions** chunk (dataset `sponsored_ads_traffic`) demonstrates that `sponsored_ads_traffic` joins to `conversions_with_relevance`, not `conversions`, because campaign columns are required. Atlas does not pick the table for you. It surfaces the rules and lets the agent make the call. ## Compare AMC attribution sources before weighting touchpoints The artifact for this kind of question is not SQL. It is a decision plan: a matrix of every AMC attribution data source side by side, then a rule set keyed off the operator's actual use case. Here is the machine-readable form the agent returned, before the prose write-up: ```json { "artifact": "decision_plan", "topic": "amc-custom-attribution-base-table", "rules": [ {"use_case": "align with Amazon DSP / Sponsored Ads UI numbers", "pick": "amazon_attributed_events_by_conversion_time", "why": "standard-reporting source; custom attribution will not reconcile by design"}, {"use_case": "Prime Day delivered-impression effect on conversions", "pick": "amazon_attributed_events_by_traffic_time", "why": "anchors on traffic time, matching the campaign window"}, {"use_case": "de-duped pixel volume across all campaigns, exposed and un-exposed users", "pick": "conversions", "why": "excludes campaign columns; one row per conversion"}, {"use_case": "linear / position-based / first-touch / last-touch multi-touch model with per-campaign credit", "pick": "conversions_with_relevance", "why": "campaign columns + engagement_scope required to split credit; one row per (conversion x relevant campaign)"}, {"use_case": "build an audience from users satisfying a custom-attribution rule", "pick": "conversions_with_relevance_for_audiences", "why": "only data source permitting SELECT user_id for audience materialization"}, {"use_case": "measure halo lift specifically", "pick": "conversions_with_relevance", "why": "filter engagement_scope = 'BRAND_HALO' vs 'PROMOTED' on the relevance table"} ] } ``` The JSON is the compact answer the Skill can hand to an operator or downstream workflow. The matrix below is the human review layer for the same decision: every row shows where the source is safe, where it silently drifts, and whether it can materialize users for activation. ## Which AMC attribution source should you choose? | Data source | What it is | ASIN coverage | Pixel coverage | `engagement_scope`? | `SELECT user_id`? | Lookback | Dedup behavior | Use it for | |---|---|---|---|---|---|---|---|---| | `amazon_attributed_events_by_conversion_time` | Standard-reporting attribution, by conversion time | Ad-attributed only | Ad-attributed only | N/A | No (`user_id` is Very high) | 14-day last-touch (pixel) | De-duped across campaigns | Aligning with the standard Amazon DSP and Sponsored Ads reports | | `amazon_attributed_events_by_traffic_time` | Standard-reporting attribution, by traffic time | Ad-attributed only | Ad-attributed only | N/A | No | 14-day last-touch (pixel) | De-duped across campaigns | Campaign-window analyses, like Prime Day delivered impressions | | `conversions` | Custom-attribution base; relevant conversions only, no campaign columns | Promoted plus brand halo | All pixel conversions tracked to the campaign, exposed and un-exposed users | No | No | 28-day ASIN relevancy; pixel lookback bounded by tracking start | De-duped | Volume questions and de-duped pixel performance across campaigns | | `conversions_with_relevance` (Analytics table) | Custom-attribution base plus campaign and advertiser columns | Same conversions as `conversions`, one row per relevant campaign | Same conversions as `conversions`, one row per relevant campaign | Yes (PROMOTED, BRAND_HALO, null) | No | Same as `conversions` | Duplicated across relevant campaigns | Multi-touch and split-credit custom models (linear, position-based, first-touch, last-touch) | | `conversions_with_relevance_for_audiences` (Audience table) | Audience-table variant of `conversions_with_relevance` | Same as `conversions_with_relevance` | Same as `conversions_with_relevance` | Yes | Yes, the only data source that permits `SELECT user_id` for audience-building | Same as `conversions_with_relevance` | Same | Materializing custom-attribution audiences for activation | A note on framing: Atlas's section 3.6 comparison lists four AMC attribution data sources by splitting `amazon_attributed_events` into the `_by_conversion_time` and `_by_traffic_time` variants. Other framings collapse those two into one and treat `conversions_with_relevance_for_audiences` as the fourth distinct surface. The matrix above shows all five rows so both views are visible; the decision rules below collapse to the four-way operator choice. ### Decision rules - If the use case is "align my custom model output with the numbers in the Amazon DSP or Sponsored Ads UI," pick `amazon_attributed_events_by_conversion_time`, because it is the standard-reporting source and custom attribution will not reconcile by design. - If the use case is "Prime Day delivered-impression effect on conversions," pick `amazon_attributed_events_by_traffic_time`, because it anchors on traffic time and matches the campaign window. - If the use case is "de-duped pixel volume across all campaigns the pixel is tracked to, exposed and un-exposed users," pick `conversions`, because it excludes campaign columns and emits one row per conversion. - If the use case is "linear, position-based, first-touch, or last-touch custom model with per-campaign credit," pick `conversions_with_relevance`, because campaign columns and `engagement_scope` are required to split credit; expect one row per (conversion x relevant campaign). - If the use case is "build an audience from the users who satisfy a custom attribution rule," pick `conversions_with_relevance_for_audiences`, because it is the only data source that permits `SELECT user_id` for audience materialization. - If the use case is "measure halo lift specifically," pick `conversions_with_relevance` and filter on `engagement_scope = 'BRAND_HALO'` versus `'PROMOTED'`. A few things to notice about how the agent broke this apart. The `amazon_attributed_events_*` family is not a custom-attribution base table. It is competition-aware, de-duped across campaigns, and follows fixed last-touch logic. Use it when you need to align with standard reporting numbers. Never use it when you are modeling, because the model logic is already baked in and you cannot unbake it. The split between `conversions` and `conversions_with_relevance` is a row-shape difference, not a coverage difference. Both tables cover the same set of relevant conversions. `conversions_with_relevance` adds campaign and advertiser dimensions, and as a consequence it emits one row per relevant campaign per conversion. A dedup-aware question like "how many users converted in the window, period" uses `conversions`. A multi-touch model that splits credit across campaigns uses `conversions_with_relevance` because the per-campaign rows are exactly what gets weighted. The audience-table variant exists because of aggregation thresholds, not because someone wanted three tables instead of two. `user_id` is classified Very high, which means a normal analytics table cannot put it in a final SELECT, cannot filter it against a literal value, and cannot use it as a join or group-by key against literals. The `_for_audiences` companion is the data source that allows audience-building semantics, and it is the only one. Anything materializing user-level audiences from custom attribution flows through it. The `engagement_scope` column on `conversions_with_relevance` is the lever for halo measurement. Values are `PROMOTED` for the promoted ASIN, `BRAND_HALO` for halo conversions to other ASINs from the same brand, and null for pixel conversions. Any custom model that wants to isolate halo lift filters on this column. Any custom model that ignores it is silently treating halo and promoted as the same thing. ## Which table caveats affect custom AMC attribution This is the part where retrieval grounding earns its keep. None of these were asked for. All of them mattered. > **Six things the agent surfaced that the operator did not ask about** > - The 28-day ASIN relevancy window for custom attribution is double the brand-based 14-day window in `amazon_attributed_events_by_conversion_time` and `_by_traffic_time`. Higher per-ASIN conversion counts in custom attribution are expected, not a bug. > - The custom-attribution data sources are not competition-aware and include both viewable and non-viewable impressions. Two more reasons custom-attribution conversion counts run higher than the attributed-events numbers. > - Pixel conversions in `conversions` and `conversions_with_relevance` do not require an ad impression or click to be relevant. If a pixel was tracked to a DSP campaign, all pixel conversions appear, including from users who were never exposed. > - Pixel lookback inside custom attribution is bounded by when the pixel was tracked to the campaign, or by the query time window. It is not the 14-day last-touch lookback that `amazon_attributed_events_*` uses. Sizing pixel volume with the wrong lookback is a common silent error. > - `engagement_scope` is always `PROMOTED` for pixel conversions (when `event_category = 'pixel'`), and `halo_code` is null for pixels. Both columns are LOW-threshold dimensions and both live only on `conversions_with_relevance`. > - `user_id` (aggregation threshold Very high) can never appear in the final SELECT, cannot be filtered with literal values, and cannot be used as a join or group-by key against literals. It can, however, be COUNT or COUNT DISTINCTed, which is how reach, optimal-frequency, and customer-journey queries get written. The `_for_audiences` audience table exists as a separate physical surface precisely because the analytics tables cannot do the audience-shaped thing. Most of these would have cost an hour each to discover the hard way. Compounded across a quarter, they are the difference between a custom attribution program that ships and one that gets quietly abandoned. ## What happens next The decision plan is the input, not the output. Once you have picked the base table, the operational moves diverge. If you picked `conversions_with_relevance` for a multi-touch model, the next step is to schedule the linear or position-based IQ as a recurring AMC workflow. Package it as a verified [Skill](/features/skills/) and export the campaign-weighted credit to a dashboard. If you picked `conversions_with_relevance_for_audiences`, the next step is to activate the resulting user set as an AMC audience, following the same pattern documented in the [cart-abandoner audience guide](/guides/amc-cart-abandoner-audience/). If you picked `conversions` for de-duped pixel volume across campaigns, feed it into the same pipeline pattern used in the [Subscribe & Save lift workflow](/guides/amc-subscribe-and-save-lift/), substituting your custom-attribution filters. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and campaign signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog context when ASIN eligibility matters, Atlas grounds the source selection, and Skills keep the attribution model on a reviewable schedule. If you picked `amazon_attributed_events_by_conversion_time` or `_by_traffic_time`, stop. You are doing standard reporting alignment, not custom modeling. Route the work to the standard-reports surface and save AMC for the questions standard reports cannot answer. ## Why source selection comes before model selection Pick the wrong base table and every downstream model inherits the wrong numbers, silently, for as long as the workflow runs. --- *Next in the series: building a path-to-conversion view in AMC that stitches DSP, Sponsored Products, Sponsored Brands, and Sponsored Display into a single touch-ordered timeline. Custom attribution sets the base table; path analysis is what you do with it.* ### Evaluate Prime Day AMC Lookalikes URL: https://www.kuudo.com/guides/amc-lookalike-audience-evaluation/ Evaluating an Amazon Marketing Cloud (AMC) lookalike audience requires a comparison against the rule-based audience it replaces, not a standalone conversion rate. This workflow measures new-to-brand share, incremental reach, and cost per acquisition for the Prime Day cohorts, then turns the read into a keep, rotate, or rebuild decision for the next activation. The Slack message came in two days after the Prime Day campaign closed: *"The lookalike audience shipped. Did it actually beat last year's rule-based audience, or did we just add a new prospecting line item?"* That question should not be answered from an Amazon DSP screenshot. The operator needs a matched-window read in [Amazon Marketing Cloud](/features/amc/): same promotional period, comparable spend, and the same conversion definition. If the lookalike audience reached more shoppers but bought new-to-brand customers at a worse cost, it did not win. It scaled noise. If it held cost per new-to-brand customer while expanding reach, the seed is worth keeping. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. [Amazon Agent Atlas](/features/agent-atlas/) supplies the AMC playbooks and [Model Context Protocol (MCP)](/features/mcp/) exposes that private context to each client. The client changes; your context and workflow do not. ## The AI is shared. Atlas changes the decision I ran the same ask without retrieval. The answer sounded reasonable: pull conversions, compare totals, report lift. That is not enough for a Prime Day audience decision. > **Five failure modes without account context and Atlas grounding** > 1. It compared the lookalike audience to total campaign performance instead of to last year's rule-based audience baseline. That makes the lookalike inherit credit from every other tactic in the flight. > 2. It treated reach as the win condition. For Prime Day prospecting, reach only matters if new-to-brand efficiency holds. > 3. It ignored the attribution settling period. Reading the audience too early undercounts late conversions and can make the model look worse than the baseline. > 4. It did not normalize for spend. A line item with 40% more spend should produce more conversions; the question is whether it produced cheaper or more incremental customers. > 5. It did not create a next action. The operator needs a keep, rotate, or rebuild decision, not a dashboard summary. This is the same problem as the seed post in reverse. The model can write the query-shaped thing. The Atlas-grounded agent knows which comparison decides the next Prime Day seed. ## What Atlas retrieves for Prime Day lookalike measurement When the agent gets the question, it pulls the audience-building post's downstream playbook pieces before writing SQL: - The **AMC Lookalike Audiences for Promotional Events** playbook: the source of the Prime Day lead-in and lead-out logic, plus the expectation that performance is evaluated after activation rather than at seed creation. - The **Introduction to AMC Lookalike Audiences** instructional query: the expansion-type context that explains why Balanced, Similar, and Broad should be evaluated differently. - The **Customer Journey Analytics** playbook: the follow-on analysis path for checking whether the lookalike is creating new conversion journeys or entering journeys the rule-based audience already covered. - The **DSP and AMC conversion reporting table notes**: the table-shape reminders for `dsp_impressions`, `dsp_clicks`, and `amazon_attributed_events_by_traffic_time`. - The **Subscribe & Save lift analysis** pattern: the upstream check that tells the operator whether SnS behavior was strong enough to be a seed in the first place. The seed can also be rebuilt around Amazon Standard Identification Number (ASIN) behavior if the evaluation shows that subscription buyers reached well but did not produce enough new customers. The retrieved set matters because this is not just a measurement query. It is a decision about whether the next seed should stay SnS, rotate to multi-ASIN purchasers, or use a spend threshold. ## The agent's working output: AMC lookalike lift SQL The agent produced an evaluation skeleton that compares the activated lookalike line item with a prior rule-based audience baseline. The placeholder IDs are intentionally visible so the operator can audit what is being compared before running it. ```sql /* AMC lookalike audience evaluation skeleton Run after the promotional flight closes and attribution has settled. Last verified: 2026-05-13. Replace: - LOOKALIKE_CAMPAIGN_ID with the Prime Day lookalike line item/campaign - BASELINE_CAMPAIGN_ID with last year's rule-based audience campaign - flight windows with matched Prime Day periods */ WITH comparison_windows AS ( SELECT 'lookalike_2026' AS cohort, TIMESTAMP '2026-07-01 00:00:00' AS window_start, TIMESTAMP '2026-07-15 23:59:59' AS window_end, 'LOOKALIKE_CAMPAIGN_ID' AS campaign_id UNION ALL SELECT 'rule_based_2025' AS cohort, TIMESTAMP '2025-07-01 00:00:00' AS window_start, TIMESTAMP '2025-07-15 23:59:59' AS window_end, 'BASELINE_CAMPAIGN_ID' AS campaign_id ), impressions AS ( SELECT w.cohort, i.user_id, COUNT(*) AS impressions, SUM(i.total_cost) AS spend FROM comparison_windows w JOIN dsp_impressions i ON i.campaign_id = w.campaign_id AND i.impression_dt_utc BETWEEN w.window_start AND w.window_end WHERE i.user_id IS NOT NULL GROUP BY 1, 2 ), conversions AS ( SELECT w.cohort, a.user_id, COUNT(*) AS orders, SUM(a.total_product_sales) AS sales, SUM(CASE WHEN a.new_to_brand = TRUE THEN 1 ELSE 0 END) AS ntb_orders FROM comparison_windows w JOIN amazon_attributed_events_by_traffic_time a ON a.campaign_id = w.campaign_id AND a.event_dt_utc BETWEEN w.window_start AND w.window_end WHERE a.conversion_event_subtype = 'order' AND a.user_id IS NOT NULL GROUP BY 1, 2 ), cohort_rollup AS ( SELECT i.cohort, COUNT(DISTINCT i.user_id) AS reached_users, SUM(i.impressions) AS impressions, SUM(i.spend) AS spend, COUNT(DISTINCT c.user_id) AS converting_users, SUM(c.orders) AS orders, SUM(c.sales) AS sales, SUM(c.ntb_orders) AS ntb_orders FROM impressions i LEFT JOIN conversions c ON i.cohort = c.cohort AND i.user_id = c.user_id GROUP BY 1 ) SELECT cohort, reached_users, spend, orders, sales, ntb_orders, orders / NULLIF(reached_users, 0) AS conversion_rate, ntb_orders / NULLIF(orders, 0) AS ntb_share, spend / NULLIF(ntb_orders, 0) AS cost_per_ntb_order, sales / NULLIF(spend, 0) AS roas FROM cohort_rollup ORDER BY cohort; ``` Three choices matter in that SQL. First, the comparison is campaign-scoped, not account-scoped. The lookalike line item should not borrow credit from branded search, retargeting, or other Prime Day tactics. Second, the windows are explicit. If you compare a 2026 lead-in window to a 2025 full-event window, the result will look precise and be useless. The agent forces both cohorts into matched promotional periods. Third, the output includes `cost_per_ntb_order`, not just return on ad spend (ROAS). For a lookalike audience, the question is not only whether it sold product. The question is whether it found new customers at a cost worth repeating in Amazon demand-side platform (DSP) activation. ## The footnotes the agent surfaces before you trust the lift read > **Things Atlas surfaced that the operator did not ask for** > 1. **Do not evaluate before attribution settles.** A same-day read can undercount late attributed conversions and distort the lookalike result. > 2. **Spend-normalize before calling lift.** More spend usually means more orders. Efficiency metrics decide whether the audience actually improved the plan. > 3. **Compare to the right baseline.** Last year's rule-based audience is a better baseline than total Prime Day performance because it isolates audience strategy. > 4. **Treat expansion type as a test variable.** If Balanced scaled reach but lost new-to-brand efficiency, test Similar next. If Similar is efficient but too small, test Broad. > 5. **Use path analysis as the next diagnostic.** If the lookalike appears in the same journeys as existing retargeting, it may not be creating new demand. The footnotes change the shape of the readout. Without them, the operator gets a table. With them, the operator gets a decision tree. ## What happens next: keep, rotate, or rebuild the seed The agent turns the output into three possible decisions. If the lookalike improves new-to-brand share and lowers cost per new-to-brand order, keep the seed and test a tighter expansion type. If reach improves but efficiency falls, rotate the seed from SnS subscribers to multi-ASIN purchasers or a spend threshold. If the rule-based audience still wins on conversion rate and cost, keep lookalikes in the lead-in phase and use rule-based audiences for lead-out retargeting. This is where the standards-based setup matters operationally. If you are in Claude, ask for the recommendation in plain language. If your team works in ChatGPT, ask the same question there. If the marketing analyst wants to inspect the SQL, open it in Cursor or Codex. If the read should run every Monday after a promotional flight, schedule it in n8n or Make. Kuudo speaks MCP, so the same Amazon context follows the work into whichever standards-supporting client your team prefers. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and DSP signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog and ASIN context, Atlas grounds the baseline comparison, and [Skills](/features/skills/) keep the post-flight read on a repeatable schedule. The follow-on analysis is the conversion path. If the lookalike wins, use the [path-to-conversion Sankey workflow](/guides/amc-path-to-conversion-sankey/) to see whether it is creating new journeys or merely joining paths your existing campaigns already owned. ## Why standards-based AI clients make this more useful This is not a Claude workflow, a ChatGPT workflow, or a Cursor workflow. It is an Amazon Marketing Cloud workflow exposed through a standard interface. The useful part is the stable context: the tables, playbooks, thresholds, and audience history. The chat or coding tool is just where the operator happens to be working. That is the same reason the evaluation produces an action instead of a report. The agent can move from question to SQL to scheduling because the context is portable. It can answer in chat, edit in code, and run in a workflow tool without rewriting the operating logic. If your lookalike readout stops at reach and ROAS, it has not answered the Prime Day question. *Next: use path-to-conversion analysis to see whether the lookalike audience created new customer journeys or just overlapped with existing retargeting paths.* ### Segment AMC Customers by CLTV URL: https://www.kuudo.com/guides/amc-cltv-cohort-segmentation/ Amazon Marketing Cloud (AMC) customer lifetime value analysis groups purchasers by repeat behavior and value so teams can compare segments, build usable audience seeds, and track movement between cohorts. The workflow defines a stable four-segment taxonomy, selects the correct audience table, and reruns the same logic over time instead of treating one snapshot as permanent. "Which of our customer cohorts are actually worth the most over time, and which look profitable but aren't?" — that was my Slack question on a Tuesday, and I handed it to our [Amazon Agent Atlas](/features/agent-atlas/) with a strict brief: no fluffy summaries, just a query-first proof of concept. I already tried three obvious approaches: a ChatGPT-style single-shot which ignored [Amazon Marketing Cloud](/features/amc/)'s 12-month customer lifetime value (CLTV) cohort window, the AMC docs alone that described CLTV but didn't surface the exact AVG(... ) OVER () CASE pattern we needed, and an agency pitch deck that happily swapped segment labels and ignored activation timing. I asked the agent to return a per-user labeling of High Value / Growth Potential / Revenue Potential / Low Value ready for a 48-hour Amazon demand-side platform (DSP) activation window for resulting audiences. ## Why generic AMC CLTV segmentation drifts between runs I ran the same prompt through a frontier model with no retrieval. The output read confidently. It was wrong in five places, each subtle enough that an operator would only catch the failures at activation time, when the DSP audience comes back empty or the segment labels don't match the deck. > **Five failure modes I saw in a single un-grounded response** > - used `conversions` rather than `conversions_for_audiences` (the audience-build table variant), which produces the wrong user set for RBA exports. > - omitted `AVG(lifetime_score) OVER ()` and `AVG(value_score) OVER ()` semantics, producing non-comparable thresholds across the two axes. > - failed to include the 48-hour DSP activation window, so audiences can't be scheduled correctly in DSP. > - returned generic tiers instead of the four AMC segments: High Value, Growth Potential, Revenue Potential, Low Value. > - omitted the year-over-year diagnostic dimension for cohort migration. This is the differentiator. The model isn't lying. It is producing a plausible-looking artifact that fails against the AMC playbook's actual mechanics: the audience-build table variant, the composite window-function semantics, the activation timing, the named segment taxonomy, and the diagnostic dimension that catches drift over time. ## What the AMC CLTV workflow must retrieve - "Understanding Customer Long-Term Value (CLTV)" playbook: the canonical AMC CLTV reference that explains CLTV versus return on ad spend (ROAS) and reminds us the AMC long-term window is bounded to 12 months. (source: Customer long-term value (CLTV): Amazon Marketing Cloud) - "Lifetime * Value CLTV SQL Export Logic": the composite-score template that defines the Lifetime Ratio and the overall Lifetime × Value construct. This includes the formula Lifetime Ratio = Purchase Frequency × Purchase Quantity × User Lifetime ratios. (source: Lifetime * Value CLTV SQL Export Logic) - "Audience Labeling" CASE statement for the four segments: the exact AVG(...) OVER () CASE pattern you need to convert scores into High Value, Growth Potential, Revenue Potential, and Low Value labels. (source: Audience segmentation (based on CLTV)) - "Identify high value customer segments": a data-interpretation guide with percentile examples and audience-size guidance so you can pick seed thresholds and respect rule-based audience (RBA) and lookalike (LAL) guardrails. (source: Identifying High Value Customer Segments: Flexible Amazon shopping insights) - "CLTV Audience Segmentation (Year-Over-Year)": the diagnostic view and recommended recurring check to detect cohort migration between segments over time. (source: Customer long-term value (CLTV): Year-over-year guidance) ## Build four stable AMC customer lifetime value segments ```sql -- Per-user CLTV segmentation skeleton for AMC WITH -- 1) base events: filter to the 12-month AMC window and to the audience build table events AS ( SELECT user_id, event_time, tracked_asin, -- optional: later join to ASIN->category mapping for descriptive stats quantity, sale_price, -- revenue per event (use Paid Features mappings if available) ad_spend -- spend attributed to the event (nullable; a proxy may be filled for off-Amazon) FROM conversions_for_audiences -- use conversions_all for sizing/coverage experiments WHERE event_time BETWEEN DATE_ADD('month', -12, CURRENT_DATE) AND CURRENT_DATE ), -- 2) per-user purchase aggregates user_purchases AS ( SELECT user_id, COUNT(DISTINCT event_time) AS purchase_count, SUM(quantity) AS total_units, SUM(COALESCE(sale_price,0) * COALESCE(quantity,0)) AS total_revenue, SUM(COALESCE(ad_spend,0)) AS total_ad_spend, MIN(event_time) AS first_purchase_ts, MAX(event_time) AS last_purchase_ts FROM events GROUP BY user_id ), -- 3) derived per-user metrics required by the playbook user_metrics AS ( SELECT user_id, purchase_count, total_units, total_revenue, total_ad_spend, -- user lifetime in days within the 12-month window (inclusive) DATE_DIFF('day', first_purchase_ts, last_purchase_ts) + 1 AS user_lifetime_days, -- purchase frequency: purchases per active 30-day period (approx) CASE WHEN DATE_DIFF('day', first_purchase_ts, last_purchase_ts) >= 30 THEN purchase_count / (GREATEST(DATE_DIFF('day', first_purchase_ts, last_purchase_ts),1) / 30.0) ELSE purchase_count END AS purchase_frequency, -- purchase quantity per purchase CASE WHEN purchase_count > 0 THEN total_units / CAST(purchase_count AS DOUBLE) ELSE 0 END AS purchase_quantity, -- customer cost: currently only ad spend; retention_cost can be added to this field (total_ad_spend) AS customer_cost FROM user_purchases ), -- 4) cohort selection: cohort_month as a simple acquisition cohort; replace with channel or category as needed cohorted AS ( SELECT um.*, DATE_FORMAT(first_purchase_ts, '%Y-%m') AS cohort_month FROM user_metrics AS um ), -- 5) cohort-level means used to normalize into ratios cohort_stats AS ( SELECT cohort_month, AVG(purchase_frequency) AS avg_purchase_frequency, AVG(purchase_quantity) AS avg_purchase_quantity, AVG(user_lifetime_days) AS avg_user_lifetime_days, AVG(total_revenue) AS avg_revenue, AVG(customer_cost) AS avg_cost, COUNT(*) AS cohort_size FROM cohorted GROUP BY cohort_month ), -- 6) per-user ratios (Lifetime and Value components) ratios AS ( SELECT c.user_id, c.cohort_month, -- Lifetime Ratio = Purchase Frequency Ratio × Purchase Quantity Ratio × User Lifetime Ratio (COALESCE(c.purchase_frequency,0) / NULLIF(s.avg_purchase_frequency,0)) * (COALESCE(c.purchase_quantity,0) / NULLIF(s.avg_purchase_quantity,0)) * (COALESCE(c.user_lifetime_days,0) / NULLIF(s.avg_user_lifetime_days,0)) AS lifetime_ratio, -- Value side: revenue vs cost ratios (higher revenue and lower cost should increase value) (COALESCE(c.total_revenue,0) / NULLIF(s.avg_revenue,0)) AS revenue_ratio, (COALESCE(c.customer_cost,0) / NULLIF(s.avg_cost,1)) AS cost_ratio FROM cohorted c JOIN cohort_stats s USING (cohort_month) ), -- 7) per-user scores: lifetime_score (product) and value_score (revenue - cost composite) scores AS ( SELECT r.user_id, r.cohort_month, r.lifetime_ratio AS lifetime_score, (r.revenue_ratio - r.cost_ratio) AS value_score FROM ratios r ) -- Final: label users using global AVG(...) OVER () thresholds so both legs compare to the same population SELECT s.user_id, s.cohort_month, s.lifetime_score, s.value_score, CASE WHEN s.lifetime_score >= AVG(s.lifetime_score) OVER () AND s.value_score >= AVG(s.value_score) OVER () THEN 'High Value' WHEN s.lifetime_score >= AVG(s.lifetime_score) OVER () AND s.value_score < AVG(s.value_score) OVER () THEN 'Growth Potential' WHEN s.lifetime_score < AVG(s.lifetime_score) OVER () AND s.value_score >= AVG(s.value_score) OVER () THEN 'Revenue Potential' WHEN s.lifetime_score < AVG(s.lifetime_score) OVER () AND s.value_score < AVG(s.value_score) OVER () THEN 'Low Value' ELSE 'Unlabeled' END AS customer_segment FROM scores s; ``` I kept the SQL intentionally modular so the parts are pluggable into our pipeline. The agent used the composite formula from the "Lifetime * Value CLTV SQL Export Logic": Lifetime Ratio = Purchase Frequency × Purchase Quantity × User Lifetime ratios, and modeled value_score as a revenue-minus-cost composite so that higher revenue and lower cost increase value. Cohorting is done by first_purchase month as a placeholder; replace cohort_month with acquisition_channel or tracked_asin category if that's more meaningful for your ops. I also made two operational choices: use conversions_for_audiences for the canonical audience build exports and run the same query against conversions_all only when you need a sizing or coverage experiment. Finally, ensure the AVG(...) OVER () comparisons are computed over the same population so the thresholds are comparable. ## Which activation limits and cohort caveats affect the result > **Agent footnotes added without asking** > - audiences ready for DSP activation have a 48-hour activation window. > - AMC long-term = one-year window; cohorts are bounded to 12 months. > - use percentile examples (e.g., top 71-100, ~748k out of 2.5M) and audience-size guardrails when creating RBA/LAL; avoid seed sizes below the platform minimum. > - AVG(lifetime_score) OVER () and AVG(value_score) OVER () must use the SAME population to produce comparable thresholds. > - run the year-over-year cohort migration check to spot cohorts moving from Growth Potential into High Value or sliding to Low Value. > - include non-ad-attributed conversions (Paid Features shopping insights) for a fuller revenue picture when available. ## What happens next - Export the four labeled audiences as rule-based audience manifests and activate into Amazon DSP with differentiated bidding: Higher bid for High Value, conversion-first creatives for Growth Potential, revenue-focused bids for Revenue Potential while excluding low-margin ASINs, and exclude or suppress Low Value audiences from high-cost buys. (See Swim Lane #3 and #4 from the Off-Amazon Conversions Playbook.) - Schedule this query as a recurring AMC job monthly, aligned to cohort_month granularity, and push audiences after respecting the 48-hour DSP activation window for resulting audiences. - Run the year-over-year diagnostic at the same cadence and produce cohort-migration charts that bucket users by percentile so you can detect movement between Growth Potential and High Value or slides toward Low Value. - For ops handoff: publish an audience manifest with Amazon Standard Identification Number (ASIN) category mappings, provide audience sizes and seed counts, and filter out cohorts under minimum seed thresholds before requesting DSP activation. Consider running overlap scoring against Persona Builder API outputs to avoid audience saturation. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and campaign signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog context for ASIN grouping, Atlas grounds the CLTV taxonomy, and [Skills](/features/skills/) turn the segment export into a reviewed recurring automation. ## Why cohort migration matters more than a one-time segment A foundation model can write CLTV-shaped SQL. What it cannot reliably do is produce a query that compiles against the AMC table variants Audiences actually accepts, uses the exact composite window-function pattern the CLTV playbook prescribes, labels users with the four-segment taxonomy operators downstream are already wired for, and bakes in the 48-hour activation window plus the year-over-year diagnostic. The agent didn't have to remember any of that. It had to know where to look. Composite Lifetime × Value cohorting. *Next guide in the series: moving from segmentation to bounded bidding decisions. See what the bidding workflow should do once you have a High Value cohort: [Path to conversion sankey](/guides/amc-path-to-conversion-sankey/).* ### Build High-Value AMC Audience Seeds URL: https://www.kuudo.com/guides/amc-high-value-audience-segmentation/ An Amazon Marketing Cloud (AMC) high-value audience starts with a defensible value rule, not an arbitrary revenue cutoff. This workflow builds three seed strategies from conversion data, checks each seed against lookalike activation limits, and preserves enough buffer for privacy suppression and normal audience decay before the segment reaches DSP. The Slack ping landed on a Thursday from our CRM lead: *"Who are our actual best customers, and can we build a demand-side platform (DSP) audience that targets people who look like them?"* It sounds like a five-minute question. Ask ChatGPT and you get a single "high value" SQL block, almost always a total-spend filter, usually against the wrong table variant, almost never inside the seed-size band [Amazon Marketing Cloud](/features/amc/) actually requires. Search the AMC console's Instructional Query library and you find the answer, except it lives in two separate playbooks you have to stitch together yourself. By the time you've reconciled them, the campaign launch has slipped a day. So I asked our agent. It runs on [Amazon Agent Atlas](/features/agent-atlas/), a curated corpus of AMC playbooks, DSP activation guides, and event-subtype references indexed for semantic retrieval. The answer came back in the shape the IQ library actually recommends: three seed audiences, tested separately, each sized into the 1,000 to 450,000 buffer before activation. ## Why a generic high-value audience definition produces a weak seed I ran the same prompt through a frontier model with no retrieval first. The SQL it produced looked competent. It would have failed at audience creation in at least four distinct ways. > **Five failure modes I saw in a single un-grounded response** > - It selected `user_id` from `conversions_all`. Audience-build queries that return user IDs must run against the `_for_audiences` variant, in this case `conversions_all_for_audiences`. The sizing wrapper does the opposite: it counts user IDs against `conversions_all`. The Atlas chunk is explicit, "change `user_id` to `count(user_id)`, remove the suffix of `_for_audiences` in the table name." > - It returned one blended seed that combined SnS, multi-purchase, and a spend threshold joined with `AND`. The result was an overly specific cohort of around 300 users. The IQ guidance is the opposite, "we recommend testing these audiences separately to avoid overly specific seeds." > - It hallucinated the event filter as `event_subtype = 'SnS'`. The real values are `event_subtype IN ('firstSnSOrder', 'repeatSnSOrder')`. The `repeatSnSOrder` value was added to Flexible Shopping Insights on 02/05/2024, so any model with a training cutoff before that date does not know it exists and will quietly miss every recurring SnS shipment. > - It said nothing about the 500 to 500,000 hard sizing bound. Audience refresh fails outside that range, silently for the operator until the DSP line item runs empty. > - It left a comment as the final line of the SQL. AMC's audience pusher rejects any query whose last line is a comment, with an error message that does not tell you which line. Any one of these would have cost the launch day. The combination would have looked like a working audience right up until the DSP line item failed to spend. ## What the AMC high-value audience workflow must retrieve When the agent gets the question, it does a semantic search across the `amazon_ads` collection and pulls six chunks before writing a single line of SQL: - The **Introduction to AMC Lookalike Audiences** playbook, section 3.2, which enumerates the three high-value seed strategies (Subscribe and Save, Multiple Purchases, Total Purchase Value) and gives the verbatim recommendation to test them separately to avoid overly specific seeds. - The same **Introduction to AMC Lookalike Audiences** chunk on the companion measurement query template, the 500 to 500,000 hard bound, and the recommended 1,000 to 450,000 seed-size buffer. - The **Understanding Amazon's Subscribe & Save Repeat Purchases** chunk, which documents the `firstSnSOrder` and `repeatSnSOrder` event subtypes, the 02/05/2024 enhancement date when repeat signals were added, and the Sandbox restriction on SnS data. - The **Flexible Shopping Insights Trial Guide** chunk, which carries the working `conversions_all_for_audiences` template for the SnS seed and the audience-vs-sizing table-name swap pattern. - The **Identifying High Value Customer Segments** chunk, which frames Total Spend seeds through percentile rank (top 71-100, top 96-100) rather than a flat dollar threshold, and endorses these segments specifically as lookalike seeds. - The **AMC Lookalike Audiences for Promotional Events** chunk, which explains the gap lookalikes fill (the high-value new-to-brand and impulse buyers that DSP rule-based audiences miss) and warns against using lookalikes in the lead-out phase of a promotion. Atlas does not generate the SQL. It surfaces the playbooks with the right caveats attached, and the agent adapts them. ## Build three AMC high-value audience seed strategies The agent returned four SQL blocks: three seed audience queries and one companion sizing wrapper. The seed queries go into the AMC Audiences query editor, the sizing wrapper runs in the main query editor against the non-`_for_audiences` table. ```sql -- ========================================================================= -- Companion measurement query (sizing): runs in the MAIN AMC query editor -- against conversions_all (not _for_audiences). Use this BEFORE pushing each -- seed to audience creation. Hard fail: <500 or >500,000. -- Recommended buffer: 1,000 to 450,000. -- ========================================================================= SELECT COUNT(user_id) AS user_count FROM ( {UPDATE: paste one of the three seed queries below here, but swap conversions_all_for_audiences -> conversions_all and SELECT DISTINCT user_id -> SELECT user_id } ) GROUP BY 1 -- ========================================================================= -- SEED 1: Subscribe & Save subscribers -- Run in the AMC AUDIENCES query editor (NOT the main editor) because it -- selects individual user_id values. -- STRIP ALL COMMENT LINES before pushing to audience creation. The trailing -- GROUP BY 1 at the very end is what makes the push succeed. -- ========================================================================= WITH sns_users AS ( SELECT user_id, COUNT(DISTINCT conversion_id) AS sns_purchase_times FROM conversions_all_for_audiences WHERE event_subtype IN ('firstSnSOrder', 'repeatSnSOrder') -- Optional: scope to your ASIN(s). Remove this AND clause to capture -- all SnS purchasers across the brand. AND tracked_asin IN ('{ASIN_1}', '{ASIN_2}') -- {UPDATE} or remove GROUP BY user_id ) SELECT DISTINCT user_id FROM sns_users WHERE sns_purchase_times >= 1 GROUP BY 1 -- ========================================================================= -- SEED 2: Multi-Purchase (users with multiple distinct ASIN orders) -- ========================================================================= WITH multi_asin_users AS ( SELECT user_id, COUNT(DISTINCT tracked_asin) AS distinct_asin_count FROM conversions_all_for_audiences WHERE event_subtype = 'order' AND tracked_asin IN ('{ASIN_1}','{ASIN_2}','{ASIN_3}') -- {UPDATE} GROUP BY user_id ) SELECT DISTINCT user_id FROM multi_asin_users WHERE distinct_asin_count >= 2 -- {UPDATE: raise to 3+ if seed > 450k} GROUP BY 1 -- ========================================================================= -- SEED 3: Total Spend threshold -- Threshold is configurable. Atlas recommends a percentile-rank approach -- (e.g., top 71-100 percentile) rather than a hardcoded dollar value. -- A flat dollar threshold is shown here for simplicity. -- ========================================================================= WITH spend_by_user AS ( SELECT user_id, SUM(purchase_amount) AS total_spend FROM conversions_all_for_audiences WHERE event_subtype = 'order' AND tracked_asin IN ('{ASIN_1}','{ASIN_2}','{ASIN_3}') -- {UPDATE} GROUP BY user_id ) SELECT DISTINCT user_id FROM spend_by_user WHERE total_spend >= {SPEND_THRESHOLD} -- {UPDATE: configurable, e.g., 200} GROUP BY 1 ``` Three queries, not one. The IQ library explicitly recommends testing these audiences separately to avoid overly specific seeds, and the math agrees: combining filters with `AND` collapses the overlap into a cohort that often falls under the 500-user hard bound. The operator runs all three through the companion sizing query first, confirms each lands in the 1,000 to 450,000 buffer, and only then optionally unions them. The agent does not start by unioning. The table name swaps between the seed and the sizing wrapper: `conversions_all_for_audiences` is required for the seed queries because AMC's audience builder only permits `SELECT user_id` against the `_for_audiences` table variants, while the sizing wrapper runs in the main query editor against the regular `conversions_all` table. The agent stages the swap inside the wrapper so the operator cannot paste the wrong table name into the wrong editor. The SnS filter includes both `firstSnSOrder` and `repeatSnSOrder`. Limiting to `repeatSnSOrder` alone would exclude users on their first scheduled subscription order, which is exactly the cohort you want to lookalike against. Both values together capture the full SnS-engaged audience that became available on 02/05/2024 when Flexible Shopping Insights was enhanced to include repeat purchase signals. The Multi-Purchase and Total Spend queries are not verbatim Atlas snippets. The agent adapted the SnS template pattern, swapping the WHERE filter to `event_subtype = 'order'` and the aggregation to either `COUNT(DISTINCT tracked_asin)` or `SUM(purchase_amount)` per user. The Total Spend threshold is a placeholder. The right number is corpus-specific. Atlas points at a percentile-rank approach (top 71-100 percentile, or the tighter top 96-100 for premium-spend seeds) rather than a hardcoded dollar value. The `{SPEND_THRESHOLD}` token in the SQL is operator-configurable, and the parenthetical "e.g., 200" is illustrative only, not a recommendation. ## Which activation checks protect AMC lookalike seeds This is the part that separates retrieval-grounded responses from fluent guesses. Without being asked, the agent attached a short list of caveats to the artifact. > **What Atlas surfaced that the operator didn't ask for** > - **Hard sizing bounds: 500 and 500,000.** Audience refresh fails if the seed size falls below 500 or above 500,000. Aim for the 1,000 to 450,000 buffer to keep refreshes alive as the cohort drifts. > - **Sandbox gap.** The `sns_subscription_id` field and the repeat SnS purchase signals are not available in AMC Sandbox. You cannot dry-run the SnS seed there. Run against production, or you will see zero rows and assume the seed is broken. > - **Version cliff on 02/05/2024.** Repeat SnS purchase signals were added to Flexible Shopping Insights on that date. Any model whose training data predates it will produce a SnS seed that returns zero rows. This is one of the cleanest examples of what Atlas catches that an ungrounded model cannot. > - **Comment-line trap.** Per the IQ playbook, we recommend removing all comment lines from the query before pushing to audience creation. The trailing `GROUP BY 1` works as a safe terminator because the audience pusher will not accept a query whose last line is a comment, but only if the comments above are also stripped. > - **Category eligibility.** Only certain product categories (Beauty, Grocery, and a handful of others) are eligible for Subscribe & Save. If your catalog sits outside those categories, the SnS seed will undersize regardless of how broad you make the ASIN filter. > - **Not for the lead-out phase.** Lookalike audiences are not appropriate for the lead-out phase of a promotional campaign, where the NTB mix shifts. For lead-out, switch to AMC rule-based audiences instead. Each of these would have cost the operator at least an hour to discover by hitting the failure first. ## What happens next Once each seed lands inside the 1,000 to 450,000 buffer, the operator pushes it to AMC Audiences from the Audiences query editor. AMC compiles the audience and activates it to Amazon DSP. The standard DSP activation lag is around 48 hours before the audience materializes and becomes targetable in line items, so the push has to land at least two days before the campaign launch, not on the day of. The agent also recommends setting each seed query as a recurring AMC workflow so the lookalike model rebuilds on a cadence. Weekly is typical for high-value cohorts, because the SnS seed in particular grows as repeat purchase signals accumulate week over week. Static lookalikes age out fast. For the DSP build, the agent targets each lookalike in a separate line item rather than stacking them in one. Three line items, one per seed, gives a clean read on NTB rate and return on ad spend (ROAS) by seed strategy after the first 14 days. The Total Spend lookalike usually wins on order value, the Multi-Purchase lookalike on order frequency, and the SnS lookalike on retention. Knowing which is which informs the next round of seeds. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and DSP signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog and Amazon Standard Identification Number (ASIN) context, Atlas grounds the seed rules, and [Skills](/features/skills/) keep each seed test on a reviewable cadence. ## Why a value rule must survive the next rerun Three seeds, three sizes, one DSP activation: the difference between a good guess and a working audience. The seed strategies are not novel, but the constraints around them are, and most of those constraints are not in any single playbook. The agent's value is not that it wrote better SQL than a model could write from memory. The agent's value is that it pulled the right two playbooks, applied the right table swap, surfaced a six-month-old `event_subtype` that most models still do not know about, and attached the activation timing the operator needed before they hit it. If your agents are guessing at AMC high-value seeds, they don't have to be. --- *Part of an ongoing series on how agents grounded in Amazon Agent Atlas approach real AMC workflows. Next: [lookalike audiences for Prime Day](/guides/amc-lookalike-audience-prime-day/), taking these three seeds through the promotional-event activation playbook, including the lead-in versus lead-out split that determines whether a lookalike belongs in the line item at all.* ### Build an AMC Lapsed-Customer Audience URL: https://www.kuudo.com/guides/amc-lapsed-customer-reengagement/ An Amazon Marketing Cloud (AMC) lapsed-customer audience identifies proven buyers whose expected repeat window has passed and who have not recently converted or seen the campaign. The workflow extends the purchase lookback, excludes current activity, checks the audience against activation thresholds, and sends only a reviewed, sufficiently sized win-back segment to DSP. The Slack message landed during a retention review: *"We have customers who bought from us three or more times last year, and we haven't heard from them in 90 days. Can we get them into a demand-side platform (DSP) campaign before they churn for good?"* It sounds like a five-minute audience build. In practice the trap is in the tables, the lookback, and the size floor. [Amazon Marketing Cloud](/features/amc/) (AMC) Audiences uses a different set of tables than the main query editor. The lookback you actually want is a 180-day rolling window, not a calendar quarter. The audience won't activate at all if it resolves to fewer than 2,000 distinct users, and you find that out only after pushing it. So I asked our agent, which has [Amazon Agent Atlas](/features/agent-atlas/) behind it. Here is what came back. ## Why generic AMC win-back SQL misclassifies active customers I ran the same prompt through a frontier model with no retrieval. The response looked workable. It wasn't. > **Four failure modes in a single un-grounded response** > 1. It pulled from `conversions_all`. The Audiences editor only accepts the `_for_audiences` variants for `SELECT user_id` queries. The right table for this workflow is `conversions_all_for_audiences`, and pasting the un-suffixed name produces a "user_id not selectable" rejection in the AMC Audiences UI. > 2. It built the 180-day lookback by hardcoding `event_dt_utc >= CURRENT_DATE - INTERVAL '180 day'`. The Atlas playbook uses `TABLE(EXTEND_TIME_WINDOW('conversions_all_for_audiences', 'P180D', 'P0D'))`, which is the only construct that reaches outside the AMC Audiences query editor's default analysis window. Without it, your "180-day lookback" is silently truncated. > 3. It dropped the `exposure_type = 'non-ad-exposed'` filter. The lapsed-loyalist audience is the one Atlas explicitly recommends building from organic repeat buyers, customers who came back on their own. Without the filter, the audience scoops up everyone you already retargeted, which is exactly the population you don't need to spend on. > 4. It didn't mention the 2,000-user activation floor. AMC Audiences refuses to push an audience smaller than 2,000 distinct users to Amazon DSP. The query returns no error in that case; the audience appears to compile, then quietly fails to land in DSP. You discover the floor by missing a campaign launch. Every one of these would have produced a query that runs without complaint and an audience that never activates. The combination would have eaten a week of debugging. ## What the AMC lapsed-customer workflow must retrieve When the agent gets the question, it does a semantic search across the `amazon_ads` collection and pulls a handful of chunks before writing any SQL: - The **Flexible Shopping Insights Trial Guide**, section 4.2 "Engage lapsed customers": the canonical AMC template for exactly this workflow, complete with the `EXTEND_TIME_WINDOW` lookback, the `conv_cnt > 2` filter on repeat purchases, and the `BUILT_IN_PARAMETER('TIME_WINDOW_START')` exclusion that defines "haven't bought recently." - The **AMC Audiences table-variant rule**, indexed from "Creating an Audience for Wishlist or Registry Additions": why `conversions_all_for_audiences` is the only table that permits `SELECT user_id`, and the suffix pattern that distinguishes Audiences-editor tables from main-editor tables. - The **Engage non-ad-exposed audience** section of the same FSI Trial Guide: the sister query in section 4.1 that establishes the `count(user_id)` sizing trick, run in the main editor against the un-suffixed table, before any audience is pushed. - The **Introduction to AMC Lookalike Audiences** playbook: the downstream activation path if the seed turns out to be too small to use directly, with the 500-to-500,000 lookalike seed constraint and the model-expansion behavior. Atlas doesn't generate the SQL. It surfaces the playbook with its constraints intact, and the agent adapts the template to the operator's hero ASINs and window. ## Build the AMC lapsed-customer audience Here is the audience query the agent produced. It pastes directly into the AMC Audiences query editor, uses the 180-day extended window, and filters to customers with three or more organic purchases who have gone silent inside the current analysis window: ```sql -- Audience Instructional Query: Re-engage lapsed three-time buyers -- Source: AMC FSI Trial Guide, section 4.2 "Engage lapsed customers" -- Run in: AMC Audiences query editor (not the main editor) -- Table: conversions_all_for_audiences (_for_audiences variant required) WITH purchase AS ( -- Count distinct organic conversions per user over the trailing 180 days, -- and capture each user's most recent purchase date. SELECT user_id, COUNT(DISTINCT conversion_id) AS conv_cnt, MAX(event_date_utc) AS event_max FROM TABLE(EXTEND_TIME_WINDOW('conversions_all_for_audiences', 'P180D', 'P0D')) WHERE event_subtype = 'order' AND exposure_type = 'non-ad-exposed' AND user_id IS NOT NULL GROUP BY user_id ), multiple_purchase AS ( -- Keep only the customers with three or more organic orders in the lookback. SELECT user_id, event_max FROM purchase WHERE conv_cnt > 2 ) -- Final seed: three-plus buyers whose most recent order predates the analysis -- window. With a 90-day window, that means no purchase in the last 90 days. SELECT user_id FROM multiple_purchase WHERE user_id NOT IN ( SELECT user_id FROM multiple_purchase WHERE event_max > BUILT_IN_PARAMETER('TIME_WINDOW_START') ) ``` Three things to notice about what the agent chose to do. First, the lookback uses `EXTEND_TIME_WINDOW('conversions_all_for_audiences', 'P180D', 'P0D')`, not a hand-rolled date filter. Inside the AMC Audiences editor, the default analysis window is shorter than the 180 days this audience needs, and `EXTEND_TIME_WINDOW` is the only mechanism that opens it. The `P180D` is an ISO-8601 duration; `P0D` means the window ends at the current analysis-window boundary. The agent inherited this directly from the FSI playbook. The `exposure_type = 'non-ad-exposed'` filter is the entire point of the audience. The retention lead wanted organic loyalists, the customers who came back without seeing ads, because those are the ones whose silence is a churn signal rather than ad fatigue. Without the filter, the seed is contaminated with customers you've already retargeted, and the DSP campaign you build from it overlaps with campaigns you're already running. Finally, the lapsed condition is expressed as a `NOT IN` against the same `multiple_purchase` CTE, gated on `BUILT_IN_PARAMETER('TIME_WINDOW_START')`. That parameter resolves to the start of the analysis window the operator sets in the editor, so a 90-day analysis window automatically defines "lapsed" as "no order in the last 90 days." Change the window in the editor, and the lapsed definition shifts with it. No code edits required. ## Which lookback and activation checks govern the audience This is the part that separates a retrieval-grounded response from a fluent guess. Without being asked, the agent attached a short list of things the retention lead needed to know but didn't think to ask about: > **What Atlas surfaced that the operator didn't ask for** > - **Size the audience before you push it.** AMC Audiences refuses to activate any audience under 2,000 distinct users. Run the query in the main editor first with `user_id` swapped for `count(user_id)` and the `_for_audiences` suffix removed from the table name; that returns the seed size without creating anything. > - **Loosen the conditions when the floor isn't met.** The FSI playbook calls out the relaxation order explicitly: change `conv_cnt > 2` to `conv_cnt > 1` first (two-plus buyers instead of three-plus), then extend the lookback from 180 to 270 days. Both are documented adjustments, not hacks. > - **Don't end the query on a comment line.** AMC's audience editor rejects any submission whose last non-blank line is a `--` comment. Strip trailing annotations before pasting, or the editor will swallow the query without a useful error. > - **`event_subtype = 'order'` is required.** `conversions_all_for_audiences` carries every conversion subtype Amazon tracks, not just purchases. Without the `'order'` filter, detail-page views, wishlist adds, and Subscribe & Save subscription events count toward the `conv_cnt` and inflate the seed with users who never actually bought. > - **Flexible Shopping Insights is a paid feature.** The `exposure_type` column and the non-ad-exposed signal only populate when FSI is enabled on the AMC instance. Without it, the filter returns zero rows and the audience appears empty for reasons the editor will not surface. Any one of these would have cost the operator a half-day to discover. The compounding effect is why this kind of audience normally takes a week to ship and not an afternoon. ## What happens next Once the seed sizes above 2,000 users, the agent flips into activation mode and routes the audience through the standard AMC Audiences-to-DSP path: paste the query into the Audiences editor, push it to a named audience, wait for the activation window to close, and verify the audience appears as targetable in Amazon DSP. If the seed comes back undersized even after both relaxations, the agent recommends pivoting to AMC Lookalike Audiences, using the lapsed loyalists as a seed (even a small one) and letting the lookalike model expand it. That path uses the same workflow we walked through for [cart abandoners](/guides/amc-cart-abandoner-audience/), but with the loyalist seed in place of the cart-abandoner seed and a 500-to-500,000 lookalike size band instead of the 2,000-user activation floor. Either way, the agent doesn't stop at SQL: it carries the audience to the activation handoff and tells you which path it took and why. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and DSP signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog context, Atlas grounds the lapsed-loyalist rules, and [Skills](/features/skills/) keep the reengagement audience on a reviewed refresh cadence. ## Why lapsed-customer timing changes the result The lapsed-loyalist workflow is the kind of audience build that is easy to ask for, hard to ship, and impossible to debug after the fact. The query has to use the right table variant, the right time-window construct, the right exposure filter, and the right size floor; miss any one of them and the audience either fails to compile, fails to activate, or activates against the wrong population. None of this is exotic knowledge, but it lives in four different sections of two different playbooks, and a model without retrieval has to guess at all of it. If your retention agents are guessing at AMC Audiences, they don't have to be. --- *Part of an ongoing series on how agents grounded in Amazon Agent Atlas approach real AMC workflows. Next: turning a too-small loyalist seed into an activated audience through [AMC Lookalike Audiences](/guides/amc-cart-abandoner-audience/), and the rules the lookalike model uses to expand a seed without diluting it.* ### Build Prime Day AMC Lookalikes URL: https://www.kuudo.com/guides/amc-lookalike-audience-prime-day/ An Amazon Marketing Cloud (AMC) lookalike audience for Prime Day starts with a seed that represents the behavior you want Amazon to expand. This workflow builds the seed from Subscribe & Save purchasers, checks that it falls within Amazon's activation window, and submits the audience only after the timing, size, expansion type, and DSP destination are reviewed. The Slack message landed six weeks before Prime Day: *"Can we build a lookalike audience of customers similar to our best buyers, sized correctly so it actually activates?"* This sounds like one SQL query. It is really four decisions: which customers count as "best," which [Amazon Marketing Cloud](/features/amc/) table variant allows `SELECT user_id`, which Amazon Standard Identification Number (ASIN) filters belong in the seed, how large the seed can be before the lookalike model refuses it, and how to size the seed before spending two days waiting on a refresh. Get one of those wrong and the audience either fails silently or trains on the wrong population. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) supplies current audience and campaign context, [Skills](/features/skills/) preserve the seed and evaluation rules, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific audience guidance, and approval controls keep the final activation call yours. ## The AI is shared. Atlas changes the seed decision The shared AI can produce a confident query-shaped answer. Without the live instance, the audience Skill, and Atlas grounding, it can still choose the wrong editor and miss every threshold that decides whether the audience activates. > **Five failure modes in the un-grounded response** > 1. It picked one seed definition, "top 20% of spenders," and committed to it. The actual playbook recommends three distinct seed strategies tested separately first: Subscribe & Save (SnS) subscribers, multi-ASIN purchasers, and a total-spend threshold. > 2. It quoted a seed size minimum of "a few thousand users" and no upper bound. The real guardrail is specific: the seed should contain between 500 and 500,000 distinct `user_id` values. > 3. It wrote the seed query against `conversions` and used `SELECT DISTINCT user_id` in the outer query. AMC Audiences requires the `_for_audiences` table family, specifically `conversions_for_audiences`, for this seed shape. > 4. It did not size the seed before submission. The playbook has a companion measurement query that runs in the main editor against `conversions_all` and returns the seed count before the audience refresh starts. > 5. It ended the query on a `--` comment line. AMC Audiences can reject submissions whose last line is a comment, so the template intentionally ends on executable SQL. None of these failures look wrong on a casual read. They are the kind of mistakes you catch after submitting an audience, waiting for refresh, and watching the status sit there. ## What Atlas retrieves for an AMC Prime Day lookalike audience When the agent gets the question, it does a semantic search across the `amazon_ads` collection and pulls five chunks before writing a line of SQL: - The **AMC Lookalike Audiences for Promotional Events** playbook, version-tagged `2023-10-01`: the canonical Prime Day and Black Friday workflow covering ASIN selection, seed creation, flight-time analysis, and activation. - The **Introduction to AMC Lookalike Audiences** instructional query (IQ): the mechanics behind seed scoring, the five expansion types, and the three-seed template for high-value customers. - The **Companion measurement query** chunk from the lookalike audiences IQ: the `SELECT COUNT(user_id) FROM (...)` pattern that runs in the main AMC editor. - The **Creating Audiences Based on High Value Customer Segments** playbook: the percentile-rank variant for "top X% by spend" seeds. - The **Flexible Shopping Insights Trial Guide** Section 5: SnS-specific seed patterns, including `firstSnSOrder` and `repeatSnSOrder` event subtype notes for advertisers with Flexible Shopping Insights (FSI). The agent does not invent the SQL. It surfaces the right template with the right caveats, then adapts it. ## Seed strategies and expansion types for AMC lookalike audiences The playbook gives the operator two taxonomies to make explicit before submission. | Taxonomy | Option | When to use it | |----------|--------|----------------| | Seed strategy | SnS subscribers | Use when repeat subscription behavior is the clearest signal of loyalty. | | Seed strategy | Multi-ASIN purchasers | Use when cross-catalog buying is more important than a single-product purchase. | | Seed strategy | Total spend threshold | Use when revenue concentration matters more than purchase frequency. | | Expansion type | Most Similar | Start here when performance matters more than reach. | | Expansion type | Similar | Use when the seed is strong but the campaign needs more scale. | | Expansion type | Balanced | Practical default for Prime Day prospecting when you need both reach and relevance. | | Expansion type | Broad | Use when the seed is valid but projected audience size is too constrained. | | Expansion type | Most Broad | Use for reach-first testing, not for the first high-efficiency launch. | This block matters because it prevents the model from collapsing three separate operator decisions into one vague "high-value lookalike" audience. ## The agent's working output: AMC seed SQL for Subscribe & Save The agent produced two artifacts. First, the seed query, lifted from the three-seed template in the **Introduction to AMC Lookalike Audiences** instructional query with the optional clauses set for the SnS strategy: ```sql /* Audience instructional query: Introduction to lookalike audiences (High Value Customers) Run in the AMC Audiences query editor, not the main editor. Last verified: 2026-05-13. Three seed strategies are supported below. Test them separately first: [1 of 4]: ASIN filter [2 of 4]: SnS subscribers [3 of 4]: Multi-ASIN purchasers [4 of 4]: Total purchase value threshold Keep the final GROUP BY 1 as executable SQL so the query does not end on a comment line. */ WITH user_sales_cte AS ( SELECT user_id, CASE WHEN event_subtype = 'snsSubscription' THEN 1 ELSE 0 END AS sns_flag, SUM(total_units_sold) AS total_purchases, SUM(total_product_sales) AS total_product_sales, COUNT(DISTINCT tracked_item) AS unique_items_purchased FROM conversions_for_audiences WHERE event_subtype IN ('snsSubscription', 'order') /* AND tracked_asin IN ('B0HERO0001','B0HERO0002') */ AND user_id IS NOT NULL GROUP BY 1, 2 ), user_aggregate AS ( SELECT user_id, CASE WHEN unique_items_purchased > 1 THEN 1 ELSE 0 END AS multi_purchase_flag, MAX(sns_flag) AS sns_flag, SUM(total_purchases) AS total_purchases, SUM(total_product_sales) AS total_product_sales, SUM(unique_items_purchased) AS unique_items_purchased FROM user_sales_cte GROUP BY 1, 2 ), audience_grouping AS ( SELECT user_id, sns_flag, MAX(multi_purchase_flag) AS multi_purchase_flag, SUM(total_purchases) AS total_purchases, SUM(total_product_sales) AS total_product_sales, SUM(unique_items_purchased) AS unique_items_purchased FROM user_aggregate GROUP BY 1, 2 ) SELECT user_id FROM audience_grouping WHERE sns_flag = 1 -- AND multi_purchase_flag = 1 -- AND total_product_sales >= 250 GROUP BY 1; ``` The `event_subtype IN ('snsSubscription', 'order')` filter keeps the inner CTE focused on purchase behavior. That prevents cart additions, wishlist saves, or other engagement events from diluting the seed. Filtering there is cheaper and cleaner than trying to fix the population at the final `WHERE`. The `audience_grouping` CTE looks redundant, and the agent kept it anyway. The playbook leaves that pass in place because the model trainer expects a clean row-grain. Stripping it can still work, but the failure mode is opaque enough that the safer version is worth the extra CTE. The optional clauses stay visible because the operator should run SnS, multi-ASIN, and spend-threshold seeds separately before combining anything. Pre-baking that comparison into the template matches the way operators actually test seeds. ## How to size an AMC seed audience before submission Before submitting any seed to AMC Audiences, the agent produced the sizing companion. This is the part the un-grounded model skipped. ```sql /* Companion measurement query. Run in the main AMC query editor, not the Audiences editor. Last verified: 2026-05-13. Change conversions_for_audiences to conversions_all for sizing. Keep SELECT user_id inside the subquery; COUNT() wraps it outside. */ SELECT COUNT(user_id) AS user_count FROM ( WITH user_sales_cte AS ( SELECT user_id, CASE WHEN event_subtype = 'snsSubscription' THEN 1 ELSE 0 END AS sns_flag, SUM(total_units_sold) AS total_purchases, SUM(total_product_sales) AS total_product_sales, COUNT(DISTINCT tracked_item) AS unique_items_purchased FROM conversions_all WHERE event_subtype IN ('snsSubscription', 'order') AND user_id IS NOT NULL GROUP BY 1, 2 ), user_aggregate AS ( SELECT user_id, MAX(sns_flag) AS sns_flag FROM user_sales_cte GROUP BY 1 ) SELECT user_id FROM user_aggregate WHERE sns_flag = 1 ) GROUP BY 1; ``` The query returns one number: the distinct `user_id` count of the seed. If it is under 500, the audience refresh can fail. If it is over 500,000, the refresh can also fail. The practical habit is to stay comfortably inside the band so a seasonal data swing does not push the audience across either edge. The diagnostic table above is the operator checkpoint. The fourth row, `Total spend >= $500` returning 470 users, is the negative result that saves the most time. Without the sizing companion, the operator would discover that failure only after submitting the audience. ## The footnotes the agent surfaces for AMC Audiences This is the part that separates an Atlas-grounded agent from a fluent one. The agent did not wait to be asked. It surfaced the caveats the operator was about to need: > **Things Atlas surfaced that the operator did not ask for** > 1. **Seed size has hard boundaries.** The seed should contain 500 to 500,000 `user_id` values. Always run the sizing query first. > 2. **Test the three seed strategies separately.** Combining SnS, multi-ASIN, and spend thresholds too early can create a seed that is technically valid but strategically empty. > 3. **Expansion type is a decision, not a default.** Balanced is a practical starting point, but Most Similar, Similar, Broad, and Most Broad change the reach-performance tradeoff. > 4. **Lookalikes address the non-ad-exposed gap.** Rule-based audiences are useful for remarketing; Prime Day prospecting usually needs users who share seed traits but were not already in the ad-exposed pool. > 5. **The query should not end on a comment line.** Keep an executable final line such as `GROUP BY 1`. > 6. **The promotional-event variant uses a fixed window.** For Prime Day, the API payload should use the prior promotional window rather than a rolling relative window. Any one of these can eat an afternoon after submission. Getting all six before the first API call is the difference between a day-one launch and a day-three debugging thread. ## What happens next: submit the AMC Audiences API payload The seed query, sizing query, and diagnostic table close the loop on creation but not activation. The next move is to flatten the chosen seed SQL into an AMC Audiences API payload, submit it with `audienceName`, `advertiserId`, `timeWindowStart`, `timeWindowEnd`, `refreshRateDays`, `timeWindowRelative`, and `lookalikeAudienceExpectedReach`, then wait for the audience to become available for Amazon demand-side platform (DSP) activation. For Prime Day, `timeWindowRelative` should be `false` because the seed is tied to a fixed promotional window. `refreshRateDays: 7` keeps the audience fresh without turning the seed into a rolling interpretation of last year's event. `lookalikeAudienceExpectedReach: "BALANCED"` is a defensible first pass because it gives the team enough scale to test without starting at the loosest expansion setting. The evaluation pass comes after activation. Compare the lookalike audience against last year's rule-based audience baseline, then decide whether to keep the SnS seed, rotate to multi-ASIN purchasers, or loosen the spend threshold. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and DSP signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog and ASIN context, Atlas grounds the sizing and payload rules, and [Skills](/features/skills/) keep the audience refresh reviewable. ## Why this matters for Prime Day audience activation A lookalike audience that fails silently is worse than no audience. The operator has staged creative and media against an audience assumption, and the refresh can still be pending when the event opens. The rules that decide success are knowable, but they are scattered across a playbook, an instructional query, an API reference, and paid-feature notes. Atlas is not a model upgrade. It is the corpus made available at the moment of need. The agent did not have to remember the 500-to-500,000 band, the `_for_audiences` suffix rule, the fixed-window payload, or the comment-line restriction. It looked them up. If your agents are guessing at AMC seed sizes, they do not have to be. *Next: use the [lookalike evaluation workflow](/guides/amc-lookalike-audience-evaluation/) to test whether the shipped audience outperformed the rule-based audience from the previous Prime Day.* ### Build an AMC Cart-Abandoner Audience URL: https://www.kuudo.com/guides/amc-cart-abandoner-audience/ An Amazon Marketing Cloud (AMC) cart-abandoner audience contains users who added a chosen ASIN to cart but did not purchase within the selected window. A reliable workflow chooses the audience-safe conversion table, excludes purchasers, checks the distinct-user count before submission, and allows for the delay between successful AMC creation and DSP activation. The Slack message came in on a Tuesday: *"Can you build me a demand-side platform (DSP) audience of people who added one of our hero Amazon Standard Identification Numbers (ASINs) to cart in the last 30 days but never bought? I want to retarget them before the next promotion."* This is the kind of ask that should take ten minutes. In practice, it eats an afternoon - not because the SQL is hard, but because [Amazon Marketing Cloud](/features/amc/) has a dozen quiet rules that aren't in any one place. The query has to hit the right table. The user-level fields have to come from the variant table that AMC Audiences will actually accept. The seed has to land between 500 and 500,000 users or the audience refresh fails silently. Attribution windows have to close before the numbers stop lying to you. So I asked our agent instead. The agent has [Amazon Agent Atlas](/features/agent-atlas/) behind it - a curated corpus of AMC playbooks, instructional queries, audience patterns, and decision rules indexed for semantic retrieval. Here's what came back. ## Why generic AMC cart-abandoner SQL targets the wrong table I tested this same prompt against a frontier model with no retrieval. The response looked confident. It wasn't. > **Four failure modes in a single un-grounded response** > 1. It pulled from `conversions` instead of `conversions_for_audiences`. AMC Audiences requires the `_for_audiences` table variants - they're the only ones that permit `SELECT user_id`. Pasting the wrong one into the Audiences query editor produces a "user_id not selectable" error that doesn't tell you which table to switch to. > 2. It used `event_type = 'add_to_cart'`. The real value is `event_subtype = 'shoppingCart'`. Close enough to look right; wrong enough to return zero rows. > 3. It didn't mention the seed-size constraint. An audience that resolves to 380 users will *appear* to compile and then quietly fail to activate. > 4. It didn't mention the 48-hour activation lag in Amazon DSP. The operator was planning to launch retargeting "tomorrow." None of this is exotic knowledge. It's all in Amazon's public docs. But it's scattered across a dozen pages, half of which surface in search rankings only when you already know what to type. ## What the cart-abandoner workflow must retrieve When the agent gets the question, it does a semantic search across the `amazon_ads` collection and pulls four chunks before it writes a single line of SQL: - The **Audience that added to cart but did not purchase** instructional query - the canonical AMC template for this exact use case - The **AMC Audiences table-variant rule** - why `conversions_for_audiences` is the only table that allows `SELECT user_id`, and the corresponding `_for_audiences` suffix pattern across other AMC datasets - The **AMC Audiences activation guide** - the 500 to 500,000 seed-size constraint and the ~48-hour DSP activation window - The **Introduction to AMC Audiences companion measurement query** - the trick of changing `user_id` to `COUNT(user_id)` and dropping the `_for_audiences` suffix to size your audience *before* you try to create it Atlas doesn't generate the SQL. It surfaces the playbook, and the agent adapts it. ## Build the AMC cart-abandoner audience SQL Here's the query the agent produced. It compiled on the first try and resolved to a seed of just over 12,000 users - comfortably inside the activation window: ```sql /* AMC Audiences IQ: Added to cart but did not purchase Adapted for hero-ASIN retargeting, 30-day window. Run in the AMC Audiences query editor (not the main editor) - _for_audiences table variants are required for SELECT user_id. */ WITH asins (asin) AS ( VALUES ('B0XXXXXXX1'), -- hero ASIN #1 ('B0XXXXXXX2'), -- hero ASIN #2 ('B0XXXXXXX3') -- hero ASIN #3 ), purchase AS ( SELECT user_id, MAX(event_dt_utc) AS purchase_dt_max FROM conversions_for_audiences WHERE event_subtype = 'order' AND tracked_item IN (SELECT asin FROM asins) GROUP BY 1 ), atc AS ( SELECT user_id, MAX(event_dt_utc) AS atc_dt_max FROM conversions_for_audiences WHERE event_subtype = 'shoppingCart' AND tracked_item IN (SELECT asin FROM asins) GROUP BY 1 ) SELECT atc.user_id FROM atc LEFT JOIN purchase ON atc.user_id = purchase.user_id WHERE atc_dt_max > purchase_dt_max OR purchase_dt_max IS NULL; ``` Three things to notice about what the agent chose to do: - The **ASIN filter is in the CTEs, not the final `SELECT`**. Filtering at the CTE level is the difference between a query that scans a focused slice of the conversions table and one that scans everything and discards 99% of it. AMC's compute budget isn't infinite, and the Atlas playbook flagged this explicitly. - The **`OR purchase_dt_max IS NULL`** clause matters. Without it, the audience would only include people who *previously* bought and then abandoned again, missing every first-time prospect who added to cart and walked. The agent inherited this from the AMC IQ template, which spells out the join semantics. - The **outer query is a plain `SELECT user_id`**, not `SELECT DISTINCT`. AMC Audiences expects user IDs and de-duplicates internally. Adding `DISTINCT` doesn't help performance and occasionally trips the audience compiler. ## The companion sizing query - run this first Before the agent suggested pushing the audience to DSP, it produced a sizing check. This is the part most ad-hoc workflows skip, and it's why most "build me an audience" requests fail their first activation: ```sql /* Audience sizing check - run this in the MAIN AMC query editor (not Audiences). Note the table name change: conversions_all, not conversions_for_audiences. */ SELECT COUNT(DISTINCT atc.user_id) AS audience_size FROM ( SELECT user_id, MAX(event_dt_utc) AS atc_dt_max FROM conversions_all WHERE event_subtype = 'shoppingCart' AND tracked_item IN ('B0XXXXXXX1','B0XXXXXXX2','B0XXXXXXX3') GROUP BY 1 ) atc LEFT JOIN ( SELECT user_id, MAX(event_dt_utc) AS purchase_dt_max FROM conversions_all WHERE event_subtype = 'order' AND tracked_item IN ('B0XXXXXXX1','B0XXXXXXX2','B0XXXXXXX3') GROUP BY 1 ) purchase ON atc.user_id = purchase.user_id WHERE atc.atc_dt_max > purchase.purchase_dt_max OR purchase.purchase_dt_max IS NULL; ``` If this returns less than 500, the audience won't activate. If it returns more than 500,000, the audience won't refresh. The agent knows both thresholds and tells you to widen or tighten the ASIN list accordingly. ## Which activation checks prevent a stale audience This is the part that separates a retrieval-grounded agent from a fluent one. Without being asked, the agent included a short list of things the operator needed to know but didn't think to ask about: > **Things Atlas surfaced that the operator didn't ask for** > - **Seed size: 500 to 500,000.** Outside that range, the audience either refuses to activate or refuses to refresh. Always run the sizing query first. > - **DSP activation lag: ~48 hours.** Build the audience two days before the campaign launch, not the day of. > - **Sandbox limitations.** AMC Sandbox doesn't populate certain event types reliably - including some Subscribe & Save signals. For audience work, run against production. > - **Comment-line restriction.** AMC Audiences queries cannot end on a comment line. Strip trailing `--` annotations before pushing to audience creation, or the editor will reject the query. > - **Upstream variants.** Swap `event_subtype = 'shoppingCart'` for `'detailPageView'` to reach further up the funnel, or for `'wishlist'` to capture lower-intent interest. The agent will adjust the seed-size expectations accordingly. Any one of these would have cost the operator an hour to discover. The compounding effect is why the workflow took ten minutes instead of an afternoon. ## What happens next The agent doesn't stop at SQL. Once the audience compiles and the sizing check passes, Atlas's activation playbook covers the next two hops: pushing the audience definition to AMC Audiences from the Audiences query editor, waiting for the activation window to close, and verifying the audience appears as targetable in Amazon DSP. The agent also flags that the audience is *static at creation time* - it doesn't auto-refresh as new users add to cart. For a retargeting program you'd want to run continuously, the agent recommends scheduling the audience as a recurring AMC workflow with a 7-day refresh cadence, and points to the corresponding Atlas playbook chunk on workflow scheduling. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings AMC and DSP context, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog and ASIN context, Atlas grounds the audience rules, and [Skills](/features/skills/) keep the refresh cadence reviewable. That's the loop closed: ask, retrieve, build, size, activate, monitor. ## Why audience sizing is part of the workflow A foundation model can write SQL. So can a junior analyst with a textbook. What neither can reliably do is produce a query that compiles against the specific table variants AMC exposes, respects the seed-size constraints DSP enforces, and bakes in the timing assumptions Amazon's attribution model requires - all without being told to. Atlas isn't a magic upgrade to model capability. It's a corpus of *Amazon's own playbook content*, indexed and addressable by an agent at the moment of need. The agent doesn't have to remember any of this. It has to know where to look. That's a lower bar - and a much more reliable one. If your agents are guessing at AMC, they don't have to be. --- *This is the first in a series on how agents grounded in Amazon Agent Atlas approach real Amazon Marketing Cloud workflows. Next: [building a Subscribe & Save lift analysis](/guides/amc-subscribe-and-save-lift/) that quantifies the spend gap between subscribers and one-off buyers, including the February 2024 `repeatSnSOrder` signal that most ungrounded models still don't know exists.* ### Measure Subscribe & Save Lift in AMC URL: https://www.kuudo.com/guides/amc-subscribe-and-save-lift/ Amazon Marketing Cloud (AMC) can measure Subscribe & Save lift by separating repeat subscription orders from other conversions, comparing exposed and unexposed cohorts, and using a long enough lookback for repeat behavior to emerge. The workflow uses the repeatSnSOrder signal, conversions_all, and an ASIN-level companion query to explain where the lift comes from. The question came up in a quarterly review: *"Is Subscribe & Save actually doing anything for us, or are we just discounting the same customers who would have bought anyway?"* It's the kind of question that sounds answerable in five minutes and isn't. Amazon's Subscribe & Save program - auto-replenishment with a small discount - generates a stream of conversion events that look like ordinary purchases in most reports. The standard Sponsored Ads reporting doesn't separate them. Brand Analytics doesn't separate them. The Business Reports in Seller Central treat an SnS unit the same as a one-off unit. To actually measure SnS lift - the spend gap between subscribers and one-off buyers - you have to query [Amazon Marketing Cloud](/features/amc/), against the right tables, with the right event subtypes, over the right window. So I asked our agent. The agent has [Amazon Agent Atlas](/features/agent-atlas/) behind it, and Atlas has the AMC Flexible Shopping Insights playbook indexed in full. Here's what came back. ## Why generic AMC Subscribe & Save SQL misses the repeat-order signal I ran the same prompt through a frontier model with no retrieval. The output was confident and wrong in a way that would have looked right until the numbers didn't make sense. > **Four things the un-grounded model missed** > 1. It didn't know `repeatSnSOrder` exists. Amazon added this `event_subtype` to Flexible Shopping Insights on **February 5, 2024**. Without it, you only count the initial subscription event and the first SnS purchase - missing every recurring shipment, which is where the actual lift lives. Most models' training data predates this change, so they confidently produce queries that undercount SnS revenue by 60–80%. > 2. It used `conversions` instead of `conversions_all`. AMC has multiple conversion tables and they don't carry the same fields. `conversions_all` is the table Flexible Shopping Insights writes the SnS signals to. > 3. It didn't mention that Flexible Shopping Insights is a **paid AMC feature** with regional availability. Running the query without an active FSI subscription returns empty results with no error - the table exists but contains no SnS rows for your account. > 4. It quoted a 30-day analysis window. The playbook recommends a **minimum of 3 months** to capture SnS cadence, because subscription cycles run on 1, 2, 3, or 6-month schedules and a 30-day query misses the majority of repeat orders entirely. Any one of these would silently corrupt the lift number. The combination would tank a quarterly business case. ## What the Subscribe & Save workflow must retrieve When the agent gets the question, it does a semantic search across the `amazon_ads` collection and pulls four chunks before writing any SQL: - The **Subscribe and Save repeat purchases** instructional query - the canonical AMC template, version-tagged `2024-02-05` - The **Flexible Shopping Insights trial guide** - the surrounding context on which AMC tables FSI writes to and which event subtypes are exposed - The **FSI access requirements note** - that FSI is a paid feature with regional restrictions, and Sandbox doesn't populate the repeat-SnS signals reliably - The **AMC query window guidance** - the 3-month minimum recommendation, the ASIN filter performance tip, and the join-grain rules for analyses that span event-level and weekly aggregations Atlas doesn't write the SQL. It surfaces the right playbook with the right caveats, and the agent adapts. ## Build the AMC Subscribe & Save lift query Here's the lift comparison query the agent produced. It runs against the main AMC query editor (not the Audiences editor - we're measuring, not building an audience), uses the three event subtypes that together capture the full SnS lifecycle, and treats every other purchaser as the comparison group: ```sql -- Subscribe & Save lift: average spend by user type -- Source: AMC IQ "Subscribe and save repeat purchases" (v 2024-02-05) -- Run window: last full 90 days (set Date range in Query Editor) -- Table: conversions_all (main editor, not _for_audiences) SELECT CASE WHEN event_subtype IN ( 'snsSubscription', -- initial subscription event 'firstSnSOrder', -- first scheduled order after subscribing 'repeatSnSOrder' -- recurring orders (added 2024-02-05) ) THEN 'subscriber' ELSE 'non-subscriber' END AS user_type, COUNT(DISTINCT user_id) AS users_that_purchased, SUM(total_product_sales) AS total_sales, SUM(total_product_sales) / COUNT(DISTINCT user_id) AS average_spend_per_user FROM conversions_all WHERE tracked_item IN ( 'B0XXXXXXX1', 'B0XXXXXXX2', 'B0XXXXXXX3' -- restrict to ASINs in SnS program for perf ) GROUP BY 1 ORDER BY 1; ``` Three things to notice about what the agent chose to do: - The **three event subtypes are an `IN` list, not a chain of `OR`s**. The IQ template uses this exact pattern because it's the most readable way to express "any of these three states means the user is in the subscriber bucket." If you swap in `event_subtype = 'snsSubscription' OR event_subtype = 'firstSnSOrder'`, you'll get the same result and a query that's harder to maintain, and a future you will forget which subtypes you included. - The **ASIN filter goes in the `WHERE` clause of the main query**, not in a CTE. SnS lift analyses are usually run against the subset of your catalog that's actually enrolled in the SnS program; there's no reason to scan the rest of conversions_all and discard 90% of it. The Atlas playbook explicitly flags this as a performance pattern. - The **comparison bucket is "every other purchaser,"** not "purchasers who explicitly opted out of SnS." Amazon doesn't expose an opt-out signal; the inverse of an SnS subscriber is just any user whose purchase event doesn't carry one of the three SnS event subtypes. The agent inherited this from the IQ template and didn't try to over-engineer it. ## The companion ASIN-level query The lift number is the headline, but the question that follows it is always *"which Amazon Standard Identification Numbers (ASINs) are pulling their weight in SnS?"* - and the playbook has a companion query for exactly that: ```sql -- SnS purchases by ASIN: volume + percentage of total purchases -- Run alongside the lift query, same window, same ASIN scope WITH sns AS ( SELECT tracked_item AS asin, COUNT(*) AS sns_purchases FROM conversions_all WHERE event_subtype IN ('firstSnSOrder', 'repeatSnSOrder') AND tracked_item IN ('B0XXXXXXX1','B0XXXXXXX2','B0XXXXXXX3') GROUP BY 1 ), total AS ( SELECT tracked_item AS asin, COUNT(*) AS total_purchases FROM conversions_all WHERE event_subtype = 'order' AND tracked_item IN ('B0XXXXXXX1','B0XXXXXXX2','B0XXXXXXX3') GROUP BY 1 ) SELECT total.asin, sns.sns_purchases, total.total_purchases, ROUND(100.0 * sns.sns_purchases / NULLIF(total.total_purchases, 0), 2) AS sns_share_pct FROM total LEFT JOIN sns ON sns.asin = total.asin ORDER BY sns_share_pct DESC NULLS LAST; ``` The `NULLIF` is the agent being defensive - a recently launched ASIN with zero recorded `order` events would otherwise divide by zero. Small thing. Saves a re-run. ## How to read the numbers The lift query returns two rows. Subscriber `average_spend_per_user` divided by non-subscriber `average_spend_per_user` is the headline lift ratio. A ratio of 2.4x means subscribers spend 2.4 times more than one-off buyers on the same ASIN set over the same window. The ASIN-level query then shows where that lift is concentrated - typically a handful of consumables (coffee, supplements, pet food, household goods) drive the majority of subscriber revenue, and the long tail of one-time products contributes almost nothing to the SnS program. The decision the marketing director was actually trying to make - *do we push more aggressively into SnS?* - turns on whether the high-lift ASINs already have full SnS enrollment, or whether there's headroom. If your top-5 lift ASINs are already at 60%+ `sns_share_pct`, the upside is in expanding the catalog. If they're at 15%, the upside is in conversion campaigns targeting existing buyers of those ASINs. ## Which caveats govern an AMC Subscribe & Save read This is the part that separates retrieval-grounded responses from fluent guesses. Without being asked, the agent included a short list of things the operator needed to know but didn't think to ask about: > **What Atlas surfaced that the operator didn't ask for** > - **Flexible Shopping Insights subscription required.** If FSI isn't enabled on your AMC instance, the query will return empty results with no error message. Check the Paid Features tab in AMC, or talk to your AdTech account executive. Regional availability varies. > - **Sandbox doesn't carry repeat-SnS signals.** `sns_subscription_id` and `repeatSnSOrder` rows are not populated in AMC Sandbox. Run this against production, or you'll see a lift ratio of 1.0x and assume SnS is doing nothing. > - **Three-month minimum window.** SnS cycles run on 1, 2, 3, or 6-month schedules. Anything shorter than 90 days will under-represent recurring orders. Six months is better if you have the data depth. > - **Household-level inflation.** AMC translates household purchases to user-level purchases by crediting the household event to each linked user. For total-sales analyses (like this one), be aware that subscriber totals may be slightly inflated when a single subscription serves a multi-person household. Adjust at the user level if precision matters. > - **Don't end the query on a comment line.** AMC's query editor will reject any submission whose last line is a `--` comment. Strip trailing annotations before running. The household-inflation footnote is the kind of thing that takes most operators a year of using AMC to discover. Atlas had it indexed from the start. ## What happens next The lift number is the input to a series of decisions, not the output of the analysis. The agent's next step - and the next guide in this series - is to take the high-lift ASINs identified here and build an AMC audience of non-subscribed buyers of those ASINs, then activate it as a Subscribe & Save promotion campaign in Amazon demand-side platform (DSP). That workflow uses the same `conversions_all_for_audiences` table variant pattern we covered in the [cart-abandoner audience guide](/guides/amc-cart-abandoner-audience/), with a different filter and a different activation playbook. The point is that no single query closes the loop. The agent runs the lift analysis, surfaces the high-lift ASINs, builds the audience, and pushes it to DSP - each step grounded in a different playbook, each playbook indexed and retrievable at the moment of need. The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): [Amazon Ads MCP](/features/amazon-ads-mcp/) brings the AMC and DSP signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog and inventory context, Atlas grounds the signal list, and [Skills](/features/skills/) keep the analysis and follow-up audience refresh on schedule. ## Why repeat-purchase measurement needs a reusable workflow The Subscribe & Save lift question is a perfect case for retrieval-grounded agents because the answer literally did not exist in most models' training data. Amazon added the `repeatSnSOrder` signal in February 2024. Models with knowledge cutoffs before that - which is most of them, even now, for the depth of detail required - will produce queries that compile, run, return data, and quietly undercount SnS revenue by the majority of its actual contribution. The operator gets a number. The number is wrong. There's no error to debug. An agent grounded in Atlas doesn't have this problem. It doesn't know SnS analysis from training data - it reads Amazon's own current IQ template and adapts it. When Amazon updates the playbook again (and they will), the corpus updates, and the agent gets the new answer without anyone retraining a model. If your agents are giving you confident SnS numbers that don't include `repeatSnSOrder`, they're giving you the wrong numbers. --- *Part of an ongoing series on how agents grounded in Amazon Agent Atlas approach real AMC workflows. Next: turning a high-lift ASIN list into a DSP-activated audience of non-subscribed buyers - the activation half of the workflow this post leaves open.* ### Turn AMC Workflows into Agent Skills URL: https://www.kuudo.com/guides/amc-agent-workflows/ Automating Amazon Marketing Cloud (AMC) workflows means packaging the query, input checks, privacy constraints, interpretation, and approval policy as a versioned agent Skill. Kuudo retrieves the relevant operator playbook, verifies that the request is sufficiently specified, runs the approved tools, and preserves a run log so the same analysis can be reviewed and repeated. The operator question was practical: *"We keep asking for the same AMC analyses. Can the agent remember the workflow instead of improvising every time?"* Yes, but the reusable unit should be a Skill, not a prompt snippet. A prompt remembers wording. A Skill remembers the steps: retrieve the right [Amazon Marketing Cloud](/features/amc/) (AMC) playbook, choose the table, run the privacy or size check, produce the artifact, route the decision, and log what happened. ## Why prompt-only AMC workflow automation breaks > **Four ways an un-grounded workflow turns brittle** > 1. It stores a generic "run AMC analysis" prompt with no table-selection rule. > 2. It forgets that measurement and audience activation can require different table variants. > 3. It treats every output as read-only, even when the next step changes a DSP audience or budget. > 4. It has no run log, so the team cannot reproduce which parameters or rules produced the result. That is how a helpful demo becomes an unreviewable production process. ## What an AMC workflow must retrieve before it runs The agent starts by pulling the relevant Amazon Marketing Cloud playbooks from [Amazon Agent Atlas](/features/agent-atlas/). For an audience workflow, it retrieves: - The audience-source rule that decides whether the Skill can use an audience-safe table. - The sizing pattern that tells the operator whether the seed can activate. - The activation timing and companion measurement guidance that belong beside the output. - The **AMC synthetic data** playbook context when the Skill needs a safe test surface before it touches production workflows. For a measurement workflow, it retrieves the attribution table, lookback rule, and caveats that belong in the output. The [Amazon Ads MCP](/features/amazon-ads-mcp/) gives the Skill live access to the Ads and AMC surfaces. Atlas tells it what the current playbook says before it acts. ## Package the AMC workflow as a versioned agent Skill The useful output is a workflow contract: ```json { "skill": "amc_audience_workflow", "mode": "read_then_approve", "steps": [ "retrieve_atlas_playbook", "resolve_table_window_filters", "run_privacy_and_seed_checks", "produce_sql_or_payload", "request_approval_for_writes", "write_run_log" ], "writeBoundary": "approval_required_before_dsp_activation" } ``` That contract is deliberately more boring than an open-ended chat. Boring is what makes it repeatable. ## Which checks belong in every automated AMC workflow > **The Skill should preserve these details every time it runs** > - Which Atlas chunks grounded the workflow. > - Which MCP tools were called and with which parameters. > - Whether the output was read-only or write-capable. > - Which approval policy applied. > - Which user approved or rejected the action. Those details are the difference between "the agent said so" and "we can audit the workflow." ## What happens next The full workflow runs through the [Amazon Agent Data layer](/features/amazon-agent-flow/): Amazon Ads MCP brings AMC and demand-side platform (DSP) campaign signals, the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) can add catalog context, Atlas grounds the operating procedure, and [Skills](/features/skills/) turn the workflow into a reviewed recurring automation. Once the Skill exists, the same workflow can run from chat, code, or an automation tool. The interface changes; the source rules and approval gates do not. ## Why repeatability matters more than one successful query A Skill is how an agent stops improvising an operating procedure. *Next: put a [human approval gate](/guides/human-approval-for-amc-activation/) in front of activation, so a Skill that builds an audience never ships it to DSP unreviewed.* ### Human Approval for AMC Activation URL: https://www.kuudo.com/guides/human-approval-for-amc-activation/ Human approval for Amazon Marketing Cloud (AMC) activation belongs between audience validation and the API submission. The workflow should show the approver the audience size, exact SQL and payload, date window, refresh cadence, and destination; only an approved artifact proceeds to DSP, while rejected or undersized audiences remain read-only. For the underlying privacy and data architecture, start with the [Amazon Marketing Cloud explainer](/docs/guides/amazon-marketing-cloud/). Run the read-only half of an AMC workflow hands-off, and stop in one place: audience activation waits inside a [Skill](/features/skills/) until a human approves the exact creation payload. The [Amazon Ads MCP](/features/amazon-ads-mcp/) runs the sizing reads automatically and holds the gated submission, while [Amazon Agent Atlas](/features/agent-atlas/) supplies the rules the approver checks against: the 2000-user rule-based floor, the 500 to 500,000 lookalike seed band, the 24-to-36-hour DSP lag. The policy answers a Slack message from our media lead: *"If the agent builds an [Amazon Marketing Cloud](/features/amc/) audience and the SQL compiles, does it push it into the demand-side platform (DSP) on its own, or does one of us sign off first?"* [Our agent](/features/ai-clients/) could do either. It stops. [ChatGPT, Claude, Perplexity, Microsoft Copilot, and whatever comes next](/features/ai-clients/) know nothing about your business out of the box. Your competitors use those tools too. Kuudo gives those same tools your edge: **your data**, **your rules**, and **the way your business operates**. That means **your account as it is right now**, **your judgment running every time**, and **your call before anything changes**. The [Amazon Ads MCP](/features/amazon-ads-mcp/) reads and tests against your authorized instance, [Skills](/features/skills/) preserve the rules your team expects, [Amazon Agent Atlas](/features/agent-atlas/) supplies Amazon-specific guidance, and approval controls keep activation and other consequential changes with you. ## An AMC audience query returns nothing you can review, so the gate must come before the POST One line of shared playbook boilerplate settles where the gate goes: "Unlike standard AMC queries, AMC Audience queries do not return visible results that you can download. Instead, the audience defined by the query is pushed directly to Amazon DSP." A rule-based audience query selects `user_id` values and nothing else; once submitted, there is no artifact left to inspect. Review after submission is not late, it is impossible. Activation queries even run on their own table surface: the `_for_audiences` variants, `conversions_for_audiences`, `conversions_all_for_audiences`, `dsp_impressions_for_audiences`, `sponsored_ads_traffic_for_audiences`, `amazon_attributed_events_by_traffic_time_for_audiences`. That suffix in a FROM clause is a machine-checkable signal that a query is an activation, not an analysis. One nuance: `conversions_all_for_audiences` is the audience copy of `conversions_all`, and the high-value-segments query built on it lists a Flexible Amazon shopping insights subscription as a requirement, so the runnable seed below stays on `conversions_for_audiences`. Cost lands on the same line. The **Off-Amazon Conversions Playbook** is explicit: "Submitting an audience to be created is not charged. There is no cost until the audience is activated in Amazon DSP." In the console the write is a paste into the Audiences query editor; through the Amazon Ads MCP it is the POST method of the AMC rule-based Audience API, which the Skill refuses to send unsigned. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI drafts plausible audience SQL without knowing that submission returns nothing reviewable; you paste it into the Audiences query editor and find out after the audience exists. > - **Your judgment, running every time.** The shared AI will not reliably flag that a `_for_audiences` table makes the query an activation, so the one query that needed a gate looks like all the others. > - **Your account, as it is right now.** Getting that draft meant pasting instance details and audience logic into a chat history you do not control. ## Approval is a numbers check: 2000 distinct user_ids for a rule-based audience, a 500 to 500,000 seed for a lookalike Two thresholds decide approvability, and they never blend. For rule-based audiences the **Off-Amazon Conversions Playbook** states that "the minimum is 2000 distinct user_ids for a rules-based audience to be activated in the market," a floor the **Programmatic Audience Framework Playbook** repeats. Lookalikes answer to a different band: **Introduction to AMC Lookalike Audiences** requires a seed of between 500 and 500,000 user_ids, warns that refresh "will fail if the size falls below 500 or goes above 500k," and recommends 1,000 to 450,000 as the working range. A correct approval card names the one threshold that applies and puts the measured number beside it. Measurement runs automatically. Before submission, the agent wraps the seed in the corpus's companion pattern, `SELECT COUNT(user_id) AS user_count FROM (seed query) GROUP BY 1`, run as an ordinary measurement query. Sizing needs no approval because it changes nothing. The seed we gated most recently, purchasers from the standard audience table, built with the same query shape the [cart-abandoner workflow](/guides/amc-cart-abandoner-audience/) assembles upstream: ```sql SELECT user_id FROM conversions_for_audiences WHERE event_subtype = 'order' ``` That seed's companion COUNT came back at 12,480 distinct user_ids: above the 2000 floor with room to decay across weekly refreshes. On a lookalike, the same COUNT places the seed inside the 500 to 500,000 band; DSP's expansion tiers then run from roughly 300K to 1M members (most similar) up to 900K to 10M (most broad), region-dependent and subject to change. The corpus suggests starting balanced; a seed pinned to a tier's edge should be revised, not shipped. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot run that companion COUNT against your AMC instance, so the seed size the approval hinges on is a guess, not a measurement. > - **Your judgment, running every time.** The shared AI does not carry the 2000-user floor or the 500/500k band; it blesses an 1,800-user audience that never activates, or a 600,000-user seed whose refresh fails, and blends both rules into "make sure it's big enough." > - **Your call, before anything changes.** The shared AI cannot hold the submission until the checks pass; you carry every figure between tabs yourself. ## The approver signs the JSON that ships: audienceName, advertiserId, the flattened query, the window, and refreshRateDays In front of the human sits the creation payload itself, not a summary. The fields match the promotional-events lookalike playbook's example: `audienceName`, `audienceDescription`, `advertiserId`, the seed SQL flattened into `query`, `timeWindowStart`, `timeWindowEnd`, `timeWindowRelative`, and `refreshRateDays`; lookalike submissions add `lookalikeAudienceExpectedReach`, `"BALANCED"` in the corpus example. The same playbook scopes two recommendations to its pattern: `refreshRateDays: 7` "to ensure optimal performance," and `timeWindowRelative: FALSE` to keep each refresh looking at individuals from the previous event. Shipped text is not drafted text. **Introduction to AMC Lookalike Audiences** recommends removing all comment lines from the query before pushing to audience creation, and the seed gets flattened into the JSON regardless, so what a human eyeballed in an editor is not automatically what ships. The Skill closes that gap by hashing the flattened, comment-stripped query and binding the approval to the hash: change `query` by one character and the gate reopens. ```json { "artifact": "audience_definition", "workflow": "amc-activation-approval", "requiresApproval": true, "activation": { "audienceName": "AMC_Purchasers_Weekly_2026Q3", "audienceDescription": "Purchasers, June 2026 order window", "advertiserId": "123456789", "query": "SELECT user_id FROM conversions_for_audiences WHERE event_subtype='order'", "timeWindowStart": "2026-06-01T00:00:00Z", "timeWindowEnd": "2026-07-01T00:00:00Z", "timeWindowRelative": "FALSE", "refreshRateDays": 7 }, "checks": { "seedCount": { "measured": 12480, "threshold": "2000 distinct user_ids rule-based activation floor" }, "tableVariant": "FROM uses _for_audiences tables only", "destination": { "advertiserId": "123456789", "instanceId": "amcinstance01" }, "querySha256": "5b3c9f1e0a4b2d83c56a1908d7e5bc2a4f8e6d0b1a9c8e7f5d3b2a1c0e9f8d7b", "atlasChunks": [ "6e39ff4c3e1ed64b", "3592514e4137c936", "9e1e29903178da48", "9fadd181160a41e4" ] }, "decision": "approve | revise | cancel", "records": { "audienceExecutionId": null, "status": null, "statusReason": null, "audienceCount": null, "dspAudienceId": null, "lastRefreshedTime": null, "approvedBy": null, "approvedAt": null } } ``` Two fields deserve more attention than the SQL. `advertiserId` is the destination: the DSP seat the audience lands in, confirmed, not assumed. And `refreshRateDays` makes activation a standing write, not a one-off: the query re-runs on cadence under this single approval, so the human is approving a recurring behavior, which is why the hash check stays live after the yes. > **The AI is shared. The moat is yours.** > - **Your call, before anything changes.** The shared AI can format a payload that looks exactly like this, but it cannot bind your approval to what ships; what you read and what you later paste can silently differ. > - **Your account, as it is right now.** The shared AI cannot see your DSP seat, so `advertiserId` is a string it copies from your prompt, not a destination it verifies. > - **Your judgment, running every time.** The shared AI reads `refreshRateDays: 7` as a field, not a standing weekly write running under a one-time approval. ## "Successful" in AMC is not "active" in DSP: allow 24 to 36 hours and keep your own run log The clock after the approved POST belongs to Amazon. The **Programmatic Audience Framework Playbook** is precise: "Once an audience creation status is returned as 'successful' in the AMC API, it can take between 24 and 36 hours to be 'active' and ready for use in Amazon DSP." I nearly resubmitted after one quiet day; the playbook says that quiet day is the system working. Amazon also leaves a hole in the audit trail. The same playbook admits there is currently "no unique id field that is common for both the AMC endpoint and Amazon DSP endpoint"; the only mapping for now is "by name and by the approximate creation date." The monitoring output does include `dspAudienceId` and `dspCanonicalId` columns, but by the playbook's own statement those values cannot join the two systems, so lean on neither. Copy its working practice instead: filter the DSP audience list by `audienceName` prefix, category "Custom-built," subCategory "AMC," which only works if the naming convention was enforced at approval time. So the run log is the join key, not bookkeeping. Per audience, the agent logs what it watches by `audienceExecutionId`: `status`, `statusReason`, `audienceCount`, `lastRefreshedTime`, the query, and the window, next to the approval record itself, the same argument that makes run logs non-negotiable for MCP writes. > **The AI is shared. The moat is yours.** > - **Your account, as it is right now.** The shared AI cannot watch the 24-to-36-hour window between AMC "successful" and DSP "active"; you refresh the console and wonder whether it failed or is not there yet. > - **Your judgment, running every time.** The shared AI does not know the two endpoints share no common ID, so it cannot reconstruct which live DSP audience came from which approved payload. > - **Your call, before anything changes.** When a refresh stalls, no chat flags the line item still targeting the stale audience; a human has to notice on their own. ## What happens next Approval closes the gate once; the Skill keeps watching. It expects the DSP entry inside the 24-to-36-hour window and logs execution metadata on every refresh, keeping the name-plus-creation-date mapping current. When an audience stops refreshing, the playbook's diagnosis is blunt: "it is most likely the audience size has dropped below 2,000 unique users," flagged after a one-day buffer past `refreshRateDays`. After several failed refreshes the instruction turns operational: decrease or stop spending on that line item, because it keeps targeting the same users as they move down the funnel. Each refresh is also the moment to re-measure overlap against segments already live in Amazon DSP (via `conversions_all`), at launch and mid-campaign, since affinity and size metrics do not necessarily indicate overlap. The loop runs on the [Amazon Agent Data layer](/features/amazon-agent-flow/): the Amazon Ads MCP carries the AMC sizing reads, the gated submission, and the refresh monitoring, and the [Selling Partner MCP](/features/amazon-selling-partner-mcp/) joins when audience logic depends on catalog facts, like which ASINs a promotion actually covers. That is the whole pattern: reads run free, the one write stops at a person, and the approval binds to the exact artifact that ships. *Next: turning gated analyses like this one into [repeatable AMC agent workflows](/guides/amc-agent-workflows/), where the approval step is part of the Skill rather than a manual afterthought.* ### Everyone in Your Category Has the Same AI Now URL: https://www.kuudo.com/guides/everyone-has-the-same-ai/ If you sell on Amazon, something quietly changed underneath you in the last two years, and most of your competitors haven't noticed what it actually means. Every seller in your category now has access to the same frontier AI you do. The same models, the same chat window, the same "analyze this search term report" prompt. The tools got extraordinary, and they got extraordinary for everyone at exactly the same moment. When the most powerful tool in your business is also sitting on your competitor's desk, the tool stops being the difference. So what is the difference? ## The model knows *about* Amazon. You *know* Amazon. Ask a frontier model how to structure a Sponsored Products campaign and it will give you a competent answer. It will give your competitor the same competent answer. It has read everything ever written about Amazon: the help docs, the blog posts, the courses, the conference talks, the prompt packs. But that's the tell. It knows *about* Amazon the way someone who read a book about swimming knows about water. That knowledge is now the floor: table stakes, priced at twenty dollars a month. Because Amazon doesn't run on what's written down. It runs on unwritten rules, and those were never in anyone's training data. The gap between what Seller Central says and what Amazon actually does. What genuinely gets a suppressed listing reinstated, versus the case wording that earns you another canned bot reply. Why your attribute update silently loses to an upstream contribution, and which "errors" you can safely ignore. How the algorithm really behaves after a price move, how enforcement really works, how long things really take. The internals, the quirks, the behaviors, the operational know-how you only earn by running the machine for years and paying tuition every time it surprises you. ### The layer that's only yours And stacked on top of that sits the layer that's exclusively yours: the size-chart rewrite that cut your bestseller's return rate 40%, the fifty thousand dollars of wasted spend encoded in your negative keyword lists, why you never run deals on that one ASIN in Q3, the campaign structure you arrived at after three years of expensive lessons. That's your DNA. Some people call it tribal knowledge. It's the reason a customer chose you over the four identical-looking listings above and below you, and in the most literal sense, it's your IP. In a marketplace as brutally commoditized as Amazon, it may be the *only* IP you have that can't be copied, undercut, or bought. We're not the only ones who see it this way. In late June 2026, Alex Karp's Palantir posted [a nine-point manifesto on AI sovereignty](https://thenextweb.com/news/palantir-ai-sovereignty-manifesto-tokenmaxxing): data as treasure, knowledge as the asset that compounds, ownership as the precondition for having a future at all. He argues it from the world of defense and statecraft. We didn't need the confirmation, but we'll take it: it's the same signal we built Kuudo on. If it's existential for nations, believe that it's existential for a business fighting for the buy box. ## Why are Amazon sellers walking into this trap faster than anyone? The path of least resistance on a busy afternoon is to paste it all into a public chatbot. The search term report. The business report. The P&L. The strategy doc for your Q4 push. Every hard-won correction, typed into a tool you don't control. Do that long enough and your edge stops being yours. It gets absorbed into the same model your competitor opens tomorrow morning. The advantage that took years to earn doesn't fade. It gets donated. ### Read the pitches yourself And you don't have to squint to see the machine, because a whole product category now advertises it right on the homepage. Look at the wave of AI listing-optimization tools and read their pitches carefully: ListingOptimization.ai leads with AI ["trained on thousands of winning listings"](https://listingoptimization.ai/) and a template library for cloning A/B test winners. Whose winning listings? Who paid for the losing variants? Pixii grades your listing against [a database of 100,000 top-performing listings](https://www.pixii.ai/) and offers proven templates drawn from high-converting ones. Somebody earned those conversions. Nozam sells [review mining on any ASIN](https://www.nozam.io/), yours or your competitor's, to surface exactly what customers hate about them. Clicco promises visuals and copy that learn from top competitors. And Selluna says it plainest of all: upload any competitor's image and it recreates the style with your product: ["Their inspiration, your listing."](https://www.selluna.ai/) None of this is hidden. It's the value proposition. These are redistribution engines: they harvest what worked (the winning image, the converting layout, the review insight) and hand the distillation to the next subscriber for a monthly fee. Every one of those "winning listings" in the training data was some seller's tuition: the photoshoots, the failed variants, the split tests, the years of learning what actually converts in one category. That edge is now a template, available to the four listings above and below yours for a monthly fee. ### The flywheel only spins one way Here's the part to sit with: the flywheel only spins one way. **They learn from you, and they offer that learning to the next user.** Today you're the customer. The day your listing starts winning, you're the inventory. One of these tools even answers "Is my data private?" in its FAQ with *yes, for paid plans*. Privacy as an upsell. That's the market telling you, in writing, what your knowledge is worth to them. But don't stop at the upsell, because a privacy line, even a sincere one, answers a narrower question than the one that matters. Several of these tools do promise they won't sell your data, and that promise can be entirely true while the flywheel spins anyway. The thing that compounds isn't your file. It's the learning stacked on top of it: which of the six generated main images you shipped, which template you cloned, which headline you kept and which you threw away, which "winner" you took into your split test. That's your operator judgment, years of expensive lessons, compressed into clicks, and it's exactly the signal a system like this needs to get smarter for the next subscriber in your category. Read the pitches again: a grader scored against "100,000 top-performing listings," AI "trained on thousands of winning listings," libraries of "proven templates from high-converting listings." Proven by whom? Somebody's photoshoots, somebody's failed variants, somebody's tuition. The learning travels even when your name doesn't. They anonymize *you*. They don't anonymize what they learned from you. That's the product. ### This isn't new. It's just faster now Sellers should recognize the pattern, because the Amazon software industry ran this play long before AI: tools that pooled your data into "category benchmarks" and sold the aggregate back to you and everyone you compete with. The new crowd is faster and slicker, but the business model is identical: build the product on top of your knowledge until your knowledge becomes the thing they sell. Take it to its end and they don't need you at all. The next seller just pays for access to the expertise you handed over. That isn't being out-competed. It's commoditizing yourself, one upload at a time. The brands still donating their edge to someone else's flywheel will look up one day and find they no longer have one. ## The answer isn't less AI. It's AI in a private place. This is [the conviction Kuudo is built on](/why-kuudo/), and it's worth saying plainly: **when everyone runs the same models, your advantage is the private knowledge only you have, and keeping it yours is no longer a matter of discipline. It's a choice.** The wrong response to the leak is abstinence. Refusing to use frontier AI while your category compounds with it isn't protecting your edge; it's forfeiting the game to protect the ball. The right response is to give AI your knowledge *and a private place to do the work*, where your years of order history, campaign judgment, and catalog-specific wisdom accelerate the business instead of leaking out of it. ### That's what Kuudo is A private place for AI to work on what only you know. It runs in your cloud, not ours. Nothing inside it trains anyone else's model: Google, Meta, Anthropic, and OpenAI can't learn from what you never gave them. There's no fine print about what counts as "your data," because in your own cloud the definition is total: the inputs, the outputs, the choices, and everything the system learns from them are yours, the learning included. And if you ever walk away, what you built stays yours, because it lives in your cloud. Private. Trusted. Owned. In that order, always together. And because you supply what no vendor can (your choices, your judgment, the calls only your team would make), no two Kuudo deployments look alike. It isn't a finished product you rent. It's the ingredients: the [data layer](/features/amazon-agent-flow/), with [Amazon Ads MCP](/features/amazon-ads-mcp/) steering spend and [Selling Partner MCP](/features/amazon-selling-partner-mcp/) touching your catalog; the [Amazon Agent Atlas](/features/agent-atlas/) grounding, and the [tools and skills](/features/skills/) your agents run. You keep the IP. You own the means of production. ## Why Amazon first We start with Amazon deliberately, because it's where some of the densest, most defensible operator knowledge in the world already lives, and where the stakes of giving it away are highest. Millions of sellers, one search results page, and a customer who can't tell you apart until *something* tells them to. That something was never the model. It was never the clever prompt. It was the unwritten rules, the hard-won know-how, the accumulated judgment about how the machine actually behaves and what actually works, earned by you, running your business, one expensive lesson at a time. Everyone has the same AI now. Your edge is what it doesn't know. Keep it that way. Build your moat, and let your competition give theirs away. *For how this plays out in practice (an agent doing real listing work with your knowledge staying yours), start with [how a suppressed listing gets diagnosed and fixed](/guides/seller-listing-agentic-audit-to-patch/).* ## Documentation ### Quick Start: ChatGPT URL: https://www.kuudo.com/docs/quick-start/chatgpt/ Ask for the Amazon work in plain language and get it back done: pull a Sponsored Products report, find why a listing went suppressed, check yesterday's orders, size an Amazon Marketing Cloud (AMC) audience. Answers come from your live Amazon Ads, Seller Central, and Vendor Central data rather than the model's training set, and every action stays scoped to the credentials you grant. Connect ChatGPT to your private MCP server in a few minutes. Use ChatGPT's Apps or custom connector flow when you want ChatGPT to call tools and retrieve live context from your own MCP deployment. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Prerequisites - A connected workspace with access to your private MCP server. - A dashboard API key or signed connector URL from the dashboard **Keys** tab. - A ChatGPT plan that supports apps, connectors, or custom MCP connectors. - Developer mode or workspace permission to create custom connectors, if your ChatGPT plan requires it. ## ChatGPT custom app or connector Use this path for ChatGPT on the web. ChatGPT's interface may call this an **app**, **custom app**, or **custom connector**, depending on your plan and workspace settings. ### Copy prompt Paste this into ChatGPT if you want it to walk you through setup: ```text Walk me through setting up my private MCP server in ChatGPT, step by step. Use ChatGPT's Settings > Apps or Apps & Connectors flow for a custom MCP app or connector. If developer mode or admin permission is required, tell me where to check before continuing. Do not ask me to paste my raw API key into this chat. If ChatGPT needs authentication, tell me to copy the key or signed connector URL from my dashboard Keys tab and paste it only into the ChatGPT connector setup form. Keep each step short, tell me what to click, and wait for me after each step. The MCP server URL will look like: https://{your-private-mcp-host}/mcp ``` ### 1. Confirm custom connectors are enabled In ChatGPT, open **Settings > Apps** or **Settings > Apps & Connectors**. If you do not see an option to create a custom app or custom connector, check developer mode or workspace permissions: - Plus and Pro accounts may need developer mode enabled before custom MCP connectors appear. - Business, Enterprise, and Edu workspaces may require an owner or admin to allow custom connectors. ### 2. Copy your MCP server details In your dashboard, open the **Keys** tab. Create a key if needed, then copy the endpoint and authentication value for ChatGPT. Use the normal MCP endpoint unless your dashboard provides a ChatGPT-specific signed connector URL: ```text https://{your-private-mcp-host}/mcp ``` Do not paste your raw API key into a chat conversation. Use the ChatGPT connector setup form or your workspace's approved secret flow. ### 3. Add the custom app or connector In ChatGPT: 1. Open **Settings > Apps** or **Settings > Apps & Connectors**. 2. Choose the option to create or add a custom app or connector. 3. Name it something clear, such as `private-mcp`. 4. Paste your MCP server URL. 5. Configure authentication using the value from your dashboard. 6. Save or connect the app. If ChatGPT asks what type of server this is, choose the MCP or remote MCP option. ### 4. Enable it in a chat Start a new ChatGPT conversation. Add the connector from the composer, usually through the **+** button, **More**, or by mentioning the app by name if your workspace supports app mentions. If ChatGPT shows a tool approval setting, start with approval enabled until you have verified the tools and behavior. ### 5. Verify the connection Ask ChatGPT to list the available tools without calling write actions: ```text What tools are available from my private MCP connector? ``` If the connector is active, ChatGPT should enumerate the tools exposed by your private MCP server. ## Troubleshooting ### The custom connector option is missing Check whether your ChatGPT plan supports custom apps or custom MCP connectors. If you are in a workspace, ask an owner or admin to enable custom connectors and developer mode permissions. ### The MCP server does not meet ChatGPT requirements ChatGPT may reject an MCP server that does not implement the required MCP shape for custom connectors. Confirm that your server exposes the expected MCP endpoint, tool discovery, and any required search or fetch tools for your ChatGPT plan. ### Unauthorized or 401 errors Create a fresh key in the dashboard and re-enter the authentication value in ChatGPT's connector setup form. Make sure the server URL ends with `/mcp` and that the key belongs to the same workspace as the MCP server. ### Tools not appearing Start a new chat after saving the connector. Add the connector from the composer or mention it by name, then ask for the available tools again. ### Slow responses Check your network path to the private host and confirm the cloud deployment is healthy. The hostname is specific to your environment, so connectivity issues are usually tied to your cloud provider, DNS, firewall, or deployment status. ## Start using tools Try read-only prompts first: - "What tools are available from my private MCP server?" - "Show me the account or workspace context this server can access." - "List the read-only tools before calling any write actions." - "Summarize the last 7 days of available advertising, inventory, or operational data." For write-capable workflows, ask ChatGPT to explain the proposed action and wait for approval before it calls any mutating tool. ## Add reusable workflows After ChatGPT can reach your MCP server, install ChatGPT skills for repeatable workflows and task-specific instructions. See the [ChatGPT Skills quick start](/docs/quick-start/chatgpt-skills/). ### Quick Start: Claude URL: https://www.kuudo.com/docs/quick-start/claude-ai/ Ask Claude for the Amazon job in plain language — reprice a set of SKUs, patch a listing that failed validation, pull last week's campaign performance, build an Amazon Marketing Cloud (AMC) audience — and it works against your live Amazon accounts instead of guessing from training data. Writes stay scoped to the credentials you grant, and the packaged Skills gate activation on your approval. Connect Claude to your private MCP server in a few minutes. Use Claude's custom connector flow for Claude on the web or Claude Desktop, and use bearer-header configuration for Claude Code. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Prerequisites - A connected workspace with access to your private MCP server. - A dashboard API key, or a signed Claude connector URL from the dashboard **Keys** tab. - Claude on the web, Claude Desktop, or Claude Code. ## Claude custom connector Use this path for Claude on the web and Claude Desktop. Claude's custom connector UI accepts a single MCP server URL, so use the signed connector URL from your dashboard instead of a raw bearer token. ### Copy prompt Paste this into Claude if you want it to walk you through setup: ```text Walk me through setting up my private MCP server in Claude using the custom connector flow, step by step. Use Claude's Customize > Connectors flow, not claude_desktop_config.json and not a local Node or mcp-remote workaround. If I need a Claude connector URL, tell me to copy it from my dashboard Keys tab instead of pasting my API key into chat. Keep each step short, tell me what to click, and wait for me after each step. The connector URL will look like: https://{your-private-mcp-host}/mcp/connect/{signed-token} ``` ### 1. Copy your Claude connector URL In your dashboard, open the **Keys** tab. Create a key if needed, then copy the Claude connector URL. That URL is already signed for Claude. You do not need to paste your raw API key into Claude. ```text https://{your-private-mcp-host}/mcp/connect/{signed-token} ``` ### 2. Add the custom connector In Claude, open the official connectors UI: 1. Open **Customize > Connectors**. 2. Click the **+** button. 3. Choose **Add custom connector**. 4. Name it something clear, such as `private-mcp`. 5. Paste the signed Claude connector URL. 6. Click **Add**. Claude's custom connector UI does not let you manually attach an `Authorization` header. If you only have the normal `/mcp` endpoint, go back to the dashboard and copy the Claude connector URL. ### 3. Enable it in a chat Start a new Claude chat, open **Connectors** from the composer, and toggle the connector on for that conversation. If Claude shows a **Tool access** setting, leave it on **Auto** unless you specifically want on-demand approvals. ### 4. Verify the connection Ask Claude to list the available tools without calling write actions: ```text What tools are available from my private MCP connector? ``` If the connector is active, Claude should enumerate the tools exposed by your private MCP server. ## Claude Code Use this path when you want Claude Code to call your private MCP server from a local project or user profile. For a deeper reference on Claude Code MCP transports, scopes, authentication, JSON config, plugins, and managed settings, see the [Claude Code MCP quick start](/docs/quick-start/claude-code-mcp/). ### Copy prompt Paste this into a Claude Code conversation if you want Claude Code to handle setup: ```text Add my private MCP server so I can use its tools from Claude Code. Use the private host from my dashboard: https://{your-private-mcp-host}/mcp Use my local MCP_API_KEY environment variable. Do not ask me to paste or share the raw key in chat. If MCP_API_KEY is not set in this shell, tell me to export it from my dashboard first. Run the Claude Code setup command using the literal env-var header: 'Authorization: Bearer ${MCP_API_KEY}' ``` ### 1. Set your local key Set the API key in the shell that launches Claude Code: ```bash export MCP_API_KEY="mcp_live_..." ``` For repeated use, store it in your shell profile or secret manager. Do not commit the raw value to `.mcp.json`. ### 2. Add the MCP server Add the remote HTTP MCP server: ```bash claude mcp add --transport http private-mcp https://{your-private-mcp-host}/mcp \ --header 'Authorization: Bearer ${MCP_API_KEY}' ``` Keep the single quotes around the header so Claude Code stores the environment-variable reference, not your raw key. ### Scope options - **Local scope**: Available only in the current local project. - **Project scope**: Shared through `.mcp.json` in the project root. Add `--scope project` to the command. - **User scope**: Available across all projects. Add `--scope user` to the command. Project scope is useful when each collaborator should use the same server entry but their own local `MCP_API_KEY`. ### Alternative: JSON config If you prefer adding the full JSON definition: ```bash claude mcp add-json private-mcp '{ "type": "http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } }' ``` ### 3. Verify the connection Type `/mcp` in any Claude Code session. You should see `private-mcp` listed with its tools. You can also inspect the saved config: ```bash claude mcp get private-mcp ``` ## Troubleshooting ### The custom connector will not add Make sure you pasted the full signed Claude connector URL from the dashboard, not the raw `/mcp` endpoint. Claude custom connectors use `/mcp/connect/{signed-token}` and do not accept a separate bearer header. ### Unauthorized or 401 errors For Claude custom connectors, create a fresh key in the dashboard and copy the Claude connector URL again. Rotated or revoked keys invalidate old connector URLs. For Claude Code, make sure `MCP_API_KEY` is exported in the shell Claude Code uses, then double-check that your server URL ends with `/mcp`. ### Tools not appearing In Claude on the web or Claude Desktop, the connector must be enabled per conversation. Open **Connectors** in the current chat and make sure your connector is toggled on. In Claude Code, run `/mcp` to check server status. If the server shows an error, verify the private hostname and local `MCP_API_KEY` value. ### Slow responses Check your network path to the private host and confirm the cloud deployment is healthy. The hostname is specific to your environment, so connectivity issues are usually tied to your cloud provider, DNS, firewall, or deployment status. ## Start using tools Try read-only prompts first: - "What tools are available from my private MCP server?" - "Show me the account or workspace context this server can access." - "List the read-only tools before calling any write actions." - "Summarize the last 7 days of available advertising, inventory, or operational data." For write-capable workflows, ask Claude to explain the proposed action and wait for approval before it calls any mutating tool. ## Add reusable workflows After Claude can reach your MCP server, install Claude Code skills for repeatable workflows and task-specific instructions. See the [Claude Code Skills quick start](/docs/quick-start/claude-skills/). ### Quick Start: Claude Code MCP URL: https://www.kuudo.com/docs/quick-start/claude-code-mcp/ From Claude Code you can wire live Amazon data into the loop you already work in — query Ads, Seller Central, and Vendor Central while you build, prototype an agent against real campaigns, or debug a listing without leaving the terminal. Use Claude Code MCP when you want Claude Code to connect directly to tools, databases, APIs, monitoring systems, issue trackers, or your private MCP server. For the official reference, see [Connect Claude Code to tools via MCP in the Claude Code docs](https://code.claude.com/docs/en/mcp). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Add a remote HTTP server Remote HTTP is the recommended transport for cloud-hosted MCP servers. ```bash export MCP_API_KEY="mcp_live_..." claude mcp add --transport http private-mcp https://{your-private-mcp-host}/mcp \ --header 'Authorization: Bearer ${MCP_API_KEY}' ``` Keep the single quotes around the header so Claude Code stores the environment-variable reference instead of your raw key. When you configure MCP servers through `.mcp.json`, `~/.claude.json`, or `claude mcp add-json`, the `type` field can use either `http` or `streamable-http`. ## Add a remote SSE server SSE is deprecated. Use HTTP when the server supports it. If you must connect an older SSE server: ```bash claude mcp add --transport sse legacy-api https://api.example.com/sse \ --header 'Authorization: Bearer ${MCP_API_KEY}' ``` ## Add a local stdio server Stdio servers run as local processes. Use them for tools that need local system access or custom scripts. ```bash claude mcp add --transport stdio --env TOOL_API_KEY="${TOOL_API_KEY}" local-tool \ -- npx -y local-tool-mcp-server ``` All Claude flags such as `--transport`, `--env`, `--scope`, and `--header` must come before the server name. The `--` separator marks the start of the command and arguments passed to the MCP server. Claude Code sets `CLAUDE_PROJECT_DIR` in the spawned server's environment to the project root. Local servers can read it to resolve project-relative paths. ## Manage servers Use the Claude Code CLI for saved configuration: ```bash claude mcp list claude mcp get private-mcp claude mcp remove private-mcp ``` Use `/mcp` inside Claude Code to inspect live server status, authenticate with OAuth servers, retry failed connections, and see connected tool counts. Claude Code refreshes tool, prompt, and resource lists when servers send MCP `list_changed` notifications. HTTP and SSE servers reconnect automatically with backoff after transient disconnects. Stdio servers are local processes and are not automatically reconnected. ## Choose a scope The `--scope` flag controls where the server is stored and who can use it. | Scope | Loads in | Shared with team | Stored in | | --- | --- | --- | --- | | `local` | Current project only | No | `~/.claude.json` under the current project path | | `project` | Current project only | Yes | `.mcp.json` in the project root | | `user` | All your projects | No | `~/.claude.json` | Local scope is the default. Use it for personal or experimental servers. Use project scope for team-shared server entries that should be checked into version control. Use user scope for personal tools you want across projects. Examples: ```bash claude mcp add --transport http private-mcp --scope local https://{your-private-mcp-host}/mcp claude mcp add --transport http private-mcp --scope project https://{your-private-mcp-host}/mcp claude mcp add --transport http private-mcp --scope user https://{your-private-mcp-host}/mcp ``` When the same server name exists in more than one scope, Claude Code uses the highest-precedence definition: local, project, user, plugin-provided servers, then Claude.ai connectors. ## Use project JSON Project-scoped MCP servers live in `.mcp.json`: ```json { "mcpServers": { "private-mcp": { "type": "http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } } } } ``` Claude Code supports environment-variable expansion in `.mcp.json` values: - `${VAR}` expands to the environment variable value. - `${VAR:-default}` uses a default when the variable is unset. Expansion works in `command`, `args`, `env`, `url`, and `headers`. If a required variable is missing and no default is provided, Claude Code fails to parse the config. For project-scoped servers, Claude Code prompts for approval before using servers from `.mcp.json`. To reset those choices, run: ```bash claude mcp reset-project-choices ``` ## Add JSON directly Use `claude mcp add-json` when you already have a JSON server definition: ```bash claude mcp add-json private-mcp '{ "type": "http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } }' ``` ## Authenticate remote servers For bearer-token servers, pass a header when adding the server or store it in JSON config. For OAuth servers, add the server first, then run `/mcp` inside Claude Code and follow the authentication flow. Claude Code can use fixed OAuth callback ports and preconfigured OAuth settings when your environment requires them. ## Plugin-provided MCP servers Claude Code plugins can bundle MCP servers. Plugin servers start when the plugin is enabled and appear alongside manually configured servers in `/mcp`. If you enable or disable a plugin during a session, run: ```text /reload-plugins ``` Plugin-provided MCP servers are managed through plugin installation rather than `claude mcp` commands. ## Output limits Claude Code warns when MCP tool output exceeds 10,000 tokens. To raise the limit for a session: ```bash MAX_MCP_OUTPUT_TOKENS=50000 claude ``` Keep tool outputs focused when possible. Large tool responses increase context pressure and make later turns harder to reason about. ## Tool search and prompts Claude Code can defer large MCP tool lists through tool search, which helps scale when many servers expose many tools. If a server is still connecting when a request needs it, Claude waits while the relevant server becomes available. MCP prompts can also appear as Claude Code commands. Use prompt commands when the server provides reusable workflows in addition to tools. ## Managed configuration Enterprise admins can manage MCP configuration through managed settings. Managed configuration can exclusively control available servers or use allowlists and denylists to restrict which command-based or URL-based servers users may add. Use managed configuration for organization-wide compliance, approved server catalogs, and consistent access rules. ## Troubleshooting ### Server does not appear Run `claude mcp list`, then open `/mcp` inside Claude Code. If the server name is `workspace`, rename it; Claude Code reserves that name for internal use. ### Authentication fails Confirm `MCP_API_KEY` is exported in the shell that launches Claude Code, or re-authenticate OAuth servers through `/mcp`. ### Project server prompts for approval That is expected for servers loaded from `.mcp.json`. Review the config and approve the server if you trust it. ### Tool output is too large Filter the tool call if possible, or start Claude Code with a larger `MAX_MCP_OUTPUT_TOKENS` value. ### Remote server disconnects Open `/mcp` and retry the server. HTTP and SSE transports reconnect automatically for transient errors, but authentication and not-found errors require config changes. ## Next steps For a shorter setup path focused only on your private MCP server, see the [Claude quick start](/docs/quick-start/claude-ai/). For reusable Claude Code workflows, see the [Claude Code Skills quick start](/docs/quick-start/claude-skills/). ### Quick Start: Claude Cowork for Amazon Workflows URL: https://www.kuudo.com/docs/quick-start/claude-cowork-amazon/ Use Claude Cowork with Kuudo's Amazon MCP servers and Amazon Skills to read Amazon data, run analyses, and produce client-ready files from one conversation. This page covers how connectors, MCP servers, and Skills fit together inside Cowork; how to connect the Kuudo Amazon servers; how to manage permissions on Team and Enterprise plans; and which Amazon workflows to try first. Replace the placeholder URLs below with the values from your Kuudo deployment: ```text {kuudo-amazon-ads-mcp-url} {kuudo-amazon-selling-partner-mcp-url} {kuudo-openbridge-mcp-url} ``` ## What Cowork does Claude Cowork is a Claude Desktop workspace for file and task work. It can work with folders you grant it access to, run code in a sandboxed environment, use remote connectors, and apply Skills for repeatable workflows. For Amazon work, the useful part is not file organization. It is that Cowork can combine a folder of client files with Kuudo's Amazon MCP servers and Skills, then produce a report, spreadsheet, listing audit, SQL query, or workflow plan without switching tools. ## The building blocks Kuudo's Amazon workflow stack uses three related concepts. | Layer | What it does | | --- | --- | | Connectors | Let Claude reach approved apps, services, and data sources. | | MCP servers | Expose tools and data through the Model Context Protocol. A connector is backed by an MCP server. | | Skills | Package task-specific expertise so Claude knows how to use the tools correctly. | In practice, the MCP server gives Claude access and the Skill gives Claude the operating playbook. For example, the [Amazon Ads MCP](/features/amazon-ads-mcp/) can pull campaign and search-term data. A search-term mining Skill knows how to separate negative candidates, generic scale terms, branded defense terms, and rising-star queries. ## Kuudo Amazon servers Connect only the servers your workflow needs. Keep write tools approval-gated unless the workflow is deliberately automated. | Server | Connects Cowork to | Common uses | | --- | --- | --- | | [Amazon Ads MCP](/features/amazon-ads-mcp/) | Amazon Ads API, Sponsored Products, Sponsored Brands, Sponsored Display, DSP (demand-side platform), and reporting | Pull campaign, keyword, search-term, targeting, and AMC-related reporting data. | | [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) | SP-API (Selling Partner API) surfaces such as catalog, listings, returns, A+ Content, orders, and reports | Audit listings, diagnose suppressed ASINs, inspect returns, and propose controlled listing edits. | | [Amazon Vendor Central MCP](/features/amazon-vendor-central-mcp/) | Vendor Central functions such as retail analytics, direct fulfillment, procurement, and chargebacks | Run vendor reporting, operational checks, and Vendor Central workflows when your deployment includes this surface. | | [Amazon Agent Data layer](/features/amazon-agent-flow/) | Openbridge pipelines and warehouse-backed Amazon data | Query durable Amazon data, inspect subscriptions, validate SQL, and build repeatable reporting workflows. | ## Kuudo Amazon Skills Skills can activate automatically when your request matches their purpose. You can also ask Cowork to use one explicitly by name. | Skill | What it produces | | --- | --- | | `search-term-gold-miner` | Sponsored Products search-term mining with negatives, generic gold terms, branded terms, and rising-star queries. | | `campaign-structure-auditor` | Portfolio health analysis with budget utilization, dead campaigns, ghost campaigns, auto/manual balance, and naming-pattern checks. | | `amazon-ads-reporting` | Amazon Ads report request bodies and report-field mapping. | | `amazon-listing-optimizer` | Listing and A+ Content audits, suppressed-listing diagnosis, and controlled edit suggestions. | | `refund-return-rate-monitor` | Returns reports by ASIN/SKU, reason, disposition, and risk category. | | `amc` and `amc-sql` | Amazon Marketing Cloud SQL, audiences, attribution, and cross-channel analyses. | | `openbridge-mcp` | Table discovery, schema inspection, validated SQL, backfills, and pipeline health checks. | If live MCP access is unavailable, several Skills can still work from a CSV or Seller Central export mounted into the Cowork folder. ## Before you start You need: 1. Claude Cowork access in Claude Desktop. 2. A Kuudo account with active Amazon connections. 3. The remote MCP URLs for the Kuudo servers you want to connect. 4. Owner approval on Team or Enterprise plans when connectors must be enabled organization-wide. Permissions are inherited from the connected source. Cowork does not get more Amazon or Kuudo access than the authenticated user already has. ## Add a Kuudo remote connector Use the custom remote connector path for Kuudo servers unless your deployment appears in a connector marketplace. 1. Open Claude's connector settings. 2. Choose **Add custom connector**. 3. Name the connector clearly, such as `Kuudo Amazon Ads`. 4. Paste the remote MCP URL from your Kuudo deployment. 5. Add OAuth client details if your deployment requires them. 6. Complete the authentication flow. Repeat for each server you need: ```text Amazon Ads MCP: {kuudo-amazon-ads-mcp-url} Amazon Selling Partner MCP: {kuudo-amazon-selling-partner-mcp-url} Openbridge MCP: {kuudo-openbridge-mcp-url} ``` ## Network requirements Cowork may run locally, but remote connectors are reached through Claude's cloud connector path. That means a Kuudo MCP server must be reachable from Claude's connector infrastructure, not just from your laptop. If a server is behind a VPN, private network, or corporate firewall, Cowork will not be able to connect unless your network team exposes an approved remote endpoint or allowlists the required cloud traffic. ## Team and Enterprise setup On Team and Enterprise plans, an Owner or Primary Owner may need to enable custom connectors before members can connect them. Owner flow: 1. Open organization connector settings. 2. Add a custom web connector. 3. Enter the Kuudo remote MCP URL. 4. Configure OAuth details if required. 5. Save the connector for the organization. Member flow: 1. Open personal connector settings. 2. Find the Kuudo connector. 3. Connect it with your own Amazon or Kuudo credentials. Each member authenticates individually. Enabling a connector does not grant shared access to every account. ## Restrict write actions Amazon workflows often mix read-only reporting tools with live write tools. Keep those modes separate. Recommended defaults: | Tool class | Setting | | --- | --- | | Amazon Ads read/report tools | Allow, when the workflow is reporting-only. | | Openbridge read/query tools | Allow, when the workflow only inspects data or runs SQL. | | Listing, catalog, A+ Content, subscription, or job mutation tools | Needs approval. | | Any tool your team never wants run from chat | Blocked. | Use **Needs approval** for listing and catalog changes unless the workflow has a separate human approval gate. ## Enable connectors in a Cowork conversation After the connector is added: 1. Open the Cowork conversation. 2. Open the connector menu from the composer. 3. Toggle on the Kuudo servers for that task. 4. Ask Cowork to list available tools before calling write actions. Example verification prompt: ```text List the Kuudo Amazon tools available in this conversation. Group them by server. Do not call any write actions. ``` If you have many connectors enabled, use on-demand tool access so Cowork loads tool definitions only when needed. ## Use Skills with live data or files Cowork can combine live MCP data with files you mount into the workspace. Useful patterns: | Pattern | How to ask | | --- | --- | | Live data first | "Use the Amazon Ads MCP to pull the latest Sponsored Products search-term report, then run search-term-gold-miner." | | CSV fallback | "Use this Seller Central export and run the campaign-structure-auditor Skill. Do not call live tools." | | File output | "Write the final report as an xlsx and a markdown summary in this folder." | | Controlled write | "Prepare the listing patch, but do not submit it until I approve the exact diff." | ## Example Amazon workflows ### Search-term mining Ask: ```text Pull the last 60 days of Sponsored Products search-term data and find negative candidates, generic gold keywords, branded defense terms, and rising queries. ``` Cowork uses the Amazon Ads MCP to gather the report, then applies the search-term mining Skill to produce an action list. ### Campaign structure audit Ask: ```text Audit my Sponsored Products portfolio for dead campaigns, budget concentration, and auto/manual balance. ``` The Skill checks the available fields, validates thresholds, and returns a portfolio-health report. ### Listing optimization Ask: ```text Review ASIN B0XXXXXXXX. Check title, bullets, images, and A+ Content. Propose a patch, but do not write anything live without approval. ``` Cowork reads listing context through the Selling Partner MCP and uses the listing optimizer Skill to produce a controlled diff. ### Returns analysis Ask: ```text Build a Q1 returns report by ASIN with top return reasons, dispositions, controllable issues, and red-flag products. Save it as a spreadsheet. ``` The returns Skill classifies issues and writes the deliverable to the mounted folder. ### Warehouse SQL through the Agent Data layer Ask: ```text Use the Amazon Agent Data layer to find my Amazon Ads tables, inspect the schema, and run SQL for spend by campaign last month. ``` The Openbridge Skill discovers tables, checks schema rules, and runs validated SQL through the query layer. ### Amazon Marketing Cloud analysis Ask: ```text Write an AMC query for new-to-brand reach across Sponsored Products and DSP last month. Explain which tables the query uses and why. ``` The AMC Skill produces SQL and explains the table choices before you schedule or export the workflow. ## Recurring work After Cowork builds a repeatable workflow, ask it to turn the steps into a recurring report or documented Skill: ```text Turn this search-term mining workflow into a weekly report. Keep write actions approval-gated. Save the runbook and output template in this folder. ``` For durable, data-backed automation, combine the Amazon Ads MCP or Selling Partner MCP with [Skills](/features/skills/) and the [Amazon Agent Data layer](/features/amazon-agent-flow/). ## Security checklist - Connect only servers you trust. - Review OAuth scopes before signing in. - Keep write tools on **Needs approval** by default. - Disable write tools before unattended or research-style runs. - Do not paste API keys, OAuth secrets, or client credentials into chat. - Use mounted folders deliberately; Cowork can only work with folders you grant. - Treat MCP tool output as untrusted input when it comes from unfamiliar servers. ## Troubleshooting | Symptom | Check | | --- | --- | | Connector does not connect | Confirm the remote MCP URL is reachable from Claude's connector path, not just your laptop. | | Authentication fails | Disconnect and reconnect with the correct Amazon or Kuudo account. | | A Skill does not activate | Name the Skill explicitly and make sure the matching MCP server is enabled. | | Tool names do not match the prompt | Ask Cowork to list available tools by server before running the task. | | Live write action appears unexpectedly | Move that tool category to **Needs approval** or **Blocked**. | ## Quick reference | Task | Connect | Skill | | --- | --- | --- | | Search-term mining | Amazon Ads MCP | `search-term-gold-miner`, `amazon-ads-reporting` | | Campaign audit | Amazon Ads MCP | `campaign-structure-auditor` | | Listing fixes | Amazon Selling Partner MCP | `amazon-listing-optimizer` | | Returns analysis | Amazon Selling Partner MCP | `refund-return-rate-monitor` | | Warehouse SQL | Amazon Agent Data layer / Openbridge MCP | `openbridge-mcp` | | AMC analysis | Amazon Ads MCP with AMC access | `amc`, `amc-sql` | ### Quick Start: Codex URL: https://www.kuudo.com/docs/quick-start/codex/ From Codex you get live Amazon data in the repository you are already working in: pull real campaign or catalog data while you build, prototype an Amazon agent against it, and keep the same scoped auth in both the CLI and the IDE extension. Connect Codex to your private MCP server so it can use your tools while working in a local repository. Codex shares MCP configuration between the CLI and IDE extension, so you only need to add the server once. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Prerequisites - Codex CLI installed and signed in. - A connected workspace with access to your private MCP server. - A dashboard API key from the **Keys** tab. - The private MCP host assigned to your deployment. ## Copy prompt Paste this into Codex if you want it to walk you through setup: ```text Walk me through setting up my private MCP server in Codex, step by step. Use the Codex CLI MCP configuration, not a local wrapper or one-off script. Use the private host from my dashboard: https://{your-private-mcp-host}/mcp Use my local MCP_API_KEY environment variable. Do not ask me to paste or share the raw key in chat. If MCP_API_KEY is not set in this shell, tell me to export it from my dashboard first. Add the server with: codex mcp add private-mcp --url https://{your-private-mcp-host}/mcp --bearer-token-env-var MCP_API_KEY Then verify it with: codex mcp list In the Codex TUI, verify the active server with: /mcp ``` ## 1. Set your local key Set the API key in the shell that launches Codex: ```bash export MCP_API_KEY="mcp_live_..." ``` For repeated use, store it in your shell profile or secret manager. Do not commit the raw value to the repository. ## 2. Add the MCP server Add the remote HTTP MCP server: ```bash codex mcp add private-mcp \ --url https://{your-private-mcp-host}/mcp \ --bearer-token-env-var MCP_API_KEY ``` Codex reads the bearer token from `MCP_API_KEY` at runtime. This keeps the raw key out of `~/.codex/config.toml`. ## 3. Verify the server List configured MCP servers: ```bash codex mcp list ``` Inspect the saved entry: ```bash codex mcp get private-mcp ``` In the Codex TUI, use `/mcp` to see active MCP servers for the current session. If the server is configured, Codex can expose its tools in CLI sessions and in the Codex IDE extension. ## Alternative: config file You can also add the server directly in `~/.codex/config.toml`. For trusted projects, you can scope configuration to the repository with `.codex/config.toml`. ```toml [mcp_servers.private-mcp] url = "https://{your-private-mcp-host}/mcp" bearer_token_env_var = "MCP_API_KEY" ``` Restart any running Codex session after editing the config file. ## Optional controls Codex supports extra controls for Streamable HTTP MCP servers. Add them only when you need tighter behavior: ```toml [mcp_servers.private-mcp] url = "https://{your-private-mcp-host}/mcp" bearer_token_env_var = "MCP_API_KEY" enabled_tools = ["search", "fetch"] disabled_tools = ["delete_record"] default_tools_approval_mode = "prompt" tool_timeout_sec = 60 [mcp_servers.private-mcp.tools.fetch] approval_mode = "approve" ``` Use `enabled_tools` for allow lists, `disabled_tools` for deny lists, and `default_tools_approval_mode` to control whether Codex calls tools automatically or prompts first. Supported approval modes are `auto`, `prompt`, and `approve`. If your MCP server uses OAuth instead of bearer-token authentication, add the server first, then run: ```bash codex mcp login private-mcp ``` ## Troubleshooting ### Unauthorized or 401 errors Make sure `MCP_API_KEY` is exported in the shell or environment that launches Codex. The key must belong to the same workspace as the private MCP server. ### Server not listed Run `codex mcp list`. If `private-mcp` is missing, add it again with the CLI command above or check `~/.codex/config.toml`. ### Tools not available in a session Restart Codex after changing MCP config. If you are using the IDE extension, restart the extension or reload the editor window. In the Codex TUI, run `/mcp` to confirm the server is active. ### CLI flags differ from your installed version Run `codex mcp add --help` and `codex mcp --help`. Codex versions can differ, but Streamable HTTP servers should be represented by a URL in `config.toml`, and bearer auth should use `bearer_token_env_var`. ### Slow responses Check your network path to the private host and confirm the cloud deployment is healthy. The hostname is specific to your environment, so connectivity issues are usually tied to your cloud provider, DNS, firewall, or deployment status. ## Start using tools Try read-only prompts first: - "What MCP tools are available from `private-mcp`?" - "List the read-only tools before calling any write actions." - "Use `private-mcp` to inspect the current workspace context." For write-capable workflows, ask Codex to explain the proposed action and wait for approval before it calls any mutating tool. ## Add reusable workflows After Codex can reach your MCP server, install Codex skills for repeatable workflows and task-specific instructions. See the [Codex Skills quick start](/docs/quick-start/codex-skills/). ### Quick Start: OpenClaw MCP URL: https://www.kuudo.com/docs/quick-start/openclaw/ OpenClaw runs long Amazon jobs end to end. Give it a goal — pull the week's Sponsored Products performance, find the ASINs losing the Buy Box, draft the listing fixes — and it plans, calls the Amazon tools in order, and hands back the result with every tool call traced. Use OpenClaw's MCP client registry when you want an OpenClaw-managed runtime to know about your private MCP server. For the official reference, see [OpenClaw `mcp`](https://docs.openclaw.ai/cli/mcp). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - Install and configure OpenClaw. - Create an API key from the dashboard **Keys** tab. - Keep the key in `MCP_API_KEY` or another local secret source. - Confirm the private MCP endpoint ends with `/mcp`. ```text https://{your-private-mcp-host}/mcp ``` ## Understand OpenClaw MCP modes OpenClaw's MCP command has two different modes: | Command shape | What it does | | --- | --- | | `openclaw mcp serve` | Runs OpenClaw itself as a stdio MCP server for another MCP client. | | `openclaw mcp set/list/show/unset` | Manages outbound MCP server definitions saved under `mcp.servers`. | Use `openclaw mcp set` to register your existing private MCP server. This saves configuration only; it does not open a live MCP session or validate that the remote server is reachable. ## Register the server Set your key in the environment OpenClaw or its runtime adapter will use: ```bash export MCP_API_KEY="mcp_live_..." ``` Add the remote MCP server definition: ```bash openclaw mcp set private-mcp '{"transport":"streamable-http","url":"https://{your-private-mcp-host}/mcp","headers":{"Authorization":"Bearer ${MCP_API_KEY}"}}' ``` Keep the single quotes around the JSON so OpenClaw stores the literal environment-variable reference. ## Inspect the saved definition List saved MCP servers: ```bash openclaw mcp list ``` Show the saved server: ```bash openclaw mcp show private-mcp --json ``` If the entry appears, OpenClaw has saved the registry definition. A runtime adapter still has to load that definition and open a connection before tools are available. ## Manual config form OpenClaw stores MCP definitions under `mcp.servers`. The manual shape is: ```json { "mcp": { "servers": { "private-mcp": { "transport": "streamable-http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } } } } } ``` OpenClaw supports stdio, SSE/HTTP, and streamable HTTP definitions. For a modern remote MCP endpoint, set `"transport": "streamable-http"`. If `transport` is omitted, OpenClaw treats a URL-based entry as SSE/HTTP. ## Troubleshooting ### The server is saved but tools do not appear `openclaw mcp set` only writes configuration. Restart or reload the OpenClaw runtime adapter that consumes MCP registry entries, then check whether it opens the saved server. ### Authentication fails Confirm `MCP_API_KEY` is set in the environment that launches the runtime adapter, not only in a separate terminal. If you changed the variable while OpenClaw was already running, restart the process. ### The endpoint does not connect Confirm the endpoint uses your private host and the normal bearer-auth MCP path: ```text https://{your-private-mcp-host}/mcp ``` Do not use a Claude-only signed connector URL such as `/mcp/connect/{signed-token}` for OpenClaw. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [Claude Code MCP](/docs/quick-start/claude-code-mcp/) - [Codex MCP](/docs/quick-start/codex/) ### Quick Start: Hermes MCP URL: https://www.kuudo.com/docs/quick-start/hermes/ Agent runtimes are where multi-step Amazon work actually finishes. Hand Hermes a goal — audit a catalog and patch the listings that fail validation, build an Amazon Marketing Cloud (AMC) audience and stage it for activation, reconcile a month of Fulfillment by Amazon (FBA) reimbursements — and it plans the work, calls the Amazon tools in order, and returns the artifact. That is the autonomous Amazon operator teams go looking for, running in the agent runtime you chose, against accounts you already own. Use Hermes Agent's MCP client support when you want Hermes to discover and call tools from your private MCP server. For the official reference, see [Hermes Agent MCP](https://hermes-agent.nousresearch.com/docs/user-guide/features/mcp). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - Install Hermes Agent. - Confirm MCP support is installed. Standard installs include it, but the Hermes docs show `uv pip install -e ".[mcp]"` when needed. - Create an API key from the dashboard **Keys** tab. - Store the key in `MCP_API_KEY` or another local secret source. - Confirm the private MCP endpoint ends with `/mcp`. ```text https://{your-private-mcp-host}/mcp ``` ## Understand Hermes MCP modes Hermes supports both directions: | Mode | What it does | | --- | --- | | Hermes as an MCP client | Hermes connects to local stdio servers or remote HTTP MCP servers listed under `mcp_servers`. | | Hermes as an MCP server | `hermes mcp serve` exposes Hermes messaging capabilities to another MCP client over stdio. | Use `mcp_servers` when you want Hermes to connect to your existing private MCP server. ## Add the remote MCP server Store the key in the environment that launches Hermes. You can use `~/.hermes/.env` or your process manager's secret mechanism: ```dotenv MCP_API_KEY=mcp_live_... ``` Add the server under the top-level `mcp_servers` key in `~/.hermes/config.yaml`: ```yaml mcp_servers: private-mcp: url: "https://{your-private-mcp-host}/mcp" headers: Authorization: "Bearer ${MCP_API_KEY}" enabled: true timeout: 120 connect_timeout: 60 ``` Hermes reads remote HTTP MCP servers from `url` and `headers`. It discovers MCP tools at startup and registers them into the normal Hermes tool registry. ## Reload Hermes After saving config, restart Hermes or reload MCP config from a Hermes session: ```text /reload-mcp ``` If `MCP_API_KEY` is unset in the environment that launches Hermes, the placeholder may remain literal and authentication will fail. ## Tool names and filtering Hermes prefixes MCP tools to avoid name collisions: ```text mcp__ ``` For a server named `private-mcp`, a tool named `search` is registered as something like: ```text mcp_private_mcp_search ``` You can limit which tools Hermes exposes from a server: ```yaml mcp_servers: private-mcp: url: "https://{your-private-mcp-host}/mcp" headers: Authorization: "Bearer ${MCP_API_KEY}" tools: include: [search, fetch] resources: false prompts: false ``` Use `include` for a small allowlist, `exclude` to hide dangerous tools, and `resources: false` or `prompts: false` when you do not want Hermes to expose MCP resource or prompt utility wrappers for that server. ## Optional parallel tool calls Hermes runs MCP tools sequentially by default. Only enable parallel execution when the server's tools are safe to run concurrently: ```yaml mcp_servers: private-mcp: url: "https://{your-private-mcp-host}/mcp" headers: Authorization: "Bearer ${MCP_API_KEY}" supports_parallel_tool_calls: true ``` Do not enable this for tools that write shared state, mutate records, or depend on strict call order. ## Troubleshooting ### Tools do not appear Check that Hermes can connect to the server, that discovery succeeds, and that your `tools.include` or `tools.exclude` settings did not filter everything out. If the server is set to `enabled: false`, Hermes skips it entirely. ### Authentication fails Confirm `MCP_API_KEY` is present in the Hermes process environment. Restart Hermes after changing environment variables. ### Resource or prompt helpers are missing Hermes only registers resource and prompt utility wrappers when the MCP server supports those capabilities and your config allows them. ### You want Hermes to be the MCP server That is a different flow. Use: ```bash hermes mcp serve ``` This starts a stdio MCP server that another MCP client manages. It is not the path for connecting Hermes to your private remote MCP server. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [OpenClaw MCP](/docs/quick-start/openclaw/) - [Codex MCP](/docs/quick-start/codex/) ### Quick Start: n8n MCP URL: https://www.kuudo.com/docs/quick-start/n8n/ Workflow builders are where Amazon work gets scheduled instead of asked for. From n8n you can run the same Amazon operations on a cron or an upstream event — the nightly Sponsored Products pull, a listing patch when a feed lands, an Amazon Marketing Cloud (AMC) audience staged for the demand-side platform (DSP) — with the same scoped auth and audit trail as chat. Use n8n's **MCP Client Tool** node when you want an n8n AI Agent workflow to call tools exposed by your private MCP server. For the official reference, see [n8n MCP Client Tool node](https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolmcp/). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - An n8n instance with AI Agent nodes available. - A workflow with an AI Agent or model node that can use tools. - A dashboard API key from the **Keys** tab. - The private MCP endpoint assigned to your deployment. ```text https://{your-private-mcp-host}/mcp ``` ## Understand n8n MCP modes n8n has two different MCP surfaces: | Node | What it does | | --- | --- | | MCP Client Tool | Connects an n8n AI Agent workflow to tools exposed by an external MCP server. | | MCP Server Trigger | Exposes n8n workflow tools to external AI agents. | Use **MCP Client Tool** when connecting n8n to your private MCP server. ## Add the MCP Client Tool node In n8n: 1. Open the workflow that contains your AI Agent. 2. Add a tool node to the agent. 3. Choose **MCP Client Tool**. 4. Connect the MCP Client Tool node to the AI Agent's tool input. The node acts as an MCP client. It discovers tools from the external MCP server and makes the selected tools available to the agent. ## Configure the endpoint In the MCP Client Tool node, set **SSE Endpoint** to the MCP endpoint from your dashboard: ```text https://{your-private-mcp-host}/mcp ``` n8n's field label is **SSE Endpoint**. Use the endpoint format your MCP server supports for n8n. If your dashboard provides a separate n8n or SSE-compatible URL, use that value instead of the generic `/mcp` endpoint. ## Configure authentication The MCP Client Tool node supports bearer, generic header, and OAuth2 authentication. For a bearer token from your dashboard: 1. Set **Authentication** to **Bearer** if available in your n8n version. 2. Paste only the API key value into n8n's credential form. 3. Save the credential. If your n8n version uses generic header authentication instead: | Field | Value | | --- | --- | | Header name | `Authorization` | | Header value | `Bearer ` | Do not paste the raw API key into prompts, sticky notes, or workflow descriptions. Store it in n8n credentials. ## Choose which tools to expose Use **Tools to Include** to control the tool surface the AI Agent can see: | Option | Effect | | --- | --- | | All | Exposes every tool returned by the MCP server. | | Selected | Exposes only the tools you select. | | All Except | Exposes every tool except the tools you exclude. | Start with **Selected** for production workflows. Give the agent only the read or action tools needed for that workflow. ## Test the workflow Run a manual execution and ask the AI Agent for a read-only check first: ```text List the available MCP tools and explain what each one can do. Do not call write actions. ``` If the tools appear, test one low-risk read action. Add human approval or review steps before giving the workflow access to mutating tools. ## Troubleshooting ### The node cannot connect Confirm the endpoint matches the private host in your dashboard and that it is reachable from the n8n runtime. Self-hosted n8n instances may need outbound network access to the private host. ### Authentication fails Create a fresh dashboard API key and update the n8n credential. Make sure the credential sends either bearer auth or an `Authorization: Bearer ...` header, not both. ### Tools do not appear Check that the MCP server exposes tools, the endpoint supports the transport expected by n8n, and **Tools to Include** is not filtering everything out. ### You want n8n to be the MCP server That is the **MCP Server Trigger** node, not the MCP Client Tool node. Use MCP Server Trigger when another agent should call n8n workflows as tools. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [OpenClaw MCP](/docs/quick-start/openclaw/) - [Hermes MCP](/docs/quick-start/hermes/) ### Quick Start: Activepieces MCP URL: https://www.kuudo.com/docs/quick-start/activepieces/ From Activepieces the Amazon work runs on a trigger rather than a prompt: schedule the report pull, patch listings when an upstream event fires, stage an audience once a threshold is crossed. Same scoped auth as every other client, no prompt required. Use the **Run Agent** step's **Agent Tools** area when you want an Activepieces agent to connect to your private MCP server. Activepieces also has built-in MCP server features for exposing Activepieces to MCP clients. That is the opposite direction. For the official server-side reference, see [Activepieces MCP Server](https://www.activepieces.com/docs/mcp/overview). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - Access to an Activepieces workspace where you can edit flows. - A flow with a **Run Agent** step, or permission to add one. - A private MCP server endpoint from your dashboard. - An API key from the dashboard **Keys** tab, if your server requires bearer or header authentication. - Confirmation of which authentication modes your Activepieces workspace exposes after opening the **Authentication Type** dropdown. ```text https://{your-private-mcp-host}/mcp ``` ## Understand the direction Activepieces can appear in MCP workflows in two different ways: | Flow | What it does | | --- | --- | | Run Agent > Agent Tools > Add MCP Server | Activepieces connects an agent step to an external MCP server and can use its tools. | | Activepieces MCP Server | Activepieces exposes its own tools to external MCP clients such as Claude, Cursor, or Windsurf. | Use **Run Agent > Agent Tools** when connecting Activepieces to your private MCP server. ## Open Run Agent In your Activepieces flow, add or open a **Run Agent** step. The observed Run Agent step includes: | Area | What to configure | | --- | --- | | Prompt | Describe what you want the assistant to do. | | AI Model | Choose the provider and model, such as OpenAI and `gpt-5-mini`. | | Agent Tools | Connect apps, flows, MCPs, and other tools the agent can use. | The **Agent Tools** area may show installed app icons, such as YouTube, Slack, GitHub, Notion, and a `+500` indicator for additional integrations. Use **Add** from this area to connect more tools. ## Add an MCP tool From **Run Agent > Agent Tools**, click **Add**, then choose the MCP option. The current MCP form is **Add MCP Server**. The form has four visible fields and two action buttons. **Validate Server** stays disabled until required fields are filled. | Field | Required | Input type | Expected input | | --- | ---: | --- | --- | | MCP Name | Yes | Text input | A short identifier, such as `private-mcp` or `my-mcp-server`. | | Server URL | Yes | Text input | Your private MCP endpoint, such as `https://{your-private-mcp-host}/mcp`. | | Protocol | Yes | Dropdown / combobox | Select **Streamable HTTP** unless your dashboard provides a different Activepieces-specific endpoint. | | Authentication Type | Yes | Dropdown / combobox | Select the auth mode that matches your server. The observed default is **None**. | Recommended generic values: ```text MCP Name: private-mcp Server URL: https://{your-private-mcp-host}/mcp Protocol: Streamable HTTP Authentication Type: Bearer or header-based auth, if available ``` ## Configure authentication The observed form only shows **Authentication Type = None** while the dropdown is closed. It does not confirm which auth modes are available or whether choosing an auth mode reveals header fields. If Activepieces supports bearer authentication, store the API key in the credential or secret field Activepieces provides. If Activepieces supports custom headers, use this header shape: ```text Authorization: Bearer ${MCP_API_KEY} ``` Do not paste a raw live key into labels, descriptions, prompts, or workflow notes. Use Activepieces' credential or secret storage if the form provides one. ## Validate the server After the required fields and authentication details are set, click **Validate Server**. Validation should confirm that Activepieces can reach the MCP endpoint, negotiate the selected protocol, and authenticate if required. After validation succeeds, save or add the MCP server so it appears in the Run Agent step's **Agent Tools** list. ## Troubleshooting ### Validate Server is disabled Fill every required field: **MCP Name**, **Server URL**, **Protocol**, and **Authentication Type**. The button should remain disabled while any required field is empty. ### Authentication Type only shows None Open the dropdown and check for bearer, header, OAuth, or custom authentication options. If none are available, the current Activepieces surface may only support unauthenticated MCP servers in that form. ### The server URL fails Confirm the URL uses the private host from your dashboard and the MCP path: ```text https://{your-private-mcp-host}/mcp ``` If your dashboard provides an Activepieces-specific or SSE-compatible endpoint, use that URL instead. ### Validation succeeds but tools do not appear Check whether Activepieces requires a second step to choose tools, enable the server, or attach the MCP server to the **Run Agent** step. Also confirm the server exposes tools over the selected protocol. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [n8n MCP](/docs/quick-start/n8n/) - [Hermes MCP](/docs/quick-start/hermes/) ### MCP Client Configuration URL: https://www.kuudo.com/docs/mcp-client-configuration/ Use these examples to connect MCP clients and custom agents to your private MCP server. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace every example host below with the private host shown in your dashboard. ```text https://{your-private-mcp-host}/mcp ``` For example, your endpoint might look like a private application hostname from AWS, GCP, Azure, Cloudflare, or your internal DNS. Do not assume any public example hostname is your production host unless your dashboard explicitly shows it. ## Before you start Create an API key from the dashboard **Keys** tab. Header-capable clients authenticate with an `Authorization` bearer header. Prefer injecting `MCP_API_KEY` from your local environment or secret manager instead of pasting keys into chat, screenshots, repositories, or shared config. Claude custom connectors are different: they use the signed Claude connector URL from the same **Keys** tab. | Client | Auth shape | | --- | --- | | Claude custom connector | Signed `/mcp/connect/{token}` URL | | Claude Code | Bearer header from `MCP_API_KEY` | | Codex | `codex mcp add` with bearer token environment variable | | OpenClaw | `mcp.servers` entry with bearer header. See [OpenClaw MCP](/docs/quick-start/openclaw/). | | Hermes Agent | `mcp_servers` entry with bearer header. See [Hermes MCP](/docs/quick-start/hermes/). | | n8n | MCP Client Tool node with bearer, header, or OAuth2 auth. See [n8n MCP](/docs/quick-start/n8n/). | | Activepieces | Run Agent > Agent Tools > Add MCP Server with Streamable HTTP and optional auth. See [Activepieces MCP](/docs/quick-start/activepieces/). | | Cursor | Bearer header from `MCP_API_KEY` | | Custom agent | Remote HTTP MCP endpoint plus bearer header | ## Hostname placeholders Use these placeholders consistently: | Placeholder | Meaning | | --- | --- | | `{your-private-mcp-host}` | The private MCP hostname assigned to your deployment by your cloud provider. | | `{signed-token}` | The dashboard-generated token embedded in the Claude connector URL. | | `MCP_API_KEY` | Your local environment variable or secret-manager value for bearer auth. | Use the normal `/mcp` endpoint for bearer-auth clients. Reserve `/mcp/connect/{signed-token}` for Claude custom connectors. ## Claude custom connector Copy the Claude connector URL from the dashboard, then add it in Claude through **Customize > Connectors > Add custom connector**. Name the connector something clear, such as `private-mcp`. ```text https://{your-private-mcp-host}/mcp/connect/{signed-token} ``` Use the signed connector URL for this flow. The Claude custom connector UI does not accept a separate `Authorization` header. ## Claude Code Set your key once, then add the remote HTTP MCP server from any Claude Code session. ```bash export MCP_API_KEY="mcp_live_..." claude mcp add --transport http private-mcp https://{your-private-mcp-host}/mcp \ --header 'Authorization: Bearer ${MCP_API_KEY}' ``` Add `--scope project` to share the server through the repository's `.mcp.json`, or `--scope user` for your local Claude Code profile. JSON form: ```bash claude mcp add-json private-mcp '{ "type": "http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } }' ``` ## Codex Set your key once, then add the remote HTTP MCP server with the Codex CLI. Codex shares MCP configuration between the CLI and IDE extension. ```bash export MCP_API_KEY="mcp_live_..." codex mcp add private-mcp \ --url https://{your-private-mcp-host}/mcp \ --bearer-token-env-var MCP_API_KEY ``` Config file form: ```toml [mcp_servers.private-mcp] url = "https://{your-private-mcp-host}/mcp" bearer_token_env_var = "MCP_API_KEY" ``` Verify with `codex mcp list`. In the Codex TUI, run `/mcp` to see active servers for the current session. For trusted projects, you can scope configuration to the repository with `.codex/config.toml`. Optional controls: ```toml [mcp_servers.private-mcp] url = "https://{your-private-mcp-host}/mcp" bearer_token_env_var = "MCP_API_KEY" enabled_tools = ["search", "fetch"] disabled_tools = ["delete_record"] default_tools_approval_mode = "prompt" tool_timeout_sec = 60 ``` If your server uses OAuth instead of bearer-token authentication, add the server first, then run `codex mcp login private-mcp`. ## OpenClaw For a dedicated walkthrough, see [Quick Start: OpenClaw MCP](/docs/quick-start/openclaw/). OpenClaw's MCP command has two different modes: - `openclaw mcp serve` runs OpenClaw itself as a stdio MCP server for another client. - `openclaw mcp set`, `list`, `show`, and `unset` manage OpenClaw-owned outbound MCP server definitions under `mcp.servers`. Use the `set` path when you want an OpenClaw-managed runtime to know about your private MCP server. This saves the server definition in OpenClaw config; it does not start a live MCP session or prove the remote server is reachable. Set your key in the environment OpenClaw uses, then register the private server with OpenClaw's `mcp.servers` config shape. ```bash export MCP_API_KEY="mcp_live_..." openclaw mcp set private-mcp '{"transport":"streamable-http","url":"https://{your-private-mcp-host}/mcp","headers":{"Authorization":"Bearer ${MCP_API_KEY}"}}' ``` Keep the single quotes around the JSON so OpenClaw stores the literal environment-variable reference. Inspect the saved definition: ```bash openclaw mcp list openclaw mcp show private-mcp --json ``` OpenClaw supports stdio, SSE/HTTP, and streamable HTTP definitions. For a modern remote MCP endpoint, set `"transport": "streamable-http"`. If `transport` is omitted, OpenClaw treats a URL-based entry as SSE/HTTP. If you set `MCP_API_KEY` after OpenClaw or a runtime adapter is already running, restart that process before expecting it to read the new environment variable. Runtime adapters decide which saved MCP definitions they consume and when they open a connection. Manual config form: ```json { "mcp": { "servers": { "private-mcp": { "transport": "streamable-http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } } } } } ``` For the official OpenClaw reference, see [OpenClaw `mcp`](https://docs.openclaw.ai/cli/mcp). ## Hermes Agent For a dedicated walkthrough, see [Quick Start: Hermes MCP](/docs/quick-start/hermes/). Store your key in `~/.hermes/.env` or the environment that launches Hermes, then add the private server under the top-level `mcp_servers` key in `~/.hermes/config.yaml`. ```dotenv MCP_API_KEY=mcp_live_... ``` ```yaml mcp_servers: private-mcp: url: "https://{your-private-mcp-host}/mcp" headers: Authorization: "Bearer ${MCP_API_KEY}" enabled: true timeout: 120 connect_timeout: 60 ``` Hermes expands `${MCP_API_KEY}` from its local environment. If the variable is unset, the placeholder remains literal and authentication fails. Restart Hermes or run `/reload-mcp` after saving config changes. Hermes discovers tools at startup or reload time and prefixes them as `mcp__`. ## n8n For a dedicated walkthrough, see [Quick Start: n8n MCP](/docs/quick-start/n8n/). Use n8n's **MCP Client Tool** node when an n8n AI Agent workflow should call tools from your private MCP server. n8n also has an **MCP Server Trigger** node, but that is the opposite direction: it exposes n8n workflows to external agents. In the MCP Client Tool node: | n8n field | Value | | --- | --- | | SSE Endpoint | `https://{your-private-mcp-host}/mcp` | | Authentication | Bearer, generic header, or OAuth2 | | Tools to Include | `Selected` for narrow production workflows, or `All` while testing | For generic header auth, use: | Header | Value | | --- | --- | | `Authorization` | `Bearer ` | Store the key in n8n credentials. Do not paste raw keys into workflow descriptions, sticky notes, or prompts. ## Activepieces For a dedicated walkthrough, see [Quick Start: Activepieces MCP](/docs/quick-start/activepieces/). Use the **Run Agent** step's **Agent Tools** area when an Activepieces agent should connect to your private MCP server. Click **Add** under Agent Tools, then choose the MCP option. This is separate from Activepieces' own MCP server feature, which exposes Activepieces tools to external MCP clients. Current visible Add MCP Server fields: | Field | Value | | --- | --- | | MCP Name | `private-mcp` | | Server URL | `https://{your-private-mcp-host}/mcp` | | Protocol | `Streamable HTTP` | | Authentication Type | Bearer or header-based auth if available; otherwise the observed default is `None` | If the form exposes custom headers, use: ```text Authorization: Bearer ${MCP_API_KEY} ``` Use Activepieces credential or secret storage for the key if the form provides it. Do not paste raw live keys into labels, descriptions, prompts, or workflow notes. ## Cursor For repository scope, save this as `.cursor/mcp.json`. For user scope, save it as `~/.cursor/mcp.json`. ```json { "mcpServers": { "private-mcp": { "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" } } } } ``` Restart Cursor or reload the MCP server list after saving the file. ## Header-capable clients Most MCP clients use an `mcpServers` object with a remote URL and request headers. ```json { "mcpServers": { "private-mcp": { "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${env:MCP_API_KEY}" } } } } ``` Check your client documentation for its exact environment-variable interpolation syntax. Some clients use `${MCP_API_KEY}`; others use `${env:MCP_API_KEY}`. ## Custom agent If your agent owns its MCP client layer, store the endpoint and header in your agent configuration and inject the API key from a secret manager or environment variable. ```json { "name": "private-mcp", "transport": "http", "url": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer ${MCP_API_KEY}" }, "capabilities": { "tools": true, "resources": true } } ``` Use `https://{your-private-mcp-host}/mcp` for normal bearer auth. Reserve `https://{your-private-mcp-host}/mcp/connect/{signed-token}` for Claude custom connectors. ## Verify the connection After adding the server, restart or reload the MCP client and ask it to list the available tools. If the server does not appear, check three things first: 1. The hostname matches the private host in your dashboard. 2. `MCP_API_KEY` is set in the environment that launches the client. 3. The client is using `/mcp` for bearer auth or `/mcp/connect/{signed-token}` for Claude custom connectors, not both. ### Quick Start: NanoClaw MCP URL: https://www.kuudo.com/docs/quick-start/nanoclaw/ NanoClaw puts that agent in a messaging app. Ask from WhatsApp, Telegram, Slack, or Discord and it runs the Amazon work on hardware you control — inventory checks, campaign pulls, listing fixes — and answers in the thread. Use NanoClaw when you want a personal agent that answers from a messaging app rather than a terminal, and you want it running on hardware you control. NanoClaw is open source (MIT), runs each agent inside a container, and is built on Anthropic's Agents SDK. For the official reference, see [NanoClaw](https://nanoclaw.dev/), the [skills index](https://nanoclaw.dev/skills), and the [repository](https://github.com/nanocoai/nanoclaw). Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - Install Node.js 20+, pnpm 10+, Docker, and Claude Code. - Use macOS, Linux, or Windows with WSL2. - Create an API key from the dashboard **Keys** tab. - Keep the key in `MCP_API_KEY` or another local secret source. - Confirm the private MCP endpoint ends with `/mcp`. ```text https://{your-private-mcp-host}/mcp ``` ## Install NanoClaw ![Terminal showing the three NanoClaw install commands: git clone into nanoclaw-v2, cd into it, then bash nanoclaw.sh, followed by prerequisite checks and container build output.](/assets/images/docs/nanoclaw-install.svg) ```bash git clone https://github.com/nanocoai/nanoclaw.git nanoclaw-v2 cd nanoclaw-v2 bash nanoclaw.sh ``` The installer resolves missing dependencies, registers credentials, builds the container, and walks you through connecting a first messaging channel. Channels are added on demand with their own skills, such as `/add-telegram` or `/add-slack`. ## Know which config you are editing NanoClaw has two separate MCP surfaces, and they are easy to confuse. | Surface | Who reads it | Shape | | --- | --- | --- | | `.mcp.json` in the repo root | Claude Code, while you work in the repo | Standard `mcpServers` client config | | `container/agent-runner` | The running agent, inside its container | Registered stdio servers plus forwarded env vars | Editing the repo root file gives *you* the tools while customizing. It does not give them to the agent answering in your messaging app. That agent loads servers registered in the container runner, whose config takes `command`, `args`, and `env` — a stdio contract, with no field for a remote URL or headers. So a remote HTTPS endpoint reaches the agent through a stdio bridge. That is what the fast path below sets up for you, and what the manual form spells out. ## Fast path: the customize skill Run Claude Code from the cloned repository and start the interactive skill: ```bash cd nanoclaw-v2 claude ``` ```text /customize ``` Ask for a remote MCP integration and give it three things: - the endpoint, `https://{your-private-mcp-host}/mcp` - the auth header, `Authorization: Bearer ${MCP_API_KEY}` - which agents should get the tools The skill writes the bridge into the agent-runner tree, registers the server, and forwards the key into the container. It is an interactive skill, so the prompts adapt to what you ask for rather than following a fixed menu. ## Manual form Use this when you want to script the setup, review the diff before it lands, or debug a fast-path run that did not take. Register the server with a stdio bridge. `mcp-remote` speaks stdio to the agent and HTTP to your endpoint: ```json { "mcpServers": { "kuudo": { "command": "npx", "args": [ "-y", "mcp-remote", "https://{your-private-mcp-host}/mcp", "--header", "Authorization: Bearer ${MCP_API_KEY}" ], "env": { "MCP_API_KEY": "mcp_live_..." } } } } ``` Two details decide whether this works: - **The key has to reach the container.** Setting `MCP_API_KEY` in your shell is not enough. The host process forwards named variables into the container, so the variable must be forwarded there as well as set locally. - **The tool allow-pattern follows the server name.** Registering the server as `kuudo` exposes its tools as `mcp__kuudo__*`. Rename the server and the pattern changes with it. ## Verify Ask the agent something only your account can answer, from whichever messaging app you connected: ```text List my Amazon advertising campaigns and show the three with the highest spend last week. ``` A grounded answer means the bridge, the key, and the endpoint are all correct. A generic answer, or a refusal that mentions missing tools, means the server registered but its tools never loaded. ## Troubleshooting ### The agent replies but has no tools The server was registered in the repo root rather than the container runner, or the container was not rebuilt after the change. Rebuild, then confirm the agent lists tools under `mcp__kuudo__*`. ### Authentication fails inside the container `MCP_API_KEY` is set on the host but not forwarded. Confirm the variable is in the forwarding list, not only in the shell that launched the installer, then restart the container. ### The endpoint does not connect Confirm the endpoint uses your private host and the normal bearer-auth path: ```text https://{your-private-mcp-host}/mcp ``` Do not use a Claude-only signed connector URL such as `/mcp/connect/{signed-token}` here. The bridge sends a standard `Authorization` header. ### Tools work in Claude Code but not from the messaging app That is the two-surfaces problem. Claude Code is reading the repo root config; the messaging agent is reading the container runner. Register the server in the runner. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [OpenClaw MCP](/docs/quick-start/openclaw/) - [Claude Code MCP](/docs/quick-start/claude-code-mcp/) ### Quick Start: OpenAI API URL: https://www.kuudo.com/docs/quick-start/openai-api/ This is the path for building your own product on top of Amazon data. Your application calls the Responses API, the model calls Kuudo's Amazon tools, and you keep scoped auth, tool allow-lists, and approval behavior without writing an integration per Amazon API. Connect the OpenAI Responses API to your private MCP server when you are building a custom agent, workflow, or application that should call MCP tools programmatically. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Prerequisites - A connected workspace with access to your private MCP server. - A dashboard API key from the **Keys** tab. - An `OPENAI_API_KEY` for the OpenAI project that will call the Responses API. - A remote MCP server that supports Streamable HTTP or HTTP/SSE. ## Add your MCP server as a Responses API tool Pass your remote MCP server in the `tools` array with `type: "mcp"` and `server_url`. ```bash export OPENAI_API_KEY="sk-proj_..." export MCP_API_KEY="mcp_live_..." curl https://api.openai.com/v1/responses \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${OPENAI_API_KEY}" \ -d '{ "model": "gpt-5.5", "tools": [ { "type": "mcp", "server_label": "private_mcp", "server_description": "Private workspace tools and data exposed through MCP.", "server_url": "https://{your-private-mcp-host}/mcp", "authorization": "'"${MCP_API_KEY}"'", "require_approval": "never" } ], "input": "List the tools available from my private MCP server." }' ``` The OpenAI request uses `OPENAI_API_KEY`. The MCP server uses `MCP_API_KEY` through the MCP tool's `authorization` field. Do not put your private MCP key in the top-level OpenAI `Authorization` header. ## Tool discovery When the request runs, the Responses API lists tools from your MCP server and may return an `mcp_list_tools` output item. If the model calls a tool, the response may also include an `mcp_call` item with the tool name, arguments, output, and any MCP error. For long-running conversations, keep the `mcp_list_tools` item in the conversation context when possible so OpenAI does not have to fetch the same tool definitions on every turn. ## Limit the available tools If your MCP server exposes many tools, restrict the model to the tools this workflow needs: ```json { "type": "mcp", "server_label": "private_mcp", "server_url": "https://{your-private-mcp-host}/mcp", "authorization": "${MCP_API_KEY}", "allowed_tools": ["search", "fetch"], "require_approval": "never" } ``` Use narrow tool sets for lower latency, lower token usage, and clearer tool selection. ## Approval mode The `require_approval` setting controls whether tool calls require your application to approve them before data is sent to the MCP server. - `"never"`: The model can call allowed tools without an approval round trip. - `"always"`: The model emits an approval request before each MCP tool call. - Object form: Use OpenAI's approval controls to require approval only for selected tools. Use approval for write-capable or sensitive tools. Only use `"never"` for servers and tool scopes you trust for that workflow. ## Troubleshooting ### Unauthorized or 401 errors Make sure `OPENAI_API_KEY` is used only for the OpenAI request and `MCP_API_KEY` is passed through the MCP tool's `authorization` field. The MCP key must belong to the same workspace as the private MCP server. ### Tools not discovered Verify that `server_url` points to the reachable remote MCP endpoint and ends with `/mcp` for this deployment. The server must support Streamable HTTP or HTTP/SSE. ### Approval requests appear unexpectedly OpenAI requires approval by default for remote MCP data sharing. Set `require_approval` deliberately for each workflow and handle any `mcp_approval_request` output items in your application. ### Slow responses Filter with `allowed_tools`, keep `mcp_list_tools` in conversation context, and check network latency between OpenAI and your private host. ## When to use this page Use this API setup when you own the application code that calls OpenAI. If you want ChatGPT itself to connect to the MCP server, use the [ChatGPT quick start](/docs/quick-start/chatgpt/). If you want OpenAI Codex to use the MCP server while working in a repository, use the [Codex quick start](/docs/quick-start/codex/). ### Quick Start: Perplexity MCP URL: https://www.kuudo.com/docs/quick-start/perplexity/ Ask in Perplexity's own interface and have it answered from your live Amazon Ads, Seller Central, and Vendor Central data — campaign performance, inventory health, listing status — rather than from the public web. Use Perplexity when you want to ask a question in Perplexity's own interface and have it answer from your Amazon data rather than the public web. Perplexity offers two connector types, and they are at different stages. Its own documentation states that local connectors are available now on macOS, and that remote connectors are rolling out to paid subscribers first. Check the [local and remote overview](https://www.perplexity.ai/help-center/en/articles/11502712-local-and-remote-mcps-for-perplexity) for current availability before you plan a rollout. | Path | Status | Use it when | | --- | --- | --- | | Local connector on macOS | Available now | You want a working connection today, on a Mac | | Custom remote connector | Rolling out | Your organization needs a shared connector with no per-machine setup | Both reach the same private endpoint. The local path runs a small bridge on your machine; the remote path has Perplexity call your endpoint directly. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. ## Before you start - Use a paid Perplexity plan. The free tier cannot add connectors. - Create an API key from the dashboard **Keys** tab. - Keep the key in `MCP_API_KEY` or another local secret source. - Confirm the private MCP endpoint ends with `/mcp`. ```text https://{your-private-mcp-host}/mcp ``` ## Local connector on macOS Perplexity's local connectors run a command on your machine. Your MCP server is remote, so the command runs `mcp-remote`, a standard bridge that speaks stdio to Perplexity and HTTPS to your endpoint. You need the Mac App Store build of Perplexity, and Node.js so that `npx` is available: ```bash brew install node ``` ### 1. Install the helper Open **Account settings → Connectors**. Perplexity prompts you to install **PerplexityXPC**, the helper that lets it talk to local servers. Install it before adding a connector. ### 2. Add the connector Back in **Connectors**, click **Add Connector** and use the **Simple** tab: | Field | Value | | --- | --- | | **Server Name** | Something clear, such as `Kuudo` | | **Command** | The bridge command below | ```bash npx -y mcp-remote https://{your-private-mcp-host}/mcp --header "Authorization: Bearer ${MCP_API_KEY}" ``` Click **Save** and wait for the connector to report **Running** in the list. A connector that never reaches Running has not started, and no amount of asking will reach it. ### 3. Enable and test On the Perplexity homepage, toggle the connector on under **Sources**, then ask something only your account can answer: ```text List my Amazon advertising campaigns and show the three with the highest spend last week. ``` The first tool call prompts you for confirmation. ## Custom remote connector Use this once remote connectors are available on your plan. Perplexity calls your endpoint directly, so there is nothing to install per machine. Open the settings page for the scope you want: | Scope | Where | | --- | --- | | Your account only | **Account settings → Connectors** | | The whole organization (admins) | **Enterprise settings → Permissions → Connectors permissions** | For an organization connector, an admin must first turn on **Allow members to add custom connectors**. It is off by default. Click **+ Custom connector** in the top-right, choose **Remote**, and fill in the form: | Field | Value | | --- | --- | | **Name** | Something clear, such as `Kuudo` | | **MCP Server URL** | `https://{your-private-mcp-host}/mcp` — HTTPS is required | | **Description** | Optional. What the connector reaches, for others in the organization | | **Authentication** | See below | | **Transport** | **Streamable HTTP** | | **Icon** | Optional, 128 KB maximum | Check the acknowledgement box and click **Add**, then click the connector card to run the authentication flow and enable it. The ellipsis (**⋮**) on the card edits or removes it later. Perplexity runs a verification probe when you save. If the connector saves without an error tag, the endpoint answered and the auth path worked end to end. ### Authentication The remote form offers three application-layer methods and no free-form header field, so pick the one your deployment is configured for. | Method | When to use it | | --- | --- | | **API Key** | A static key supplied at setup. Use the key from your dashboard **Keys** tab. | | **OAuth 2.0** | Deployments configured for OAuth. Perplexity discovers endpoints and scopes automatically when the server publishes `/.well-known/oauth-authorization-server`; otherwise supply a Client ID and Client Secret. | | **None** | Only when the endpoint carries its own credential, such as a pre-signed URL. | If you register an OAuth application, the redirect URL is fixed: ```text https://www.perplexity.ai/rest/connections/oauth_callback ``` Organizations on the Enterprise subdomain register this instead: ```text https://enterprise.perplexity.ai/rest/connections/oauth_callback ``` For an organization-scoped OAuth connector, an admin can authenticate once for everyone, or require each member to authenticate individually. ### If your endpoint sits behind Cloudflare Access Because your deployment runs in your own cloud, the MCP hostname is often fronted by a zero-trust edge. Perplexity authenticates to that edge before any application-layer auth runs, so the two stack rather than compete. On the **+ Custom connector** form, set **Network access** to **Cloudflare Access** and supply both values. The key names are exact: ```text CF-Access-Client-Id CF-Access-Client-Secret ``` Perplexity injects these on every request, including the verification probe, so a bad token fails when you save rather than silently later. Values are stored encrypted and redacted in the interface. On the Cloudflare side, do this once in the Zero Trust dashboard: 1. **Create a service token.** Go to **Access → Service Auth → Service Tokens**. Copy the Client ID and Client Secret immediately — the secret is shown only once. 2. **Create an Access application** of type **Self-hosted**, pointed at the public hostname Perplexity will call. 3. **Add a policy with Action set to `Service Auth`.** Include the service token from step 1. Setting the action to **Allow** instead of **Service Auth** is the usual mistake. Allow expects an interactive browser login, which a machine client cannot satisfy, so the verification probe fails. ## Troubleshooting ### The local connector never reaches Running Confirm Node.js is installed and `npx` resolves in the shell Perplexity inherits. Confirm `MCP_API_KEY` is set for that same environment — a variable exported in one terminal is not visible to an application launched from the Dock. ### Verification or tool calls return 403 Work through these in order: - **Incomplete or expired service token.** Re-paste both values in full; the secret is long and partial pastes are easy to miss. Service tokens expire, one year by default. - **Wrong policy action.** The Access policy must use **Service Auth**, not **Allow** or **Bypass**. - **Propagation delay.** New Access applications, policies, and tokens take a few minutes to reach Cloudflare's edge. Wait, then retry before assuming a misconfiguration. - **A challenge is blocking the request.** Perplexity connects from datacenter address ranges. If your zone challenges automated traffic, the endpoint receives a managed challenge no machine client can solve, which surfaces as a 403. Add a firewall skip or bot-management exception for the MCP hostname. Access still gates the endpoint through the service token. If all four check out and it still fails, the problem is in the application layer rather than the edge. ### The connector saved but answers from the web It is not enabled for that thread. Toggle it on under **Sources** before asking. ### Other members cannot see an organization connector Sharing is a separate step. The creator has to share it from the **Permissions** screen in **Enterprise settings**, and newly shared connectors do not always appear immediately. ### The endpoint does not connect Confirm the URL uses your private host, ends with `/mcp`, and is HTTPS. Perplexity rejects plain HTTP. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [ChatGPT MCP](/docs/quick-start/chatgpt/) - [NanoClaw MCP](/docs/quick-start/nanoclaw/) ### Quick Start: Google Antigravity URL: https://www.kuudo.com/docs/quick-start/antigravity/ From Antigravity you get live Amazon data in the agentic editor you are already building in — query Ads, Seller Central, and Vendor Central while you work, prototype an Amazon agent against real campaigns, and hand it multi-step jobs without leaving the IDE. Antigravity reads MCP servers from a JSON config file, and its remote-server field is named `serverUrl` rather than the `url` most clients use. That one difference is the most common reason a working config from another client fails here. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. For the official reference, see [MCP in the Antigravity docs](https://antigravity.google/docs/mcp). ## Prerequisites - Antigravity installed, in either the IDE or CLI form. - A connected workspace with access to your private MCP server. - A dashboard API key from the **Keys** tab. - The private MCP host assigned to your deployment. ## 1. Set your local key Set the API key in the shell that launches Antigravity: ```bash export MCP_API_KEY="mcp_live_..." ``` Store it in your shell profile or secret manager for repeated use. Do not commit the raw value — the workspace config file below is checked in with the repository. ## 2. Add the MCP server Antigravity reads two config locations: | Scope | Path | Use when | | --- | --- | --- | | Global | `~/.gemini/config/mcp_config.json` | The server should be available in every project. | | Workspace | `.agents/mcp_config.json` | Only this repository should see the server. Checked into the repo. | Add the server under the top-level `mcpServers` key: ```json { "mcpServers": { "private-mcp": { "serverUrl": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer YOUR_API_TOKEN" } } } } ``` Use `serverUrl` for remote Streamable HTTP, SSE, and websocket connections. A remote entry that uses `command` and `args` — the stdio shape — will not connect. Prefer the global config when the header carries a live key, so the token never lands in a committed workspace file. ## 3. Verify the server Open the MCP manager for the surface you are using: | Surface | Where | | --- | --- | | Antigravity CLI | Type `/mcp` in the prompt panel for the Interactive MCP Manager. | | Antigravity IDE | Agent side panel **…** > **MCP Servers** > **Manage MCP Servers**. | | Antigravity 2.0 | **Settings** > **Customizations** > **Installed MCP Servers**. | `private-mcp` should be listed with its tools discovered. Ask for a read-only call first: ```text What MCP tools are available from private-mcp? List the read-only ones before calling anything. ``` ## Alternative: OAuth and Google credentials If your deployment fronts the MCP server with OAuth, Antigravity handles dynamic client registration automatically, or accepts a manual `clientId` and `clientSecret` under an `oauth` key. Setting `authProviderType` to `google_credentials` uses your local application-default credentials instead, configured with `gcloud auth application-default login`. Bearer-header auth above is the path for a Kuudo dashboard API key. ## Optional controls MCP tools run in **Ask** mode by default, so Antigravity prompts before calling one. Permissions are expressed as patterns: ```text mcp(private-mcp/list_campaigns) a single tool mcp(private-mcp/*) every tool on this server mcp(*) every MCP tool ``` Grant read tools broadly and keep write tools on Ask until you have watched the agent work. Two further fields help: ```json { "mcpServers": { "private-mcp": { "serverUrl": "https://{your-private-mcp-host}/mcp", "headers": { "Authorization": "Bearer YOUR_API_TOKEN" }, "disabledTools": ["delete_record"], "disabled": false } } } ``` Use `disabledTools` to withhold specific tools from the agent, and `disabled` to switch the whole server off without deleting the entry. ## Troubleshooting ### The server never connects Check that the entry uses `serverUrl` and not `url` or `command`. A config copied from Claude Code or Codex will use a different field name and silently fail to register a remote server. ### Unauthorized or 401 errors Confirm the `Authorization` header reads `Bearer ` followed by a dashboard key that belongs to the same workspace as the MCP server. Rotated keys invalidate the old value. ### Tools are listed but never called MCP tools default to Ask mode. If the agent is running unattended, grant the tools it needs with an `mcp(private-mcp/*)` permission, or approve each prompt as it appears. ### Some tools are missing Check `disabledTools` on the server entry, and confirm the server itself is not `disabled`. ### Config changes are not picked up Restart Antigravity after editing the JSON. The IDE and CLI read the file at startup. ## Start using tools Read-only prompts first: - "Using `private-mcp`, list my Sponsored Products campaigns from the last 7 days." - "Pull the current Buy Box status for these ASINs and show which ones I am losing." - "Summarize yesterday's orders by marketplace." For write-capable work, ask Antigravity to explain the change it intends before it calls a mutating tool. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [Claude Code MCP](/docs/quick-start/claude-code-mcp/) - [Codex MCP](/docs/quick-start/codex/) ### Quick Start: Lovable URL: https://www.kuudo.com/docs/quick-start/lovable/ From Lovable you can build against live Amazon data while you are still designing the app — pull real campaign, order, and catalog data into the chat as you iterate, so the interface you ship is shaped by what your Seller Central and Ads accounts actually return rather than by placeholder JSON. One thing to know before you start: a custom MCP server in Lovable is a **chat connector**. It is personal to your account and is never part of your published app. It shapes what you build; it is not a runtime dependency your users inherit. If the app itself needs Amazon data at runtime, that is a server-side integration you build, not this connector. Your MCP server hostname is private to your deployment. It comes from your cloud provider, belongs to your environment, and is not shared across customers. Replace `{your-private-mcp-host}` with the private host shown in your dashboard. For the official reference, see [Custom MCP in the Lovable docs](https://docs.lovable.dev/integrations/custom-mcp). ## Prerequisites - A Lovable account. Custom MCP servers are available on all plans. - A dashboard API key from the **Keys** tab, unless your deployment fronts the server with OAuth. - The private MCP endpoint assigned to your deployment. ```text https://{your-private-mcp-host}/mcp ``` ## 1. Open the custom MCP form Open the **Connectors** dashboard. Scroll to the bottom of the **All** view and choose the **Custom** card labelled **MCP** — "Connect your own MCP". ## 2. Add the server The form takes two fields: | Field | Value | | --- | --- | | Server name | A descriptive identifier you will name in chat, such as `Kuudo Amazon`. | | Server URL | Your private MCP endpoint, `https://{your-private-mcp-host}/mcp`. | Then pick how Lovable authenticates: | Method | Use when | | --- | --- | | **OAuth** (default) | Your deployment fronts the MCP server with OAuth. Click **Add & authorize** and complete the flow. | | **Bearer token or API key** | The usual path for a Kuudo dashboard key. | | **No authentication** | Only for a server that requires no credentials. | Pick a server name you will actually type. You name the connector in the prompt, so `Kuudo Amazon` reads better mid-sentence than `mcp-prod-1`. ## 3. Verify the connection Ask for something only the connector can answer, naming it directly: ```text Using the Kuudo Amazon connector, list my five most recent orders. ``` If the connector is live, the answer comes back from your account rather than from a guess. Start with reads before letting it write anything. ## Sharing and governance **Chat connections are per-user.** A colleague opening the same project sees the connector suggested but has to connect their own. There is no shared workspace credential, which is usually what you want for Amazon access — each person's calls run under their own key. Workspace admins can turn the whole capability off under **Connectors → Admin settings → Chat connectors**. If the Custom MCP card is missing entirely, check there first. ## Troubleshooting ### The connector works in chat but not in the published app That is the documented behaviour, not a bug. Chat connectors are personal and never ship with a published app. Build the runtime integration server-side if your users need Amazon data. ### A teammate cannot see the data Connections are per-user. Have them add the connector under their own account with their own key. ### The Custom MCP card is not there A workspace admin has disabled chat connectors, or you are looking above the bottom of the **All** view — the Custom card sits at the end of the list. ### Unauthorized errors Confirm the key belongs to the same workspace as the MCP server, and that you chose **Bearer token or API key** rather than leaving the form on OAuth. ## Start using tools Read-only prompts first: - "Using the Kuudo Amazon connector, show my top 10 ASINs by units last week." - "Pull current inventory levels so I can shape the low-stock view." - "What campaign metrics are available? I want to design a dashboard around them." Building a view against the real shape of your data is the point — it saves the round trip where the mock schema and the live response disagree. ## Related docs - [MCP Client Configuration](/docs/mcp-client-configuration/) - [Google Antigravity](/docs/quick-start/antigravity/) - [Cursor and other IDEs](/docs/quick-start/claude-code-mcp/) ### Quick Start: ChatGPT Skills URL: https://www.kuudo.com/docs/quick-start/chatgpt-skills/ Use ChatGPT skills when you want ChatGPT to follow a reusable workflow, apply task-specific instructions, or use bundled examples and resources for a repeatable job. For the official reference, see [Skills in ChatGPT in the OpenAI Help Center](https://help.openai.com/en/articles/20001066-skills-in-chatgpt). ## Availability ChatGPT skills are currently in beta. They are available in supported workspace plans, including Business, Enterprise, Edu, Teachers, and Healthcare plans. Skills are also supported in Codex and the OpenAI API, but skills do not sync across products yet. OpenAI skills follow the open Agent Skills standard, so a skill can be downloaded from one product and installed in another compatible product. ## Open the Skills page In ChatGPT: 1. Click your profile icon. 2. Select **Skills**. The Skills page shows skills that are installed, created by you, and shared with you. ChatGPT includes `skill-creator` by default. When you ask ChatGPT to create, modify, or troubleshoot a skill, it can use `skill-creator` to guide the process. ## Create a skill in chat Use this path when you know the workflow but want ChatGPT to help structure it. ### Copy prompt ```text Create a reusable ChatGPT skill for this workflow: Workflow name: [name] When ChatGPT should use it: [trigger conditions] Inputs the user will provide: [inputs] Steps ChatGPT should follow every time: [steps] Outputs ChatGPT should produce: [output format] Ask me any missing questions first, then generate the skill and tell me how to install it. ``` ChatGPT should ask follow-up questions, generate the skill, and prompt you to install it when it is ready. ## Create a skill in the editor Use the editor when you want to build or manage the skill directly. 1. Open **Skills** from your profile menu. 2. Click **New skill**. 3. Select **Create with editor**. 4. Add the skill instructions, examples, and any supporting resources. 5. Save and install the skill. ## Upload a skill Use upload when you already have a skill file or folder from another product or teammate. 1. Open **Skills** from your profile menu. 2. Click **New skill**. 3. Choose **Upload from your computer**. 4. Select the skill package. 5. Review and install it. ## Install a shared skill Use this path for skills another teammate or workspace owner has shared with you. 1. Open **Skills** from your profile menu. 2. Select **Shared with you**. 3. Hover over the skill. 4. Click the **...** menu. 5. Select **Install**. After installation, ChatGPT can automatically use the skill when your request matches the skill's purpose. ## Share a skill with your workspace Turn strong workflows into shared workspace skills when teammates should use the same process. 1. Open **Skills** from your profile menu. 2. Hover over the skill you want to share. 3. Click the **...** menu. 4. Select **Share**. 5. Search for people or groups, or copy a direct share link. 6. Set access permissions for the skill. Use limited access for workflows that include sensitive instructions, private context, or operational steps. ## Admin controls Enterprise and Edu admins can control ChatGPT skills from **Permissions & roles**. Admins can enable or disable: - Skills usage. - Skills publishing and sharing. - Skills installing for workspace members. While skills are in beta, Enterprise and Edu workspaces may have skills off by default until an admin enables them. ## Compliance notes Workspace admins can use compliance logs to review metadata and audit events such as skill creation, sharing, updates, and installation. ChatGPT conversations that use skills follow the workspace's data residency settings. For ChatGPT business plans, data shared with a skill is not used to improve OpenAI models by default. ## Verify a skill After installing a skill: 1. Start a new ChatGPT conversation. 2. Ask for a task that clearly matches the skill description. 3. Confirm ChatGPT uses the expected workflow. 4. If it does not, update the skill description so the trigger conditions are clearer. For Codex-specific skill installation, see the [Codex Skills quick start](/docs/quick-start/codex-skills/). ### Quick Start: Claude Skills URL: https://www.kuudo.com/docs/quick-start/claude-app-skills/ Use Claude skills when you want Claude on the web or Claude Desktop to follow reusable instructions, work with bundled files, or apply a repeatable workflow inside chat. For the official reference, see [Use Skills in Claude](https://support.claude.com/en/articles/12512180-use-skills-in-claude). Claude app skills are different from Claude Code skills. Claude app skills are managed from **Customize > Skills** in Claude. Claude Code skills are local folders such as `~/.claude/skills/` or `.claude/skills/` and are covered separately in the [Claude Code Skills quick start](/docs/quick-start/claude-skills/). ## Before you start - Use a Claude plan or workspace that supports skills. - Make sure skills are enabled for your account or organization. - Keep skill packages in a trusted location before uploading them. - Review any skill before installing it, especially if it includes files, scripts, or instructions from outside your organization. For team or enterprise workspaces, an admin may need to enable skills before users can add or run them. ## Open Skills In Claude Desktop or Claude on the web: 1. Open **Customize**. 2. Find the **Skills** section. 3. Use the available action to **Add**, **Create**, or **Replace** a skill. If the Skills section is hidden or disabled, check **Settings > Capabilities** and your workspace admin settings. ## Add a skill Use **Add** when you already have a skill package from a trusted source. 1. Open **Customize > Skills**. 2. Select **Add**. 3. Choose the skill package. 4. Review the skill name, description, and contents. 5. Confirm the upload or installation. 6. Toggle the skill on if Claude does not enable it automatically. Do not upload a skill package unless you trust its contents. Skills can influence how Claude interprets requests and may include supporting files. ## Create a skill Use **Create** when you want Claude to help build a new reusable workflow. Start with a concrete prompt: ```text Create a Claude skill for this workflow: Skill name: [name] When Claude should use it: [trigger conditions] Inputs I will provide: [inputs] Steps Claude should follow: [steps] Output format: [format] Ask me any missing questions before creating the skill. ``` After Claude drafts the skill, review the instructions and test it with a low-risk request before relying on it for production work. ## Replace a skill Use **Replace** when you have a newer version of a skill that should take the place of an existing one. 1. Open **Customize > Skills**. 2. Select the existing skill. 3. Choose **Replace**. 4. Upload the new version. 5. Confirm the name and description still match the intended workflow. 6. Test the skill with a known request. Replacing a skill can change Claude's behavior in future chats. Keep a copy of the previous version if your team may need to roll back. ## Enable or disable skills In **Customize > Skills**, toggle individual skills on or off. Keep only the skills you need enabled for a task. Disable old, experimental, or overlapping skills when they are not relevant. ## Verify a skill Start a new Claude chat and ask for the workflow the skill should handle: ```text Use my [skill name] workflow to process this request: [task] ``` If Claude does not appear to use the skill, make the skill description more explicit, confirm the skill is enabled, and start a new chat. ## Troubleshooting ### Skills are not visible Check whether your plan supports skills, whether skills are enabled under **Settings > Capabilities**, and whether your workspace admin has enabled skills. ### Add, Create, or Replace is disabled Your workspace may restrict who can create or upload custom skills. Ask an admin to enable skill creation or to install the skill for the workspace. ### Claude ignores the skill Confirm the skill is enabled and its description clearly matches the request. Start a new conversation after changing skill settings. ### The wrong skill activates Disable overlapping skills or rewrite the descriptions so each skill has a clear trigger condition. ## Related docs - [Claude Code Skills](/docs/quick-start/claude-skills/) - [Claude MCP](/docs/quick-start/claude-ai/) - [MCP Client Configuration](/docs/mcp-client-configuration/) ### Quick Start: Claude Code Skills URL: https://www.kuudo.com/docs/quick-start/claude-skills/ Use Claude Code skills when you want Claude Code to apply a reusable local workflow, load task-specific instructions only when needed, or expose a repeatable command such as `/summarize-changes`, `/deploy`, or `/review-pr`. For Claude Desktop or Claude on the web skills managed through **Customize > Skills**, see the [Claude Skills quick start](/docs/quick-start/claude-app-skills/). For the official reference, see [Extend Claude with skills in the Claude Code docs](https://code.claude.com/docs/en/skills). ## What a Claude Code skill is A skill is a directory with a required `SKILL.md` file and optional supporting files: ```text my-skill/ SKILL.md reference.md examples/ scripts/ ``` Claude sees each skill's name, description, and path up front. The full `SKILL.md` body loads only when Claude invokes the skill or you invoke it directly with `/skill-name`. Skills are the recommended replacement for most custom commands. Existing `.claude/commands/` files still work, but a skill with the same name takes precedence. ## Install a personal skill Use personal skills for workflows you want across all projects. ```bash mkdir -p ~/.claude/skills/summarize-changes ``` Create `~/.claude/skills/summarize-changes/SKILL.md`: ```markdown --- description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff. --- Current changes: !`git diff HEAD` Instructions: Summarize the changes above in two or three bullet points, then list any risks you notice such as missing error handling, hardcoded values, or tests that need updating. If the diff is empty, say there are no uncommitted changes. ``` Start Claude Code in a git project and test it two ways: ```text What did I change? ``` Or invoke it directly: ```text /summarize-changes ``` ## Install a project skill Use project skills when the workflow belongs with the repository and should be shared with the team. ```text .claude/skills//SKILL.md ``` Example: ```bash mkdir -p .claude/skills/release-checklist ``` Project skills load from `.claude/skills/` in the starting directory and parent directories up to the repository root. Claude Code can also discover nested `.claude/skills/` directories as you work in subdirectories, which is useful for monorepos. ## Skill locations | Scope | Path | Applies to | | --- | --- | --- | | Enterprise | Managed settings | All users in the organization. | | Personal | `~/.claude/skills//SKILL.md` | All your projects. | | Project | `.claude/skills//SKILL.md` | The current project. | | Plugin | `/skills//SKILL.md` | Wherever the plugin is enabled. | When skills share a name across levels, enterprise overrides personal, and personal overrides project. Plugin skills use a `plugin-name:skill-name` namespace. Claude Code watches existing skill directories for changes. If you add, edit, or remove a skill in an already-watched directory, the change takes effect in the current session. If you create a top-level skills directory after Claude Code has started, restart Claude Code. ## Frontmatter basics `SKILL.md` starts with YAML frontmatter. Only `description` is recommended, but additional fields control invocation, arguments, tools, model choice, and execution context. ```markdown --- name: deploy description: Deploy the application to production argument-hint: "[environment]" disable-model-invocation: true allowed-tools: Bash(git status *) Bash(npm test *) Bash(npm run build *) --- Deploy $ARGUMENTS to production: 1. Run the test suite. 2. Build the application. 3. Push to the deployment target. 4. Verify the deployment succeeded. ``` Useful fields: | Field | Use | | --- | --- | | `name` | Display name. If omitted, Claude uses the directory name. | | `description` | What the skill does and when Claude should use it. | | `when_to_use` | Extra trigger guidance appended to the description. | | `argument-hint` | Autocomplete hint for expected arguments. | | `arguments` | Named positional arguments for substitutions. | | `disable-model-invocation` | Set `true` when only the user should trigger the skill manually. | | `user-invocable` | Set `false` to hide background knowledge from the `/` menu. | | `allowed-tools` | Tools Claude may use without asking while the skill is active. | | `paths` | File globs that limit automatic activation. | | `context` | Set `fork` to run the skill in a subagent context. | | `agent` | Subagent type to use when `context: fork` is set. | ## Control invocation By default, both you and Claude can invoke a skill: - You can type `/skill-name`. - Claude can load the skill automatically when your request matches the description. Use `disable-model-invocation: true` for workflows with side effects, such as deploys, commits, or messages. Use `user-invocable: false` for background knowledge that Claude may use automatically but users should not run as a command. ## Pass arguments Claude Code passes text after the skill name into `$ARGUMENTS`. ```markdown --- name: fix-issue description: Fix a GitHub issue disable-model-invocation: true --- Fix GitHub issue $ARGUMENTS following our coding standards. ``` Invocation: ```text /fix-issue 123 ``` For positional values, use `$ARGUMENTS[0]`, `$ARGUMENTS[1]`, or shorthand values like `$0` and `$1`. ## Add dynamic context Inline shell injection runs a shell command before Claude sees the skill. The command output replaces the placeholder in the rendered skill. ```markdown Pull request context: - PR diff: !`gh pr diff` - PR comments: !`gh pr view --comments` - Changed files: !`gh pr diff --name-only` ``` Use dynamic context for live diffs, issue data, environment details, or generated reports. To disable shell execution for user, project, plugin, or additional-directory skills, set `disableSkillShellExecution` in Claude Code settings. ## Pre-approve tools carefully The `allowed-tools` field lets Claude use listed tools without per-use approval while the skill is active. It does not restrict all other tools; your normal permission settings still apply. Review project skills before trusting a repository. A project skill can grant broad tool access after you accept the workspace trust dialog. ## Override visibility Use `/skills` to manage skill visibility. Highlight a skill, press `Space` to cycle states, then press `Enter` to save to `.claude/settings.local.json`. The underlying setting is `skillOverrides`: ```json { "skillOverrides": { "legacy-context": "name-only", "deploy": "off" } } ``` States include: | State | Listed to Claude | In `/` menu | | --- | --- | --- | | `on` | Name and description | Yes | | `name-only` | Name only | Yes | | `user-invocable-only` | Hidden | Yes | | `off` | Hidden | Hidden | Plugin skills are managed through `/plugin`, not `skillOverrides`. ## Share skills Share skills at the scope that matches the audience: - Project: Commit `.claude/skills/` to version control. - Plugin: Create a `skills/` directory in a Claude Code plugin. - Managed: Deploy organization-wide through managed settings. ## Verify a skill After installing a skill: 1. Run Claude Code in a project where the skill should be available. 2. Type `/` and confirm the skill appears. 3. Invoke it directly with `/skill-name`. 4. Ask a natural-language request that should match the skill description. 5. If Claude does not select it, tighten the `description` and `when_to_use` fields. For MCP setup in Claude Code, see the [Claude quick start](/docs/quick-start/claude-ai/). ### Quick Start: Codex Skills URL: https://www.kuudo.com/docs/quick-start/codex-skills/ Install Codex skills when you want Codex to follow a reusable workflow, load task-specific instructions, or bring supporting scripts and references into a session. For the official reference, see [Agent Skills in the OpenAI Codex docs](https://developers.openai.com/codex/skills). ## What a skill is A skill is a folder with a required `SKILL.md` file and optional supporting files: ```text my-skill/ SKILL.md scripts/ references/ assets/ agents/ openai.yaml ``` `SKILL.md` contains the skill metadata and instructions. It must include `name` and `description` frontmatter so Codex can decide when to use it. ```markdown --- name: skill-name description: Explain exactly when this skill should and should not trigger. --- Skill instructions for Codex to follow. ``` Codex uses progressive disclosure for skills. It starts with each skill's name, description, and file path, then reads the full `SKILL.md` only when the skill is selected. ## Install curated skills For local setup and experimentation, use the built-in installer from a Codex session: ```text $skill-installer linear ``` You can also ask `$skill-installer` to install skills from another repository: ```text $skill-installer install a skill from github.com/{owner}/{repo} ``` Codex detects newly installed skills automatically. If a skill does not appear, restart Codex. ## Install a repo skill Use repo-scoped skills when the workflow belongs with the codebase and should be shared by the team. Create the folder under the repository root: ```text .agents/skills/my-skill/SKILL.md ``` Example: ```bash mkdir -p .agents/skills/release-checklist ``` Then add `.agents/skills/release-checklist/SKILL.md`: ```markdown --- name: release-checklist description: Use when preparing a release branch, verifying changelog entries, and checking release gates. --- Follow the repository release checklist before marking a release ready. ``` Codex scans `.agents/skills` from the current working directory up to the repository root, so nested projects can have local skills while the root can provide shared team skills. ## Install a user skill Use user-scoped skills for personal workflows that should be available across repositories. ```text $HOME/.agents/skills/my-skill/SKILL.md ``` Example: ```bash mkdir -p ~/.agents/skills/personal-review ``` Then add `~/.agents/skills/personal-review/SKILL.md`. ## Admin and system skills Codex also reads admin and system-level skills: | Scope | Location | Use | | --- | --- | --- | | Repo | `$REPO_ROOT/.agents/skills` | Team or project workflows checked into a repository. | | User | `$HOME/.agents/skills` | Personal workflows available across repositories. | | Admin | `/etc/codex/skills` | Shared machine or container defaults. | | System | Bundled with Codex | OpenAI-provided default skills. | Codex supports symlinked skill folders and follows the symlink target when scanning skill locations. ## Enable or disable a skill Disable a skill without deleting it by adding a `[[skills.config]]` entry to `~/.codex/config.toml`: ```toml [[skills.config]] path = "/path/to/skill/SKILL.md" enabled = false ``` Restart Codex after changing `~/.codex/config.toml`. ## Optional app metadata Add `agents/openai.yaml` when you want Codex app metadata, invocation policy, or declared tool dependencies: ```yaml interface: display_name: "Release Checklist" short_description: "Release readiness workflow" icon_small: "./assets/small-logo.svg" icon_large: "./assets/large-logo.png" brand_color: "#3B82F6" default_prompt: "Run the release checklist." policy: allow_implicit_invocation: false dependencies: tools: - type: "mcp" value: "private-mcp" description: "Private MCP server" transport: "streamable_http" url: "https://{your-private-mcp-host}/mcp" ``` Set `allow_implicit_invocation: false` when the skill should only run after an explicit `$skill-name` mention. ## Skills vs plugins Use direct skill folders for local authoring, repo-scoped workflows, and personal setup. Use plugins when you want to distribute reusable skills, bundle multiple skills together, or ship skills alongside app mappings, MCP server configuration, or presentation assets. ## Verify installation In Codex CLI or the IDE extension: 1. Run `/skills` or type `$` to open the skill selector. 2. Confirm the new skill appears by name. 3. Invoke it directly with `$skill-name`. 4. If it does not appear, restart Codex and check the skill path. For skills that depend on MCP tools, verify the MCP server separately with `/mcp`. See the [Codex MCP quick start](/docs/quick-start/codex/) for MCP setup. ### Kuudo MCP Servers URL: https://www.kuudo.com/docs/mcp-reference/tools/ Kuudo runs one MCP server per Amazon surface. Each server exposes domain-scoped tools so agents can read, analyze, and safely queue changes — this page is the index: what's available today, and where to find the full tool-by-tool reference for each one. ## MCP servers ### Amazon Ads MCP Sponsored Products, Brands, Display, and TV, plus DSP (demand-side platform), AMC (Amazon Marketing Cloud), the unified Ads API v1, attribution, and reporting — see the Ads MCP tool reference for the live resource and tool counts. [See every Amazon Ads MCP tool ->](/docs/mcp-reference/amazon-ads-tools/) ### Amazon Selling Partner MCP Catalog, listings, orders, pricing, FBA (Fulfillment by Amazon), fulfillment, finances, and more across the seller side of the Selling Partner API — see the SP MCP tool reference for the live resource and tool counts. [See every Amazon Selling Partner MCP tool ->](/docs/mcp-reference/amazon-sp-tools/) More servers are on the way, starting with Vendor Central MCP. ## Read vs. write tools Every tool on every server is tagged read or write. Read tools return scoped data and are safe for normal agent use — exploration, diagnostics, reporting. Write tools are guarded: they validate input, apply workspace policy, and may require explicit human approval before changes reach Amazon. ## Auditability Every tool call records the requesting user, agent, workspace, parameters, result summary, and approval state, across every server. Use the run log to reproduce outputs or review changes before activation. ### Amazon Ads MCP Tools URL: https://www.kuudo.com/docs/mcp-reference/amazon-ads-tools/ This page lists every tool the Amazon Ads MCP exposes, grouped by the underlying Amazon Ads API resource. Each entry shows whether it's a read tool (safe to call freely) or a write tool (guarded, may require approval), the tool name, and a short description. See [Kuudo MCP Servers](/docs/mcp-reference/tools/) for the full index of Kuudo MCP servers. Expand any resource below to see its tools. Your browser's find-in-page (Ctrl+F / Cmd+F) searches across every tool on this page, even inside collapsed sections. ## Amazon Marketing Cloud API tools Kuudo exposes Amazon Marketing Cloud API operations as callable MCP tools across four resource groups: [AMC administration](#amcadmin), [workflows](#amcworkflow), [rule-based audiences](#amcruleaudience), and [ad-based audiences](#amcadaudience). Use this page for Kuudo tool names and access modes; use Amazon's official documentation for canonical API schemas and policies. Kuudo's [Amazon Marketing Cloud software](/features/amc/) packages these operations into governed workflows, while the [Amazon Ads MCP page](/features/amazon-ads-mcp/) explains the broader server. ## All Amazon Ads MCP tools ## Tool reference 53 resources with tools · 711 tools ### AccountsAccountBudgets | Access | Tool | Description | | --- | --- | --- | | read | `AccountsAccountBudgets_getAccountBudgetFeatureFlags` | Gets account budget feature flags information. | | write | `AccountsAccountBudgets_updateAccountBudgetFeatureFlags` | Creates or Updates account budget feature flags information. | ### AccountsAdsAccounts | Access | Tool | Description | | --- | --- | --- | | write | `AccountsAdsAccounts_RegisterAdsAccount` | Create a new advertising account tied to a specific Amazon vendor, seller or author, or to a business who does not sell on Amazon. | | read | `AccountsAdsAccounts_ListAdsAccounts` | List all advertising accounts for the user associated with the access token. | | read | `AccountsAdsAccounts_GetAccount` | Request attributes of a given advertising account. | | write | `AccountsAdsAccounts_CreateTermsToken` | Create a new UUID terms token for the customer to accept advertising terms Requires one of these permissions: [] | | read | `AccountsAdsAccounts_GetTermsToken` | Get the terms token status for the customer Requires one of these permissions: [] | ### AccountsBilling | Access | Tool | Description | | --- | --- | --- | | read | `AccountsBilling_GetDocument` | Gets billing document(s) with id. | | write | `AccountsBilling_PayInvoices` | Executes payment on a set of or all of an advertisers open invoices. | | read | `AccountsBilling_bulkGetBillingNotifications` | Gets an array of all currently valid billing notifications associated for each advertising account. | | write | `AccountsBilling_CreatePaymentAgreements` | Creates or updates payment agreements. | | read | `AccountsBilling_GetPaymentAgreements` | Gets current payment agreement for a customer. | | read | `AccountsBilling_GetCustomerPaymentMethods` | Retrieves eligible payment methods for a customer. | | write | `AccountsBilling_CreatePaymentProfiles` | Creates or updates payment profiles. | | read | `AccountsBilling_bulkGetBillingStatus` | Gets the current billing status associated for each advertising account. | | read | `AccountsBilling_GetBillingProfileAgreementContent` | User needs to provide consent to certain agreements before creating a billing profile. | | write | `AccountsBilling_ApplyBillingProfile` | API to link one or more countries with a billing profile. | | read | `AccountsBilling_GetBillingProfileUsages` | Lists the billing profiles linked to each country of global ads account. | | write | `AccountsBilling_CreateBillingProfiles` | Creates one or more billing profiles. | | write | `AccountsBilling_UpdateBillingProfiles` | Updates one or more billing profiles under a global account Please note that isBillTo and type are immutable attributes and cannot be updated -- in this case, user can always create a new billing... | | read | `AccountsBilling_GetBillingProfiles` | Fetches billing profiles present under the global account. | | write | `AccountsBilling_CreateBillingStatement` | Request to create billing statement for advertiser advertising in Sponsored Products/Brands/Display segment. | | read | `AccountsBilling_GetBillingStatement` | API to fetch the latest status of Billing Statements creation request and billing statement download link if available. | | read | `AccountsBilling_GetBillingInvoiceSummaries` | Lists the billing invoice summary(s) in a global ads account. | | read | `AccountsBilling_getAdvertiserInvoices` | Requires one of these permissions: ["nemo_transactions_view","nemo_transactions_edit"] | | read | `AccountsBilling_getInvoice` | Requires one of these permissions: ["nemo_transactions_view","nemo_transactions_edit"] | ### AccountsManagerAccounts | Access | Tool | Description | | --- | --- | --- | | read | `AccountsManagerAccounts_getManagerAccountsForUser` | Returns all manager accounts that a user has access to, along with metadata for the Amazon Ads accounts t... | | write | `AccountsManagerAccounts_createManagerAccount` | Creates a new Amazon Advertising Manager account. | | write | `AccountsManagerAccounts_LinkAdvertisingAccountsToManagerAccountPublicAPI` | Link Amazon Advertising accounts or advertisers with a Manager Account. | | write | `AccountsManagerAccounts_UnlinkAdvertisingAccountsToManagerAccountPublicAPI` | Unlink Amazon Advertising accounts or advertisers with a Manager Account. | ### AccountsPortfolios | Access | Tool | Description | | --- | --- | --- | | write | `AccountsPortfolios_CreatePortfolios` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AccountsPortfolios_UpdatePortfolios` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AccountsPortfolios_portfolioBudgetUsage` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `AccountsPortfolios_ListPortfolios` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | ### AccountsProfiles | Access | Tool | Description | | --- | --- | --- | | read | `AccountsProfiles_listProfiles` | Note that this operation does not return a response unless the current account has created at least one campaign using the advertising console. | | write | `AccountsProfiles_updateProfiles` | Note that this operation is only used for Sellers using Sponsored Products. | | read | `AccountsProfiles_getProfileById` | This operation does not return a response unless the current account has created at least one campaign using the advertising console. | ### AdsAPIv1All | Access | Tool | Description | | --- | --- | --- | | read | `AdsAPIv1All_ListBrandStoreEdition` | Retrieve brand store page content Requires one of these permissions: ["amazon_stores_edit","amazon_stores_view"] | | read | `AdsAPIv1All_DSPListCommitment` | List commitments Requires one of these permissions: [] | | write | `AdsAPIv1All_CreateAccountCombinationInvitation` | Create an invitation to combine Advertising Accounts under a Single Global Accou... | | write | `AdsAPIv1All_CreateAdAssociation` | Create Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1All_CreateAdExtension` | Create ad extensions - API is in open beta Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1All_CreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_e... | | write | `AdsAPIv1All_CreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1All_CreateAdvertiserAccount` | Create advertiser accounts Requires one of these permissions: [] | | write | `AdsAPIv1All_CreateAdvertiserProductGroupEligibility` | Create request for specific advertiser product group eligibility Requires one... | | write | `AdsAPIv1All_SBCreateAdvertisingDealTarget` | Create advertisingDealTarget Requires one of these permissions: ["advertise... | | write | `AdsAPIv1All_SBCreateAdvertisingDeal` | Create advertisingDeal Requires one of these permissions: ["advertiser_campaign_edit", "advertiser_campaign_view"] | | write | `AdsAPIv1All_SBCreateBrandedKeywordsPricing` | Create brandedKeywords pricing Requires one of these permissions: ["adverti... | | write | `AdsAPIv1All_CreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_e... | | write | `AdsAPIv1All_DSPCreateCommitment` | Create commitments Requires one of these permissions: [] | | write | `AdsAPIv1All_CreateEvent` | Create Event Data Requires one of these permissions: ["event_manager_view", "event_manager_edit"] | | write | `AdsAPIv1All_CreateGeoLocation` | Create geo location targeting definitions. | | write | `AdsAPIv1All_DSPAdsApiv1CreateInventoryGroup` | Create inventory groups Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1All_SBCreateKeywordReservationValidation` | Validate keyword reservation Requires one of these permissions: ["advertise... | | write | `AdsAPIv1All_CreateLinearTvIncrementalReachForecast` | Generate Linear TV incremental reach forecast comparing with supported Streaming... | | write | `AdsAPIv1All_CreateLinearTvReachForecast` | Generate Linear TV reach forecast Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1All_CreateLocationIndex` | Create a Smart Location Index. | | write | `AdsAPIv1All_CreateManagerAccount` | Create manager accounts Requires one of these permissions: [] | | write | `AdsAPIv1All_SBCreateRecommendation` | Create recommendations Requires one of these permissions: ["advertiser_campaign_view"] | | write | `AdsAPIv1All_AdsApiv1CreateReport` | Create a report Requires one of these permissions: ["ManagerAccount_Dev", "... | | write | `AdsAPIv1All_DSPAdsApiv1CreateSupplierAdProductPrice` | Create supplier ad product price Requires one of these permissions: ["inventory_view"] | | write | `AdsAPIv1All_DSPAdsApiv1CreateSupplierProposal` | Create supplier proposal Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1All_DSPAdsApiv1CreateSupplierProposedDealForecast` | Create supplier proposed deal forecast Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1All_DSPAdsApiv1CreateSupplierProposedDeal` | Create supplier proposed deal Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1All_CreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1All_DSPListDealPlanningMetrics` | List deal planning metrics for specified deals. | | write | `AdsAPIv1All_DeleteAdAssociation` | Delete Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1All_DeleteAdGroup` | Delete ad groups Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1All_DeleteAd` | Delete ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1All_SBDeleteAdvertisingDealTarget` | Delete advertisingDealTarget Requires one of these permissions: ["advertise... | | write | `AdsAPIv1All_SBDeleteAdvertisingDeal` | Delete advertisingDeal Requires one of these permissions: ["advertiser_campaign_edit", "advertiser_campaign_view"] | | write | `AdsAPIv1All_DeleteCampaign` | Delete campaigns Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1All_AdsApiv1DeleteReport` | Delete a report by ID Requires one of these permissions: [] | | write | `AdsAPIv1All_DeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1All_ListLocationIndex` | List all Smart Location Indexes for the authenticated advertiser. | | read | `AdsAPIv1All_QueryAccountCombinationInvitation` | Query invitations to combine advertising accounts under a Single Global Account. | | read | `AdsAPIv1All_QueryAdAssociation` | Query Ad Association Requires one of these permissions: ["creatives_view", "campaign_view"] | | read | `AdsAPIv1All_QueryAdExtension` | Query ad_extension - API is in open beta Requires one of these permissions:... | | read | `AdsAPIv1All_QueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edi... | | read | `AdsAPIv1All_QueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit", "c... | | read | `AdsAPIv1All_QueryAdvertiserAccount` | List advertiser accounts Requires one of these permissions: [] | | read | `AdsAPIv1All_QueryAdvertiserProductGroupEligibility` | Query requests for specific advertiser product group eligibility based on advert... | | read | `AdsAPIv1All_SBQueryAdvertisingDealTarget` | Query advertisingDealTarget Requires one of these permissions: ["advertiser... | | read | `AdsAPIv1All_SBQueryAdvertisingDeal` | Query advertisingDeal Requires one of these permissions: ["advertiser_campaign_edit", "advertiser_campaign_view"] | | read | `AdsAPIv1All_QueryBrandStoreEditionPublishVersion` | Query store edition publish versions Requires one of these permissions: ["amazon_stores_edit","amazon_stores_view"] | | read | `AdsAPIv1All_QueryBrandStorePage` | Retrieve brand store page content Requires one of these permissions: ["amazon_stores_edit","amazon_stores_view"] | | read | `AdsAPIv1All_QueryBrandStore` | Query brand store content Requires one of these permissions: ["advertiser_c... | | read | `AdsAPIv1All_QueryCampaign` | Query campaign Requires one of these permissions: ["advertiser_campaign_edi... | | read | `AdsAPIv1All_DSPQueryDealAvails` | Query deal avails by advertising deal ID. | | read | `AdsAPIv1All_DSPAdsApiv1QueryInventoryGroup` | Query inventory groups with filters Requires one of these permissions: ["inventory_edit", "inventory_view"] | | read | `AdsAPIv1All_QueryLinearTvDaypart` | List all supported Linear TV Daypart Requires one of these permissions: [] | | read | `AdsAPIv1All_QueryManagerAccount` | List manager accounts Requires one of these permissions: [] | | read | `AdsAPIv1All_AdsApiv1QueryPublisher` | List all Publishers Requires one of these permissions: [] | | read | `AdsAPIv1All_SBQueryRecommendationType` | Query RecommendationTypes Requires one of these permissions: ["advertiser_campaign_view"] | | read | `AdsAPIv1All_QuerySellingAccount` | List selling accounts Requires one of these permissions: [] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplierAdProduct` | Query supplier ad products Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplierProposal` | Query supplier proposal Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplierProposedDeal` | Query supplier proposed deals Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplierPublisher` | Query supplier publishers Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplierTargetItem` | Fetch supplier target items Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_DSPAdsApiv1QuerySupplier` | Query suppliers Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1All_QueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit",... | | write | `AdsAPIv1All_DSPRetrieveCampaignForecast` | Retrieve campaign forecast Requires one of these permissions: ["campaign_view", "advertiser_campaign_view"] | | write | `AdsAPIv1All_DSPRetrieveCommitmentSpend` | Retrieve commitment spend Requires one of these permissions: [] | | write | `AdsAPIv1All_DSPRetrieveCommitment` | Get Commitments Requires one of these permissions: [] | | write | `AdsAPIv1All_DSPAdsApiv1RetrieveInventoryGroup` | Retrieve inventory groups by ID Requires one of these permissions: ["inventory_edit", "inventory_view"] | | write | `AdsAPIv1All_RetrieveLocationIndex` | Retrieve one or more Smart Location Indexes by ID. | | write | `AdsAPIv1All_AdsApiv1RetrieveReport` | Retrieve a report by ID Requires one of these permissions: [] | | write | `AdsAPIv1All_UpdateAccountCombinationInvitation` | Update an invitation to combine Advertising Accounts under a Single Global Accou... | | write | `AdsAPIv1All_UpdateAdAssociation` | Update Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1All_UpdateAdExtension` | Update ad_extension - API is in open beta Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1All_UpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_e... | | write | `AdsAPIv1All_UpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1All_UpdateAdvertiserAccount` | Update advertiser accounts Requires one of these permissions: [] | | write | `AdsAPIv1All_SBUpdateAdvertisingDeal` | Update advertisingDeal Requires one of these permissions: ["advertiser_campaign_edit", "advertiser_campaign_view"] | | write | `AdsAPIv1All_UpdateBrandStoreEditionPublishVersion` | Update store edition publish versions Requires one of these permissions: ["amazon_stores_edit"] | | write | `AdsAPIv1All_UpdateBrandStorePage` | Update brand store page content Requires one of these permissions: ["amazon_stores_edit"] | | write | `AdsAPIv1All_UpdateCampaign` | Update campaign Requires one of these permissions: ["advertiser_campaign_ed... | | write | `AdsAPIv1All_DSPUpdateCommitment` | Update commitments Requires one of these permissions: [] | | write | `AdsAPIv1All_DSPAdsApiv1UpdateInventoryGroup` | Update inventory groups Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1All_UpdateLocationIndex` | Update the data for an existing Smart Location Index. | | write | `AdsAPIv1All_UpdateManagerAccount` | Update manager accounts Requires one of these permissions: [] | | write | `AdsAPIv1All_UpdateTarget` | Update target Requires one of these permissions: ["advertiser_campaign_edit"] | ### AdsAPIv1Beta | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1Beta_CreateAccountCombinationInvitation` | Create an invitation to combine Advertising Accounts under a Single Global Accou... | | write | `AdsAPIv1Beta_CreateAdvertiserAccount` | Create advertiser accounts Requires one of these permissions: [] | | write | `AdsAPIv1Beta_CreateAdvertiserProductGroupEligibility` | Create request for specific advertiser product group eligibility Requires one... | | write | `AdsAPIv1Beta_CreateEvent` | Create Event Data Requires one of these permissions: ["event_manager_view", "event_manager_edit"] | | write | `AdsAPIv1Beta_CreateGeoLocation` | Create geo location targeting definitions. | | write | `AdsAPIv1Beta_DSPAdsApiv1CreateInventoryGroup` | Create inventory groups Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1Beta_CreateLinearTvIncrementalReachForecast` | Generate Linear TV incremental reach forecast comparing with supported Streaming... | | write | `AdsAPIv1Beta_CreateLinearTvReachForecast` | Generate Linear TV reach forecast Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1Beta_CreateLocationIndex` | Create a Smart Location Index. | | write | `AdsAPIv1Beta_CreateManagerAccount` | Create manager accounts Requires one of these permissions: [] | | write | `AdsAPIv1Beta_AdsApiv1CreateReport` | Create a report Requires one of these permissions: ["ManagerAccount_Dev", "... | | write | `AdsAPIv1Beta_DSPAdsApiv1CreateSupplierAdProductPrice` | Create supplier ad product price Requires one of these permissions: ["inventory_view"] | | write | `AdsAPIv1Beta_DSPAdsApiv1CreateSupplierProposal` | Create supplier proposal Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1Beta_DSPAdsApiv1CreateSupplierProposedDealForecast` | Create supplier proposed deal forecast Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1Beta_DSPAdsApiv1CreateSupplierProposedDeal` | Create supplier proposed deal Requires one of these permissions: ["inventory_edit"] | | read | `AdsAPIv1Beta_DSPListDealPlanningMetrics` | List deal planning metrics for specified deals. | | write | `AdsAPIv1Beta_AdsApiv1DeleteReport` | Delete a report by ID Requires one of these permissions: [] | | read | `AdsAPIv1Beta_ListLocationIndex` | List all Smart Location Indexes for the authenticated advertiser. | | read | `AdsAPIv1Beta_QueryAccountCombinationInvitation` | Query invitations to combine advertising accounts under a Single Global Account. | | read | `AdsAPIv1Beta_QueryAdvertiserAccount` | List advertiser accounts Requires one of these permissions: [] | | read | `AdsAPIv1Beta_QueryAdvertiserProductGroupEligibility` | Query requests for specific advertiser product group eligibility based on advert... | | read | `AdsAPIv1Beta_DSPQueryDealAvails` | Query deal avails by advertising deal ID. | | read | `AdsAPIv1Beta_DSPAdsApiv1QueryInventoryGroup` | Query inventory groups with filters Requires one of these permissions: ["inventory_edit", "inventory_view"] | | read | `AdsAPIv1Beta_QueryLinearTvDaypart` | List all supported Linear TV Daypart Requires one of these permissions: [] | | read | `AdsAPIv1Beta_QueryManagerAccount` | List manager accounts Requires one of these permissions: [] | | read | `AdsAPIv1Beta_AdsApiv1QueryPublisher` | List all Publishers Requires one of these permissions: [] | | read | `AdsAPIv1Beta_QuerySellingAccount` | List selling accounts Requires one of these permissions: [] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplierAdProduct` | Query supplier ad products Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplierProposal` | Query supplier proposal Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplierProposedDeal` | Query supplier proposed deals Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplierPublisher` | Query supplier publishers Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplierTargetItem` | Fetch supplier target items Requires one of these permissions: ["inventory_view"] | | read | `AdsAPIv1Beta_DSPAdsApiv1QuerySupplier` | Query suppliers Requires one of these permissions: ["inventory_view"] | | write | `AdsAPIv1Beta_DSPAdsApiv1RetrieveInventoryGroup` | Retrieve inventory groups by ID Requires one of these permissions: ["inventory_edit", "inventory_view"] | | write | `AdsAPIv1Beta_RetrieveLocationIndex` | Retrieve one or more Smart Location Indexes by ID. | | write | `AdsAPIv1Beta_AdsApiv1RetrieveReport` | Retrieve a report by ID Requires one of these permissions: [] | | write | `AdsAPIv1Beta_UpdateAccountCombinationInvitation` | Update an invitation to combine Advertising Accounts under a Single Global Accou... | | write | `AdsAPIv1Beta_UpdateAdvertiserAccount` | Update advertiser accounts Requires one of these permissions: [] | | write | `AdsAPIv1Beta_UpdateBrandStorePage` | Update brand store page content Requires one of these permissions: ["amazon_stores_edit"] | | write | `AdsAPIv1Beta_DSPAdsApiv1UpdateInventoryGroup` | Update inventory groups Requires one of these permissions: ["inventory_edit"] | | write | `AdsAPIv1Beta_UpdateLocationIndex` | Update the data for an existing Smart Location Index. | | write | `AdsAPIv1Beta_UpdateManagerAccount` | Update manager accounts Requires one of these permissions: [] | ### AdsAPIv1DSP | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1DSP_DSPCreateAdAssociation` | Create Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1DSP_DSPCreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1DSP_DSPCreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1DSP_DSPCreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1DSP_DSPCreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1DSP_DSPDeleteAdAssociation` | Delete Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1DSP_DSPDeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1DSP_DSPQueryAdAssociation` | Query Ad Association Requires one of these permissions: ["creatives_view", "campaign_view"] | | read | `AdsAPIv1DSP_DSPQueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1DSP_DSPQueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_view", "advertiser_campaign_view"] | | read | `AdsAPIv1DSP_DSPQueryCampaign` | Query campaign Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1DSP_DSPQueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1DSP_DSPUpdateAdAssociation` | Update Ad Association Requires one of these permissions: ["campaign_edit"] | | write | `AdsAPIv1DSP_DSPUpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1DSP_DSPUpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1DSP_DSPUpdateCampaign` | Update campaign Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | ### AdsAPIv1SponsoredBrands | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1SponsoredBrands_SBCreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBCreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredBrands_SBCreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBCreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBDeleteAdGroup` | Delete ad groups Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBDeleteAd` | Delete ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBDeleteCampaign` | Delete campaigns Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBDeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1SponsoredBrands_SBQueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredBrands_SBQueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredBrands_SBQueryCampaign` | Query campaign Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredBrands_SBQueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBUpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBUpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredBrands_SBUpdateCampaign` | Update campaign Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredBrands_SBUpdateTarget` | Update target Requires one of these permissions: ["advertiser_campaign_edit"] | ### AdsAPIv1SponsoredDisplay | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1SponsoredDisplay_SDCreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDCreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDCreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDCreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDDeleteAdGroup` | Delete ad groups Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDDeleteAd` | Delete ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDDeleteCampaign` | Delete campaigns Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDDeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1SponsoredDisplay_SDQueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredDisplay_SDQueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredDisplay_SDQueryCampaign` | Query campaign Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredDisplay_SDQueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDUpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDUpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDUpdateCampaign` | Update campaign Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredDisplay_SDUpdateTarget` | Update target Requires one of these permissions: ["advertiser_campaign_edit"] | ### AdsAPIv1SponsoredProducts | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1SponsoredProducts_SPCreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPCreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredProducts_SPCreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPCreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPDeleteAdGroup` | Delete ad groups Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPDeleteAd` | Delete ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPDeleteCampaign` | Delete campaigns Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPDeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1SponsoredProducts_SPQueryAdExtension` | Query ad_extension - API is in open beta Requires one of these permissions: ["advertiser_campaign_edit", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredProducts_SPQueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredProducts_SPQueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredProducts_SPQueryCampaign` | Query campaign Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredProducts_SPQueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPUpdateAdExtension` | Update ad_extension - API is in open beta Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPUpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPUpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit", "creatives_edit"] | | write | `AdsAPIv1SponsoredProducts_SPUpdateCampaign` | Update campaign Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredProducts_SPUpdateTarget` | Update target Requires one of these permissions: ["advertiser_campaign_edit"] | ### AdsAPIv1SponsoredTelevision | Access | Tool | Description | | --- | --- | --- | | write | `AdsAPIv1SponsoredTelevision_STCreateAdGroup` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STCreateAd` | Create ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STCreateCampaign` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STCreateTarget` | Create target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STDeleteAd` | Delete ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STDeleteTarget` | Delete target Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | read | `AdsAPIv1SponsoredTelevision_STQueryAdGroup` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredTelevision_STQueryAd` | List ads Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `AdsAPIv1SponsoredTelevision_STQueryCampaign` | Query campaign Requires one of these permissions: [] | | read | `AdsAPIv1SponsoredTelevision_STQueryTarget` | List target Requires one of these permissions: ["advertiser_campaign_edit", "dsp_campaign_view", "campaign_view", "advertiser_campaign_view", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STUpdateAdGroup` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit", "campaign_edit", "dsp_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STUpdateAd` | Update ads Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `AdsAPIv1SponsoredTelevision_STUpdateCampaign` | Update campaign Requires one of these permissions: [] | | write | `AdsAPIv1SponsoredTelevision_STUpdateTarget` | Update target Requires one of these permissions: ["advertiser_campaign_edit"] | ### AmazonAttribution | Access | Tool | Description | | --- | --- | --- | | read | `AmazonAttribution_getAdvertisersByProfile` | For sellers, an attribution profile has one associated advertiser. | | read | `AmazonAttribution_getPublishers` | Use the response to determine whether to use either the macroTags or nonMacroTemplateTags resource to get tags for a certain publisher. | | read | `AmazonAttribution_getAttributionTagsByCampaign` | Gets an attribution report for a specified list of advertisers. | | read | `AmazonAttribution_getPublisherAttributionTagTemplate` | Third-party publishers, such as Google Ads, Facebook, Microsoft Ads, and Pinterest support tags that include macro parameters. | | read | `AmazonAttribution_getPublisherMacroAttributionTag` | Some third-party publishers do not support tags that include macro parameters. | ### AmazonDSPAudiences | Access | Tool | Description | | --- | --- | --- | | write | `AmazonDSPAudiences_dspCreateAudiencesPost` | Creates a targeting audience based on an audience definition. | ### AmazonDSPConversions | Access | Tool | Description | | --- | --- | --- | | read | `AmazonDSPConversions_dspAmazonAdTagGetEventsByAdTagId` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | read | `AmazonDSPConversions_dspAmazonAdTagGetAdTagByAdvertiserId` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | read | `AmazonDSPConversions_dspAmazonBatchGetConversionDefinitionsForOrders` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_view"] | | write | `AmazonDSPConversions_dspAmazonCreateConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `AmazonDSPConversions_dspAmazonUpdateConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `AmazonDSPConversions_dspAmazonDeletionRequest` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `AmazonDSPConversions_dspAmazonIngestConversionData` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | read | `AmazonDSPConversions_dspAmazonListConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | read | `AmazonDSPConversions_dspAmazonGetAdTagAssociatedEvent` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | write | `AmazonDSPConversions_dspAmazonUpdateAdTagAssociatedEvent` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | read | `AmazonDSPConversions_dspAmazonGetAssociatedMobileAppForConversionDefinition` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | write | `AmazonDSPConversions_dspAmazonBatchCreateMobileMeasurementPartnerAppRegistration` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `AmazonDSPConversions_dspAmazonBatchUpdateMobileMeasurementPartnerAppRegistration` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `AmazonDSPConversions_dspAmazonDeleteMeasurementPartnerAppRegistrations` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | read | `AmazonDSPConversions_dspAmazonListMobileMeasurementPartnerAppRegistrations` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | read | `AmazonDSPConversions_dspAmazonGetAssociatedConversionDefinitionsForOrder` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_view"] | | write | `AmazonDSPConversions_dspAmazonUpdateAssociatedConversionDefinitionsForOrder` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_edit"] | ### AmazonDSPMeasurement | Access | Tool | Description | | --- | --- | --- | | write | `AmazonDSPMeasurement_CheckDSPAudienceResearchEligibility` | Checks the DSP AUDIENCE_RESEARCH study type eligibility status against vendor products. | | write | `AmazonDSPMeasurement_CheckDSPBrandLiftEligibility` | Checks the DSP BRAND_LIFT study type eligibility status against vendor products. | | write | `AmazonDSPMeasurement_CheckDSPCreativeTestingEligibility` | Checks the DSP CREATIVE_TESTING study type eligibility status against vendor products. | | write | `AmazonDSPMeasurement_CheckDSPOmnichannelMetricsEligibility` | Checks the DSP OMNICHANNEL_METRICS study type eligibility status against vendor products. | | read | `AmazonDSPMeasurement_GetDSPAudienceResearchStudies` | Gets one or more DSP AUDIENCE_RESEARCH studies with requested study identifiers or an advertiser identifier. | | write | `AmazonDSPMeasurement_CreateDSPAudienceResearchStudy` | Create new DSP AUDIENCE_RESEARCH study. | | write | `AmazonDSPMeasurement_UpdateDSPAudienceResearchStudy` | Update DSP AUDIENCE_RESEARCH study. | | read | `AmazonDSPMeasurement_GetDSPAudienceResearchStudyResult` | Get result of a DSP AUDIENCE_RESEARCH study. | | read | `AmazonDSPMeasurement_GetDSPBrandLiftStudies` | Gets one or more DSP BRAND_LIFT studies with requested study identifiers or an advertiser identifier. | | write | `AmazonDSPMeasurement_CreateDSPBrandLiftStudies` | Create new DSP BRAND_LIFT studies. | | write | `AmazonDSPMeasurement_UpdateDSPBrandLiftStudies` | Update DSP BRAND_LIFT studies. | | read | `AmazonDSPMeasurement_GetDSPCreativeTestingStudies` | Gets one or more DSP CREATIVE_TESTING studies with requested study identifiers or an advertiser identifier. | | write | `AmazonDSPMeasurement_CreateDSPCreativeTestingStudy` | Create new DSP CREATIVE_TESTING study. | | write | `AmazonDSPMeasurement_UpdateDSPCreativeTestingStudy` | Update DSP CREATIVE_TESTING study. | | read | `AmazonDSPMeasurement_GetDSPCreativeTestingStudyResult` | Get result of a DSP CREATIVE_TESTING study. | | read | `AmazonDSPMeasurement_GetDSPOmnichannelMetricsStudies` | Gets one or more DSP OMNICHANNEL_METRICS studies with requested study identifiers or an advertiser identifier. | | write | `AmazonDSPMeasurement_CreateDSPOmnichannelMetricsStudies` | Create new DSP OMNICHANNEL_METRICS studies. | | write | `AmazonDSPMeasurement_UpdateDSPOmnichannelMetricsStudies` | Update DSP OMNICHANNEL_METRICS studies. | | read | `AmazonDSPMeasurement_GetDSPOmnichannelMetricsStudyResult` | Get result of a DSP OMNICHANNEL_METRICS study. | | write | `AmazonDSPMeasurement_CheckPlanningEligibility` | Checks eligibility against all vendor products. | | write | `AmazonDSPMeasurement_CancelMeasurementStudies` | Cancel existing studies. | | read | `AmazonDSPMeasurement_GetStudies` | Gets base study objects given a list of studyIds or a list of advertiserIds. | | read | `AmazonDSPMeasurement_GetDSPBrandLiftStudyResult` | Get result of a DSP BRAND_LIFT study. | | read | `AmazonDSPMeasurement_GetSurveys` | Gets one or more study surveys with requested survey identifiers or a study identifier. | | write | `AmazonDSPMeasurement_CreateSurveys` | Create new study surveys. | | write | `AmazonDSPMeasurement_UpdateSurveys` | Update measurement surveys. | | write | `AmazonDSPMeasurement_vendorProduct` | Lists the supported measurement vendors products. | | read | `AmazonDSPMeasurement_omnichannelMetricsBrandSearch` | Search for brands to be used in the OMNICHANNEL_METRICS vendor product. | | read | `AmazonDSPMeasurement_vendorProductPolicy` | Gets the policies for the specific vendor product(s). | | read | `AmazonDSPMeasurement_vendorProductSurveyQuestionTemplates` | Gets the survey question templates for the specific vendor product(s). | ### AmazonDSPTargetKPIRecommendations | Access | Tool | Description | | --- | --- | --- | | read | `AmazonDSPTargetKPIRecommendations_getGsbTargetKpiRecommendation` | Creates a Target KPI recommendation for advertisers when they are in the process of creating a new campaign (ADSP). | ### AmazonMarketingStreamSubscriptions | Access | Tool | Description | | --- | --- | --- | | read | `AmazonMarketingStreamSubscriptions_ListDspStreamSubscriptions` | List subscriptions Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-Account-ID Parameter in:… | | write | `AmazonMarketingStreamSubscriptions_CreateDspStreamSubscription` | Create a new subscription Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-Account-ID Parameter… | | read | `AmazonMarketingStreamSubscriptions_GetDspStreamSubscription` | Fetch a specific subscription by Id Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-Acc... | | write | `AmazonMarketingStreamSubscriptions_UpdateDspStreamSubscription` | Update an existing subscription Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-Account-ID… | | read | `AmazonMarketingStreamSubscriptions_ListStreamSubscriptions` | List subscriptions Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in:… | | write | `AmazonMarketingStreamSubscriptions_CreateStreamSubscription` | Create a new subscription Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter… | | read | `AmazonMarketingStreamSubscriptions_GetStreamSubscription` | Fetch a specific subscription by Id Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-Acc... | | write | `AmazonMarketingStreamSubscriptions_UpdateStreamSubscription` | Update an existing subscription Note: trailing slash in request uri is not allowed Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId… | ### AMCAdAudience | Access | Tool | Description | | --- | --- | --- | | write | `AMCAdAudience_AmcpLinkRemoveConnectionV2` | Delete a connection between the Partner and Advertiser's AMC Instances and/or DSP Advertisers. | | read | `AMCAdAudience_AmcpLinkGetConnectionsV2` | Get a list of connections between the Partner and Advertiser's AMC Instances & DSP Advertisers. | | write | `AMCAdAudience_AmcpLinkAddConnectionV2` | Create a new connection between the Partner and Advertiser's AMC Instances and/or DSP Advertisers. | | read | `AMCAdAudience_AmcpLinkGetTermsV2` | Get the Customer's AMC Terms and Conditions acceptance. | | write | `AMCAdAudience_AmcpLinkSetTermsAcceptanceV2` | Set the Customer's AMC Terms and Conditions acceptance. | | write | `AMCAdAudience_CreateAudienceMetadataV2` | Create a new Advertiser Audience Metadata. | | read | `AMCAdAudience_GetAudienceMetadataV2` | Get an Advertiser Audience Metadata using AudienceId. | | write | `AMCAdAudience_UpdateAudienceMetadataV2` | Update an existing Advertiser Audience Metadata. | | write | `AMCAdAudience_ManageAudienceV2` | Manage Advertiser audiences by adding or removing members from an Audience. | | read | `AMCAdAudience_ManageAudienceStatusV2` | Get the status of a manage audience members request. | ### AMCAdmin | Access | Tool | Description | | --- | --- | --- | | read | `AMCAdmin_AmcpLinkListAmcAccounts` | Get a list of AMC Accounts that the user have access to. | | read | `AMCAdmin_listInstances` | Gets information about all AMC instances that the requesting entity has access to. | | write | `AMCAdmin_createInstance` | Creates a new AMC instance. | | write | `AMCAdmin_deleteInstance` | Deletes the requested AMC instance. | | read | `AMCAdmin_getInstance` | Gets information about the requested AMC instance. | | write | `AMCAdmin_updateInstance` | Updates the requested AMC instance. | | read | `AMCAdmin_getInstanceAdvertisers` | Gets advertisers information about the requested AMC instance. | | read | `AMCAdmin_listAdvertiserUpdates` | Gets advertiser updates for the requested AMC instance. | | write | `AMCAdmin_createAdvertiserUpdate` | Creates a new advertiser update for the requested AMC instance. | | read | `AMCAdmin_getAdvertiserUpdate` | Gets the requested advertiser update for the requested AMC instance. | | read | `AMCAdmin_getInstanceCollaboration` | Gets the collaboration metadata for the requested AMC instance. | | write | `AMCAdmin_createCollaborationIdMappingTable` | Creates an ID Mapping Table in the requested AMC instance collaboration and starts the job to populate the table. | | read | `AMCAdmin_listCollaborationIdMappingTables` | Lists the ID mapping tables in the collaboration in the requested AMC instance. | | write | `AMCAdmin_deleteCollaborationIdMappingTable` | Deletes the given ID Mapping Table in the collaboration for the requested AMC instance. | | read | `AMCAdmin_getCollaborationIdMappingJobForTrackingId` | Retrieves the ID mapping workflow job associated to the tracking ID. | | read | `AMCAdmin_listCollaborationIdMappingJobs` | Lists the jobs associated to the given ID mapping table in the collaboration for the requested AMC instance. | | read | `AMCAdmin_getCollaborationIdMappingJob` | Gets the metadata of the job associated to the ID Mapping Table in the collaboration for the requested AMC instance. | | write | `AMCAdmin_refreshCollaborationIdMappingTable` | Starts a workflow job to refresh the data in the given ID Mapping Table in the collaboration for the requested AMC instance. | | read | `AMCAdmin_listCollaborationIdNamespaces` | Lists the advertiser ID namespaces that are not connected to an ID mapping table in the collaboration in the requested AMC instance. | | write | `AMCAdmin_updateInstanceCustomerAwsAccountMetadata` | Updates customer's AWS account metadata in the requested AMC instance. | ### AMCRuleAudience | Access | Tool | Description | | --- | --- | --- | | write | `AMCRuleAudience_createLookalikeAudience` | Creates a lookalike audience execution metadata. | | read | `AMCRuleAudience_getAllQueryBasedAudiencesByInstanceId` | Returns list of execution metadata information for a given instanceId. | | write | `AMCRuleAudience_createQueryBasedAudience` | Creates a query based audience execution metadata. | | write | `AMCRuleAudience_deleteQueryBasedAudienceByAudienceExecutionId` | Deletes an audience for a given instanceId and audienceExecutionId. | | read | `AMCRuleAudience_getQueryBasedAudienceByAudienceExecutionId` | Returns execution metadata information for a given instanceId and audienceExecutionId. | | write | `AMCRuleAudience_updateQueryBasedAudienceByAudienceExecutionId` | Updates audience configuration for a given audienceExecutionId. | ### AMCWorkflow | Access | Tool | Description | | --- | --- | --- | | read | `AMCWorkflow_listDataSources` | Returns a list of available data sources. | | read | `AMCWorkflow_getDataSource` | Gets information about the requested data source. | | read | `AMCWorkflow_listSchedules` | Returns a list of schedules. | | write | `AMCWorkflow_createSchedule` | Creates a new schedule. | | write | `AMCWorkflow_deleteSchedule` | Deletes the requested schedule. | | read | `AMCWorkflow_getSchedule` | Gets the requested schedule. | | write | `AMCWorkflow_updateSchedule` | Updates the requested schedule. | | read | `AMCWorkflow_listWorkflowExecutions` | Returns a list of workflow executions. | | write | `AMCWorkflow_createWorkflowExecution` | Creates a new, ad-hoc execution of an existing workflow. | | read | `AMCWorkflow_getWorkflowExecution` | Gets status information about the requested workflow execution. | | write | `AMCWorkflow_UpdateWorkflowExecution` | Updates the requested workflow execution. | | read | `AMCWorkflow_getWorkflowExecutionDownloadUrls` | Generates and returns pre-signed S3 URLs for the result files produced by and metadata used by the provided workflow execution. | | read | `AMCWorkflow_listWorkflows` | Returns a list of workflows. | | write | `AMCWorkflow_createWorkflow` | Creates a new workflow. | | write | `AMCWorkflow_deleteWorkflow` | Deletes the requested workflow. | | read | `AMCWorkflow_getWorkflow` | Gets the requested workflow. | | write | `AMCWorkflow_updateWorkflow` | Updates the requested workflow, using the request body directly as the new workflow definition. | ### AudiencesDiscovery | Access | Tool | Description | | --- | --- | --- | | read | `AudiencesDiscovery_listAudiences` | Returns a list of audience segments for an advertiser. | | read | `AudiencesDiscovery_fetchTaxonomy` | Returns a list of audience categories for a given category path Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Advertising-AccountId Param... | | write | `AudiencesDiscovery_DspAudienceDelete` | Deletes an existing targeting audience based on audience ID. | | write | `AudiencesDiscovery_DspAudienceEdit` | Updates an existing targeting audience based on an audience definition and audience ID. | ### BrandBenchmarks | Access | Tool | Description | | --- | --- | --- | | read | `BrandBenchmarks_ListAdvertiserReportMetadata` | Gets all of the report metadata the specified advertiser at the specified marketplace. | | read | `BrandBenchmarks_GetAdvertiserReport` | Gets the download link for an advertiser's metric report in the specified marketplace. | ### BrandMetrics | Access | Tool | Description | | --- | --- | --- | | write | `BrandMetrics_generateBrandMetricsReport` | Generates the Brand Metrics report in CSV or JSON format. | | read | `BrandMetrics_getBrandMetricsReport` | Fetch the location and status of the report for the brands for which the metrics are available. | ### BrandStoresManagement | Access | Tool | Description | | --- | --- | --- | | read | `BrandStoresManagement_ListBrandStoreEdition` | Requires one of these permissions: ["amazon_stores_edit","amazon_stores_view"] | | read | `BrandStoresManagement_QueryBrandStoreEditionPublishVersion` | A search read, allowing use of more complex filters. | | read | `BrandStoresManagement_QueryBrandStorePage` | A search read, allowing use of more complex filters. | | write | `BrandStoresManagement_UpdateBrandStoreEditionPublishVersion` | Updates BrandStoreEditionPublishVersions. | | write | `BrandStoresManagement_UpdateBrandStorePage` | Updates BrandStorePages. | ### CampaignConversionTracking | Access | Tool | Description | | --- | --- | --- | | read | `CampaignConversionTracking_DspGetCampaignConversionTrackingProductsV1` | Gets the conversion tracking products for a given campaign. | | write | `CampaignConversionTracking_DspPostProductConversionTrackingV1` | Adds products to a campaign to enable product-related conversion metrics. | | write | `CampaignConversionTracking_DspDeleteProductConversionTrackingV1` | Removes one or more products from campaign conversion tracking. | ### CampaignManage | Access | Tool | Description | | --- | --- | --- | | write | `CampaignManage_CreateAdAssociation` | Creates AdAssociations. | | write | `CampaignManage_CreateAdGroup` | Creates AdGroups. | | write | `CampaignManage_CreateAd` | Creates Ads. | | write | `CampaignManage_CreateCampaign` | Creates Campaigns. | | write | `CampaignManage_CreateTarget` | Creates Targets. | | write | `CampaignManage_DeleteAdAssociation` | Archives or deletes AdAssociations. | | write | `CampaignManage_DeleteAdGroup` | Archives or deletes AdGroups. | | write | `CampaignManage_DeleteAd` | Archives or deletes Ads. | | write | `CampaignManage_DeleteCampaign` | Archives or deletes Campaigns. | | write | `CampaignManage_DeleteTarget` | Archives or deletes Targets. | | read | `CampaignManage_QueryAdAssociation` | A search read, allowing use of more complex filters. | | read | `CampaignManage_QueryAdGroup` | A search read, allowing use of more complex filters. | | read | `CampaignManage_QueryAd` | A search read, allowing use of more complex filters. | | read | `CampaignManage_QueryCampaign` | A search read, allowing use of more complex filters. | | read | `CampaignManage_QueryTarget` | A search read, allowing use of more complex filters. | | write | `CampaignManage_UpdateAdAssociation` | Updates AdAssociations. | | write | `CampaignManage_UpdateAdGroup` | Updates AdGroups. | | write | `CampaignManage_UpdateAd` | Updates Ads. | | write | `CampaignManage_UpdateCampaign` | Updates Campaigns. | | write | `CampaignManage_UpdateTarget` | Updates Targets. | ### ChangeHistory | Access | Tool | Description | | --- | --- | --- | | read | `ChangeHistory_getHistory` | Returns history of changes for provided event sources that match the filters and time ranges specified. | ### Conversions | Access | Tool | Description | | --- | --- | --- | | read | `Conversions_dspAmazonAdTagGetEventsByAdTagId` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | read | `Conversions_dspAmazonAdTagGetAdTagByAdvertiserId` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | read | `Conversions_dspAmazonBatchGetConversionDefinitionsForOrders` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_view"] | | write | `Conversions_dspAmazonCreateConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | write | `Conversions_dspAmazonUpdateConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | write | `Conversions_dspAmazonDeletionRequest` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `Conversions_dspAmazonIngestConversionData` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | read | `Conversions_dspAmazonListConversionDefinitions` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | read | `Conversions_dspAmazonGetAdTagAssociatedEvent` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | write | `Conversions_dspAmazonUpdateAdTagAssociatedEvent` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manag... | | read | `Conversions_dspAmazonGetAssociatedMobileAppForConversionDefinition` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | write | `Conversions_dspAmazonBatchCreateMobileMeasurementPartnerAppRegistration` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `Conversions_dspAmazonBatchUpdateMobileMeasurementPartnerAppRegistration` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | write | `Conversions_dspAmazonDeleteMeasurementPartnerAppRegistrations` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_edit"] | | read | `Conversions_dspAmazonListMobileMeasurementPartnerAppRegistrations` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["event_manager_view"] | | read | `Conversions_dspAmazonGetAssociatedConversionDefinitionsForOrder` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_view"] | | write | `Conversions_dspAmazonUpdateAssociatedConversionDefinitionsForOrder` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions: ["campaign_edit"] | ### CreativesAssets | Access | Tool | Description | | --- | --- | --- | | read | `CreativesAssets_getAsset` | Retrieve an asset | | write | `CreativesAssets_assetsBatchRegister` | This is an asynchronous api that provides clients an identifier for their batch registration request. | | read | `CreativesAssets_getAssetsBatchRegister` | Retrieves status of the batch asset registration request, uniquely identified by requestId. | | write | `CreativesAssets_registerAsset` | The API should be called once the asset is uploaded to the location provided by the /asset/upload API endpoint. | | read | `CreativesAssets_searchAssets` | Search assets | | read | `CreativesAssets_getUploadLocation` | Creates an ephemeral resource (upload location) to upload Assets to Creative Assets tool. | ### ExportsSnapshots | Access | Tool | Description | | --- | --- | --- | | write | `ExportsSnapshots_AdGroupExport` | Creates a file-based export of Ad Groups in the account satisfying the filtering criteria. | | write | `ExportsSnapshots_AdExport` | Creates a file-based export of Ads in the account satisfying the filtering criteria. | | write | `ExportsSnapshots_CampaignExport` | Creates a file-based export of Campaigns in the account satisfying the filtering criteria. | | read | `ExportsSnapshots_GetExport` | This API will return a status of the specified export. | | write | `ExportsSnapshots_TargetExport` | Creates a file-based export of Targets in the account satisfying the filtering criteria. | ### Forecasts | Access | Tool | Description | | --- | --- | --- | | write | `Forecasts_DSPRetrieveCampaignForecast` | A retrieve by ID read. | ### Locations | Access | Tool | Description | | --- | --- | --- | | read | `Locations_listLocations` | Note: This endpoint is currently limited to US only. | ### MediaPlanningReachForecasting | Access | Tool | Description | | --- | --- | --- | | write | `MediaPlanningReachForecasting_CreateDeduplicatedReachForecastsV1` | Creates a list of De-duplicated Reach Forecasts. | | write | `MediaPlanningReachForecasting_CreateReachForecastsV1` | Creates a list of new Reach Forecasts in bulk action. | | read | `MediaPlanningReachForecasting_ListReachForecastsV1` | Gets a list of Reach Forecasts by IDs Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these… | | read | `MediaPlanningReachForecasting_ListReachForecastTargetsV1` | Gets a list of targets of a Reach Forecast Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these… | ### ModerationResults | Access | Tool | Description | | --- | --- | --- | | write | `ModerationResults_moderationResults` | API to get the moderation results for the ad. | ### ProductsEligibility | Access | Tool | Description | | --- | --- | --- | | write | `ProductsEligibility_productEligibility` | Gets a list of advertising eligibility objects for a set of products. | | write | `ProductsEligibility_ProgramEligibility` | Checks the advertiser's eligibility to ad programs. | ### ProductsMetadata | Access | Tool | Description | | --- | --- | --- | | write | `ProductsMetadata_ProductMetadata` | Authorized resource type: DSP Rodeo Entity ID, DSP Advertiser Account ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions:… | ### RecommendationsAudienceInsights | Access | Tool | Description | | --- | --- | --- | | read | `RecommendationsAudienceInsights_insightsGetAudiencesOverlappingAudiences` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | ### RecommendationsInsightsPartnerOpportunities | Access | Tool | Description | | --- | --- | --- | | read | `RecommendationsInsightsPartnerOpportunities_partnerOpportunitiesListOpportunities` | Gets a list of opportunities specific to the partner making the request. | | read | `RecommendationsInsightsPartnerOpportunities_partnerOpportunitiesSummarizeOpportunities` | Gets aggregated information about all opportunities specific to the partner making the request. | | write | `RecommendationsInsightsPartnerOpportunities_partnerOpportunitiesApplicationStatus` | Retrieves the current status of applied recommendations. | | write | `RecommendationsInsightsPartnerOpportunities_partnerOpportunitiesApply` | Applies a given set of recommendations. | | read | `RecommendationsInsightsPartnerOpportunities_partnerOpportunitiesGetOpportunityFile` | Gets a 307 - TEMPORARY_REDIRECT to an opportunity data file. | ### RecommendationsInsightsPersonaBuilder | Access | Tool | Description | | --- | --- | --- | | write | `RecommendationsInsightsPersonaBuilder_BandedSize` | Get banded size of number of unique customers that are in the input expression. | | write | `RecommendationsInsightsPersonaBuilder_Demographics` | Get demographic insights for the input expression. | | write | `RecommendationsInsightsPersonaBuilder_PrimeVideo` | Get Prime Video insights for the input expression. | | write | `RecommendationsInsightsPersonaBuilder_TopCategoriesPurchased` | Get insights on top retail categories purchased by customers in the input expression. | | write | `RecommendationsInsightsPersonaBuilder_TopOverlappingAudiences` | Get top audiences overlapping with the input expression. | ### RecommendationsInsightsTacticalRecommendations | Access | Tool | Description | | --- | --- | --- | | write | `RecommendationsInsightsTacticalRecommendations_ApplyRecommendations` | Applies one or more recommendations. | | read | `RecommendationsInsightsTacticalRecommendations_ListRecommendations` | Retrieves a paginated list of recommendations with optional filtering. | | write | `RecommendationsInsightsTacticalRecommendations_UpdateRecommendation` | Updates a recommendation. | ### ReportingMarketingMixModeling | Access | Tool | Description | | --- | --- | --- | | read | `ReportingMarketingMixModeling_listMmmBrandGroups` | Lists the predefined brand groups for which reports may be requested. | | write | `ReportingMarketingMixModeling_createMmmReport` | Creates a report. | | write | `ReportingMarketingMixModeling_deleteMmmReport` | Deletes a report by ID. | | read | `ReportingMarketingMixModeling_getMmmReport` | Gets the generation status of a report by ID. | ### ReportingVersion3 | Access | Tool | Description | | --- | --- | --- | | write | `ReportingVersion3_createAsyncReport` | Creates a report request. | | write | `ReportingVersion3_deleteAsyncReport` | Deletes a report by id. | | read | `ReportingVersion3_getAsyncReport` | Gets a generation status of a report by id. | ### SponsoredBrandsV3 | Access | Tool | Description | | --- | --- | --- | | read | `SponsoredBrandsV3_getBrands` | Gets an array of Brand data objects for the Brand associated with the profile ID passed in the header. | | write | `SponsoredBrandsV3_completeUpload` | The API should be called once the media is uploaded to the location provided by the /media/upload API endpoint. | | read | `SponsoredBrandsV3_describeMedia` | API to poll for media status. | | read | `SponsoredBrandsV3_listAsins` | Note that for sellers, the addresss must be a Store page. | | read | `SponsoredBrandsV3_SBGetBudgetRulesRecommendation` | A rule enables an automatic budget increase for a specified date range or for a special event. | | read | `SponsoredBrandsV3_listKeywords` | Note: Keywords associated with BrandVideo ad groups are only available in v3.2 version. | | write | `SponsoredBrandsV3_createKeywords` | Note that state can't be set at keyword creation. | | write | `SponsoredBrandsV3_updateKeywords` | Updates one or more targeting clauses. | | write | `SponsoredBrandsV3_archiveKeyword` | This operation is equivalent to an update operation that sets the status field to 'archived'. | | read | `SponsoredBrandsV3_getKeyword` | Gets a keyword specified by identifier. | | read | `SponsoredBrandsV3_listNegativeKeywords` | Note: Negative keywords associated with BrandVideo ad groups are only available in v3.2 version. | | write | `SponsoredBrandsV3_createNegativeKeywords` | Creates one or more negative targeting clauses. | | write | `SponsoredBrandsV3_updateNegativeKeywords` | Updates one or more targeting clauses. | | write | `SponsoredBrandsV3_archiveNegativeKeyword` | This operation is equivalent to an update operation that sets the status field to 'archived'. | | read | `SponsoredBrandsV3_getNegativeKeyword` | Gets a negative keyword specified by identifier. | | write | `SponsoredBrandsV3_createNegativeTargets` | Create one or more negative targets. | | write | `SponsoredBrandsV3_updateNegativeTargets` | Updates one or more negative targets. | | read | `SponsoredBrandsV3_listNegativeTargets` | Note: Negative targets associated with BrandVideo ad groups are only available in v3.2 version. | | write | `SponsoredBrandsV3_archiveNegativeTarget` | Archives a negative target specified by identifier. | | read | `SponsoredBrandsV3_getNegativeTarget` | Gets a negative target specified by identifier. | | read | `SponsoredBrandsV3_getBidsRecommendations` | Get a list of bid recommendation objects for a specified list of keywords or products. | | read | `SponsoredBrandsV3_getKeywordRecommendations` | Gets an array of keyword recommendation objects for a set of ASINs included either on a landing page or a Stores page. | | read | `SponsoredBrandsV3_getBrandRecommendations` | The Brand suggestions are based on a list of either category identifiers or keywords passed in the request. | | read | `SponsoredBrandsV3_getTargetingCategories` | Recommendations are based on the ASINs that are passed in the request. | | read | `SponsoredBrandsV3_getProductRecommendations` | Recommendations are based on the ASINs that are passed in the request. | | write | `SponsoredBrandsV3_createTargets` | Create one or more targets. | | write | `SponsoredBrandsV3_updateTargets` | Updates one or more targets. | | read | `SponsoredBrandsV3_listTargets` | Gets a list of product targets associated with the client identifier passed in the authorization header, filtered by specified criteria. | | write | `SponsoredBrandsV3_archiveTarget` | The identifier of an existing target. | | read | `SponsoredBrandsV3_getTarget` | Gets a target specified by identifier. | | write | `SponsoredBrandsV3_sbCreateThemes` | Note that this endpoint does not support for Author profiles. | | write | `SponsoredBrandsV3_sbUpdateThemes` | Note that this endpoint does not support for Author profiles. | | read | `SponsoredBrandsV3_sbListThemes` | Note that this endpoint does not support for Author profiles. | | read | `SponsoredBrandsV3_listAssets` | For sellers or vendors, gets an array of assets associated with the specified brand entity identifier. | | write | `SponsoredBrandsV3_createAsset` | Image assets are stored in the Store Assets Library. | | read | `SponsoredBrandsV3_downloadReport` | Gets a 307 Temporary Redirect response that includes a location header with the value set to an AWS S3 path where the report is located. | ### SponsoredBrandsV4 | Access | Tool | Description | | --- | --- | --- | | write | `SponsoredBrandsV4_CreateBrandVideoCreative` | This API creates a new version of an existing creative for given Sponsored Brands Ad by supplying brand video creative content Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredBrandsV4_ListCreatives` | This API gets an array of all Sponsored Brands creatives that qualify the given resource identifiers and filters Requires one of these permissions:… | | write | `SponsoredBrandsV4_CreateProductCollectionCreative` | This API creates a new version of creative for given Sponsored Brands ad by supplying product collection creative content Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredBrandsV4_CreateExtendedProductCollectionCreative` | This API creates a new version of creative for given Sponsored Brands ad by supplying extended product collection creative content Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredBrandsV4_CreateStoreSpotlightCreative` | This API creates a new version of creative for given Sponsored Brands ad by supplying store spotlight creative content Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredBrandsV4_CreateVideoCreative` | This API creates a new version of an existing creative for given Sponsored Brands ad by supplying video creative content Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredBrandsV4_GetSBBudgetRulesForAdvertiser` | Get budget rules | | write | `SponsoredBrandsV4_CreateBudgetRulesForSBCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredBrandsV4_UpdateBudgetRulesForSBCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredBrandsV4_GetBudgetRuleByRuleIdForSBCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredBrandsV4_GetCampaignsAssociatedWithSBBudgetRule` | Get campaigns associated with budget rule | | write | `SponsoredBrandsV4_sbCampaignsBudgetUsage` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredBrandsV4_GetBudgetRecommendations` | Provides daily budget recommendations for a list of requested Sponsored Brands campaigns, with context on estimated historical missed opportunities. | | write | `SponsoredBrandsV4_SBInsightsCampaignInsights` | Creates campaign level insights. | | read | `SponsoredBrandsV4_ListAssociatedBudgetRulesForSBCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredBrandsV4_CreateAssociatedBudgetRulesForSBCampaigns` | A maximum of 250 rules can be associated to a campaign. | | write | `SponsoredBrandsV4_DisassociateAssociatedBudgetRuleForSBCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredBrandsV4_SBCampaignPerformanceForecasts` | Returns forecasts for a list of new campaigns specified in SB forecast request. | | read | `SponsoredBrandsV4_SBTargetingGetNegativeBrands` | Returns brands recommended for negative targeting. | | read | `SponsoredBrandsV4_getHeadlineRecommendations` | API to receive creative headline suggestions. | | write | `SponsoredBrandsV4_SBOptimizationRecommendation` | Returns recommended bid value for optimization rule enable campaigns. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsOptimizationRules` | Currently available in beta. | | write | `SponsoredBrandsV4_UpdateSponsoredBrandsOptimizationRules` | Currently available in beta. | | write | `SponsoredBrandsV4_AssociateSponsoredBrandsOptimizationRules` | Currently available in beta. | | write | `SponsoredBrandsV4_DisassociateSponsoredBrandsOptimizationRules` | Currently available in beta. | | read | `SponsoredBrandsV4_ListSponsoredBrandsOptimizationRules` | Currently available in beta. | | read | `SponsoredBrandsV4_SBTargetingGetTargetableCategories` | Returns all targetable categories by default in a list. | | read | `SponsoredBrandsV4_SBTargetingGetRefinementsForCategory` | Returns refinements according to category input. | | read | `SponsoredBrandsV4_SBTargetingGetTargetableASINCounts` | Get number of targetable asins based on refinements provided by the user. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsAdGroups` | Creates Sponsored Brands ad groups. | | write | `SponsoredBrandsV4_UpdateSponsoredBrandsAdGroups` | Updates Sponsored Brands ad groups. | | write | `SponsoredBrandsV4_DeleteSponsoredBrandsAdGroups` | Deletes Sponsored Brands ad groups. | | read | `SponsoredBrandsV4_ListSponsoredBrandsAdGroups` | Lists Sponsored Brands ad groups. | | write | `SponsoredBrandsV4_UpdateSponsoredBrandsAds` | Updates Sponsored Brands ads. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsBrandVideoAds` | Creates Sponsored Brands brand video ads. | | write | `SponsoredBrandsV4_DeleteSponsoredBrandsAds` | Deletes Sponsored Brands ads. | | read | `SponsoredBrandsV4_ListSponsoredBrandsAds` | Lists Sponsored Brands ads. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsProductCollectionAds` | Creates Sponsored Brands product collection ads. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsExtendedProductCollectionAds` | Creates Sponsored Brands product collection ads with collection of custom images[1-5]. | | write | `SponsoredBrandsV4_CreateSponsoredBrandStoreSpotlightAds` | Creates Sponsored Brands store spotlight ads. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsVideoAds` | Creates Sponsored Brands video ads. | | write | `SponsoredBrandsV4_CreateSponsoredBrandsCampaigns` | Creates Sponsored Brands campaigns. | | write | `SponsoredBrandsV4_UpdateSponsoredBrandsCampaigns` | Updates Sponsored Brands campaigns. | | write | `SponsoredBrandsV4_DeleteSponsoredBrandsCampaigns` | Deletes Sponsored Brands campaigns. | | read | `SponsoredBrandsV4_ListSponsoredBrandsCampaigns` | Lists Sponsored Brands campaigns. | | write | `SponsoredBrandsV4_StartMigrationJob` | Creates Migration Job for V3 campaigns. | | write | `SponsoredBrandsV4_MigrationJobResults` | List Migration Results of all Campaign. | | write | `SponsoredBrandsV4_MigrationJobStatus` | List Migration Job Status. | | write | `SponsoredBrandsV4_MigrationResults` | Lists all Campaign Migration results for an advertiser | ### SponsoredDisplay | Access | Tool | Description | | --- | --- | --- | | read | `SponsoredDisplay_listAdGroups` | Gets an array of AdGroup objects for a requested set of Sponsored Display ad groups. | | write | `SponsoredDisplay_createAdGroups` | Creates one or more ad groups. | | write | `SponsoredDisplay_updateAdGroups` | Updates on or more ad groups. | | read | `SponsoredDisplay_listAdGroupsEx` | Gets an array of AdGroupResponseEx objects for a set of requested ad groups. | | read | `SponsoredDisplay_getAdGroupResponseEx` | Gets extended information for a requested ad group. | | write | `SponsoredDisplay_archiveAdGroup` | This operation is equivalent to an update operation that sets the status field to 'archived'. | | read | `SponsoredDisplay_getAdGroup` | Returns an AdGroup object for a requested campaign. | | write | `SponsoredDisplay_associateOptimizationRulesWithAdGroup` | When an optimization rule is associated to an ad group, manual bids for individual targets will be overridden. | | write | `SponsoredDisplay_disassociateOptimizationRulesFromAdGroup` | Only one optimization rule can be disassociated per adGroup. | | write | `SponsoredDisplay_deleteBrandSafetyDenyList` | Archives all of the domains in the Brand Safety Deny List. | | read | `SponsoredDisplay_listDomains` | Gets an array of websites/apps that are on the advertiser's Brand Safety Deny List. | | write | `SponsoredDisplay_createBrandSafetyDenyListDomains` | Creates one or more domains to add to a Brand Safety Deny List. | | read | `SponsoredDisplay_listRequestStatus` | List status of all Brand Safety List requests. | | read | `SponsoredDisplay_getRequestResults` | When a user adds domains to their Brand Safety Deny List, the request is processed asynchronously, and a requestId is provided to the user. | | read | `SponsoredDisplay_getRequestStatus` | When a user modifies their Brand Safety Deny List, the request is processed asynchronously, and a requestId is provided to the user. | | read | `SponsoredDisplay_GetSDBudgetRulesForAdvertiser` | Get all budget rules created by an advertiser | | write | `SponsoredDisplay_CreateBudgetRulesForSDCampaigns` | Creates one or more budget rules. | | write | `SponsoredDisplay_UpdateBudgetRulesForSDCampaigns` | Update one or more budget rules. | | read | `SponsoredDisplay_GetBudgetRuleByRuleIdForSDCampaigns` | Gets a budget rule specified by identifier. | | read | `SponsoredDisplay_GetCampaignsAssociatedWithSDBudgetRule` | Gets all the campaigns associated with a budget rule | | read | `SponsoredDisplay_listCampaigns` | Gets an array of Campaign objects for a requested set of Sponsored Display campaigns. | | write | `SponsoredDisplay_createCampaigns` | Creates one or more campaigns. | | write | `SponsoredDisplay_updateCampaigns` | Updates one or more campaigns. | | write | `SponsoredDisplay_sdCampaignsBudgetUsage` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredDisplay_getSDBudgetRecommendations` | Given a list of campaigns as input, this API provides the following metrics: 1. | | read | `SponsoredDisplay_listCampaignsEx` | Gets an array of CampaignResponseEx objects for a set of requested campaigns. | | read | `SponsoredDisplay_getCampaignResponseEx` | Returns a CampaignResponseEx object for a requested campaign. | | write | `SponsoredDisplay_archiveCampaign` | This operation is equivalent to an update operation that sets the status field to 'archived'. | | read | `SponsoredDisplay_getCampaign` | Returns a Campaign object for a requested campaign. | | read | `SponsoredDisplay_ListAssociatedBudgetRulesForSDCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredDisplay_CreateAssociatedBudgetRulesForSDCampaigns` | A maximum of 250 rules can be associated to a campaign. | | write | `SponsoredDisplay_DisassociateAssociatedBudgetRuleForSDCampaigns` | Disassociates a budget rule specified by identifier from a campaign specified by identifier. | | read | `SponsoredDisplay_listCreatives` | Gets a list of creatives | | write | `SponsoredDisplay_createCreatives` | A POST request of one or more creatives. | | write | `SponsoredDisplay_updateCreatives` | Updates one or more creatives. | | write | `SponsoredDisplay_postCreativePreview` | Gets creative preview HTML. | | write | `SponsoredDisplay_createSDForecast` | Returns forecasts for a given ad group specified in SD forecast request. | | read | `SponsoredDisplay_listLocations` | Gets a list of Sponsored Display Location objects. | | write | `SponsoredDisplay_createLocations` | This resource is not available when productAds have ASIN or SKU fields and only available for advertisers that do not sell products on Amazon. | | write | `SponsoredDisplay_archiveLocations` | This is a bulk operation that accepts up to a limit of 1000 Location Expression Ids at a time. | | read | `SponsoredDisplay_listCreativeModerations` | Gets a list of creative moderations | | read | `SponsoredDisplay_listNegativeTargetingClauses` | Gets a list of negative targeting clauses objects for a requested set of Sponsored Display negative targets. | | write | `SponsoredDisplay_createNegativeTargetingClauses` | Successfully created negative targeting clauses associated with an ad group are assigned a unique target identifier. | | write | `SponsoredDisplay_updateNegativeTargetingClauses` | Updates one or more negative targeting clauses. | | read | `SponsoredDisplay_listNegativeTargetingClausesEx` | Gets an array of NegativeTargetingClauseEx objects for a set of requested negative targets. | | read | `SponsoredDisplay_getNegativeTargetsEx` | Gets a negative targeting clause with extended fields. | | write | `SponsoredDisplay_archiveNegativeTargetingClause` | Equivalent to using the updateNegativeTargetingClauses operation to set the state property of a targeting clause to archived. | | read | `SponsoredDisplay_getNegativeTargets` | This call returns the minimal set of negative targeting clause fields, but is more efficient than getNegativeTargetsEx. | | read | `SponsoredDisplay_listOptimizationRules` | Gets an array of OptimizationRule objects for a requested set of Sponsored Display optimization rules. | | write | `SponsoredDisplay_createOptimizationRules` | When an optimization rule is associated to an ad group, manual bids for individual targets will be overridden. | | write | `SponsoredDisplay_updateOptimizationRules` | Updates one or more optimization rules. | | read | `SponsoredDisplay_listProductAds` | Gets an array of ProductAd objects for a requested set of Sponsored Display product ads. | | write | `SponsoredDisplay_createProductAds` | Creates one or more product ads. | | write | `SponsoredDisplay_updateProductAds` | Updates one or more product ads. | | read | `SponsoredDisplay_listProductAdsEx` | Gets an array of ProductAdResponseEx objects for a set of requested ad groups. | | read | `SponsoredDisplay_getProductAdResponseEx` | Gets extended information for a product ad. | | write | `SponsoredDisplay_archiveProductAd` | This operation is equivalent to an update operation that sets the status field to 'archived'. | | read | `SponsoredDisplay_getProductAd` | Note that the ProductAd object is designed for performance, and includes a small set of commonly used fields to reduce size. | | read | `SponsoredDisplay_getHeadlineRecommendationsForSD` | You can use this Sponsored Display API to retrieve creative headline recommendations from an array of ASINs. | | read | `SponsoredDisplay_getSnapshot` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | | read | `SponsoredDisplay_downloadSnapshot` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | | read | `SponsoredDisplay_listTargetingClauses` | Gets a list of targeting clauses objects for a requested set of Sponsored Display targets. | | write | `SponsoredDisplay_createTargetingClauses` | Successfully created targeting clauses are assigned a unique targetId value. | | write | `SponsoredDisplay_updateTargetingClauses` | Updates one or more targeting clauses. | | read | `SponsoredDisplay_getTargetBidRecommendations` | Provides a list of bid recommendations based on the list of input advertised ASINs and targeting clauses in the same format as the targeting API. | | read | `SponsoredDisplay_listTargetingClausesEx` | Gets an array of TargetingClauseEx objects for a set of requested targets. | | read | `SponsoredDisplay_getTargetsEx` | Gets a targeting clause object with extended fields. | | read | `SponsoredDisplay_getTargetRecommendations` | This API provides product, category and standard audience recommendations to target based on the list of input ASINs. | | write | `SponsoredDisplay_archiveTargetingClause` | Equivalent to using the updateTargetingClauses operation to set the state property of a targeting clause to archived. | | read | `SponsoredDisplay_getTargets` | This call returns the minimal set of targeting clause fields. | | write | `SponsoredDisplay_requestReport` | To understand the call flow for asynchronous reports, see Getting started with sponsored ads reports. | | write | `SponsoredDisplay_createSnapshot` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | | read | `SponsoredDisplay_getReportStatus` | Uses the reportId value from the response of a report previously requested via POST method of the /sd/{recordType}/report operation. | | read | `SponsoredDisplay_downloadReport` | Gets a 307 Temporary Redirect response that includes a location header with the value set to an AWS S3 path where the report is located. | ### SponsoredProducts | Access | Tool | Description | | --- | --- | --- | | write | `SponsoredProducts_CreateSponsoredProductsAdGroups` | Create ad groups Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsAdGroups` | Update ad groups Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsAdGroups` | Delete ad groups Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsAdGroups` | List ad groups Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredProducts_GetSPBudgetRulesForAdvertiser` | Get all budget rules created by an advertiser | | write | `SponsoredProducts_CreateBudgetRulesForSPCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredProducts_UpdateBudgetRulesForSPCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_GetBudgetRuleByRuleIdForSPCampaigns` | Authorized resource type: Global Ad Account ID, Profile ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions:… | | read | `SponsoredProducts_GetCampaignsAssociatedWithSPBudgetRule` | Gets all the campaigns associated with a budget rule | | write | `SponsoredProducts_BulkBudgetRulesAssociationForSP` | A maximum of 250 rules can be associated to a campaign. | | write | `SponsoredProducts_BulkBudgetRulesDisAssociationForSP` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_getCampaignRecommendations` | Gets the top consolidated recommendations across bid, budget, targeting for SP campaigns given an advertiser profile id. | | write | `SponsoredProducts_CreateSponsoredProductsCampaignNegativeKeywords` | Create campaign negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsCampaignNegativeKeywords` | Update campaign negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsCampaignNegativeKeywords` | Delete campaign negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsCampaignNegativeKeywords` | List campaign negative keywords Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateSponsoredProductsCampaignNegativeTargetingClauses` | Create campaign negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsCampaignNegativeTargetingClauses` | Update campaign negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsCampaignNegativeTargetingClauses` | Delete campaign negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsCampaignNegativeTargetingClauses` | List campaign negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateSponsoredProductsCampaigns` | Create campaigns Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsCampaigns` | Update campaigns Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_spCampaignsBudgetUsage` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredProducts_getBudgetRecommendations` | Given a list of campaigns as input, this API provides the following metrics - 1. | | read | `SponsoredProducts_SPGetBudgetRulesRecommendation` | A rule enables an automatic budget increase for a specified date range or for a special event. | | write | `SponsoredProducts_DeleteSponsoredProductsCampaigns` | Delete campaigns Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_getBudgetRecommendation` | Creates daily budget recommendation along with benchmark metrics when creating a new campaign. | | read | `SponsoredProducts_ListSponsoredProductsCampaigns` | List campaigns Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredProducts_ListAssociatedBudgetRulesForSPCampaigns` | Authorized resource type: Global Ad Account ID, Profile ID Parameter name: Amazon-Ads-AccountId Parameter in: header Requires one of these permissions:… | | write | `SponsoredProducts_CreateAssociatedBudgetRulesForSPCampaigns` | A maximum of 250 rules can be associated to a campaign. | | write | `SponsoredProducts_DisassociateAssociatedBudgetRuleForSPCampaigns` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredProducts_AssociateOptimizationRulesToCampaign` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_GetMultiCountryThemeBasedBidRecommendationForAdGroup_v1` | The POST /sp/targets/bid/recommendations endpoint returns recommended bids for each target given either A) new ad group (a list of ad ASINs) or B) existing ad group (a campaign ID and ad grou... | | read | `SponsoredProducts_getGlobalRankedKeywordRecommendation` | The POST /sp/global/targets/keywords/recommendations/list endpoint returns recommended keyword targets for a list of countries given either A) a list of ad ASINs per target country or B) a gl... | | write | `SponsoredProducts_CreateSponsoredProductsKeywords` | Create keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsKeywords` | Update keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsKeywords` | Delete keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsKeywords` | List keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_CreateSponsoredProductsNegativeKeywords` | Create negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsNegativeKeywords` | Update negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsNegativeKeywords` | Delete negative keywords Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsNegativeKeywords` | List negative keywords Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateSponsoredProductsNegativeTargetingClauses` | Create negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsNegativeTargetingClauses` | Update negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_getNegativeBrands` | Returns brands recommended for negative targeting. | | read | `SponsoredProducts_searchBrands` | Returns up to 100 brands related to keyword input for negative targeting. | | write | `SponsoredProducts_DeleteSponsoredProductsNegativeTargetingClauses` | Delete negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsNegativeTargetingClauses` | List negative targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateSponsoredProductsProductAds` | Create product ads Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsProductAds` | Update product ads Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_DeleteSponsoredProductsProductAds` | Delete product ads Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_ListSponsoredProductsProductAds` | List product ads Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateOptimizationRule` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredProducts_UpdateOptimizationRule` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_GetOptimizationRuleEligibility` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredProducts_GetRuleNotification` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_DeleteCampaignOptimizationRule` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_GetCampaignOptimizationRule` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateOptimizationRules` | Requires one of these permissions: ["advertiser_campaign_edit"] | | write | `SponsoredProducts_UpdateOptimizationRules` | Requires one of these permissions: ["advertiser_campaign_edit"] | | read | `SponsoredProducts_SearchOptimizationRules` | Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | write | `SponsoredProducts_CreateTargetPromotionGroups` | Creates a target promotion group, by grouping the auto-targeting adGroupId and manual-targeting adGroups, divided by keyword targeting adGroups, and product targeting adGroups. | | read | `SponsoredProducts_ListTargetPromotionGroups` | Returns the target promotion groups for an advertiser and / or adGroupId, and / or target promotion group id. | | read | `SponsoredProducts_GetTargetPromotionGroupsRecommendations` | Retrieves keyword and product targets of an auto-targeting campaign as recommendations for promoting to a manual-targeting campaign. | | write | `SponsoredProducts_CreateTargetPromotionGroupTargets` | Creates keyword and/or product targets in the manual adGroup that are part of the target promotion group Requires one of these permissions: ["advertiser_campaign_edit","advertiser_campaign_view"] | | read | `SponsoredProducts_ListTargetPromotionGroupTargets` | Returns the targets created through target promotion groups for an advertiser and / or given target promotion group. | | read | `SponsoredProducts_getKeywordGroupRecommendations` | This API (currently beta) recommends Keyword Group targets for a given list of Ad ASINs. | | write | `SponsoredProducts_CreateSponsoredProductsTargetingClauses` | Create targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | write | `SponsoredProducts_UpdateSponsoredProductsTargetingClauses` | Update targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_GetThemeBasedBidRecommendationForAdGroup_v1` | The POST /sp/targets/bid/recommendations endpoint returns recommended bids for each target given either A) new ad group (a list of ad ASINs) or B) existing ad group (a campaign ID and ad grou... | | read | `SponsoredProducts_getTargetableCategories` | Returns all targetable categories. | | read | `SponsoredProducts_getCategoryRecommendationsForASINs` | Returns a list of category recommendations for the input list of ASINs. | | read | `SponsoredProducts_getRefinementsForCategory` | Returns refinements according to category input. | | write | `SponsoredProducts_DeleteSponsoredProductsTargetingClauses` | Delete targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_getRankedKeywordRecommendation` | The POST /sp/targets/keywords/recommendations endpoint returns recommended keyword targets given either A) a list of ad ASINs or B) a campaign ID and ad group ID. | | read | `SponsoredProducts_ListSponsoredProductsTargetingClauses` | List targeting clauses Requires one of these permissions: ["advertiser_campaign_edit","campaign_proposed"] | | read | `SponsoredProducts_getTargetableASINCounts` | Get number of targetable asins based on refinements provided by the user. | | read | `SponsoredProducts_getProductRecommendations` | Given an advertised ASIN as input, this API returns suggested ASINs to target in a product targeting campaign. | | read | `SponsoredProducts_SPGetAllRuleEvents` | A rule enables an automatic budget increase for a specified date range or for a special event. | ### SPSnapshotsSuggestedKeywords | Access | Tool | Description | | --- | --- | --- | | read | `SPSnapshotsSuggestedKeywords_getAdGroupBidRecommendations` | Deprecation notice: This endpoint will be deprecated on March 27, 2024. | | read | `SPSnapshotsSuggestedKeywords_getAdGroupSuggestedKeywords` | Gets suggested keywords for the specified ad group. | | read | `SPSnapshotsSuggestedKeywords_getAdGroupSuggestedKeywordsEx` | Gets suggested keywords with extended data for the specified ad group. | | read | `SPSnapshotsSuggestedKeywords_bulkGetAsinSuggestedKeywords` | Suggested keywords are returned in an array ordered by descending effectiveness. | | read | `SPSnapshotsSuggestedKeywords_getAsinSuggestedKeywords` | Suggested keywords are returned in an array ordered by descending effectiveness. | | write | `SPSnapshotsSuggestedKeywords_createKeywordBidRecommendations` | Deprecation notice: This endpoint will be deprecated on March 27, 2024. | | read | `SPSnapshotsSuggestedKeywords_getKeywordBidRecommendations` | Deprecation notice: This endpoint will be deprecated on March 27, 2024. | | read | `SPSnapshotsSuggestedKeywords_getSnapshotStatus` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | | read | `SPSnapshotsSuggestedKeywords_downloadSnapshot` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | | read | `SPSnapshotsSuggestedKeywords_getBidRecommendations` | Gets a list of bid recommendations for keyword, product, or auto targeting expressions. | | write | `SPSnapshotsSuggestedKeywords_requestSnapshot` | Note: Snapshots APIs are deprecated and will be shut off on October 15, 2024. | ### StoresAnalytics | Access | Tool | Description | | --- | --- | --- | | read | `StoresAnalytics_getAsinEngagementForStore` | Store asin metrics provides information about your store asin performance, including rendered impressions, viewed impressions, clicks and sales. | | read | `StoresAnalytics_getInsightsForStoreAPI` | Stores insights provides information about your store's performance, including traffic and sales. | ### TestAccount | Access | Tool | Description | | --- | --- | --- | | read | `TestAccount_GetAccountInformation` | API to get Account information. | | write | `TestAccount_createAccount` | Submit a account creation request. | ### UnifiedPreModerationResults | Access | Tool | Description | | --- | --- | --- | | write | `UnifiedPreModerationResults_preModeration` | This API will be accepting different components of the ad/page and will be automatically validating the components and send back the policy violations if any. | ### Amazon Selling Partner MCP Tools URL: https://www.kuudo.com/docs/mcp-reference/amazon-sp-tools/ This page lists every tool the Amazon Selling Partner MCP exposes, grouped by the underlying Selling Partner API resource. Each entry shows whether it's a read tool (safe to call freely) or a write tool (guarded, may require approval), the tool name, and a short description. Vendor Central tools aren't included yet — that's a separate MCP surface. See [Kuudo MCP Servers](/docs/mcp-reference/tools/) for the full index of Kuudo MCP servers. Expand any resource below to see its tools. Your browser's find-in-page (Ctrl+F / Cmd+F) searches across every tool on this page, even inside collapsed sections. ## Tool reference 51 resources with tools · 304 tools ### AplusContent | Access | Tool | Description | | --- | --- | --- | | write | `AplusContent_validateContentDocumentAsinRelations` | Checks if the A+ Content document is valid for use on a set of ASINs. | | read | `AplusContent_searchContentDocuments` | Returns a list of all A+ Content documents, including metadata, that are assigned to a selling partner. | | write | `AplusContent_createContentDocument` | Creates a new A+ Content document. | | read | `AplusContent_getContentDocument` | Returns an A+ Content document, if available. | | write | `AplusContent_updateContentDocument` | Updates an existing A+ Content document. | | write | `AplusContent_postContentDocumentApprovalSubmission` | Submits an A+ Content document for review, approval, and publishing. | | read | `AplusContent_listContentDocumentAsinRelations` | Returns a list of ASINs that are related to the specified A+ Content document, if available. | | write | `AplusContent_postContentDocumentAsinRelations` | Replaces all ASINs related to the specified A+ Content document, if available. | | write | `AplusContent_postContentDocumentSuspendSubmission` | Submits a request to suspend visible A+ Content. | | read | `AplusContent_searchContentPublishRecords` | Searches for A+ Content publishing records, if available. | ### AppIntegrations | Access | Tool | Description | | --- | --- | --- | | write | `AppIntegrations_createNotification` | Create a notification for sellers in Seller Central. | | write | `AppIntegrations_deleteNotifications` | Remove your application's notifications from the Appstore notifications dashboard. | | write | `AppIntegrations_recordActionFeedback` | Records the seller's response to a notification. | ### ApplicationManagement | Access | Tool | Description | | --- | --- | --- | | write | `ApplicationManagement_rotateApplicationClientSecret` | Rotates application client secrets for a developer application. | ### Awd | Access | Tool | Description | | --- | --- | --- | | write | `Awd_checkInboundEligibility` | Determines if the packages you specify are eligible for an AWD inbound order and contains error details for ineligible packages. | | write | `Awd_createInbound` | Creates a draft AWD inbound order with a list of packages for inbound shipment. | | read | `Awd_getInbound` | Retrieves an AWD inbound order. | | write | `Awd_updateInbound` | Updates an AWD inbound order that is in DRAFT status and not yet confirmed. | | write | `Awd_cancelInbound` | Cancels an AWD Inbound order and its associated shipment. | | write | `Awd_confirmInbound` | Confirms an AWD inbound order in DRAFT status. | | read | `Awd_listInboundShipments` | Retrieves a summary of all the inbound AWD shipments associated with a merchant, with the ability to apply optional filters. | | read | `Awd_getInboundShipment` | Retrieves an AWD inbound shipment. | | read | `Awd_getInboundShipmentLabels` | Retrieves the box labels for a shipment ID that you specify. | | write | `Awd_updateInboundShipmentTransportDetails` | Updates transport details for an AWD shipment. | | read | `Awd_listInventory` | Lists AWD inventory associated with a merchant with the ability to apply optional filters. | ### CatalogItems | Access | Tool | Description | | --- | --- | --- | | read | `CatalogItems_searchCatalogItems` | Search for a list of Amazon catalog items and item-related information. | | read | `CatalogItems_getCatalogItem` | Retrieves details for an item in the Amazon catalog. | ### CatalogItems20201201 | Access | Tool | Description | | --- | --- | --- | | read | `CatalogItems20201201_searchCatalogItems` | Search for and return a list of Amazon catalog items and associated information. | | read | `CatalogItems20201201_getCatalogItem` | Retrieves details for an item in the Amazon catalog. | ### CatalogItemsV0 | Access | Tool | Description | | --- | --- | --- | | read | `CatalogItemsV0_listCatalogCategories` | Returns the parent categories to which an item belongs, based on the specified ASIN or SellerSKU. | ### CustomerFeedback | Access | Tool | Description | | --- | --- | --- | | read | `CustomerFeedback_getBrowseNodeReturnTopics` | Retrieve the topics that customers mention when they return items in a browse node. | | read | `CustomerFeedback_getBrowseNodeReturnTrends` | Retrieve the trends of topics that customers mention when they return items in a browse node. | | read | `CustomerFeedback_getBrowseNodeReviewTopics` | Retrieve a browse node's ten most positive and ten most negative review topics. | | read | `CustomerFeedback_getBrowseNodeReviewTrends` | Retrieve the positive and negative review trends of items in a browse node for the past six months. | | read | `CustomerFeedback_getItemBrowseNode` | This API returns the associated browse node of the requested ASIN. | | read | `CustomerFeedback_getItemReviewTopics` | Retrieve an item's ten most positive and ten most negative review topics. | | read | `CustomerFeedback_getItemReviewTrends` | Retrieve an item's positive and negative review trends for the past six months. | ### DataKiosk | Access | Tool | Description | | --- | --- | --- | | read | `DataKiosk_getDocument` | Returns the information required for retrieving a Data Kiosk document's contents. | | read | `DataKiosk_getQueries` | Returns details for the Data Kiosk queries that match the specified filters. | | write | `DataKiosk_createQuery` | Creates a Data Kiosk query request. | | write | `DataKiosk_cancelQuery` | Cancels the query specified by the queryId parameter. | | read | `DataKiosk_getQuery` | Returns query details for the query specified by the queryId parameter. | ### DeliveryByAmazon | Access | Tool | Description | | --- | --- | --- | | write | `DeliveryByAmazon_submitInvoice` | Submits a shipment invoice for a given order or shipment. | | read | `DeliveryByAmazon_getInvoiceStatus` | Returns the invoice status for the order or shipment you specify. | ### EasyShip | Access | Tool | Description | | --- | --- | --- | | read | `EasyShip_getScheduledPackage` | Returns information about a package, including dimensions, weight, time slot information for handover, invoice and item information, and status. | | write | `EasyShip_updateScheduledPackages` | Updates the time slot for handing over the package indicated by the specified scheduledPackageId. | | write | `EasyShip_createScheduledPackage` | Schedules an Easy Ship order and returns the scheduled package information. | | write | `EasyShip_createScheduledPackageBulk` | This operation automatically schedules a time slot for all the amazonOrderIds given as input, generating the associated shipping labels, along with other compliance documents according to the… | | read | `EasyShip_listHandoverSlots` | Returns time slots available for Easy Ship orders to be scheduled based on the package weight and dimensions that the seller specifies. | ### ExternalFulfillmentInventory | Access | Tool | Description | | --- | --- | --- | | write | `ExternalFulfillmentInventory_batchInventory` | Make up to 10 inventory requests. | ### ExternalFulfillmentReturns | Access | Tool | Description | | --- | --- | --- | | read | `ExternalFulfillmentReturns_listReturns` | Retrieve a list of return items. | | read | `ExternalFulfillmentReturns_getReturn` | Retrieve the return item with the specified ID. | ### ExternalFulfillmentShipments | Access | Tool | Description | | --- | --- | --- | | read | `ExternalFulfillmentShipments_getShipments` | Get a list of shipments created for the seller in the status you specify. | | read | `ExternalFulfillmentShipments_getShipment` | Get a single shipment with the ID you specify. | | write | `ExternalFulfillmentShipments_processShipment` | Confirm or reject the specified shipment. | | read | `ExternalFulfillmentShipments_retrieveInvoice` | Retrieve invoices for the shipment you specify. | | write | `ExternalFulfillmentShipments_generateInvoice` | Get invoices for the shipment you specify. | | write | `ExternalFulfillmentShipments_createPackages` | Provide details about the packages in the specified shipment. | | write | `ExternalFulfillmentShipments_updatePackageStatus` | Updates the status of the packages. | | write | `ExternalFulfillmentShipments_updatePackage` | Updates the details about the packages that will be used to fulfill the specified shipment. | | write | `ExternalFulfillmentShipments_generateShipLabels` | Generate and retrieve all shipping labels for one or more packages in the shipment you specify. | | read | `ExternalFulfillmentShipments_retrieveShippingOptions` | Get a list of shipping options for a package in a shipment given the shipment's marketplace and channel. | ### FbaInboundEligibility | Access | Tool | Description | | --- | --- | --- | | read | `FbaInboundEligibility_getItemEligibilityPreview` | This operation gets an eligibility preview for an item that you specify. | ### FbaInventory | Access | Tool | Description | | --- | --- | --- | | write | `FbaInventory_createInventoryItem` | Requests that Amazon create product-details in the Sandbox Inventory in the sandbox environment. | | write | `FbaInventory_addInventory` | Requests that Amazon add items to the Sandbox Inventory with desired amount of quantity in the sandbox environment. | | write | `FbaInventory_deleteInventoryItem` | Requests that Amazon Deletes an item from the Sandbox Inventory in the sandbox environment. | | read | `FbaInventory_getInventorySummaries` | Returns a list of inventory summaries. | ### Feeds | Access | Tool | Description | | --- | --- | --- | | write | `Feeds_createFeedDocument` | Creates a feed document for the feed type that you specify. | | read | `Feeds_getFeedDocument` | Returns the information required for retrieving a feed document's contents. | | read | `Feeds_getFeeds` | Returns feed details for the feeds that match the filters that you specify. | | write | `Feeds_createFeed` | Creates a feed. | | write | `Feeds_cancelFeed` | Cancels the feed that you specify. | | read | `Feeds_getFeed` | Returns feed details (including the resultDocumentId, if available) for the feed that you specify. | ### Fees | Access | Tool | Description | | --- | --- | --- | | read | `Fees_getMyFeesEstimates` | Returns the estimated fees for a list of products. | | read | `Fees_getMyFeesEstimateForASIN` | Returns the estimated fees for the item indicated by the specified ASIN in the marketplace specified in the request body. | | read | `Fees_getMyFeesEstimateForSKU` | Returns the estimated fees for the item indicated by the specified seller SKU in the marketplace specified in the request body. | ### Finances | Access | Tool | Description | | --- | --- | --- | | read | `Finances_listTransactions` | Returns transactions for the given parameters. | ### FinancesV0 | Access | Tool | Description | | --- | --- | --- | | read | `FinancesV0_listFinancialEventGroups` | Returns financial event groups for a given date range. | | read | `FinancesV0_listFinancialEventsByGroupId` | Returns all financial events for the specified financial event group. | | read | `FinancesV0_listFinancialEvents` | Returns financial events for the specified data range. | | read | `FinancesV0_listFinancialEventsByOrderId` | Returns all financial events for the specified order. | ### FulfillmentInbound | Access | Tool | Description | | --- | --- | --- | | read | `FulfillmentInbound_listInboundPlans` | Provides a list of inbound plans with minimal information. | | write | `FulfillmentInbound_createInboundPlan` | Creates an inbound plan. | | read | `FulfillmentInbound_getInboundPlan` | Fetches the top level information about an inbound plan. | | read | `FulfillmentInbound_listInboundPlanBoxes` | Provides a paginated list of box packages in an inbound plan. | | write | `FulfillmentInbound_cancelInboundPlan` | Cancels an Inbound Plan. | | read | `FulfillmentInbound_listInboundPlanItems` | Provides a paginated list of item packages in an inbound plan. | | write | `FulfillmentInbound_updateInboundPlanName` | Updates the name of an existing inbound plan. | | read | `FulfillmentInbound_listPackingGroupBoxes` | Retrieves a page of boxes from a given packing group. | | read | `FulfillmentInbound_listPackingGroupItems` | Retrieves a page of items in a given packing group. | | write | `FulfillmentInbound_setPackingInformation` | Sets packing information for an inbound plan. | | read | `FulfillmentInbound_listPackingOptions` | Retrieves a list of all packing options for an inbound plan. | | write | `FulfillmentInbound_generatePackingOptions` | Generates available packing options for the inbound plan. | | write | `FulfillmentInbound_confirmPackingOption` | Confirms the packing option for an inbound plan. | | read | `FulfillmentInbound_listInboundPlanPallets` | Provides a paginated list of pallet packages in an inbound plan. | | read | `FulfillmentInbound_listPlacementOptions` | Provides a list of all placement options for an inbound plan. | | write | `FulfillmentInbound_generatePlacementOptions` | Generates placement options for the inbound plan. | | write | `FulfillmentInbound_confirmPlacementOption` | Confirms the placement option for an inbound plan. | | read | `FulfillmentInbound_getShipment` | Provides the full details for a specific shipment within an inbound plan. | | read | `FulfillmentInbound_listShipmentBoxes` | Provides a paginated list of box packages in a shipment. | | read | `FulfillmentInbound_listShipmentContentUpdatePreviews` | Retrieve a paginated list of shipment content update previews for a given shipment. | | write | `FulfillmentInbound_generateShipmentContentUpdatePreviews` | Generate a shipment content update preview given a set of intended boxes and/or items for a shipment with a confirmed carrier. | | read | `FulfillmentInbound_getShipmentContentUpdatePreview` | Retrieve a shipment content update preview which provides a summary of the requested shipment content changes along with the transportation cost implications of the change that can only be confirmed… | | write | `FulfillmentInbound_confirmShipmentContentUpdatePreview` | Confirm a shipment content update preview and accept the changes in transportation cost. | | read | `FulfillmentInbound_getDeliveryChallanDocument` | Provide delivery challan document for PCP transportation in IN marketplace. | | read | `FulfillmentInbound_listDeliveryWindowOptions` | Retrieves all delivery window options for a shipment. | | write | `FulfillmentInbound_generateDeliveryWindowOptions` | Generates available delivery window options for a given shipment. | | write | `FulfillmentInbound_confirmDeliveryWindowOptions` | Confirms the delivery window option for chosen shipment within an inbound plan. | | read | `FulfillmentInbound_listShipmentItems` | Provides a paginated list of item packages in a shipment. | | write | `FulfillmentInbound_updateShipmentName` | Updates the name of an existing shipment. | | read | `FulfillmentInbound_listShipmentPallets` | Provides a paginated list of pallet packages in a shipment. | | write | `FulfillmentInbound_cancelSelfShipAppointment` | Cancels a self-ship appointment slot against a shipment. | | read | `FulfillmentInbound_getSelfShipAppointmentSlots` | Retrieves a list of available self-ship appointment slots used to drop off a shipment at a warehouse. | | write | `FulfillmentInbound_generateSelfShipAppointmentSlots` | Initiates the process of generating the appointment slots list. | | write | `FulfillmentInbound_scheduleSelfShipAppointment` | Confirms or reschedules a self-ship appointment slot against a shipment. | | write | `FulfillmentInbound_updateShipmentSourceAddress` | Updates the source address of an existing shipment. | | write | `FulfillmentInbound_updateShipmentTrackingDetails` | Updates a shipment's tracking details. | | read | `FulfillmentInbound_listTransportationOptions` | Retrieves all transportation options for a shipment. | | write | `FulfillmentInbound_generateTransportationOptions` | Generates available transportation options for a given placement option. | | write | `FulfillmentInbound_confirmTransportationOptions` | Confirms all the transportation options for an inbound plan. | | read | `FulfillmentInbound_listItemComplianceDetails` | List the inbound compliance details for MSKUs in a given marketplace. | | write | `FulfillmentInbound_updateItemComplianceDetails` | Update compliance details for a list of MSKUs. | | write | `FulfillmentInbound_createMarketplaceItemLabels` | For a given marketplace - creates labels for a list of MSKUs. | | read | `FulfillmentInbound_listPrepDetails` | Get preparation details for a list of MSKUs in a specified marketplace.\n\nNote: MSKUs that contain certain characters must be encoded. | | write | `FulfillmentInbound_setPrepDetails` | Set the preparation details for a list of MSKUs in a specified marketplace. | | read | `FulfillmentInbound_getInboundOperationStatus` | Gets the status of the processing of an asynchronous API call. | ### FulfillmentInboundV0 | Access | Tool | Description | | --- | --- | --- | | read | `FulfillmentInboundV0_getPrepInstructions` | Returns labeling requirements and item preparation instructions to help prepare items for shipment to Amazon's fulfillment network. | | read | `FulfillmentInboundV0_getShipmentItems` | Returns a list of items in a specified inbound shipment, or a list of items that were updated within a specified time frame. | | read | `FulfillmentInboundV0_getShipments` | Returns a list of inbound shipments based on criteria that you specify. | | read | `FulfillmentInboundV0_getBillOfLading` | Returns a bill of lading for a Less Than Truckload/Full Truckload (LTL/FTL) shipment. | | read | `FulfillmentInboundV0_getShipmentItemsByShipmentId` | Returns a list of items in a specified inbound shipment. | | read | `FulfillmentInboundV0_getLabels` | Returns package/pallet labels for faster and more accurate shipment processing at the Amazon fulfillment center. | ### FulfillmentOutbound | Access | Tool | Description | | --- | --- | --- | | write | `FulfillmentOutbound_deliveryOffers` | Returns delivery options that include an estimated delivery date and offer expiration, based on criteria that you specify. | | read | `FulfillmentOutbound_getFeatures` | Returns a list of features available for Multi-Channel Fulfillment orders in the marketplace you specify, and whether the seller for which you made the call is enrolled for each feature. | | read | `FulfillmentOutbound_getFeatureInventory` | Returns a list of inventory items that are eligible for the fulfillment feature you specify. | | read | `FulfillmentOutbound_getFeatureSKU` | Returns the number of items with the sellerSku you specify that can have orders fulfilled using the specified feature. | | read | `FulfillmentOutbound_listAllFulfillmentOrders` | Returns a list of fulfillment orders fulfilled after (or at) a specified date-time, or indicated by the nextToken parameter. | | write | `FulfillmentOutbound_createFulfillmentOrder` | Requests that Amazon ship items from the seller's inventory in Amazon's fulfillment network to a destination address. | | read | `FulfillmentOutbound_getFulfillmentPreview` | Returns a list of fulfillment order previews based on shipping criteria that you specify. | | read | `FulfillmentOutbound_getFulfillmentOrder` | Returns the fulfillment order indicated by the specified order identifier. | | write | `FulfillmentOutbound_updateFulfillmentOrder` | Updates and/or requests shipment for a fulfillment order with an order hold on it. | | write | `FulfillmentOutbound_cancelFulfillmentOrder` | Requests that Amazon stop attempting to fulfill the fulfillment order indicated by the specified order identifier. | | write | `FulfillmentOutbound_createFulfillmentReturn` | Creates a fulfillment return. | | write | `FulfillmentOutbound_submitFulfillmentOrderStatusUpdate` | Requests that Amazon update the status of an order in the sandbox testing environment. | | read | `FulfillmentOutbound_listReturnReasonCodes` | Returns a list of return reason codes for a seller SKU in a given marketplace. | | read | `FulfillmentOutbound_getPackageTrackingDetails` | Returns delivery tracking information for a package in an outbound shipment for a Multi-Channel Fulfillment order. | ### Invoices | Access | Tool | Description | | --- | --- | --- | | read | `Invoices_getInvoicesAttributes` | Returns marketplace-dependent schemas and their respective set of possible values. | | read | `Invoices_getInvoicesDocument` | Returns the invoice document's ID and URL. | | read | `Invoices_getInvoicesExports` | Returns invoice exports details for exports that match the filters that you specify. | | write | `Invoices_createInvoicesExport` | Creates an invoice export request. | | read | `Invoices_getInvoicesExport` | Returns invoice export details (including the exportDocumentId, if available) for the export that you specify. | | read | `Invoices_getGovernmentInvoiceStatus` | Returns the status of an invoice generation request. | | write | `Invoices_createGovernmentInvoice` | Submits an asynchronous government invoice creation request. | | read | `Invoices_getGovernmentInvoiceDocument` | Returns an invoiceDocument object containing an invoiceDocumentUrl . | | read | `Invoices_getInvoices` | Returns invoice details for the invoices that match the filters that you specify. | | read | `Invoices_getInvoice` | Returns invoice data for the specified invoice. | ### ListingsItems | Access | Tool | Description | | --- | --- | --- | | read | `ListingsItems_searchListingsItems` | Search for and return a list of selling partner listings items and their respective details. | | write | `ListingsItems_deleteListingsItem` | Delete a listings item for a selling partner. | | read | `ListingsItems_getListingsItem` | Returns details about a listings item for a selling partner. | | write | `ListingsItems_patchListingsItem` | Partially update (patch) a listings item for a selling partner. | | write | `ListingsItems_putListingsItem` | Creates a new or fully-updates an existing listings item for a selling partner. | ### ListingsItems20200901 | Access | Tool | Description | | --- | --- | --- | | write | `ListingsItems20200901_deleteListingsItem` | Delete a listings item for a selling partner. | | write | `ListingsItems20200901_patchListingsItem` | Partially update (patch) a listings item for a selling partner. | | write | `ListingsItems20200901_putListingsItem` | Creates a new or fully-updates an existing listings item for a selling partner. | ### ListingsRestrictions | Access | Tool | Description | | --- | --- | --- | | read | `ListingsRestrictions_getListingsRestrictions` | Returns listing restrictions for an item in the Amazon Catalog. | ### MerchantFulfillment | Access | Tool | Description | | --- | --- | --- | | read | `MerchantFulfillment_getAdditionalSellerInputs` | Gets a list of additional seller inputs required for a ship method. | | read | `MerchantFulfillment_getEligibleShipmentServices` | Returns a list of shipping service offers that satisfy the specified shipment request details. | | write | `MerchantFulfillment_createShipment` | Create a shipment with the information provided. | | write | `MerchantFulfillment_cancelShipment` | Cancel the shipment indicated by the specified shipment identifier. | | read | `MerchantFulfillment_getShipment` | Returns the shipment information for an existing shipment. | ### Messaging | Access | Tool | Description | | --- | --- | --- | | read | `Messaging_getMessagingActionsForOrder` | Returns a list of message types that are available for an order that you specify. | | read | `Messaging_GetAttributes` | Returns a response containing attributes related to an order. | | write | `Messaging_CreateAmazonMotors` | Sends a message to a buyer to provide details about an Amazon Motors order. | | write | `Messaging_confirmCustomizationDetails` | Sends a message asking a buyer to provide or verify customization details such as name spelling, images, initials, etc. | | write | `Messaging_createConfirmDeliveryDetails` | Sends a message to a buyer to arrange a delivery or to confirm contact information for making a delivery. | | write | `Messaging_createConfirmOrderDetails` | Sends a message to ask a buyer an order-related question prior to shipping their order. | | write | `Messaging_createConfirmServiceDetails` | Sends a message to contact a Home Service customer to arrange a service call or to gather information prior to a service call. | | write | `Messaging_createDigitalAccessKey` | Sends a buyer a message to share a digital access key that is required to utilize digital content in their order. | | write | `Messaging_sendInvoice` | Sends a message providing the buyer an invoice | | write | `Messaging_createLegalDisclosure` | Sends a critical message that contains documents that a seller is legally obligated to provide to the buyer. | | write | `Messaging_createUnexpectedProblem` | Sends a critical message to a buyer that an unexpected problem was encountered affecting the completion of the order. | | write | `Messaging_CreateWarranty` | Sends a message to a buyer to provide details about warranty information on a purchase in their order. | ### Notifications | Access | Tool | Description | | --- | --- | --- | | read | `Notifications_getDestinations` | Returns information about all destinations. | | write | `Notifications_createDestination` | Creates a destination resource to receive notifications. | | write | `Notifications_deleteDestination` | Deletes the destination that you specify. | | read | `Notifications_getDestination` | Returns information about the destination that you specify. | | read | `Notifications_getSubscription` | Returns information about subscription of the specified notification type and payload version. | | write | `Notifications_createSubscription` | Creates a subscription for the specified notification type to be delivered to the specified destination. | | write | `Notifications_deleteSubscriptionById` | Deletes the subscription indicated by the subscription identifier and notification type that you specify. | | read | `Notifications_getSubscriptionById` | Returns information about a subscription for the specified notification type. | ### Orders | Access | Tool | Description | | --- | --- | --- | | read | `Orders_getOrders` | Returns orders that are created or updated during the specified time period. | | read | `Orders_getOrder` | Returns the order that you specify. | | read | `Orders_getOrderAddress` | Returns the shipping address for the order that you specify. | | read | `Orders_getOrderBuyerInfo` | Returns buyer information for the order that you specify. | | read | `Orders_getOrderItems` | Returns detailed order item information for the order that you specify. | | read | `Orders_getOrderItemsBuyerInfo` | Returns buyer information for the order items in the order that you specify. | | read | `Orders_getOrderRegulatedInfo` | Returns regulated information for the order that you specify. | | write | `Orders_updateVerificationStatus` | Updates (approves or rejects) the verification status of an order containing regulated products. | | write | `Orders_updateShipmentStatus` | Update the shipment status for an order that you specify. | | write | `Orders_confirmShipment` | Updates the shipment confirmation status for a specified order. | ### Ordersv2 | Access | Tool | Description | | --- | --- | --- | | read | `Ordersv2_searchOrders` | Returns orders that are created or updated during the time period that you specify. | | read | `Ordersv2_getOrder` | Returns the order that you specify. | ### ProductPricing | Access | Tool | Description | | --- | --- | --- | | read | `ProductPricing_getItemOffersBatch` | Returns the lowest priced offers for a batch of items based on ASIN. | | read | `ProductPricing_getListingOffersBatch` | Returns the lowest priced offers for a batch of listings by SKU. | | read | `ProductPricing_getCompetitivePricing` | Returns competitive pricing information for a seller's offer listings based on seller SKU or ASIN. | | read | `ProductPricing_getItemOffers` | Returns the lowest priced offers for a single item based on ASIN. | | read | `ProductPricing_getListingOffers` | Returns the lowest priced offers for a single SKU listing. | | read | `ProductPricing_getPricing` | Returns pricing information for a seller's offer listings based on seller SKU or ASIN. | ### ProductPricing20220501 | Access | Tool | Description | | --- | --- | --- | | read | `ProductPricing20220501_getCompetitiveSummary` | Returns the competitive summary response, including featured buying options for the ASIN and marketplaceId combination. | | read | `ProductPricing20220501_getFeaturedOfferExpectedPriceBatch` | Returns the set of responses that correspond to the batched list of up to 40 requests defined in the request body. | ### ProductType | Access | Tool | Description | | --- | --- | --- | | read | `ProductType_searchDefinitionsProductTypes` | Search for and return a list of Amazon product types that have definitions available. | | read | `ProductType_getDefinitionsProductType` | Retrieve an Amazon product type definition. | ### Replenishment | Access | Tool | Description | | --- | --- | --- | | read | `Replenishment_listOfferMetrics` | Returns aggregated replenishment program metrics for a selling partner's offers. | | read | `Replenishment_listOffers` | Returns the details of a selling partner's replenishment program offers. | | read | `Replenishment_getSellingPartnerMetrics` | Returns aggregated replenishment program metrics for a selling partner. | ### Replenishment20221107 | Access | Tool | Description | | --- | --- | --- | | read | `Replenishment20221107_listOfferMetrics` | Returns aggregated replenishment program metrics for a selling partner's offers. | | read | `Replenishment20221107_listOffers` | Returns the details of a selling partner's replenishment program offers. | | read | `Replenishment20221107_getSellingPartnerMetrics` | Returns aggregated replenishment program metrics for a selling partner. | ### Reports | Access | Tool | Description | | --- | --- | --- | | read | `Reports_getReportDocument` | Returns the information required for retrieving a report document's contents. | | read | `Reports_getReports` | Returns report details for the reports that match the filters that you specify. | | write | `Reports_createReport` | Creates a report. | | write | `Reports_cancelReport` | Cancels the report that you specify. | | read | `Reports_getReport` | Returns report details (including the reportDocumentId, if available) for the report that you specify. | | read | `Reports_getReportSchedules` | Returns report schedule details that match the filters that you specify. | | write | `Reports_createReportSchedule` | Creates a report schedule. | | write | `Reports_cancelReportSchedule` | Cancels the report schedule that you specify. | | read | `Reports_getReportSchedule` | Returns report schedule details for the report schedule that you specify. | ### Sales | Access | Tool | Description | | --- | --- | --- | | read | `Sales_getOrderMetrics` | Returns aggregated order metrics for given interval, broken down by granularity, for given buyer type. | ### Sellers | Access | Tool | Description | | --- | --- | --- | | read | `Sellers_getAccount` | Returns information about a seller account and its marketplaces. | | read | `Sellers_getMarketplaceParticipations` | Returns a list of marketplaces where the seller can list items and information about the seller's participation in those marketplaces. | ### SellerWallet | Access | Tool | Description | | --- | --- | --- | | read | `SellerWallet_listAccounts` | Get Seller Wallet accounts for a seller. | | read | `SellerWallet_getAccount` | Retrieve a Seller Wallet bank account by Amazon account identifier. | | read | `SellerWallet_listAccountBalances` | Retrieve the balance in a given Seller Wallet bank account. | | read | `SellerWallet_listAccountTransactions` | Retrieve a list of transactions for a given Seller Wallet bank account. | | write | `SellerWallet_createTransaction` | Create a transaction request from a Seller Wallet account to another customer-provided account. | | read | `SellerWallet_getTransaction` | Find a transaction by the Amazon transaction identifier. | | read | `SellerWallet_getTransferPreview` | Retrieve a list of potential fees on a transaction. | | read | `SellerWallet_listTransferSchedules` | Retrieve transfer schedules of a Seller Wallet bank account. | | write | `SellerWallet_createTransferSchedule` | Create a transfer schedule request from a Seller Wallet account to another customer-provided account. | | write | `SellerWallet_updateTransferSchedule` | Update transfer schedule information. | | write | `SellerWallet_deleteScheduleTransaction` | Delete a transaction request that is scheduled from Amazon Seller Wallet account to another customer-provided account. | | read | `SellerWallet_getTransferSchedule` | Find a particular Amazon Seller Wallet account transfer schedule. | ### Services | Access | Tool | Description | | --- | --- | --- | | read | `Services_getAppointmentSlots` | Gets appointment slots as per the service context specified. | | write | `Services_createServiceDocumentUploadDestination` | Creates an upload destination. | | write | `Services_createReservation` | Create a reservation. | | write | `Services_cancelReservation` | Cancel a reservation. | | write | `Services_updateReservation` | Update a reservation. | | read | `Services_getServiceJobs` | Gets service job details for the specified filter query. | | read | `Services_getServiceJobByServiceJobId` | Gets details of service job indicated by the provided serviceJobID. | | read | `Services_getAppointmmentSlotsByJobId` | Gets appointment slots for the service associated with the service job id specified. | | write | `Services_addAppointmentForServiceJobByServiceJobId` | Adds an appointment to the service job indicated by the service job identifier specified. | | write | `Services_rescheduleAppointmentForServiceJobByServiceJobId` | Reschedules an appointment for the service job indicated by the service job identifier specified. | | write | `Services_setAppointmentFulfillmentData` | Updates the appointment fulfillment data related to a given jobID and appointmentID. | | write | `Services_assignAppointmentResources` | Assigns new resource(s) or overwrite/update the existing one(s) to a service job appointment. | | write | `Services_cancelServiceJobByServiceJobId` | Cancels the service job indicated by the service job identifier specified. | | write | `Services_completeServiceJobByServiceJobId` | Completes the service job indicated by the service job identifier specified. | | read | `Services_getFixedSlotCapacity` | Provides capacity in fixed-size slots. | | read | `Services_getRangeSlotCapacity` | Provides capacity slots in a format similar to availability records. | | write | `Services_updateSchedule` | Update the schedule of the given resource. | ### ShipmentInvoicing | Access | Tool | Description | | --- | --- | --- | | read | `ShipmentInvoicing_getShipmentDetails` | Returns the shipment details required to issue an invoice for the specified shipment. | | write | `ShipmentInvoicing_submitInvoice` | Submits a shipment invoice document for a given shipment. | | read | `ShipmentInvoicing_getInvoiceStatus` | Returns the invoice status for the shipment you specify. | ### Shipping | Access | Tool | Description | | --- | --- | --- | | read | `Shipping_getAccessPoints` | Returns a list of access points in proximity of input postal code. | | read | `Shipping_getCarrierAccountFormInputs` | This API will return a list of input schema required to register a shipper account with the carrier. | | read | `Shipping_getCarrierAccounts` | This API will return Get all carrier accounts for a merchant. | | write | `Shipping_linkCarrierAccount` | This API associates/links the specified carrier account with the merchant. | | write | `Shipping_linkCarrierAccount` | This API associates/links the specified carrier account with the merchant. | | write | `Shipping_unlinkCarrierAccount` | This API Unlink the specified carrier account with the merchant. | | write | `Shipping_createClaim` | This API will be used to create claim for single eligible shipment. | | write | `Shipping_generateCollectionForm` | This API Call to generate the collection form. | | read | `Shipping_getCollectionFormHistory` | This API Call to get the history of the previously generated collection forms. | | read | `Shipping_getCollectionForm` | This API reprint a collection form. | | write | `Shipping_submitNdrFeedback` | This API submits the NDR (Non-delivery Report) Feedback for any eligible shipment. | | write | `Shipping_oneClickShipment` | Purchases a shipping service identifier and returns purchase-related details and documents. | | write | `Shipping_purchaseShipment` | Purchases a shipping service and returns purchase related details and documents. | | read | `Shipping_getAdditionalInputs` | Returns the JSON schema to use for providing additional inputs when needed to purchase a shipping offering. | | write | `Shipping_directPurchaseShipment` | Purchases the shipping service for a shipment using the best fit service offering. | | read | `Shipping_getRates` | Returns the available shipping service offerings. | | write | `Shipping_cancelShipment` | Cancels a purchased shipment. | | read | `Shipping_getShipmentDocuments` | Returns the shipping documents associated with a package in a shipment. | | read | `Shipping_getTracking` | Returns tracking information for a purchased shipment. | | read | `Shipping_getUnmanifestedShipments` | This API Get all unmanifested carriers with shipment locations. | ### ShippingLegacy | Access | Tool | Description | | --- | --- | --- | | read | `ShippingLegacy_getAccount` | Verify if the current account is valid. | | write | `ShippingLegacy_purchaseShipment` | Purchase shipping labels. | | read | `ShippingLegacy_getRates` | Get service rates. | | write | `ShippingLegacy_createShipment` | Create a new shipment. | | read | `ShippingLegacy_getShipment` | Return the entire shipment object for the shipmentId. | | write | `ShippingLegacy_cancelShipment` | Cancel a shipment by the given shipmentId. | | write | `ShippingLegacy_retrieveShippingLabel` | Retrieve shipping label based on the shipment id and tracking id. | | write | `ShippingLegacy_purchaseLabels` | Purchase shipping labels based on a given rate. | | read | `ShippingLegacy_getTrackingInformation` | Return the tracking information of a shipment. | ### Solicitations | Access | Tool | Description | | --- | --- | --- | | read | `Solicitations_getSolicitationActionsForOrder` | Returns a list of solicitation types that are available for an order that you specify. | | write | `Solicitations_createProductReviewAndSellerFeedbackSolicitation` | Sends a solicitation to a buyer asking for seller feedback and a product review for the specified order. | ### SupplySources | Access | Tool | Description | | --- | --- | --- | | read | `SupplySources_getSupplySources` | The path to retrieve paginated supply sources. | | write | `SupplySources_createSupplySource` | Create a new supply source. | | write | `SupplySources_archiveSupplySource` | Archive a supply source, making it inactive. | | read | `SupplySources_getSupplySource` | Retrieve a supply source. | | write | `SupplySources_updateSupplySource` | Update the configuration and capabilities of a supply source. | | write | `SupplySources_updateSupplySourceStatus` | Update the status of a supply source. | ### Tokens | Access | Tool | Description | | --- | --- | --- | | write | `Tokens_createRestrictedDataToken` | Returns a Restricted Data Token (RDT) for one or more restricted resources that you specify. | ### Transfers | Access | Tool | Description | | --- | --- | --- | | read | `Transfers_getPaymentMethods` | Returns the list of payment methods for the seller, which can be filtered by method type. | | write | `Transfers_initiatePayout` | Initiates an on-demand payout to the seller's default deposit method in Seller Central for the given marketplaceId and accountType, if eligible. | ### Uploads | Access | Tool | Description | | --- | --- | --- | | write | `Uploads_createUploadDestinationForResource` | Creates an upload destination, returning the information required to upload a file to the destination and to programmatically access the file. | ### Vehicles | Access | Tool | Description | | --- | --- | --- | | read | `Vehicles_getVehicles` | Get the latest collection of vehicles | ### Amazon Agent Flow Documentation URL: https://www.kuudo.com/docs/amazon-agent-flow/ Amazon Agent Flow is Kuudo's AI-native data layer for Amazon operators. It uses proven data-lake patterns - durable ingestion, artifacts, Iceberg tables, scan-budgeted queries, scheduled refreshes, and lineage - but it is designed around agents as the primary users, not dashboards. Traditional analytics tools can still read the lake. Snowflake, Athena, DuckDB, Databricks, Power BI, Tableau, and similar tools can be useful downstream consumers. They are not the center of the system. The center is an agent that needs fresh, governed, account-specific data it can retrieve, reason over, and act on without copying private business data into chat. Use this page when you want to know what to ask, what must be connected first, which tools are involved, and how to tell whether a run actually finished. ## The Bigger Picture Agent Flow is not only a job runner or report exporter. It is the agent-facing data layer between Amazon systems, private lake storage, governed query tools, and the AI clients that need to do work. The foundation is familiar data-lake architecture: - Amazon SP-API (Selling Partner API) and Ads operations ingest source data. - Large outputs land as artifacts instead of chat messages. - Eligible outputs are delivered into open lake tables. - Queries run with filters, result limits, and scan budgets. - Run ids, artifacts, schedules, and delivery records preserve lineage. The product difference is the primary user. A traditional analytics stack usually assumes a human analyst will open Tableau, Power BI, or a warehouse console. Agent Flow assumes an agent will discover operations, inspect contracts, launch durable runs, poll status, read bounded previews, pass result handles into analysis tools, and explain the next action. The practical result is lower interactive latency. Some Amazon reports, exports, and data pulls take minutes or hours to generate. Agent Flow moves that wait into scheduled background work. The agent can refresh orders, listings, inventory, finance, and Ads datasets on a window, land them in the local/private lake, and answer from data already local to the agent's data layer. The user waits for a bounded local query, not for Amazon to generate the report during the chat turn. The data is also available beyond the agent that created it. Because Agent Flow exposes an MCP-native interface, any authorized MCP-native client can schedule flows, inspect deliveries, query lake datasets, or run its own downstream data process against result handles. ChatGPT, Claude, Cursor, workflow tools, or internal agent clients can all work from the same governed agent lake instead of each one pulling from Amazon separately. | Traditional analytics layer | Agent Flow | | --- | --- | | Dashboards and published reports are the main interface. | Agent-accessible operations, artifacts, and bounded data tools are the main interface. | | Humans click through BI views. | Agents discover, run, poll, query, analyze, and summarize. | | Warehouses and BI tools are the center of consumption. | Result handles, compact previews, lake tables, and sandbox analysis are optimized for agent context. | | Data freshness is usually managed around reporting cadence. | Data freshness is managed around agent tasks, schedules, retries, and operational questions. | | The BI user is the principal consumer. | The AI agent is the principal consumer, with BI tools still available downstream. | ## Two Parts: Pipelines and Queries Agent Flow has two connected surfaces. The first creates the data layer. The second lets agents use it. | Surface | What agents do | Typical tools | | --- | --- | --- | | Amazon-to-agent-lake pipelines | Discover Amazon SP-API or Ads operations, inspect schemas, run or schedule durable jobs, persist artifacts, and deliver eligible outputs into the agent lake. | `list_amazon_sp_operations`, `list_amazon_ads_operations`, `run_amazon_sp_operation`, `run_amazon_ads_operation`, `upsert_schedule`, `list_lake_deliveries` | | Agent data consumption | Query delivered datasets, read bounded previews, pass result handles into analysis, and build downstream actions from local governed data. | `query_lake_dataset`, `query_lake_sql`, `read_analysis_result`, `run_sandbox_python`, `run_curated_analysis` | Those surfaces can be used by the same agent or by different authorized clients. One MCP-native client might schedule the daily Amazon reports, another might query the lake for an ads diagnosis, and a third might run a sandboxed analysis over the latest result handle. The shared contract is the Agent Flow MCP surface and the governed lake underneath it. ## What Agent Flow Does Agent Flow turns Amazon work and Amazon data into a layer agents can use repeatedly. The named operations create or refresh the data, schedules keep slow Amazon outputs warm, the lake keeps it cost-effective and scalable, and the query and analysis tools expose only the slices an agent needs for the next decision. Instead of asking an assistant to improvise against Amazon APIs, you ask it to build or use the data layer: 1. Find the right operation. 2. Inspect the operation's required input. 3. Bind the run to a registered Amazon account. 4. Start the durable run. 5. Schedule it if the data should be warm before an agent needs it. 6. Watch status until it completes or needs attention. 7. Deliver or read the result, artifact, or lake table. The runtime is built for work that may take longer than a single chat turn: paginated order pulls, asynchronous Amazon reports, scheduled jobs, report downloads, retries after throttling, and data delivery into Iceberg tables. ## When to Use It Use Agent Flow for Amazon workflows that need durability, auditability, or repeatable data delivery. | Goal | Use Agent Flow? | Why | | --- | --- | --- | | Pull orders, listings, inventory, or finance data | Yes | SP-API calls can paginate, throttle, and produce large artifacts. | | Create and retrieve Amazon Ads reports | Yes | Ads reports are asynchronous and need create, poll, download, and retry handling. | | Schedule recurring Amazon data refreshes | Yes | Schedules launch durable operation runs on a window. | | Query already-delivered Amazon data | Yes | Lake tools expose structured, budgeted reads instead of raw database access. | | Give an agent fresh data for a decision | Yes | Agent Flow returns bounded previews, lineage, and result handles instead of dashboard-only output. | | Reduce latency for slow Amazon reports | Yes | Scheduled flows move report generation into the background so the agent answers from local lake data. | | Share governed Amazon data across AI clients | Yes | Any authorized MCP-native client can consume the same delivered datasets or schedule its own refreshes. | | Feed Tableau, Power BI, or a warehouse | Optional | These tools can consume the lake, but they are downstream of the agent-native flow. | | Rewrite a listing title or analyze a one-off screenshot | Usually no | Use the relevant Skill, Agent Iris, or Atlas unless an operation must run. | | Ask a general Amazon policy question | Usually no | Use [Amazon Agent Atlas](/docs/amazon-agent-atlas/) for grounded operating knowledge. | If your first consumer is a dashboard, the lake can still be useful. If your first consumer is an agent that needs to do work, Agent Flow is the primary interface. ## What You Need First Agent Flow needs at least one connected Amazon capability. The exact requirement depends on the operation. | Operation type | Required connection | Typical account fields | | --- | --- | --- | | Amazon Selling Partner operations | [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) | `account_ref`, `identity_id`, `marketplace_id`, credential | | Amazon Ads operations | [Amazon Ads MCP](/features/amazon-ads-mcp/) | `account_ref`, `profile_id`, `region`, credential | | Lake delivery or lake queries | Agent Flow app database plus a lake destination | `destination_id`, dataset, scan budget | | Scheduled operations | Operation, account, app database, and schedule worker | `schedule_name`, cron, window size | For Kuudo Cloud, Kuudo helps provision the runtime and connector MCP servers in your cloud. For self-hosted deployments, the Agent Flow server expects external MCP servers for Amazon SP and Ads. The source runtime uses these default local MCP targets: | Connector | Default local MCP URL | Token environment variable | | --- | --- | --- | | Amazon Selling Partner MCP | `http://localhost:8013/mcp` | `AMAZON_SP_OPENBRIDGE_REFRESH_TOKEN` | | Amazon Ads MCP | `http://localhost:9080/mcp` | `AMAZON_ADS_OPENBRIDGE_REFRESH_TOKEN` | Those connectors still require the underlying Amazon permissions, profiles, marketplaces, and API access. Agent Flow does not create Amazon approvals or bypass Amazon limits. ## How Activation Works Activation means giving Agent Flow enough context to run one durable operation against one registered account. ### 1. Connect the Agent Flow MCP server In Kuudo Cloud, your AI client connects to the Agent Flow MCP endpoint Kuudo provides. For self-hosted local work, start the server from the Agent Flow repo: ```bash uv run agent-flow ``` Then connect your MCP client to that server. If you are using the REST control plane instead of MCP tools, the same concepts are available through `/v1/*` endpoints. ### 2. Register an Amazon account Each run uses an `account_ref`. The account stores the Amazon identity fields that should not be retyped into every prompt. For an SP-API account, the important fields are usually: - `connector_id`: `amazon_sp` - `account_ref`: your stable name, such as `seller-us-main` - `identity_id`: the Openbridge or connector identity id - `marketplace_id`: the Amazon marketplace id, such as `ATVPDKIKX0DER` For an Ads account, the important fields are usually: - `connector_id`: `amazon_ads` - `account_ref`: your stable name, such as `ads-brand-us` - `profile_id`: the Amazon Ads profile id - `region`: the Ads region, such as `na` **Prompt** > Register `seller-us-main` as an Amazon SP account for marketplace `ATVPDKIKX0DER` using identity id `3438`. Then show me the account record and confirm which fields will be bound automatically on future runs. **MCP tools the assistant may use** - `upsert_account` - `get_account` - `list_accounts` If the credential store is enabled, the assistant may also use `create_account_credential`. Credentials are accepted as plaintext input to the tool and stored encrypted at rest; the tool returns redacted metadata, not the secret. ### 3. Find the operation Operations are the stable units of work Agent Flow can run. They are grouped by connector, resource, lifecycle, and risk. Useful filters: - `resource`: examples include `orders`, `reports`, `campaigns` - `lifecycle`: `transactional_query`, `paginated_query`, `async_report`, `mutation` - `risk_level`: `read`, `export`, `mutation`, `financial` **Prompt** > I need orders data for `seller-us-main`. Before you run anything, show me the safe options Agent Flow can use. **MCP tools the assistant may use** - `list_amazon_sp_operations` - `list_amazon_ads_operations` ### 4. Inspect the operation schema Before running an operation, inspect its input schema. Some fields are account-bound and should come from `account_ref`; other fields must be supplied in the run payload. **Prompt** > For the orders option you recommend, tell me what information you need from me and what will be filled in from `seller-us-main`. **MCP tools the assistant may use** - `get_amazon_sp_operation` - `get_amazon_ads_operation` ### 5. Start the run For normal use, start the asynchronous durable run and poll it. Use sync tools only for short smoke tests or local demos. **Prompt** > Show me orders from the last 24 hours for my `seller-us-main` seller account. Use Agent Flow if the data needs to be refreshed. The assistant should translate that plain request into a durable run, return the `run_id` when available, and poll until the run completes or needs attention. **MCP tools the assistant may use** - `run_amazon_sp_operation` - `run_amazon_ads_operation` - `get_flow_run` The REST equivalent is: ```bash curl -X POST http://127.0.0.1:8080/v1/operation-runs \ -H 'X-Agent-Flow-Tenant: tenant-a' \ -H 'content-type: application/json' \ -d '{ "connector_id": "amazon_sp", "operation_id": "", "account_ref": "seller-us-main", "payload": {} }' ``` Then fetch status: ```bash curl http://127.0.0.1:8080/v1/operation-runs/ \ -H 'X-Agent-Flow-Tenant: tenant-a' ``` ## Operation Mechanics Agent Flow operations are not generic prompts. Each operation has a contract. | Field | What it tells the agent | | --- | --- | | `operation_id` | Stable name to run. | | `connector_id` | `amazon_sp` or `amazon_ads`. | | `resource` | Business area such as orders, reports, campaigns, or listings. | | `lifecycle` | How the operation runs: query, paginated query, async report, or mutation. | | `risk_level` | Whether the operation is read-only, export-like, financial, or a mutation. | | `input_model` | Pydantic input contract. | | `output_model` | Result shape returned when the run completes. | | `account_bound_fields` | Fields Agent Flow fills from the registered account. | | `artifact_policy` | Whether large output is persisted as a flow artifact. | | `landing_artifact_policy` | Whether output can land as a lake dataset. | ### Lifecycles | Lifecycle | What happens | | --- | --- | | `transactional_query` | One bounded connector call returns a structured result. | | `paginated_query` | Agent Flow walks pages, writes a larger artifact, and returns a summary. | | `async_report` | Agent Flow creates a report, polls status, downloads the document, and records artifacts. | | `mutation` | Agent Flow performs a change through a connector operation. Use only when your workspace has approved that operation and policy. | ### Durability Agent Flow uses DBOS-backed workflows for operation execution. That means a run can be inspected by `run_id`, resumed after runtime recovery, retried where allowed, and audited through its recorded state. For Amazon reports, equivalent active report requests attach to the same scheduler job instead of creating duplicate Amazon reports. The scheduler owns create, poll, terminal status, and download state. ### Artifacts Large outputs are written as artifacts instead of being pasted into chat. A typical artifact path includes: ```text {artifact_root}/{source}/{flow_id}/{run_id}/{step_name}/{artifact_kind}/{filename} ``` For example, an SP orders run can write raw orders under an `amazon_sp` flow path, while a listings report can write the downloaded Amazon report document as a raw report artifact. ### Lake delivery Lake delivery is the cost and scale layer behind the agent experience. Operations can write local lake-shaped artifacts first, then optionally deliver them to an Iceberg destination. The zero-config floor uses `iceberg_local`. Production deployments can use destinations such as Cloudflare R2 or AWS Glue Iceberg when configured. Open table formats make the data usable by warehouses and BI tools, but the default access path is agent-native: result handles, bounded previews, scan budgets, lineage, and server-side analysis. This is also where Agent Flow changes latency. A scheduled report may still take Amazon minutes or hours to produce, but that wait happens before the agent needs the answer. Once delivered, the data is local to the agent's operating layer, so the next question can use a bounded lake query instead of starting a fresh Amazon report and waiting for it to finish. Client SQL addresses tables uniformly as: ```sql lake..
``` Agents should query with scan budgets and bounded result sizes, not unbounded raw SQL. For larger analysis, they should keep intermediate data server-side with `read_analysis_result` and `run_sandbox_python` instead of copying raw tables into chat context. ## Tool Map These are the Agent Flow tools users most often need in chat. The tool surface covers both sides of the system: pipeline tools that create or schedule Amazon-to-lake data, and consumption tools that let agents query or process the delivered data. | Task | MCP tools | | --- | --- | | Discover SP operations | `list_amazon_sp_operations`, `get_amazon_sp_operation` | | Discover Ads operations | `list_amazon_ads_operations`, `get_amazon_ads_operation` | | Register accounts | `upsert_account`, `list_accounts`, `get_account`, `update_account`, `deactivate_account` | | Store credentials | `create_account_credential`, `rotate_account_credential`, `get_account_credential_status`, `revoke_account_credential` | | Run operations | `run_amazon_sp_operation`, `run_amazon_ads_operation` | | Local smoke runs | `run_amazon_sp_operation_sync`, `run_amazon_ads_operation_sync` | | Inspect run status | `get_flow_run`, `list_report_scheduler_jobs`, `get_report_scheduler_job` | | Retry or cancel | `retry_operation_run`, `cancel_report_scheduler_job` | | Register lake destinations | `list_lake_providers`, `create_lake_destination`, `list_lake_destinations` | | Track delivery | `list_lake_deliveries`, `get_lake_delivery` | | Query delivered data | `query_lake_dataset`, `query_lake_sql`, `read_analysis_result` | | Run bounded analysis | `run_sandbox_python`, `list_curated_analyses`, `run_curated_analysis` | | Create recurring work | `upsert_schedule`, `list_operation_schedules`, `trigger_operation_schedule` | Different deployments may expose a subset depending on enabled stores, credentials, lake providers, and admin policy. ## Example Prompts Use these as starting points in Claude, ChatGPT, Cursor, or another MCP-capable client connected to Agent Flow. The quoted prompts are written the way an end user can ask. The expected behavior explains the technical work the agent should perform behind the scenes. ### Discover the right SP-API operation > I need last week's Amazon orders for `seller-us-main`. Before you pull anything, tell me what information you need and what data source you will use. Expected behavior: - The assistant filters operations instead of dumping the entire catalog. - It calls `get_amazon_sp_operation` for the likely operation. - It tells you which fields are required and which fields are account-bound. - It waits before starting the run. ### Run a read-only order pull > Show me orders for `seller-us-main` from July 1 through July 7, 2026. Refresh from Amazon if needed and summarize where the data was saved. Expected behavior: - The assistant uses `run_amazon_sp_operation`, not a direct Amazon call. - It tracks the run with `get_flow_run`. - It can return the `run_id` for audit or troubleshooting. - It does not paste a large order payload into chat. - It summarizes artifact metadata and record counts when available. ### Create an Amazon Ads report > Pull Sponsored Products campaign daily performance for `ads-brand-us` from July 1 to July 7, 2026. Include spend, sales, clicks, impressions, and campaign id if available. Expected behavior: - The assistant uses `list_amazon_ads_operations` and `get_amazon_ads_operation`. - It starts `run_amazon_ads_operation`. - It understands that report operations can be asynchronous. - It may use `list_report_scheduler_jobs` when the run attaches to a scheduler job. ### Keep the run read-only > Diagnose campaign performance for `ads-brand-us`, but do not make any changes to campaigns, budgets, or account settings. Expected behavior: - The assistant filters operation discovery by risk. - It refuses or asks for explicit approval if a requested operation is a mutation. - It explains any limitation created by the read-only constraint. ### Register a recurring schedule > Schedule the Amazon SP listings report to pull from Amazon every day at 2 AM America/New_York for my `seller-us-main` seller account. Show me when the next refresh will run. Expected behavior: - The assistant inspects the relevant report operation first. - It creates or updates an operation schedule. - It can call the trigger tool after the schedule exists. - It returns the next due time and schedule name. ### Query delivered lake data > What were order counts by purchase date for `seller-us-main` over the last 7 days? Use the latest delivered orders data and keep the answer short. Expected behavior: - The assistant checks lake destinations and deliveries. - It uses `query_lake_dataset` or `query_lake_sql` with `max_scan_bytes`. - It returns a small table or result handle, not unbounded raw data. ### Check a record count > Show me the record count for the latest delivered `orders` data for `seller-us-main`. Expected behavior: - The assistant finds the latest delivered orders dataset for the account. - It runs a bounded count query against the lake, not a fresh Amazon pull unless the data is missing or stale. - It returns the count, dataset name, delivery timestamp, and any freshness caveat. ### Run a SQL query on delivered data > Run this SQL query on the latest delivered `orders` data: > > `select count(*) as order_count from orders where purchase_date >= date '2026-07-01'` Expected behavior: - The assistant maps the user's logical data name to the governed lake table. - It validates that the query is read-only before running it. - It applies a scan budget and bounded preview size. - It returns a small result, result handle, and lineage instead of dumping raw rows. ### Analyze a result in a sandbox > Which SKUs drove the most revenue for `seller-us-main` in the last 30 days? Use the latest orders data and keep any intermediate data out of chat. Expected behavior: - The assistant runs a governed query first. - It passes the result handle to `run_sandbox_python`. - It returns a bounded table and describes the lineage. ### Retry a failed read/report run > Some Amazon data refreshes failed after throttling. Retry the read-only ones and do not retry anything that changes Amazon data. Expected behavior: - The assistant lists operation runs or relevant scheduler jobs. - It retries only allowed read/report runs. - It does not retry mutation runs. - It includes the new run id and audit reason. ## Activation Checklist Before you ask Agent Flow to run production work, confirm: - The Agent Flow MCP server or REST API is reachable from your AI client. - The relevant connector is installed: Amazon SP MCP for SP-API work, Amazon Ads MCP for Ads work. - The connector has valid credentials and required Amazon permissions. - The account is registered with a stable `account_ref`. - The operation exists and is enabled. - The operation input schema has been inspected. - The operation risk level is acceptable for the task. - Large outputs have an artifact or lake plan. - Mutations and financial-risk operations are covered by your approval policy. ## Common Workflows ### Agent-native data refresh Start here when the agent needs fresh Amazon data before answering an operating question, especially when the underlying Amazon report or export is too slow for an interactive chat turn. **Prompt** > What changed since yesterday for `seller-us-main` and `ads-brand-us`? Refresh the Amazon data if needed, then tell me which datasets are ready to query. Use: - Amazon Selling Partner MCP - Amazon Ads MCP - relevant SP and Ads operations - artifact storage - lake delivery - `query_lake_dataset` or `query_lake_sql` ### Scheduled low-latency agent data Start here when the agent repeatedly needs the same Amazon data and should not wait for Amazon report generation during each conversation. **Prompt** > Schedule the orders, listings, and Ads performance reports to pull from Amazon every day at 2 AM America/New_York for my `seller-us-main` seller account and `ads-brand-us` Ads account. Keep the latest data ready in the agent lake. Use: - Amazon Selling Partner MCP - Amazon Ads MCP - `upsert_schedule` - report or paginated operations - artifact storage - lake delivery - `list_lake_deliveries` ### Orders and sales operations Start here when the task depends on Seller Central orders or selling activity. **Prompt** > Show me orders for `seller-us-main` from the last 48 hours. Refresh from Amazon if needed and summarize order count, earliest purchase date, and latest purchase date. Use: - Amazon Selling Partner MCP - `list_amazon_sp_operations` - `get_amazon_sp_operation` - `run_amazon_sp_operation` - `get_flow_run` ### Listings report Start here when you need listing state as a report, not a one-off catalog lookup. **Prompt** > Get the current merchant listings report for `seller-us-main`. Tell me when it is ready and where the report data was saved. Use: - Amazon Selling Partner MCP - SP reports operation - report scheduler - artifact storage ### Sponsored Products reporting Start here when you need Ads performance over a date range. **Prompt** > Get Sponsored Products campaign daily performance for `ads-brand-us` from July 1 to July 7, 2026. Include spend, sales, clicks, impressions, and campaign id if available. Use: - Amazon Ads MCP - Ads reporting operation - async report scheduler - artifact storage or lake delivery ### Data-lake query Start here after data has been delivered to an Iceberg destination. **Prompt** > Show daily order count and estimated revenue after July 1, 2026 from the latest delivered `orders` data. Keep the answer short. **SQL prompt** > Run this SQL query on the latest delivered `orders` data for `seller-us-main`: > > `select count(*) as order_count from orders` Use: - `list_lake_destinations` - `list_lake_deliveries` - `query_lake_sql` or `query_lake_dataset` - `read_analysis_result` when the result is larger than the preview ## What Good Output Looks Like A good Agent Flow answer includes: - The operation id it chose. - The connector and account ref. - The risk level and lifecycle. - The payload it submitted, with secrets omitted. - The run id. - Current status or terminal status. - The data source it used: operation output, artifact, lake table, or analysis result. - Artifact or lake delivery details when applicable. - A bounded preview or result handle instead of a raw data dump. - Scan budget, filters, and row limits when it queried the lake. - Any Amazon throttling, pending report, missing permission, or retry state. - The next action you should take. It should not: - Call a mutation when you asked for read-only work. - Paste secrets into the response. - Dump hundreds of operation summaries into chat. - Claim a report is complete without checking the run or scheduler status. - Query a lake without a scan budget. - Treat an Amazon API failure as a finished business answer. ## Troubleshooting | Symptom | What to ask | | --- | --- | | "Unknown account" | "List accounts for this tenant and confirm the exact `account_ref`." | | "Unknown operation" | "List operations filtered by connector, resource, and lifecycle before choosing." | | Input validation failed | "Inspect the operation schema and show required fields plus account-bound fields." | | Report stays pending | "Check the flow run and report scheduler job, including `next_poll_after` and terminal status." | | Duplicate report concern | "Check whether this request attached to an existing scheduler job." | | Lake query says dataset not found | "List deliveries for this run and destination, then confirm the dataset name." | | Query budget exceeded | "Narrow the date range, add filters, or increase `max_scan_bytes` if approved." | | Credential error | "Check credential status for the account; do not print the credential." | | Mutation risk | "Show the operation risk level and stop unless the workspace approval policy allows it." | ## Related Material - [Amazon Agent Flow feature page](/features/amazon-agent-flow/) explains the product surface and private lake model. - [Kuudo MCP Servers](/docs/mcp-reference/tools/) maps the broader Kuudo MCP tool model. - [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) covers the SP-API connector. - [Amazon Ads MCP](/features/amazon-ads-mcp/) covers the Ads connector. - [Amazon Agent Atlas](/docs/amazon-agent-atlas/) covers grounded Amazon operating knowledge for tasks that need policy, table, or playbook context. ### What Is Amazon Marketing Cloud? How AMC Works URL: https://www.kuudo.com/docs/guides/amazon-marketing-cloud/ Amazon Marketing Cloud (AMC) is Amazon Ads' privacy-safe clean room for analyzing pseudonymized advertising, shopping, and advertiser-owned signals. Eligible advertisers use it to measure journeys, build audiences, compare media exposure, and answer questions that standard reports cannot, while aggregation controls prevent the output from revealing individual customers. ## How does Amazon Marketing Cloud work? AMC gives an eligible advertiser a dedicated cloud environment containing the signals connected to its instance. An analyst or software workflow submits a query, Amazon applies the clean room's privacy rules, and the instance returns only results that satisfy the required aggregation controls. A typical analysis follows four stages: 1. Define a business question, population, conversion event, and time window. 2. Select the tables and fields that represent those concepts. 3. Run a Structured Query Language (SQL) query, instructional query, template, application programming interface (API) workflow, or agent-assisted workflow. 4. Interpret the aggregated result, then decide whether to report, rerun, or create an eligible audience. AMC is not a replacement for every Amazon Ads report. Standard reports remain better for routine campaign totals. AMC becomes useful when the question depends on event-level relationships across time, campaigns, ad products, audiences, or advertiser-owned signals. ## What data does Amazon Marketing Cloud use? The exact tables depend on the accounts and features connected to the instance. Common categories include: - Amazon demand-side platform (DSP) impressions, clicks, views, and attributed events. - Sponsored Ads traffic and conversion signals for supported ad products. - Amazon shopping and conversion events available under the advertiser's permissions. - Advertiser-owned pseudonymized signals, such as eligible web, app, store, or customer relationship management events. - Optional paid features and partner datasets when the advertiser has subscribed and the instance supports them. The data remains scoped to the advertiser's AMC instance. A table being documented does not mean every advertiser can query it; permissions, account connections, marketplace support, lookback windows, and subscriptions determine what is actually available. ## What is Amazon Marketing Cloud used for? AMC is a foundation for questions that ordinary campaign rows cannot answer cleanly. Use the shortest existing guide that matches the job: | Area | Start here | Question it answers | | --- | --- | --- | | SQL and foundations | [Introduction to AMC SQL](/guides/amc-sql-introduction/) | Which syntax, table, privacy, and output rules shape a valid first query? | | Audiences | [Build a cart-abandoner audience](/guides/amc-cart-abandoner-audience/) | Which shoppers added a product to cart but did not purchase within the defined window? | | Measurement and journeys | [Map a path to conversion](/guides/amc-path-to-conversion-sankey/) | Which sequences of DSP and Sponsored Ads exposure precede conversion? | | Activation and governance | [Add human approval to AMC activation](/guides/human-approval-for-amc-activation/) | How can analysis run automatically while the audience write still waits for review? | Other common uses include media-overlap analysis, new-to-brand measurement, custom attribution, customer-value cohorts, optimal-frequency analysis, incrementality, and off-Amazon conversion measurement. Each use case still needs a defined population, window, source table, metric, and privacy-safe output grain. ## How does the Amazon Marketing Cloud clean room protect data? AMC is designed for aggregate analysis, not customer lookup. Inputs use pseudonymized identifiers, access is scoped to the advertiser's environment, and queries must satisfy Amazon's privacy and aggregation policies before results are returned. Rows that do not meet the applicable aggregation threshold may be suppressed. That changes how an analyst should interpret an empty or partial result. It can mean the query is valid but the population is too small, a table is unavailable, the time window is wrong, or privacy controls removed rows. It should not be converted automatically into a zero or treated as proof that no customers took the action. ## Who can access Amazon Marketing Cloud? [Amazon describes AMC as available to eligible advertisers](https://advertising.amazon.com/solutions/products/amazon-marketing-cloud) through a web interface and APIs. Eligibility, regional availability, connected advertising accounts, user permissions, and the data sources enabled for an instance determine what each team can access. Before planning an analysis, confirm that the correct advertiser and marketplace are connected, the user or integration has permission, and the required table or paid signal is present. Access to an AMC instance does not guarantee access to every dataset or activation feature. ## What does Amazon Marketing Cloud cost? Amazon says the core AMC service is available at no cost to eligible advertisers. That does not make every AMC program free: paid features or signal subscriptions, agency or partner services, implementation work, cloud infrastructure, and software used to operate workflows can add separate costs. Treat those as different economic layers. Confirm Amazon-side eligibility and subscriptions first, then price the people, tooling, infrastructure, and governance needed to turn the analysis into a repeatable operating process. ## How Kuudo operationalizes AMC [Kuudo's Amazon Marketing Cloud software](/features/amc/) packages the question, grounded playbook, query or payload, checks, result, approval state, and run history into a repeatable workflow. Teams can start from an MCP-compatible AI client while keeping the generated SQL inspectable and holding audience activation behind human approval. ### What is Amazon Marketing Cloud? Amazon Marketing Cloud is Amazon Ads' privacy-safe clean room for analyzing pseudonymized advertising, shopping, and advertiser-owned signals. It returns aggregated insights and audience outputs rather than individual customer records. ### How does Amazon Marketing Cloud work? AMC stores eligible signals in an advertiser-specific clean-room instance, where teams run SQL queries, templates, APIs, or agent-assisted workflows. Privacy checks and aggregation thresholds govern the results that leave the environment. ### What is Amazon Marketing Cloud used for? Advertisers use AMC for audience building, customer-journey analysis, attribution, reach and frequency, incrementality, media overlap, and on- or off-Amazon measurement. The available analysis depends on the data connected to the advertiser's instance. ### Is Amazon Marketing Cloud a clean room? Yes. AMC is a privacy-safe cloud environment where pseudonymized signals can be analyzed without exposing individual customers in the output. ### Who can access Amazon Marketing Cloud? Amazon says AMC is available to eligible advertisers; account, marketplace, and data-source requirements still apply. Check your Amazon Ads account or contact your Amazon Ads representative to confirm eligibility. ### How much does Amazon Marketing Cloud cost? Amazon says AMC is available at no cost to eligible advertisers. Paid signal subscriptions, partner services, implementation, cloud infrastructure, and third-party software are separate costs. ### Do you need SQL to use Amazon Marketing Cloud? SQL remains the most transparent way to inspect custom AMC analysis, but it is not the only operating option. Instructional queries, templates, APIs, Amazon Ads Agent, and governed software workflows can help teams start from a question instead of a blank query. ### Amazon Agent Crawl MCP URL: https://www.kuudo.com/docs/amazon-agent-crawl/ Amazon Agent Crawl MCP lets connected agents search Amazon, open product detail pages, extract listing facts, and compare products from current page data. ## What Agent Crawl Does **Agent Crawl** is a crawling and extraction stack aimed at **real websites**, not static copy-paste. You can run it from a **command line**, **Docker**, or wire it up as an **MCP server** so an AI assistant can call it like any other tool. Under the hood it builds on **crawl4ai**-style browser crawling: it can load pages the way a user’s browser would, respect sensible rate limits, optionally use stealth-friendly settings for difficult sites, and save **RAG-ready** text (markdown, cleaned HTML, chunks) for search and Q&A pipelines. In practice it helps you: - **Crawl** a single page or many linked pages with strategies (breadth-first, depth-first, scored links, adaptive “stop when you know enough” runs). - **Shape output** with topics, search-style filters, chunk sizes, and optional deep-crawl limits so results fit retrieval or summarization. - **Extract structure** where templates exist, so a page becomes **fields** (titles, prices, specs) instead of one big blob of HTML. Packaged flows, including **Amazon** product and search helpers, sit on top of that core: same engine, opinionated defaults for a specific class of pages. ## Why Agents Use It Models are strong at language and reasoning but **weak guarantees** on facts about the live web: training data goes stale, URLs change, and “what I remember about this product” is not the same as “what Amazon shows today.” Agent Crawl gives an agent **repeatable tools** that fetch and normalize **current** pages. That reduces invention on prices, availability, and specs, and turns “describe this URL” into a **grounded** step the user can audit. For agents specifically, the value is: - **Grounding**: answers tied to a fetch the user or tool chain can trace back to a real page. - **Structure**: listings become comparable records, such as search rows or product fields, instead of prose-only summaries. - **Composable workflows**: search, then open several PDPs, then summarize. The same pattern works for docs sites, support portals, or internal wikis when you use the general crawl tools; Amazon is one high-leverage example. The sections below are written for **you in chat**: concrete prompts and outcomes. The assistant is the one invoking Agent Crawl on your behalf when those tools are connected. ## How to Ask You’re in a **normal chat** with an assistant that can use **Amazon lookup tools** on your behalf (for example in Claude, ChatGPT, or another app that supports the same kind of connector). You describe what you want; you do **not** need to know how the tools work under the hood. **What helps every time** - Paste the **full product link** from your browser when you mean one exact listing (the address bar URL from Amazon). - Say **how many** items matter to you (“top three results”, “first dozen hits”). - Say what you care about: **price only**, **full specs**, **reviews**, **questions & answers**, **images**, and so on. Amazon’s pages are built for shoppers, not for robots. Sometimes a field is missing or a page is partly blocked; if that happens, ask again with a narrower question or a different link. **Continuing in the same chat** - You can say **“same link, but now include reviews”** or **“here’s a second URL — compare the two”** without managing anything technical yourself. - If the answer felt thin, say **“open the full listing details”** or **“only the spec table”** so the assistant knows to go deeper on the next pass. --- ## Product Page Prompts Use this when you have a **single** Amazon product URL and want facts from that listing pulled into the conversation. ### Example: “Should I buy this for my desk?” **You might write** > I’m looking at this standing desk converter. Here’s the link: [paste URL]. Pull the title, current price, whether it says in stock, the bullet features, and the weight capacity or size from the details. Summarize in three pros and two cons for someone who works 8 hours a day. **What you should get** - A short summary grounded in that page: price, availability-style wording, key bullets, dimensions or weight limits if the listing shows them, and a balanced opinion framed from those facts. ### Example: “Will this fit in my bag?” **You might write** > [paste URL] - This is a portable monitor. I need the **package dimensions** and **item weight** from the product details, and whether the stand folds flat. Tell me if it’s realistic for a 15" laptop backpack. **What you should get** - Numbers or clear “not listed on the page” answers, plus a plain-English take on carryability. ### Example: “What are people complaining about?” **You might write** > Same link as before. **Include customer reviews** — up to about 20. Group themes: shipping, quality, setup difficulty, anything recurring. Don’t quote long rants; paraphrase. **What you should get** - Themes from recent reviews, not a dump of every star rating. ### Example: “Does anyone answer ‘X’ in the Q&A?” **You might write** > [paste URL] - Turn on **Q&A** if you can. I need to know whether this router works with my ISP’s modem-only setup. Quote or paraphrase only relevant Q&A. **What you should get** - Answers drawn from the Questions & Answers section when the listing has them. ### Example: “Images for a slide deck” **You might write** > [paste URL] — I need **product image links** (main image and gallery if available), plus the official product name as Amazon shows it. One bullet list, no essay. **What you should get** - A tidy list of titles and image URLs you can reuse (respect copyright and Amazon’s rules for how you publish them). ### Example: “Skip the fluff — title, price, Prime?” **You might write** > [paste URL] — Just **title, price, rating, review count**, and whether it looks Prime-eligible. One short paragraph. **What you should get** - A compact fact card, no long spec tables unless you ask for them. --- ## Amazon Search Prompts Use this when you want a **list of options** from a search, like typing into Amazon’s search box, but explained in chat. ### Example: “Shortlist under a budget” **You might write** > Search Amazon for **wireless earbuds under $50** (use whatever filters the search naturally applies). Give me up to **12** results in a **table**: name, price, star rating, approximate review count, Prime yes/no, and mark which ones say **Sponsored** if that’s visible. **What you should get** - A scannable table or list you can sort mentally by price or rating, with sponsored items called out so you’re not comparing ads to organic results by mistake. ### Example: “What exists in this category?” **You might write** > Search for **mechanical keyboard hot swap 75%**. I’m not loyal to a brand — I want **10** options with **price** and **rating**. Highlight any that look like a strong default pick for a first mechanical keyboard. **What you should get** - A curated-feeling shortlist with enough variety to continue in a follow-up message (“now open the top three and compare switches” — see the next section). ### Example: “Find me a gift pattern” **You might write** > Search Amazon for **gifts for a hobby gardener under $30**. List **8** concrete products (not generic categories) with price and one-line “why it’s a nice gift.” **What you should get** - Concrete product names and prices tied to the search, ready to refine (“remove tools they already own” in a follow-up). ### Example: “Compare delivery promises at a glance” **You might write** > Search **USB-C hub pass-through charging MacBook**. Return **15** results; for each, include **price** and any **delivery** snippet the search card shows. **What you should get** - Enough rows to see which listings emphasize fast shipping vs. lowest price. --- ## Search Then Compare Prompts Use this when you want the assistant to **run a search**, then **open the first few product pages** and pull the richer detail you’d get by clicking each listing (spec tables, long descriptions, sometimes reviews depending on what you asked). ### Example: “Pick the best warranty among the top hits” **You might write** > Search **electric kettle glass no plastic contact water**. Then open the **top 4** product pages. For each kettle, extract **warranty** and **materials / BPA-free** language from the listing. End with a recommendation: best for someone who cares about plastic touching hot water. **What you should get** - Side-by-side facts from full pages, not only the thin search snippet. ### Example: “Noise cancelling — battery life battle” **You might write** > Search **over ear noise cancelling headphones** and open the **top 3** results. From each full product page, pull **battery life** claims and **USB-C vs micro-USB** charging. Table + one winner for long flights. **What you should get** - Comparable numbers or quoted phrases as shown on each PDP, with a clear comparison. ### Example: “Monitor arms — will they hold my display?” **You might write** > Search **monitor arm single 32 inch VESA**. Open **top 5** listings. From each page, get **max weight** and **max screen size** if stated. Flag any mismatch with a 32" 9 lb monitor. **What you should get** - A compatibility-oriented matrix and a short “safe / risky / unknown” readout. ### Example: “Baby gear — narrow after reading details” **You might write** > Search **video baby monitor no WiFi**. Open the **top 3** products. From full pages, list **range**, **battery**, and whether **WiFi is required** or optional. Then tell me which one matches “apartment, two rooms, paranoid about hacking.” **What you should get** - Specs that rarely fit in search cards, plus a reasoned pick aligned to your constraints. ### Example: “Same search, but I only trust deep specs” **You might write** > Search **portable SSD 2TB**. Open **top 4** pages. I care about **read/write speeds** and **IP rating** if any. Build a comparison table; if a field isn’t on the page, say “not stated.” **What you should get** - Honest gaps (“not stated”) instead of guessed numbers. --- ## Limits and Expectations - Listings are fetched as Amazon serves them, often **amazon.com (US)**. Prices, wording, and availability are **what the page showed at lookup time**, not a live cart or checkout. - **Sponsored** products appear in search; ask the assistant to **label** them when you’re comparing. - Heavy automation can hit **captchas or empty sections**; retry later, try another product link, or ask for a smaller slice of the page (e.g. title + price only). Use these tools for **personal research and drafting** (gift lists, spec checks, comparison notes). Respect **Amazon’s terms** and **local rules** for scraping or automated access; don’t use this to hammer the site or replace Amazon’s own apps for purchasing. ### Amazon Agent Iris Documentation URL: https://www.kuudo.com/docs/amazon-agent-iris/ Amazon Agent Iris is an MCP server that turns image generation and Amazon listing compliance into one motion. It exposes Google Gemini and OpenAI image models as MCP tools, wraps them in the `amazon-product-image` skill that encodes current Seller Central rules, and hands every result back as a signed URL your assistant or pipeline can use directly. You talk to it in plain language from the AI client you already run — Claude, ChatGPT, Cursor, or your own automation. It generates a compliant main image, builds the rest of the merchandising stack, or audits a photo you already have and tells you exactly what to fix before Amazon ever sees it. This page covers what Iris does, the rules it enforces, what you can ask it for, which model key to bring, and how your images get published to Amazon. Iris runs in your own infrastructure — Docker-ready, like the rest of the Kuudo stack — and you connect it from the AI client you already use. ## What it does The server works in three modes, all driven by natural language: - **Generate** — create a new Amazon-ready image from a prompt (main image, infographic, lifestyle, detail, fashion shot). - **Edit** — modify an existing image to fix a compliance problem or change one element while preserving product identity. - **Audit** — check an image against the current rule set and return a verdict with the exact fix. Generate and audit are the same surface, so you can create an image, audit it, and remediate it without leaving the conversation. ## Example prompts You drive Iris in plain language from your AI client — no tool names, fields, or APIs to remember. Replace bracketed values like `[ASIN]` (Amazon Standard Identification Number), `[SKU]`, or `[brand]` with your own. > **You stay in control** > Image generation and any change that writes to a live listing pause for your review before publishing. Nothing goes live until you approve it. ### Find problems across the catalog Surface what needs attention before you decide where to spend effort. - "Scan my US listings and show me which ones have image problems." - "Which of my live listings have empty image slots — fewer images than Amazon allows?" - "Find my listings that are suppressed and tell me why." - "Show me listings that only have a single image." - "Give me a health check on [parent ASIN] and all its variations." ### Diagnose one listing Zoom in on a specific SKU to understand its current state. - "Pull up [SKU] and tell me what's wrong with it." - "Review the listing for [ASIN] — title, bullets, description, and images." - "Why isn't [SKU] showing in search? What does it need to come back online?" - "How many images does [ASIN] have, and what's missing?" ### Fix listing copy Clean up or rewrite text, grounded in the product's real attributes. - "Rewrite the bullet points for [SKU] — it only has one and it's weak." - "The description for [SKU] has copy-pasted text from another product. Clean it up." - "Improve the bullets for [ASIN] using only what's true from the listing — don't invent features." - "Make the title for [SKU] clearer and keep the brand out of the body copy." ### Generate product imagery Create new images from your real product. Lead with the scene you want. **Lifestyle / in-use shots** - "Create a lifestyle photo of [product] styled on a cream sofa in a bright, neutral living room." - "Show a person using [product] in a warm, natural-light kitchen scene." - "Make a cozy bedroom scene with [product] as the focal point." **Hero / product shots** - "Generate a clean studio shot of [product] on a pure white background." - "Zoom in on the [product] so it fills more of the frame and reads as the hero image." **Infographics** (added text is allowed on these auxiliary images) - "Build a size and dimension graphic for [SKU] using the real measurements from the listing." - "Create a feature-callout image for [product] highlighting its material, stitching, and zipper." - "Make a care-instructions card for [product] — machine washable, etc." - "Create a 'set of 2, covers only — no insert' graphic so buyers know what they're getting." - "Make a color-range image showing all the available colors for this variation family." ### Edit and iterate on an image Refine an image you've already generated, in plain language. - "Zoom in slightly on the pillow in this image." - "Remove the books in the background — keep everything else the same." - "Same scene, but brighter and with more natural light." - "Make the product larger and more centered." - "Try this again on a grey sofa instead of cream." ### Check compliance before publishing Validate against Amazon's image rules before anything goes live. - "Check whether this image meets Amazon's compliance requirements." - "Is text allowed on this image for the slot it's going into?" - "Validate the file format, resolution, and color space on this image." - "Run the policy check on all the images I've generated so far." ### Publish to the listing Write approved changes directly to the live listing. - "Add this image to [SKU] as the first alternate image." - "Put the size graphic into the next open slot on [SKU]." - "Patch [SKU] with this image." - "Apply the rewritten bullets and description to [SKU]." ### Confirm it actually went live Verify the change was accepted and the image was ingested by Amazon. - "Check whether the new image on [SKU] ingested cleanly." - "Did my last change to [SKU] go through, or are there any errors?" - "Re-check [SKU] and confirm the gallery now shows the images I added." ### Reuse imagery within a variation family Fill gaps by borrowing the right images from sibling SKUs — safely. - "This color is missing alternate images. Which images from its sibling colors can I reuse without misrepresenting it?" - "Show me the best-stocked sibling of [SKU] and which of its images are color-neutral enough to copy." - "Fill the empty slots on [SKU] with the generic infographics from the family, but not the color-specific shots." ### Work at scale Move from one listing to many. - "Make a list of every listing missing a main image so I can prioritize them." - "Which listings would benefit most from a lifestyle image? Rank them." - "Walk me through fixing the thinnest listings one at a time." Most real sessions chain these together. A typical one runs: *"Scan my listings for image gaps"* → *"Let's start with [SKU]"* → *"Create a lifestyle photo on a cream sofa"* → *"Zoom in slightly"* → *"Check it for compliance"* → *"Add it to the listing"* → *"Confirm it ingested."* Treat it as a conversation, not a set of one-off commands. ## The audit verdict When you ask for an audit, the skill returns a structured verdict rather than a vague opinion: ```text Status: Compliant | Needs changes | Reject Scope: [main image / alternate / fashion / multipack / ...] Blocking issues: - [issue and the rule category it violates] Non-blocking suggestions: - [quality or conversion improvement] Fix: - [the exact edit instruction or replacement prompt] References checked: - [which rule references were applied] ``` The verdict is graded by rule category, so a near-white background that reads `~254` instead of `255` comes back as a specific, fixable note — not a silent pass that gets your listing suppressed later. ## Compliance rules it enforces The `amazon-product-image` skill carries a distilled rule set from Amazon Seller Central policy. These are the rules applied during generation and checked during audit. ### Main image — hard requirements | Rule | Spec | | --- | --- | | Background | Uniform pure white, RGB 255, 255, 255 | | Product fill | ~85% of the frame, full product visible | | Single view | One unit, one main view (unless a multipack or assortment) | | Props | Product only — no accessories that aren't included | | Text & marks | No added text, logos, borders, watermarks, or badges | | Apparel model rule | Adult apparel on-model and standing; kids, accessories, and multipacks flat (off-model); no mannequins or hangers | | Footwear | Single shoe, left foot, 45° angle | ### Technical file requirements | Spec | Rule | | --- | --- | | Formats | JPEG (preferred), TIFF, PNG, non-animated GIF | | Longest side | 500–10,000 px; 1,000 px+ enables zoom; 1,600–2,000 px+ preferred | | Color | RGB preferred; CMYK may shift tonally; grayscale only for genuinely gray/silver products | | File naming | `ASIN.jpg` or `ASIN.VARIANT.jpg` (periods as separators, no spaces or dashes) | | Image count | Up to 9 (1 main + 8 additional); ~7 gallery thumbnails shown by default | ### All-image content rules These apply to every image in the stack, not just the main: - No reviews, stars, ratings, prices, deals, coupons, or free-shipping claims. - No seller info, email, copyright marks, or watermarks. - No Amazon, Prime, Alexa, "Choice," or "Best Seller" branding or lookalikes. - No promotional, warranty, certification, or unsubstantiated safety/health/regulatory claims. - Every image must match the product title, ASIN, variant, color, quantity, and scale. ### Special cases | Case | Rule | | --- | --- | | Multipacks | Show the total quantity delivered; the title must state the count | | Variety packs | Show representative items plus the total count | | Used / collectible offer photos | Optional, separate from detail images; non-white backgrounds accepted | ### Suppression issues and fixes The skill maps the common suppression and rejection reasons to a concrete fix: | Issue | Fix | | --- | --- | | Non-white background | Use pure white, or normalize the background field to 255 downstream | | Text, logo, or graphics | Remove all non-product overlays | | Cropped product | Reframe so the full product is visible | | Additional items / props | Show the product only | | Mannequin or hanger visible | Flat-lay, or an invisible-mannequin edit that doesn't crop the product | | Model not standing | Use a standing model (wheelchair exception allowed) | | Multiple views in one frame | One main view per image | | Blurry, pixelated, or too small | Replace with a sharp image ≥500 px on the longest side | | Unsupported / corrupted file | Re-export as JPEG/PNG/TIFF, flattened, verified locally | | **Error 100239** | Image and title don't match — correct either the title or the image and resubmit; if they already match, escalate to support with the SKU, `item_name`, and image URL | ### Fashion, apparel, and footwear - Adult apparel on-model and standing; kids, babies, accessories, and multipacks off-model (flat laydown). - Framing by garment: full-length for dresses and suits, top-body for shirts, waist-down for pants and skirts. - Footwear main image: single shoe, left foot, 45°. - Off-model laydowns: white surface, steamed, loose threads removed, square framing. - A 13-point fashion validation checklist covers parent/child coverage, naming (`ASIN.MAIN.jpg`, `ASIN.PT01.jpg`, `ASIN.FL01.jpg`), the image set, and mobile readability. ## The image stack Most listings stop at the main image. The skill helps you build the full stack that actually converts: | Slot | Type | Purpose | | --- | --- | --- | | 1 | Main | Product on pure white; drives the search click | | 2 | Feature | Close-up of the key differentiator | | 3 | Infographic | Labels, dimensions, materials, contents, compatibility | | 4 | Lifestyle | Product in real use; environment and models allowed | | 5–7 | Angles / details | Front, side, back, interior, hardware, texture | | 8 | Instructional | Assembly, fit, usage, or truthful before/after | ## Providers and models One server, two best-in-class image engines. The provider is chosen **per session from the API key on the request** — no lock-in, and the compliance rules apply identically to both. - A Google key (`AIza…`) routes to **Google Gemini**. Default model is `gemini-3.1-flash-image` ("Nano Banana"), with `gemini-3-pro-image` and `gemini-2.5-flash-image` also available. Resolutions `0.5K`, `1K`, `2K`, `4K` and the full aspect-ratio set (`1:1` through `21:9`). - An OpenAI key (`sk-…`) routes to **OpenAI `gpt-image-2`** via the Responses API, with `size`, `quality`, and `background` controls. We recommend Gemini Nano Banana for speed and cost; switch any time by changing the key. ## MCP tools The server exposes a small, clean tool surface. The two generation tools are the ones you'll use most. | Tool | What it does | Key parameters | | --- | --- | --- | | `start_here` | Built-in workflow guide for the LLM | none | | `generate_image` | Generate or edit with Google Gemini | `prompt`, `n` (1–14), `input_images`, `operation`, `aspect_ratio`, `resolution`, `model`, `interaction_id`, `use_grounding` | | `generate_openai_image` | Generate with OpenAI `gpt-image-2` | `prompt`, `n` (1–10), `input_images`, `size`, `quality`, `background`, `output_format`, `previous_response_id` | | `create_upload_url` | Mint a short-lived signed upload URL for your own image bytes (up to 40 MB) | none | | `server_status` | Diagnostics: auth, active providers, models, storage, health | none | | `read_resource` | Read a resource by URI, e.g. `skill://amazon-product-image/SKILL.md` | `uri` | Every generated asset comes back as a **signed download URL** (plus a thumbnail and expiry) and is hosted for you, so it drops straight into a PIM, DAM, or ad campaign — or onto an Amazon listing, which fetches that URL directly. See [Built-in media hosting](#built-in-media-hosting-amazon-pulls-it-doesnt-receive) below for why that matters. ### Chained edits and brand-mark fidelity To iterate without re-uploading the source image, pass the prior interaction back in: - **Gemini:** reuse `interaction_id` from a previous `generate_image` result (valid ~55 days on paid accounts, 1 day on free). - **OpenAI:** reuse `previous_response_id` from a previous `generate_openai_image` result. Brand-mark fidelity is a first-class rule on every edit. Logos, labels, and printed text are reproduced exactly — when you recolor a material, only that material changes; the wording, typography, and logo artwork stay identical to the source. A re-lettered or garbled label is treated as a compliance failure. ## Built-in media hosting: Amazon pulls, it doesn't receive Amazon's listing system is **pull-based, not push-based**. You never upload image bytes to Amazon. You hand it a URL, and Amazon's content pipeline runs its own `GET` against that URL, fetches the asset, and copies it into its CDN. The consequence is strict: an image exists to Amazon only if Amazon can reach it over the public internet, **unauthenticated**, at the moment it fetches. That is why Iris hosts your media for you. A freshly generated image otherwise lives nowhere addressable — it has no point of egress, so there is literally nothing for Amazon to pull. Iris gives every generated (or uploaded) asset a public, fetchable URL the instant it is created, which is exactly the contract Amazon's pull model expects. Why this matters more than it looks: - **It speaks Amazon's protocol natively.** Amazon expects an unauthenticated, `GET`-able asset; the host serves precisely that, so the integration meets the requirement instead of approximating it. - **It removes the most common failure point.** Without an integrated host you bolt on separate hosting — buckets, credentials, public-read policies, URL signing — each a chance to misconfigure. The classic listing-image failure is exactly this: the asset isn't reachable, Amazon's fetch fails, and the image silently never appears. - **It makes "generate and publish" one continuous motion.** Because the asset is born already addressable, the handoff from "image exists" to "Amazon can fetch it" is instant. There is no manual upload step in the middle. > **You don't host anything** > Public egress, the URL, and how long it stays alive are all handled for you. Iris keeps each image reachable through Amazon's fetch window, so the asset is still there when Amazon's pipeline comes to ingest it. Built-in egress is the headline; staying reachable long enough is the part Iris takes care of so a fetch never silently fails. Bottom line: an image with no public point of egress is, to Amazon, an image that doesn't exist. The built-in media host is the reachable front door that lets the whole pipeline meet Amazon's pull model natively, rather than relying on fragile, hand-rolled hosting. ## Limits and honest framing - **Amazon makes the final call.** The skill materially de-risks and accelerates compliance, but it does not guarantee acceptance — Amazon does. - **The ~254 vs 255 white-background caveat.** Current image models render a non-uniform near-white background around `254`, not exact `255`, even with a perfect prompt. This is a model rendering limit, not a compression issue, and switching to PNG/TIFF doesn't fix it. Reaching exact-255 compliance needs a deterministic downstream normalization pass that clamps the background field to 255. The audit flags when that step is needed and tells you to sample the corners rather than judge by eye. - **One provider per session.** A request uses Gemini or OpenAI based on its key — not both in the same call. - **Synthetic images must be honest.** AI-generated product images are acceptable only when they realistically represent the actual product and never hide defects, change identity, color, scale, or contents, or add prohibited text or claims. ## Related - [Amazon Agent Iris feature overview](/features/amazon-agent-iris/) — the product surface at a higher level. - [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) — publish generated images straight onto listings and verify ingestion. - [Amazon Ads MCP](/features/amazon-ads-mcp/) — pull live ad performance to seed creative. - [MCP client configuration](/docs/mcp-client-configuration/) — connect Claude, ChatGPT, Cursor, and other clients. ### Using a Knowledge Library With Your Agent URL: https://www.kuudo.com/docs/agent-knowledge-usage/ This page is for people who work in ChatGPT, Claude, or similar assistants that can **search a curated library** (for example product rules, playbooks, or internal guides) instead of relying only on what the model remembers from training. You do not need to know how the library is stored. If your workspace or project already connects the assistant to that library, treat this as **how to ask** so answers stay grounded, specific, and useful. On Kuudo, **Amazon Agent Atlas** is one way that library is connected; the habits below apply to any similar setup. ## What Changes When a Library Is Connected A connected library is **searchable text your agent can pull in during the conversation**. It is not magic: the assistant finds passages that match your question, then reads them to answer. Clear questions and explicit goals produce better retrieval than vague ones. **You should expect** - Answers that quote or follow your organization’s material when it is relevant. - Less invention on topics where your library is the source of truth. - The assistant to say when it cannot find a match, instead of guessing. ## How to Ask Use the same habits you would use with a sharp colleague who has access to your file share. - **Name the domain** in plain language (Amazon listings, sponsored ads, compliance, style guides). - **Say the output shape** you want: checklist, table, rewrite, pros and cons, email draft, bullet talking points. - **Add constraints**: audience (seller, executive, new hire), region, tone, or “only use our library; say if something is missing.” - **One main task per message** when possible. Long laundry lists get partial answers. ## Use Case: Category or Channel Rules Use this when you need **how we title, bullet, or structure listings** for a specific category or channel. **You might write** > Pull our style guidance for Amazon **Grocery** listings. Summarize title patterns and bullet order in a short checklist I can hand to a copywriter. **You might write** > We are launching a **pet food** SKU. What does our library say about species, life stage, and parent/child titles? Give me a do/don’t list. **What you should get** - A compact checklist or bullets tied to your guides, not generic e-commerce advice. - Explicit callouts when your library distinguishes sub-categories (for example food vs toys). ## Use Case: Rewrite or Strengthen Copy Use this when you have **draft listing text** and want it aligned to your stored rules. **You might write** > Here is our draft title and five bullets for a **chromebook**. Rewrite them to match our Computers style guide. Keep the same facts; flag anything we should verify on Seller Central. **You might write** > Rewrite these bullets for a **baby car seat** listing in a calmer, compliance-first tone. Cite which rules from our library you applied. **What you should get** - Revised copy plus a short “why” tied to your library (title formula, claim limits, required attributes). - Flags where the library says to verify with compliance or live templates. ## Use Case: Compare Two Approaches Use this when you are **choosing between strategies** and want the assistant to ground the tradeoffs in your material. **You might write** > Compare “keyword-stuffed titles” vs “spec-first titles” for **consumer electronics** accessories using only our guidance. End with a recommendation for a three-SKU cable line. **What you should get** - A side-by-side or short table grounded in your docs, plus a clear recommendation and assumptions stated explicitly. ## Use Case: Onboarding and Training Use this when someone **new needs the shape of a domain** without reading hundreds of pages. **You might write** > New hire starts Monday on **Amazon Ads reporting**. Give a 10-minute spoken overview outline: what to read first, three concepts that confuse people, and five questions they should ask the account lead. **You might write** > What topics does our library cover under **vendor operational compliance**? List them as a syllabus with one sentence each on why it matters. **What you should get** - A syllabus, outline, or FAQ-style map that reflects what is actually in the library. ## Use Case: Pre-Flight Before a Launch or Audit Use this when you want a **last pass against stored rules** before publishing or before a client call. **You might write** > We are about to publish a **supplements** detail page. Scan against our Health & Personal Care guidance: structure/function vs disease claims, disclaimer placement, and title formula. Output pass / fix / escalate. **You might write** > Tomorrow’s meeting: **sponsored ads bidding**. List the top five risks or misconceptions our library warns about, with one example question to ask the client for each. **What you should get** - A prioritized review list with “fix” vs “escalate to legal/compliance” style buckets when your material supports that split. ## Use Case: “What Do We Actually Say About X?” Use this for **pointed fact finding** when rumors or forum advice conflict with your standards. **You might write** > What does our library say about **refurbished** computer listings and disclosure wording? Quote short phrases if helpful. **You might write** > Find anything we have on **live plants** or **hardiness zones** in listing copy. If we have nothing, say so clearly. **What you should get** - Direct excerpts or faithful paraphrases with scope (“this is from the Garden guide, not legal advice”). - A clear “not found in library” when there is no match. ## Limits and Expectations - **Staleness:** Your library reflects the documents that were indexed. Amazon templates, policies, and UI change; always confirm critical details in Seller Central or official sources when stakes are high. - **Not legal advice:** Guides summarize patterns and internal standards. Compliance decisions stay with your counsel and account owners. - **Retrieval is not perfect:** Unusual wording or missing tags in source files can make matches weaker. Rephrase or add a keyword from your domain if the first answer feels thin. - **Privacy:** Do not paste secrets into chats unless your organization approves that workflow. Ask about **sanitized examples** when demonstrating issues. ## Related Material - [Amazon Agent Atlas](/docs/amazon-agent-atlas/) — Kuudo’s curated Amazon operating library and how it fits agents. - [Claude quick start](/docs/quick-start/claude-ai/) and [ChatGPT quick start](/docs/quick-start/chatgpt/) — connect a client, then use the prompts above. For the open-source **Chroma MCP** knowledge pipeline (developers), see the **chroma-mcp** repository README and agent instructions at the repository root. ### Amazon Listing Optimizer URL: https://www.kuudo.com/docs/amazon-listing-optimizer/ Amazon Listing Optimizer audits, rewrites, and fixes your Amazon listings from the AI client you already use. It reads the live catalog and your submitted listing, finds what's wrong or thin — suppressions, contradictions, weak bullets, broken variations, empty search terms, missing A+ Content — rewrites it grounded in your product's real attributes and Amazon's current rules, and publishes the approved changes straight to the listing. You talk to it in plain language. Name an ASIN (Amazon Standard Identification Number) or SKU and say "review it," "clean up the bullets," "why is this suppressed," or "fix the whole catalog," and it takes it from there. It runs on Kuudo's hosted infrastructure as a Skill on the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/). There is nothing to deploy or operate, and nothing it writes goes live without your sign-off. This page covers what it does, what to say, what it checks, how changes get published safely, and how it pairs with [Amazon Agent Iris](/features/amazon-agent-iris/) for images. ## What it does - **Audit** — review one listing or scan the whole catalog: surface suppressions, policy warnings, contradictions, thin copy, broken variations, and missing A+ Content. - **Optimize** — rewrite titles, bullets, descriptions, and search terms grounded in the product's real attributes and the category's Amazon style guide. Fix variation scope and fill gaps. - **Publish** — write approved changes straight to the live listing, then confirm Amazon accepted and ingested them. Audit and optimize are one conversation: review a listing, pick the fixes, preview the change, approve, publish — without leaving chat. ## Example prompts You drive it in plain language from your AI client — no tool names, fields, or APIs to remember. Replace bracketed values like `[ASIN]`, `[SKU]`, or `[brand]` with your own. > **Nothing goes live without your sign-off** > Every change that writes to a live listing is previewed first and waits for your approval. You see the exact before/after before anything is submitted. ### Find problems across the catalog Surface what needs attention before you decide where to spend effort. - "Scan my US listings and show me which ones have problems." - "Which of my listings are suppressed, and why?" - "Find my thin listings — a single image, one weak bullet, or no description." - "Give me a health check on [parent ASIN] and all its variations." - "Which of my brand-registered listings are missing A+ Content?" ### Diagnose one listing Zoom in on a specific SKU to understand its current state. - "Pull up [SKU] and tell me what's wrong with it." - "Review the listing for [ASIN] — title, bullets, description, search terms, and images." - "Why isn't [SKU] showing in search? What does it need to come back online?" - "Is there anything in [SKU]'s copy that looks tampered with or hijacked?" ### Fix the copy Rewrite text grounded in the product's real attributes — never invented features. - "Rewrite the bullets for [SKU] — it only has one and it's weak. Use only what's true from the listing." - "The description for [SKU] has copy-pasted text from another product. Clean it up." - "My title is over the limit and stuffed with keywords — tighten it to the category formula." - "Make the bullets answer who it's for and why to buy, not just list features." - "There's a contradiction — the bullet says 'cast iron' but the attributes say metal. Fix it the right way." ### Fix variations and the family - "This is a variation parent but the bullets name a specific color — fix the scope so it describes the family." - "Compare the parent's child ASINs to its child SKUs and find the orphan." - "Each child should show its own color in the main image — which ones don't?" ### Search terms and SEO - "My backend search terms are empty — fill them from the listing and customer reviews." - "Audit [SKU]'s search terms: strip out brand names, duplicates, and anything over the limit." ### A+ Content - "Does [ASIN] have A+ Content? If not, what would it add?" - "Audit the A+ Content on [ASIN] against Amazon's policy." - "Draft A+ modules for [ASIN] — lifestyle, feature callouts, and a same-brand comparison." ### Check compliance before you change anything - "Check [SKU] against Amazon's listing rules before I touch it." - "Is my responsible-party address DSA-compliant?" - "What would suppress this listing if I submitted it as-is?" ### Preview and publish - "Draft the cleaned bullets and show me the exact before and after." - "Apply the rewritten bullets and description to [SKU]." *(it previews; you confirm)* - "Patch [SKU]'s title with the tightened version." ### Confirm it actually went live - "Did my last change to [SKU] go through, or were there errors?" - "Re-check [SKU] in a bit and confirm the detail page updated." ### Work at scale - "List every suppressed listing so I can prioritize them." - "Rank my listings by how much a copy cleanup would help." - "Walk me through fixing the thinnest listings one at a time." Most real sessions chain these together. A typical one runs: *"Scan my catalog for problems"* → *"Start with [SKU]"* → *"Why is it suppressed?"* → *"Rewrite the bullets and fix the contradiction"* → *"Preview the patch"* → *"Confirm"* → *"Re-check it went live."* Treat it as a conversation, not a set of one-off commands. ## What it checks When it audits a listing, it looks for the problems that actually cost you visibility and conversions, and reports them ranked by priority: - **Active warnings and suppressions** — policy errors, missing required fields, and category-specific gaps that hide the listing from search. - **Hijacked or tampered copy** — adult terms, slurs, or sabotage phrases ("do not buy," "counterfeit") injected by a compromised account or a hijacker. Flagged as a security issue to investigate, not a typo to quietly rewrite. - **Self-contradictions** — title versus bullets versus description versus structured attributes: a size mismatch, "machine washable" with a hand-wash attribute, "cast iron" on a part that can't be. - **Thin or feature-dump copy** — bullets that list specs but never say who the product is for or why to buy it. - **Wrong variation scope** — color- or size-specific copy on a parent that spans the family, or an orphaned child SKU. - **Weak SEO** — empty or keyword-stuffed backend search terms, and titles that ignore the category formula. - **Missing A+ Content** — brand-registered ASINs leaving conversion lift on the table. - **Compliance gaps** — brand-name policy, the EU DSA responsible-party address, and the rest of Amazon's hard rules. It leads with the warning that's hiding your listing, groups the cosmetic fixes, and offers concrete next actions rather than dumping a thirty-item list. ## Nothing goes live without your sign-off Listings are public content, so every change that writes to a live listing runs the same three steps: > **Preview → Confirm → Submit** > It shows you the exact change — which field, from what, to what — and waits. Nothing is submitted until you reply with your approval. Then it submits and reports the result. "Accepted" means Amazon validated the change; the detail page itself updates a few minutes to a few hours later. It won't run back-to-back edits without your say-so, and it won't "fix" a field you didn't ask about. If you request a change that contradicts the product's reality — a spec the attributes say isn't true — it surfaces the conflict and offers the correct fix instead of silently shipping something that drives returns. ## Grounded in Amazon's rules The optimizer applies Amazon's hard policy — title and bullet limits, prohibited content, suppression triggers, brand-name rules, per-category required attributes — and grounds category-specific decisions like title formulas and bullet conventions in [Amazon Agent Atlas](/features/agent-atlas/), Kuudo's indexed Amazon knowledge base. When Atlas doesn't have a rule for a category, it says so rather than inventing one. ## Images: pair it with Amazon Agent Iris The optimizer handles everything text and structure — copy, variations, search terms, A+ Content. For the image side of a listing it pairs with **[Amazon Agent Iris](/features/amazon-agent-iris/)**, and that pairing matters more than it looks. Amazon's image fields don't take bytes — they take a **URL Amazon fetches** and copies into its CDN, so a listing image has to be generated *and* hosted somewhere publicly reachable. Iris does both: it generates compliant images from your real product and hosts each one at a fetchable URL the instant it's created. You skip the part that usually breaks image work — standing up a storage bucket, credentials, public-read policies, and staging — and the optimizer publishes the resulting URL straight onto the listing through the same preview-and-confirm flow. The full motion across both reads: *audit the listing → rewrite the copy → generate the missing images with Iris → check everything against Amazon's rules → publish → confirm it went live.* See the [Amazon Agent Iris docs](/docs/amazon-agent-iris/) for the image side, and the [Rebuild Your Listing Images From Your Own Photos](/guides/seller-listing-image-regeneration-seeded/) guide for the seeded-image workflow. ## Limits and honest framing - **You own the call.** It rewrites and previews; you approve every live change. It will push back on edits that contradict the product, but the final decision is yours. - **"Accepted" isn't "live."** Amazon validates the change immediately; the detail page propagates over minutes to hours. Re-check shortly after to confirm. - **Some fixes need you at Seller Central.** Brand-approval errors and A+ rejections require a support case or a resubmission you own — it tells you exactly what to file. - **A+ Content goes through review.** Amazon reviews new A+ Content (about seven business days); rejections usually trace to competitor comparisons or promotional claims. - **It only touches listings your account owns.** Edits are scoped to the connected seller identity and marketplace. ## Related - [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) — the product surface this runs on. - [Amazon Agent Iris](/features/amazon-agent-iris/) — generate and host compliant listing images; the perfect companion for the image side of optimization. - [Amazon Agent Iris docs](/docs/amazon-agent-iris/) — the image generation, audit, and hosting workflow. - [Amazon Agent Atlas](/features/agent-atlas/) — the indexed Amazon knowledge the optimizer grounds its category decisions in. - [Rebuild Your Listing Images From Your Own Photos](/guides/seller-listing-image-regeneration-seeded/) — a worked listing-image workflow. - [MCP client configuration](/docs/mcp-client-configuration/) — connect Claude, ChatGPT, Cursor, and other clients. ### Amazon Returns Monitor URL: https://www.kuudo.com/docs/amazon-returns-monitor/ Amazon Returns Monitor turns your returns data into a report that tells you which products are coming back, why, and which ones you can actually do something about. You ask for it in plain language from the AI client you already use — name a window, say "run my returns report," and it pulls the data, breaks it down, and hands you the result. It reads your FBA (Fulfillment by Amazon) and MFN returns, ranks the ASINs driving the volume, sorts every return reason into what you can fix versus what you can't, and flags the products that need attention now — with the recurring complaint pulled straight from customer comments. It runs on Kuudo's hosted infrastructure as a Skill on the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/). There is nothing to deploy or operate, and it never changes your account — it analyzes returns and produces a deliverable you control. This page covers what it does, what to say, what it surfaces, how it keeps the numbers honest, and how it pairs with [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) and [Amazon Agent Iris](/docs/amazon-agent-iris/) to fix the returns you can fix. ## What it does - **Break it down** — returns by ASIN (Amazon Standard Identification Number), SKU, reason, and disposition: which products drive the volume, and the dominant reason for each. - **Separate what you can fix from what you can't** — every return reason sorted into controllable, remorse, ops, and other, so you spend effort where a fix would actually move the number. - **Flag the ones that need action now** — red-flag ASINs with a high return rate and a fixable cause, each with two or three representative customer comments and a grounded hypothesis for what's wrong. - **Trend it and hand it off** — this period versus the one before, delivered as a chat summary, a Word document, an Excel workbook, or an interactive dashboard. ## Example prompts You drive it in plain language from your AI client — no report types, columns, or APIs to remember. Replace bracketed values like `[ASIN]`, `[SKU]`, or `[brand]` with your own. > **This one reads; it never writes** > The monitor pulls your returns data and analyzes it. It doesn't change a listing, issue a refund, or touch your account — so there's nothing to approve. Acting on what it finds is a separate, deliberate step. ### Run the report - "Run my returns report for the last 30 days." - "Pull FBA returns for last month and compare them to the month before." - "Give me a returns report for my US account I can send to the team." ### See what's coming back - "Which of my ASINs have the most returns?" - "Break my returns down by product and reason." - "What's my most-returned SKU, and what's the top reason for it?" ### Understand the reasons - "Group my return reasons into what I can fix versus buyer's remorse." - "How much of my return volume is 'not as described' versus 'changed their mind'?" - "Which returns are defects or damage, and which are just preference?" ### Find the problem ASINs - "Flag any ASIN with a return rate high enough to worry about." - "Show me the products where the returns are something I could actually fix." - "Which ASINs come back as 'customer damaged' more than half the time?" ### Get the return rate right - "What's my return rate for [ASIN] — and tell me how you measured it." - "I only have the returns file, no sales data. What can you still tell me?" - "Compare [ASIN]'s return rate to my portfolio median." ### Read the customer comments - "What are customers actually saying when they return [ASIN]?" - "Pull the recurring complaint from the comments on my worst-returning products." - "Are buyers complaining about sizing, the photos, or quality?" ### Disposition and reimbursement - "How many of my returns came back unsellable?" - "Where's the gap between units returned and units I can resell — am I owed reimbursements?" - "Break down returns by disposition for [ASIN]." ### Trend it over time - "Is my return rate going up or down versus last period?" - "Which reasons grew the most this month?" - "Did my red-flag ASIN list change from last month?" ### Pick the format - "Give me the workbook so my BI team can slice it." - "Make it a one-page exec summary." - "Build me a dashboard I can explore and filter by reason." ### Turn the findings into fixes - "Which of these returns trace back to a bad listing or a misleading photo?" - "Take my top 'not as described' ASINs and tell me what to fix." - "List the controllable-return ASINs so I can hand them to the listing optimizer." Most real sessions chain these together. A typical one runs: *"Run my returns report for last month"* → *"Which ASINs are red-flagged?"* → *"Why is [ASIN] coming back?"* → *"What are customers saying?"* → *"Which of these are listing or image problems?"* Treat it as a conversation, not a set of one-off commands. ## What it surfaces When it runs a full report, it leads with the urgent and works down, so you read the things that need action first: - **Red-flag ASINs, up top** — the products to deal with now: a high return rate, enough shipped volume to be real, and a top reason you can fix. These lead the report; they're never buried in an appendix. - **Reason breakdown** — every return reason, grouped into controllable, remorse, ops, and other, so you can see at a glance what a fix would move and what it wouldn't. - **Return concentration** — which ASINs drive the volume, with the dominant reason and per-ASIN rate for each. - **Customer-comment themes** — the recurring complaint behind your worst returns, mined from real buyer comments, not guessed. - **Disposition and reimbursement** — what came back sellable versus unsellable, with a callout when the gap suggests you're owed a reimbursement. - **Fulfillment-center concentration** — when returns cluster at one FC, a packaging or handling signal worth a look. - **Time trend** — daily and weekly movement, with partial weeks flagged so a refresh lag doesn't read as a real dip. - **Prior-period deltas** — this window against the one before, on volume, rate, reason mix, and which ASINs joined or left the red-flag list. A product gets red-flagged when more than 40% of its shipped units come back, it shipped enough to clear the noise, and the top reason is something a listing, catalog, or quality fix would address. It also flags any ASIN coming back as "customer damaged" more than half the time, regardless of rate — that pattern usually means a packaging failure or return abuse. ## Straight talk on the return rate A return rate is one of the easiest numbers on Amazon to quote wrong, because returns and sales are dated and counted differently. The monitor won't hand you a bare percentage and let you misread it: > Every rate carries a label for how it was measured. A *share of returns* — what slice of your returns a reason or ASIN accounts for — is never dressed up as a *return rate* against units sold. When the data lets it tie returns back to the orders that caused them, it says so and states the follow window. And it keeps facts separate from policy. What the data shows — counts, reasons, concentration — it states plainly. Account-health thresholds, which Amazon changes by program and region, it flags as *verify in Seller Central* rather than quoting a number as if it were the rule. When a recommendation leans on an actual Amazon rule, it grounds that in [Amazon Agent Atlas](/features/agent-atlas/), Kuudo's indexed Amazon knowledge base, instead of inventing one. ## From diagnosis to fix The monitor is a diagnosis. It tells you which products are bleeding returns and the reason behind each — but it doesn't change anything. The controllable bucket it surfaces is exactly what two companion tools repair. - **"Not as described" and image-driven returns** usually mean the photos oversell or under-show the product. [Amazon Agent Iris](/features/amazon-agent-iris/) regenerates compliant, accurate images from your real product and hosts them where Amazon can fetch them, so the picture matches what shows up at the door. See the [Iris docs](/docs/amazon-agent-iris/). - **Sizing, fit, contradictions, and thin or wrong copy** live in the listing text. [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) rewrites titles, bullets, and size guidance grounded in the product's real attributes and Amazon's rules, then publishes the fix once you approve it. The full loop runs in one place: *run the returns report → read the controllable ASINs and their modal reason → hand the copy and sizing problems to the Optimizer and the image problems to Iris → re-list → re-run the report next period and watch the controllable rate fall.* Diagnosis, fix, proof — without leaving your AI client. ## Limits and honest framing - **It reads; it doesn't change anything.** No writes to your account. It analyzes returns and produces a report; fixing the listings behind them is a separate, opt-in step. - **A real return rate needs shipment data.** Without a units-sold denominator it gives you rankings and reason shares, not a validated rate — and labels them as exactly that. - **Account-health thresholds aren't hard-coded.** Policies shift by program and region; it flags risk and points you to your own Seller Central numbers rather than stating a threshold as fact. - **Returns data lags and uses its own dates.** Amazon's daily refresh trails real time, and returns and sales are indexed by different events; it states the exact UTC window it pulled so a time-zone or refresh gap doesn't fool you. - **It only sees the account you connect.** Every pull is scoped to the selected seller identity and marketplace. ## Related - [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) — the product surface this runs on. - [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) — fix the copy, sizing, and contradictions behind controllable returns. - [Amazon Agent Iris](/features/amazon-agent-iris/) — regenerate the misleading or thin images behind "not as described" returns. - [Amazon Agent Iris docs](/docs/amazon-agent-iris/) — the image generation, audit, and hosting workflow. - [Amazon Agent Atlas](/features/agent-atlas/) — the indexed Amazon knowledge that grounds rule-dependent recommendations. - [MCP client configuration](/docs/mcp-client-configuration/) — connect Claude, ChatGPT, Cursor, and other clients. ### Amazon Search Query Analyzer URL: https://www.kuudo.com/docs/amazon-search-query-analyzer/ Amazon Search Query Analyzer reads your Search Query Performance (SQP) data and tells you, for each ASIN (Amazon Standard Identification Number) and each search term, exactly where you're losing — showing up, getting clicked, or closing the sale — and what to change to win it back. You ask for it in plain language from the AI client you already use. It walks the funnel one search term at a time. For a query like "large dog bed," it compares your share of impressions, clicks, and purchases against the whole market and your peers, finds the stage that's leaking, and names the fix — a title, a hero image, a price test, a faster delivery promise, or a variant cleanup. It runs on Kuudo's hosted infrastructure as a Skill on the [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/). There is nothing to deploy or operate, and it never changes your account — it diagnoses, ranks, and recommends; making the changes is your call. This page covers what it does, what to say, what it surfaces, how it keeps the calls honest, and how it pairs with [Amazon Agent Iris](/docs/amazon-agent-iris/) and [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) to fix what it finds. ## What it does - **Diagnose the funnel** — for every search-term-and-ASIN pair, where you stand on impression share, click share, and purchase share against the market and your peers, and which stage is the bottleneck. - **Name the fix for each gap** — invisible for a term, weak click-through, conversion friction, slow delivery, price, or variant cannibalization, each mapped to a concrete change. - **Rank by upside, not volume** — opportunities scored by how much recoverable sales sit behind the gap, so the top of the list is the highest-leverage work, not just the biggest keyword. - **Tag who owns each fix** — every recommendation marked as a listing change, an ops change, or an ad play, so the right person picks it up. ## Example prompts You drive it in plain language from your AI client — no report types, columns, or metrics to remember. Replace bracketed values like `[ASIN]`, `[SKU]`, or `[search term]` with your own. > **This one reads; it never writes** > The analyzer pulls your search data and diagnoses it. It doesn't edit a listing, change a price, or touch a campaign — so there's nothing to approve. Acting on what it finds is a separate, deliberate step. ### Run the analysis - "Analyze my Search Query Performance for [ASIN]." - "Run an SQP diagnostic on my top sellers for the last month." - "Pull search query performance for [parent ASIN] and all its children." ### Find where you're losing - "For [ASIN], am I losing on visibility, clicks, or conversion?" - "Where in the funnel is [ASIN] leaking — show me the biggest gap." - "Which search terms drive impressions for [ASIN] but no sales?" ### Visibility and SEO - "Which high-volume searches is [ASIN] barely showing up for?" - "What terms should [ASIN] rank for but doesn't?" - "My impression share is low on '[search term]' — what would help?" ### Click-through - "People see [ASIN] but don't click — is it the image or the title?" - "Which queries have strong impressions but a weak click-through rate?" - "Why is my CTR below the benchmark on '[search term]'?" ### Conversion - "People click [ASIN] but don't buy — what's the friction?" - "Where is my click share beating my purchase share?" - "Is it price or shipping that's killing conversion on '[search term]'?" ### Price and shipping - "Am I priced above the market on the terms where I'm losing sales?" - "Which ASINs lose purchases to slow delivery?" - "Show me queries where a faster shipping promise would move the needle." ### Variants and cannibalization - "Are my own variants competing for the same search?" - "Which child ASIN should own '[search term]'?" - "Find where my products eat into each other in search." ### Rank the opportunities - "Rank my search-term opportunities by upside, not just volume." - "What are the ten fixes that would recover the most sales?" - "Give me a prioritized to-do list from this report." ### Sort by who owns the fix - "Split the recommendations into listing fixes, ops fixes, and ad plays." - "Show me only the changes I can make on the listing itself." - "Which of these are shipping or inventory problems, not listing ones?" ### Turn the findings into fixes - "Take my low-CTR ASINs and tell me which images to redo." - "Hand the SEO and copy fixes to the listing optimizer." - "Which of these need a new hero image versus a title rewrite?" Most real sessions chain these together: *"Run SQP for [ASIN]"* → *"Where am I losing?"* → *"It's clicks — is it the image?"* → *"Which terms is this worst on?"* → *"Send those to Iris."* Treat it as a conversation, not a set of one-off commands. ## What it surfaces The analyzer reads each search term as a funnel and tells you where it breaks: - **The funnel, stage by stage** — your impression share, click share, and purchase share for the term, against the market total and your peer set, so the leaking stage is obvious. - **A named cause for every gap**, each pointing at a specific fix: - High-volume term, low impression share → an **SEO and PDP update** toward the intent behind the query. You're invisible for something people search. - Clicks beat impressions but purchases don't keep up → a **conversion fix** on price versus the market median, ratings, or the delivery promise. - CTR below the query benchmark while you hold impression share → a **hero image and title** fix. You show up; you just don't win the click. - Purchases trail clicks with a high slow-shipping share → a **delivery-speed fix** and a clearer delivery promise. - Priced above the market median with weak purchase share → a **bounded price test**. - Underperforming your own brand peers on a term → a **variant fix** or a re-balance of on-page emphasis, so your products stop competing with each other. - **Lift-aware ranking** — every opportunity scored by the size of the gap times the volume behind it, so the list leads with recoverable sales rather than raw search volume. - **Owner tags** — each recommendation marked listing, ops, or ads, so a copy change, a fulfillment change, and a campaign change never get confused for one another. - **Confidence badges** — each finding carries whether it had enough data, which peer benchmarks it used, and its attribution scope, so you can tell a real signal from noise at a glance. ## Straight talk on the numbers Search Query Performance is powerful and easy to over-read. The analyzer holds a few lines so it doesn't send you chasing noise: > It won't make a strong call on thin data. Below a floor of impressions and clicks for a term, it flags the row as thin and softens the recommendation instead of telling you to rebuild a listing over a handful of events. - **The scope is the search results page, over a recent window** — not lifetime, and not every path to a sale. Every finding says so, so you weigh it accordingly. - **It keeps organic and paid apart.** SQP is organic search; it won't compute ACoS or ROAS (return on ad spend) by bolting ad spend onto these totals. Sponsored tactics are tagged separately and routed to your ads workflow. - **It paces changes.** After it recommends a test on a term, it waits out a cooldown before recommending another on the same term, so you measure the result instead of stacking edits you can't tell apart. ## From diagnosis to fix The analyzer tells you where each product loses in search and what to change. It doesn't change anything itself — and the fixes it names are exactly what its companion tools execute. - **"You show up but nobody clicks"** is almost always the main image or the title. [Amazon Agent Iris](/features/amazon-agent-iris/) regenerates a compliant, click-winning hero image from your real product and hosts it where Amazon can fetch it. See the [Iris docs](/docs/amazon-agent-iris/). - **"You're invisible for a term," or "people click but the copy doesn't close"** lives in the listing text. [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) rewrites titles, bullets, and search terms toward the intent the analyzer found, grounded in the product's real attributes and Amazon's rules, and publishes once you approve. - **The ad-tagged plays** — bids, budget, and placement on the terms where you're strong and want more — belong on the [Amazon Ads MCP](/features/amazon-ads-mcp/). The loop runs in one place: *run the SQP diagnostic → read the ranked gaps → send image gaps to Iris, copy and SEO gaps to the Optimizer, bid gaps to Ads → re-pull next window and watch the share gaps close.* From "where am I losing" to "fixed," without leaving your AI client. ## Limits and honest framing - **It reads; it doesn't change anything.** It diagnoses and ranks; making the changes is a separate, opt-in step. - **Thin data gets soft calls.** Low-traffic terms are flagged, not force-ranked. Give it a window with real volume for confident recommendations. - **Short window, search-page scope.** SQP captures recent search-results behavior, not lifetime performance or every route to purchase. - **Organic only.** It won't blend ad spend into these numbers; ad tactics route to your ads workflow with their own tags. - **It only sees the account you connect.** Every pull is scoped to the selected seller identity and marketplace. ## Related - [Amazon Selling Partner MCP](/features/amazon-selling-partner-mcp/) — the product surface this runs on. - [Amazon Listing Optimizer](/docs/amazon-listing-optimizer/) — execute the SEO, copy, and PDP fixes the analyzer recommends. - [Amazon Agent Iris](/features/amazon-agent-iris/) — regenerate the hero image behind a low click-through rate. - [Amazon Agent Iris docs](/docs/amazon-agent-iris/) — the image generation, audit, and hosting workflow. - [Amazon Ads MCP](/features/amazon-ads-mcp/) — run the bid, budget, and placement plays on terms where you're strong. - [Amazon Agent Atlas](/features/agent-atlas/) — the indexed Amazon knowledge that grounds rule-dependent recommendations. - [MCP client configuration](/docs/mcp-client-configuration/) — connect Claude, ChatGPT, Cursor, and other clients. ### Cloud Deployment Destinations URL: https://www.kuudo.com/docs/cloud/ Use these guides to prepare one or more cloud accounts before creating a deployment environment in the app. Each guide focuses on the provider-side setup: credentials, project or subscription selection, required APIs or resource providers, and a verification step that separates cloud-account problems from product provisioning problems. ## Pick a destination | Provider | Best fit | What you prepare | | --- | --- | --- | | [AWS ECS Express](/docs/cloud/aws-ecs-express-activation-guide/) | AWS accounts that standardize on ECS, ECR, CloudFormation, and IAM roles. | IAM access key, region, CloudFormation permissions, ECR, ECS, logs, and ECS load balancer role permissions. | | [Google Cloud](/docs/cloud/gcp-activation-guide/) | Teams that want Cloud Run with Artifact Registry. | Project ID, Cloud Run region, enabled APIs, service account roles, and a service account JSON key. | | [Azure](/docs/cloud/azure-activation-guide/) | Azure subscriptions using service principals and resource provider registration. | Subscription ID, Tenant ID, Client ID, Client Secret, region, RBAC, and required resource providers. | | [Cloudflare](/docs/cloud/cloudflare-activation-guide/) | Cloudflare Workers, Containers, and R2-backed deployments. | Scoped API token, target account access, Workers permissions, Containers permission, and optional Account ID for troubleshooting. | ## What to have ready Before you open the environment wizard, decide: - Which cloud account, project, or subscription should own the deployment. - Which region or global runtime should host the deployment. - Whether your organization allows long-lived keys, service account keys, API tokens, or client secrets. - Who can create IAM/RBAC bindings and provider registrations. - Where generated credentials will be stored after you paste them into the wizard. ## Security baseline Use dedicated credentials for the deployment workflow. Avoid root keys, personal admin credentials, broad tenant-wide grants, and credentials shared between unrelated systems. Rotate credentials periodically, and rotate immediately when someone leaves the team or a token may have been exposed. Prefer cloud-native temporary credential or managed identity flows when the product supports them; use the documented key, token, or service principal flows when the wizard requires those values directly. ## Verify before provisioning Each provider guide includes a CLI or API verification step. Run it before opening the wizard: - AWS: `sts get-caller-identity`, CloudFormation, ECR, and ECS read checks. - Google Cloud: project metadata, Cloud Run, and Artifact Registry read checks. - Azure: service-principal login and subscription read check. - Cloudflare: accounts API and account read checks. If verification fails locally, fix the cloud setup first. The wizard cannot provision resources with credentials that fail the provider's own read checks. ## Continue Start with the provider you plan to deploy first: - [AWS ECS Express Activation Guide](/docs/cloud/aws-ecs-express-activation-guide/) - [Google Cloud Activation Guide](/docs/cloud/gcp-activation-guide/) - [Azure Activation Guide](/docs/cloud/azure-activation-guide/) - [Cloudflare Activation Guide](/docs/cloud/cloudflare-activation-guide/) ### AWS ECS Express Activation Guide URL: https://www.kuudo.com/docs/cloud/aws-ecs-express-activation-guide/ This guide explains how to prepare AWS so the in-app **environment wizard** (Choose Deployment Pattern → Credentials → Configuration → Review & create) can create infrastructure for the **ECS Express** deployment pattern successfully. **Core idea:** prepare AWS first (usually with the AWS CLI), then paste the access key, secret, and region into the product. The app validates credentials via STS at create time, then provisioning creates a small CloudFormation stack (ECR repository + IAM roles) that ECS Express uses on subsequent deploys. > **About ECS Express** > ECS Express is the lightweight ECS deployment pattern (replacing App Runner). The platform creates only an ECR repository plus two IAM roles up front; ECS Express manages the load balancer, security group, target group, and auto-scaling for each service at deploy time. No VPC, ALB, or cluster pre-provisioning is required. --- ## Quick path 1. Sign in with an AWS user that has IAM admin rights and select the correct account ([§ Sign in and select the account](#sign-in-and-select-the-account)). 2. Pick a region where ECS Express is supported and you want to deploy ([§ Region](#choose-a-region)). 3. Create or reuse a dedicated IAM user for the access-key flow and attach a policy with the required permissions ([§ IAM user](#create-or-reuse-an-iam-user)). 4. Create an access key for that user and save the **Access Key ID** and **Secret Access Key** ([§ Access key](#create-an-access-key)). 5. Verify the credentials can call STS and CloudFormation in your region ([§ Verify the credentials](#verify-the-credentials)). 6. Open the wizard: **Choose Deployment Pattern** → **Credentials** → **Configuration** → **Review & create** ([§ Complete the wizard](#complete-the-wizard-in-the-app)). 7. If something fails, use [§ Common issues](#common-issues). --- ## Prerequisites - Access to the target AWS account. - Permission to create IAM users / policies (for example **AdministratorAccess**, or an equivalent IAM admin). - The **ECS Express** deployment pattern selected when creating the environment in the wizard (the `aws-ecs` pattern uses a different, larger CloudFormation stack). - [AWS CLI v2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed locally, or [CloudShell](https://docs.aws.amazon.com/cloudshell/latest/userguide/welcome.html) from the AWS console. The CLI is recommended because console labels and layout change more often than command-line flows. --- ## Field reference The wizard matches AWS outputs like this: | What you do in AWS | Output to copy | Wizard field | | ------------------------------------------------------ | ------------------------------ | ----------------------- | | IAM → Users → Security credentials → Create access key | Access key ID (e.g. `AKIA...`) | **Access Key ID** | | Same dialog | Secret access key (shown once) | **Secret Access Key** | | Your choice of deploy location | Region code, e.g. `us-east-1` | **Region** | | (your label only) | Any name | **Environment Name** | | Account console / `aws sts get-caller-identity` | 12-digit Account ID | _(auto-derived by app)_ | **Account ID vs other IDs:** AWS account ID is still important for verification and troubleshooting, but the current wizard derives it from credentials (STS) instead of asking you to type it. **Region:** lowercase region code with hyphens (e.g. `us-east-1`, `eu-west-1`). List regions enabled on your account: ```bash aws ec2 describe-regions --query "Regions[].RegionName" --output text ``` ECS Express must be available in the region you choose. If you are unsure, start with a major region like `us-east-1` or `us-west-2`. **Access keys are long-lived:** rotate them periodically. AWS recommends temporary credentials where possible; use this IAM-user key flow only when the deployment wizard requires an access key and secret. Create a new key, update the wizard, then deactivate and delete the old one. --- ## Prepare AWS with the CLI Run the steps below as an **AWS admin user** unless noted. Replace placeholders such as ``, ``, and `` with your values. ### Sign in and select the account ```bash aws configure # set admin profile aws sts get-caller-identity --output json ``` From the JSON output, record: - `Account` → **AWS Account ID** ### Choose a region ```bash export AWS_REGION= # e.g. us-east-1 aws ec2 describe-regions \ --query "Regions[?RegionName=='$AWS_REGION'].RegionName" --output text ``` A non-empty result confirms the region is enabled on your account. ### Create or reuse an IAM user Reuse an existing IAM user if it is dedicated to this deployment workflow and already has the right permissions. Avoid root access keys. If your organization prefers IAM roles or AWS IAM Identity Center, use that path only if the deployment wizard supports temporary credentials or role assumption. **Option A — new IAM user:** ```bash aws iam create-user --user-name ``` **Option B — console:** **IAM** → **Users** → **Create user**. ### Attach a permissions policy ECS Express provisioning needs to create a CloudFormation stack containing an ECR repository and two IAM roles, and to manage ECS services, log groups, and (via the ECS Express infrastructure role) elastic load balancing. The simplest path is **PowerUserAccess + IAMFullAccess** (CloudFormation needs IAM to create the execution and infrastructure roles). ```bash aws iam attach-user-policy \ --user-name \ --policy-arn arn:aws:iam::aws:policy/PowerUserAccess aws iam attach-user-policy \ --user-name \ --policy-arn arn:aws:iam::aws:policy/IAMFullAccess ``` For tighter least privilege, use a custom policy with at least these actions, scoped to your account/region as appropriate: | Service / namespace | Why | | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `cloudformation:*` | Create / update / delete the ECS Express infrastructure stack | | `ecr:*` | Manage the ECR repository created by the stack and push images during deploys | | `ecs:*` | Create and update ECS Express services | | `iam:CreateRole`, `iam:GetRole`, `iam:PassRole`, `iam:AttachRolePolicy`, `iam:PutRolePolicy`, `iam:DeleteRole`, `iam:DetachRolePolicy`, `iam:DeleteRolePolicy` | Stack creates the execution and infrastructure roles using `CAPABILITY_NAMED_IAM`; deploys `PassRole` them to ECS | | `logs:*` | Create CloudWatch log groups for ECS tasks | | `elasticloadbalancing:*` | Allow the ECS Express infrastructure role to attach services to managed ALBs | | `sts:GetCallerIdentity` | Discover the AWS account ID at deploy time | > **Why CAPABILITY_NAMED_IAM?** > The stack creates **named** IAM roles (e.g. `mcp--execution-role`, `mcp--infra-role`) so ECS Express can find them by name. CloudFormation requires explicit acknowledgement when a template names IAM resources, which is why the user must have `iam:*` on those role names. Verify: ```bash aws iam list-attached-user-policies --user-name --output table ``` ### Create an access key ```bash aws iam create-access-key --user-name ``` Record `AccessKey.AccessKeyId` (**Access Key ID**) and `AccessKey.SecretAccessKey` (**Secret Access Key**) immediately. The secret is only shown once. Treat it like a password — anyone with these two values can act as the user. > **Rotation** > to rotate, create a second access key, update the wizard, then `aws iam update-access-key --status Inactive` and `aws iam delete-access-key` for the old one. ### Verify the credentials This separates AWS misconfiguration from product issues. Configure a temporary profile that uses the new key, then run a few read calls. ```bash aws configure --profile mcp-deployer # AWS Access Key ID: # AWS Secret Access Key: # Default region name: # Default output format: json # 1. STS works (matches the wizard's identity check) aws sts get-caller-identity --profile mcp-deployer # 2. CloudFormation is reachable in the chosen region aws cloudformation list-stacks --profile mcp-deployer --region \ --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE # 3. ECR is reachable aws ecr describe-repositories --profile mcp-deployer --region \ --max-items 1 || true # 4. ECS is reachable aws ecs list-clusters --profile mcp-deployer --region --max-items 1 ``` If `sts get-caller-identity` returns the expected account ID and the other calls return JSON (even an empty list), authentication and the most-used APIs are working. --- ## Complete the wizard in the app Use the [Field reference](#field-reference) for definitions. ### Choose Deployment Pattern - **Deployment Pattern** — choose **ECS Express** (not the full ECS pattern). ### Credentials - **Environment Name** — label in your app (for example `aws-ecs-express`). - **Access Key ID**, **Secret Access Key**, **Region** — from [§ Access key](#create-an-access-key). ### Configuration - Optional settings page (for ECS Express, no additional required fields). ### Review & create After submit, the platform creates a CloudFormation stack (name pattern `mcp--`) containing: - An **ECR repository** for your container images - An **ECS task execution role** (ECR pull + CloudWatch logs) - An **ECS Express infrastructure role** (manages ALB, security groups, auto-scaling) You can watch progress in the AWS console under **CloudFormation → Stacks**. --- ## Common issues | Symptom or error | Likely cause | What to do | | -------------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | | `InvalidClientTokenId` / `The security token included in the request is invalid` | Wrong access key ID, deactivated key, or typo in secret | Re-create the access key, update the wizard. | | `SignatureDoesNotMatch` | Secret access key copied with extra whitespace or partial value | Re-paste the exact secret from `aws iam create-access-key` output. | | Account ID mismatch / "Account ID does not match credentials" | Pasted the wrong account, or used an alias | Run `aws sts get-caller-identity` and copy the **12-digit `Account` field**. | | Environment creation fails with `AccessDenied` during credential checks | IAM user lacks `sts:GetCallerIdentity` | Attach `PowerUserAccess` (or at least include STS read + required deploy actions in [§ Permissions](#attach-a-permissions-policy)). | | `User: ... is not authorized to perform: iam:CreateRole` | IAM user is missing IAM permissions | Attach `IAMFullAccess` or grant the IAM actions listed in [§ Permissions](#attach-a-permissions-policy). | | `User ... is not authorized to perform: iam:PassRole` during deploy | Deploy step cannot pass the execution role to ECS | Add `iam:PassRole` for the role ARNs created by the stack (or use `*` while developing). | | `Stack mcp- already exists` on retry | Previous provisioning partially completed | The platform will reuse the existing stack on retry. If it is in `ROLLBACK_COMPLETE`, delete it manually with `aws cloudformation delete-stack` and retry. | | Region rejected or `OptInRequired` | Region not enabled on the account, or ECS Express unavailable there | Enable the region in **Account → AWS Regions**, or pick a major region such as `us-east-1` / `us-west-2`. | | Environment created, but deploy fails creating an ALB | ECS Express infrastructure role is missing `elasticloadbalancing:*` | Re-attach `PowerUserAccess`, or add `elasticloadbalancing:*` to your custom policy. | | Pasted **root account access keys** | Root access keys are blocked by AWS best practice | Create a dedicated IAM user, attach the policies, and use that user's access keys. | ## Official references - [AWS IAM: Secure access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/securing_access-keys.html) - [AWS CloudFormation: Control access with IAM](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/control-access-with-iam.html) - [Amazon ECS infrastructure IAM role for load balancers](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AmazonECSInfrastructureRolePolicyForLoadBalancers.html) - [Install or update the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) --- ## Appendix: Copy-paste activation script The block below repeats [Prepare AWS with the CLI](#prepare-aws-with-the-cli) in one place for convenience. Replace placeholders before running. ```bash # --- As your AWS admin --- ACCOUNT_ID= REGION= # e.g. us-east-1 USER_NAME= # e.g. mcp-deployer aws configure # admin profile aws sts get-caller-identity --output json # Create dedicated IAM user aws iam create-user --user-name "$USER_NAME" # Attach permissions (broad — see guide for least-privilege alternative) aws iam attach-user-policy \ --user-name "$USER_NAME" \ --policy-arn arn:aws:iam::aws:policy/PowerUserAccess aws iam attach-user-policy \ --user-name "$USER_NAME" \ --policy-arn arn:aws:iam::aws:policy/IAMFullAccess aws iam list-attached-user-policies --user-name "$USER_NAME" --output table # Create access key (capture both fields immediately) aws iam create-access-key --user-name "$USER_NAME" # --- Test as the new user --- aws configure --profile mcp-deployer # paste the new key, secret, region aws sts get-caller-identity --profile mcp-deployer aws cloudformation list-stacks --profile mcp-deployer --region "$REGION" \ --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE aws ecr describe-repositories --profile mcp-deployer --region "$REGION" \ --max-items 1 || true aws ecs list-clusters --profile mcp-deployer --region "$REGION" --max-items 1 ``` After this succeeds, fill the wizard using the [Field reference](#field-reference) and complete [§ Complete the wizard](#complete-the-wizard-in-the-app). ### Google Cloud Activation Guide URL: https://www.kuudo.com/docs/cloud/gcp-activation-guide/ This guide explains how to prepare Google Cloud so the in-app **environment wizard** (Configuration → Credentials → Validation → Review & create) can create infrastructure successfully. **Core idea:** prepare GCP first (usually with the `gcloud` CLI), then copy the project ID, region, and service account key into the product. Validation checks credential and project access, then provisioning verifies Cloud Run access and prepares Artifact Registry. --- ## Quick path 1. Sign in with your Google account and select the correct project ([§ Sign in and select the project](#sign-in-and-select-the-project)). 2. Enable the required APIs: **Cloud Run**, **Artifact Registry**, **Cloud Resource Manager** ([§ Enable APIs](#enable-required-apis)). 3. Create or reuse a service account and grant it the right roles ([§ Service account](#create-or-reuse-a-service-account)). 4. Create a JSON key for the service account and save the file ([§ Service account key](#create-a-service-account-key)). 5. Verify the key works against your project ([§ Verify the service account](#verify-the-service-account)). 6. Open the wizard: **Configuration** → **Credentials** → run **Validation** → **Review & create** ([§ Complete the wizard](#complete-the-wizard-in-the-app)). 7. If something fails, use [§ Common issues](#common-issues). --- ## Prerequisites - Access to the target Google Cloud project. - Permission to grant IAM roles (for example **Owner** or **IAM Admin**) when binding roles to the service account. - Billing **enabled** on the project (Cloud Run and Artifact Registry require it, even at $0 usage). - [`gcloud` CLI](https://cloud.google.com/sdk/docs/install) installed locally, or [Cloud Shell](https://cloud.google.com/shell) from the GCP console. The CLI is recommended because console labels and layout change more often than command-line flows. --- ## Field reference The wizard matches GCP outputs like this: | What you do in GCP | Output to copy | Wizard field | | -------------------------------------------------- | ---------------------------------------- | ------------------------------ | | Project picker / `gcloud config get-value project` | Project ID (not the project number/name) | **Project ID** | | Your choice of deploy location | Cloud Run region, e.g. `us-central1` | **Region** (Credentials step) | | IAM & Admin → Service Accounts → Keys → Add Key | Downloaded **JSON file contents** | **Service Account Key (JSON)** | | (your label only) | Any name | **Environment Name** | **Project ID vs project number vs project name:** the wizard wants the **Project ID** (e.g. `my-mcp-project-481923`), which is a lowercase string with optional digits/dashes — not the numeric project number and not the human-readable display name. **Region:** use a valid Cloud Run region identifier (lowercase with hyphens). List supported regions: ```bash gcloud run regions list ``` **Service Account Key (JSON):** paste the **entire contents** of the downloaded JSON file, including the `{ ... }` braces. The wizard validates that it has `type: "service_account"`, a PEM-formatted `private_key`, and a `client_email` ending in `.iam.gserviceaccount.com`. Google recommends avoiding user-managed service account keys when a safer alternative is available; use this key flow only when the deployment wizard requires a JSON key. --- ## Prepare GCP with the CLI Run the steps below as **your GCP user/admin** unless noted. Replace placeholders such as `` and `` with your values. ### Sign in and select the project ```bash gcloud auth login gcloud projects list gcloud config set project gcloud config get-value project ``` Record the value of `gcloud config get-value project` — this becomes the wizard's **Project ID**. ### Enable required APIs These APIs are required for the MCP deployment path described here: - `run.googleapis.com` — Cloud Run (runtime) - `artifactregistry.googleapis.com` — Artifact Registry (private image storage) - `cloudresourcemanager.googleapis.com` — Resource Manager (used by validation to fetch project metadata) ```bash gcloud services enable \ run.googleapis.com \ artifactregistry.googleapis.com \ cloudresourcemanager.googleapis.com \ --project ``` Verify (each should appear in the list): ```bash gcloud services list --enabled --project \ --filter="config.name:(run.googleapis.com OR artifactregistry.googleapis.com OR cloudresourcemanager.googleapis.com)" \ --format="value(config.name)" ``` API enablement can take a minute or two to propagate. If validation fails immediately after enabling, wait and retry. ### Create or reuse a service account Reuse an existing service account if you already have one with the right roles. **Option A — new service account (quick setup):** ```bash gcloud iam service-accounts create \ --display-name "MCP Deployment Service Account" \ --project ``` The full email becomes `@.iam.gserviceaccount.com`. **Option B — console:** **IAM & Admin** → **Service Accounts** → **Create service account**. ### Grant IAM roles Validation needs the service account to read project metadata; provisioning needs permission to manage Cloud Run services and Artifact Registry repositories. Grant these roles at **project scope**: | Role | Why | | ------------------------------------------ | --------------------------------------------------------------- | | `roles/run.developer` | Create, update, and delete Cloud Run services | | `roles/artifactregistry.admin` | First-run repository creation + image push in Artifact Registry | | `roles/iam.serviceAccountUser` | Allow Cloud Run to act as the runtime service account | | `roles/serviceusage.serviceUsageConsumer` | Commonly required in org policies for service usage checks | | `roles/viewer` (or equivalent read access) | Read project metadata (`cloudresourcemanager.projects.get`) | ```bash SA_EMAIL=@.iam.gserviceaccount.com for ROLE in \ roles/run.developer \ roles/artifactregistry.admin \ roles/iam.serviceAccountUser \ roles/serviceusage.serviceUsageConsumer \ roles/viewer do gcloud projects add-iam-policy-binding \ --member "serviceAccount:$SA_EMAIL" \ --role "$ROLE" done ``` Verify: ```bash gcloud projects get-iam-policy \ --flatten="bindings[].members" \ --filter="bindings.members:$SA_EMAIL" \ --format="value(bindings.role)" ``` You should see these roles listed for that service account. > **Least privilege note** > For tighter scoping, replace `roles/artifactregistry.admin` with `roles/artifactregistry.writer` **after** the repository has been created the first time. Initial provisioning may need repository-create permissions. > **Public endpoint note** > If deployments succeed but your endpoint still requires authentication, add `roles/run.admin` so the deploy flow can set the Cloud Run service IAM policy and grant `roles/run.invoker` to `allUsers`. ### Create a service account key ```bash gcloud iam service-accounts keys create ./mcp-sa-key.json \ --iam-account "$SA_EMAIL" \ --project ``` This writes a JSON file. Open it and copy the **entire contents** when filling out the wizard. Treat this file as secret — anyone with it can act as the service account. > **Key rotation** > GCP service account keys do not expire by default but should be rotated periodically. List existing keys with `gcloud iam service-accounts keys list --iam-account "$SA_EMAIL"`, create a new one, update the wizard, then delete the old key with `gcloud iam service-accounts keys delete --iam-account "$SA_EMAIL"`. ### Verify the service account This separates GCP misconfiguration from product issues. Activate the key and run a few read-only calls. ```bash gcloud auth activate-service-account --key-file ./mcp-sa-key.json gcloud config set project # 1. Project is readable (matches the wizard's validation call) gcloud projects describe # 2. Cloud Run API is enabled and listable gcloud run services list --region # 3. Artifact Registry is accessible gcloud artifacts repositories list --location ``` If all three calls succeed, authentication, project access, and the required APIs are working. Switch back to your user account when done: ```bash gcloud auth login gcloud config set account ``` --- ## Complete the wizard in the app Use the [Field reference](#field-reference) for definitions. ### Configuration - **Environment Name** — label in your app (for example `gcp`). - **Project ID** — GCP project ID from [§ Sign in](#sign-in-and-select-the-project). - **Region** — Cloud Run region, e.g. `us-central1`. ### Credentials - **Service Account Key (JSON)** — paste the full contents of the JSON file from [§ Service account key](#create-a-service-account-key). ### Validation Run validation in the UI. It should succeed when the key parses cleanly and the service account can read the project via Resource Manager. Cloud Run API access checks and Artifact Registry repository preparation happen during provisioning right after **Review & create**. ### Review & create After validation succeeds, finish creating the environment. --- ## Common issues | Symptom or error | Likely cause | What to do | | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | `Missing required field: ` from validation | Pasted only part of the JSON, or pasted a non-key file | Re-paste the **entire** JSON file contents, including `{` and `}`. | | `Invalid private key format. Expected PEM format.` | Newlines mangled when copy/pasting (e.g. `\n` literals replaced) | Re-download or re-open the original JSON file and paste it exactly — do not edit. | | `Invalid service account email format` | Pasted a user OAuth credential, not a service account key | Create a key under **IAM & Admin → Service Accounts**, not **APIs & Services → Credentials**. | | Validation succeeds, but provisioning fails with `Cloud Run API is not enabled` | `run.googleapis.com` is disabled on the project | `gcloud services enable run.googleapis.com` ([§ Enable APIs](#enable-required-apis)). Wait 1–2 minutes and retry. | | Validation succeeds, but provisioning fails with `PERMISSION_DENIED` listing Cloud Run services | Service account is missing **Cloud Run Developer** | Add `roles/run.developer` ([§ IAM roles](#grant-iam-roles)). | | Validation succeeds, but provisioning fails creating Artifact Registry repository | Service account is missing repository-create permission | Add `roles/artifactregistry.admin` for first run, then consider downgrading to `roles/artifactregistry.writer`. | | Deployment works but service URL requires auth | Service account cannot set IAM policy for unauthenticated invoker | Add `roles/run.admin` and redeploy so `roles/run.invoker` can be granted to `allUsers`. | | `Permission 'resourcemanager.projects.get' denied` | Project ID is wrong, or the SA can't read the project | Confirm `gcloud config get-value project` matches **Project ID**; ensure the SA has any role on the project (e.g. `roles/viewer`). | | `Billing has not been enabled` | Project has no billing account attached | Link a billing account in the GCP console (**Billing → Link a billing account**), then retry. | | Pasted the **project number** (e.g. `483921047215`) instead of the Project ID | Wrong identifier | Use the lowercase string from `gcloud config get-value project`, not the numeric project number. | | Region rejected or `Cloud Run not available in ` | Region typo or unsupported region | Pick a value from `gcloud run regions list`. | | Key recently created but validation still fails with auth errors | IAM/key propagation delay | Wait ~60 seconds and retry; GCP IAM is eventually consistent. | ## Official references - [Cloud Run IAM roles](https://cloud.google.com/run/docs/reference/iam/roles) - [Cloud Run regions](https://cloud.google.com/run/docs/locations) - [Artifact Registry roles and permissions](https://cloud.google.com/artifact-registry/docs/access-control) - [Best practices for managing service account keys](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys) - [Install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install) --- ## Appendix: Copy-paste activation script The block below repeats [Prepare GCP with the CLI](#prepare-gcp-with-the-cli) in one place for convenience. Replace placeholders before running. ```bash # --- As your GCP user --- PROJECT_ID= REGION= # e.g. us-central1 SA_NAME= # e.g. mcp-deployer SA_EMAIL="$SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" gcloud auth login gcloud config set project "$PROJECT_ID" # Enable APIs gcloud services enable \ run.googleapis.com \ artifactregistry.googleapis.com \ cloudresourcemanager.googleapis.com \ --project "$PROJECT_ID" # Create service account gcloud iam service-accounts create "$SA_NAME" \ --display-name "MCP Deployment Service Account" \ --project "$PROJECT_ID" # Grant roles for ROLE in \ roles/run.developer \ roles/artifactregistry.admin \ roles/iam.serviceAccountUser \ roles/serviceusage.serviceUsageConsumer \ roles/viewer do gcloud projects add-iam-policy-binding "$PROJECT_ID" \ --member "serviceAccount:$SA_EMAIL" \ --role "$ROLE" done # Create JSON key (treat as secret) gcloud iam service-accounts keys create ./mcp-sa-key.json \ --iam-account "$SA_EMAIL" \ --project "$PROJECT_ID" # --- Test as the service account --- gcloud auth activate-service-account --key-file ./mcp-sa-key.json gcloud config set project "$PROJECT_ID" gcloud projects describe "$PROJECT_ID" gcloud run services list --region "$REGION" gcloud artifacts repositories list --location "$REGION" ``` After this succeeds, fill the wizard using the [Field reference](#field-reference) and complete [§ Complete the wizard](#complete-the-wizard-in-the-app). ### Azure Activation Guide URL: https://www.kuudo.com/docs/cloud/azure-activation-guide/ This guide explains how to prepare Azure so the in-app **environment wizard** (Configuration → Credentials → Validation → Review & create) can create infrastructure successfully. **Core idea:** every value you paste into the wizard is an **output of Azure setup**. Prepare Azure first (usually with the Azure CLI), then copy IDs and secrets into the product. --- ## Quick path 1. Sign in with your user account and select the correct subscription ([§ Sign in and select the subscription](#sign-in-and-select-the-subscription)). 2. Create or reuse an app registration / service principal and save **Client ID**, **Client Secret** (value), and **Tenant ID** ([§ Service principal](#create-or-reuse-a-service-principal)). 3. Grant the service principal **Contributor** on that subscription ([§ RBAC](#grant-rbac-on-the-subscription)). 4. Register **Microsoft.ContainerRegistry** and **Microsoft.App** ([§ Resource providers](#register-resource-providers)). 5. Log in as the service principal and confirm subscription access ([§ Verify the service principal](#verify-the-service-principal)). 6. Open the wizard: **Configuration** → **Credentials** → run **Validation** → **Review & create** ([§ Complete the wizard](#complete-the-wizard-in-the-app)). 7. If something fails, use [§ Common issues](#common-issues). --- ## Prerequisites - Access to the target Azure subscription. - Permission to assign RBAC (for example **Owner** or **User Access Administrator**) when granting the service principal access. - [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) installed locally, or [Azure Cloud Shell](https://learn.microsoft.com/en-us/azure/cloud-shell/overview). The CLI is recommended because portal labels and layout change more often than command-line flows. --- ## Field reference The wizard matches Azure outputs like this: | What you do in Azure | Output to copy | Wizard field | | ------------------------------------ | ------------------------------------ | ---------------------------------------- | | Microsoft Entra ID (tenant) | Tenant ID | **Tenant ID (Directory ID)** | | Subscription | Subscription ID | **Subscription ID** (Configuration step) | | App registration / service principal | Application (client) ID | **Client ID (Application ID)** | | Certificates & secrets | **Secret value** (not the secret ID) | **Client Secret** | | Your choice of deploy location | Region name, e.g. `eastus` | **Region** | | (your label only) | Any name | **Environment Name** | **Subscription ID vs Tenant ID:** both look like GUIDs (for example `eb620971-347b-41f1-bb45-e9c8b4f5020d`), but they are different. **Subscription ID** belongs in **Configuration**; **Tenant ID** belongs in **Credentials**. **Region:** use a valid Azure region identifier (lowercase, no spaces). List names for your subscription: ```bash az account list-locations --query "[].name" -o tsv ``` --- ## Prepare Azure with the CLI Run the steps below as **your Azure user/admin** unless noted. Replace placeholders such as `` and `` with your values. ### Sign in and select the subscription ```bash az login az account list --output table az account set --subscription az account show --output json ``` From the JSON output, record: - `subscription.id` → **Subscription ID** - `tenantId` → **Tenant ID** ### Create or reuse a service principal Reuse an existing app registration if you already have one. **Option A — new service principal (quick setup):** ```bash az ad sp create-for-rbac --name ``` Note `appId` (**Client ID**), `password` (**Client Secret**), and `tenant` (**Tenant ID**). Save the secret immediately; it is not shown again. **Option B — portal:** create an app registration in Microsoft Entra ID, add a **client secret** under **Certificates & secrets**, and copy the **Value** column (not the Secret ID). **Option C — new secret for an existing app:** ```bash az ad app credential reset --id --append ``` Use `--append` when rotating so Azure adds a second credential instead of replacing existing credentials. In production, prefer adding a second secret, updating the wizard, verifying deployment, then removing the old secret. Entra secrets **expire**. When one expires, create a new secret and update **Client Secret** in the wizard. ### Grant RBAC on the subscription Validation needs the service principal to read the subscription; provisioning needs permissions to create resources (resource groups, Container Apps, registries, etc.). **Contributor** at subscription scope is the usual choice. ```bash az login az account set --subscription az role assignment create \ --assignee \ --role Contributor \ --scope /subscriptions/ ``` Verify: ```bash az role assignment list \ --assignee \ --scope /subscriptions/ \ --output table ``` You should see **Contributor** for that service principal on the subscription. For tighter least privilege, some organizations scope **Contributor** to a **resource group** instead of the whole subscription. That only works if all resources the worker creates stay inside that group. ### Register resource providers Provisioning fails if the subscription is not registered for the services in use. These namespaces are required for the MCP deployment path described here: - `Microsoft.ContainerRegistry` - `Microsoft.App` ```bash az account set --subscription az provider register --namespace Microsoft.ContainerRegistry --wait az provider register --namespace Microsoft.App --wait ``` Verify (each should print `Registered`): ```bash az provider show --namespace Microsoft.ContainerRegistry --query registrationState -o tsv az provider show --namespace Microsoft.App --query registrationState -o tsv ``` Depending on what the worker provisions, you may also need: ```bash az provider register --namespace Microsoft.OperationalInsights --wait az provider register --namespace Microsoft.ManagedIdentity --wait az provider register --namespace Microsoft.Network --wait ``` ### Verify the service principal This separates Azure misconfiguration from product issues. ```bash az logout az login --service-principal \ --username \ --password \ --tenant az account set --subscription az rest \ --method get \ --url "https://management.azure.com/subscriptions/?api-version=2020-01-01" ``` If this returns subscription JSON, authentication and subscription access are working. --- ## Complete the wizard in the app Use the [Field reference](#field-reference) for definitions. ### Configuration - **Environment Name** — label in your app (for example `azure`). - **Subscription ID** — Azure subscription GUID from [§ Sign in](#sign-in-and-select-the-subscription). ### Credentials - **Tenant ID**, **Client ID**, **Client Secret** (secret **value**), **Region** — from Entra and the service principal ([§ Service principal](#create-or-reuse-a-service-principal), [§ Sign in](#sign-in-and-select-the-subscription)). ### Validation Run validation in the UI. It should succeed when the secret is valid, tenant and subscription IDs are correct, and the service principal can read the subscription. ### Review & create After validation succeeds, finish creating the environment. --- ## Common issues | Symptom or error | Likely cause | What to do | | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | Tenant ID pasted into **Subscription ID** (or the reverse) | Same GUID shape, wrong meaning | Put subscription GUID in **Configuration**; tenant GUID in **Credentials** ([Field reference](#field-reference)). | | `AADSTS7000215 Invalid client secret provided` | Wrong secret, expired secret, or **Secret ID** used instead of **Value** | Create a new client secret; copy the **value**; update the wizard. | | Validation OK but provisioning fails with insufficient access | **Reader** only, or wrong scope | Use **Contributor** (or equivalent) on the subscription or target resource group ([§ RBAC](#grant-rbac-on-the-subscription)). | | `Microsoft.Resources/subscriptions/read` (or similar read denial) | SP cannot read the subscription | Assign **Reader** or **Contributor** on the correct subscription; confirm **Subscription ID**. | | `roleAssignments/write` when running CLI | Current user cannot assign RBAC | Sign in as an admin or a user with **Owner** / **User Access Administrator** on the subscription. | | Role assignment commands do nothing useful while logged in as the SP | SP cannot grant itself roles | Use your **user** account for `az role assignment create`. | | `The subscription is not registered to use namespace 'Microsoft.App'` or `'Microsoft.ContainerRegistry'` | Resource provider not registered | `az provider register --namespace --wait` ([§ Resource providers](#register-resource-providers)). | | `The subscription is not registered to use namespace 'Microsoft.X'` | Other provider missing | `az provider register --namespace Microsoft.X --wait` | ## Official references - [Create an Azure service principal with Azure CLI](https://learn.microsoft.com/en-us/cli/azure/azure-cli-sp-tutorial-1) - [Azure resource providers and types](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-providers-and-types) - [Azure built-in roles](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles) - [Install the Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) --- ## Appendix: Copy-paste activation script The block below repeats [Prepare Azure with the CLI](#prepare-azure-with-the-cli) in one place for convenience. Replace placeholders before running. ```bash # --- As your Azure user --- az login az account list --output table az account set --subscription # RBAC (Contributor on subscription) az role assignment create \ --assignee \ --role Contributor \ --scope /subscriptions/ az role assignment list \ --assignee \ --scope /subscriptions/ \ --output table # Required providers az provider register --namespace Microsoft.ContainerRegistry --wait az provider register --namespace Microsoft.App --wait az provider show --namespace Microsoft.ContainerRegistry --query registrationState -o tsv az provider show --namespace Microsoft.App --query registrationState -o tsv # --- Test as the service principal --- az logout az login --service-principal \ --username \ --password \ --tenant az account set --subscription az rest \ --method get \ --url "https://management.azure.com/subscriptions/?api-version=2020-01-01" ``` After this succeeds, fill the wizard using the [Field reference](#field-reference) and complete [§ Complete the wizard](#complete-the-wizard-in-the-app). ### Cloudflare Activation Guide URL: https://www.kuudo.com/docs/cloud/cloudflare-activation-guide/ This guide explains how to prepare Cloudflare so the in-app **environment wizard** (Configuration → Credentials with inline Validate → Review & create) can create infrastructure successfully. **Core idea:** prepare Cloudflare first (in the dashboard, optionally verified with `curl` or `wrangler`), then paste your API token into the product. The app derives account details from that token during validation. --- ## Quick path 1. Sign in to the [Cloudflare dashboard](https://dash.cloudflare.com) and select the correct account ([§ Sign in and select the account](#sign-in-and-select-the-account)). 2. (Optional) Copy the **Account ID** from account home for troubleshooting ([§ Account ID](#copy-the-account-id-optional)). 3. Create an **API token** with the required Cloudflare permissions and save the **token value** ([§ API token](#create-an-api-token)). 4. Confirm the token can read your account ([§ Verify the token](#verify-the-api-token)). 5. Open the wizard: **Configuration** → **Credentials** (click **Validate**) → **Review & create** ([§ Complete the wizard](#complete-the-wizard-in-the-app)). 6. If something fails, use [§ Common issues](#common-issues). --- ## Prerequisites - Access to the target Cloudflare account. - Permission to create API tokens for that account (Super Administrator, or a role that includes "Account API Tokens: Edit"). - Optional: [`wrangler`](https://developers.cloudflare.com/workers/wrangler/install-and-update/) installed locally, or any tool that can call `https://api.cloudflare.com`. The dashboard is recommended for token creation because tokens can only be created from the UI. CLI tools are useful for verification. --- ## Field reference The wizard matches Cloudflare outputs like this: | What you do in Cloudflare | Output to copy | Wizard field | | -------------------------------------- | ------------------------------- | -------------------------------- | | My Profile → API Tokens → Create Token | **Token value** (shown once) | **Cloudflare API Token** | | (your label only) | Any name | **Environment Name** | | Account home → right sidebar | Account ID (optional reference) | _(auto-derived during validate)_ | **Account ID vs Token:** the Account ID is a 32-character hex string (for example `a1b2c3d4e5f67890abcdef1234567890`). The API token is a longer secret string starting with letters and numbers — **not** a Global API Key. Never use the Global API Key with this wizard; it grants too much access and is not scoped to the resources we provision. **Workers subdomain** (`.workers.dev`) is discovered automatically during provisioning/deployment flows; you do not need to enter it. **Region:** Cloudflare runs on a global anycast network, so there is no region field. Containers and Workers are placed automatically near end-users. --- ## Prepare Cloudflare with the dashboard Run the steps below as **a Cloudflare account admin**. ### Sign in and select the account 1. Open . 2. If you belong to multiple accounts, pick the correct one from the account switcher (top-left). ### Copy the Account ID (optional) 1. Click the account name to land on the **account home** page. 2. In the right sidebar, find **Account ID** and click the copy icon. 3. Save it as a troubleshooting reference (the wizard does not ask for Account ID directly). You can also retrieve it with `wrangler`: ```bash wrangler whoami ``` The output lists the accounts your current `wrangler` login can see along with their IDs. ### Create an API token 1. Top-right avatar → **My Profile** → **API Tokens** → **Create Token**. 2. Choose **Create Custom Token** (the templates do not include Containers). 3. Name it something memorable, e.g. `mcp-deployment-token`. 4. Add the following **permissions** (all Account-scoped): | Resource | Permission | | ------------------------------- | ---------- | | Account → Account Settings | Read | | Account → Workers Scripts | Edit | | Account → Workers Scripts | Read | | Account → Workers R2 Storage | Edit | | Account → Containers | Edit | 5. Under **Account Resources**, select **Include → Specific account → \**. 6. Leave **Client IP Address Filtering** blank unless you have a fixed egress IP. 7. **TTL:** leave open-ended unless your security policy requires expiry. If you set a TTL, you will need to rotate the token in the wizard before it expires. 8. Click **Continue to summary** → **Create Token**. 9. Copy the **token value immediately**. Cloudflare only shows it once. > **Why these permissions?** > Account Settings: Read enables account discovery during token validation. Workers Scripts permissions support worker deployment and reads. Containers is required for container-based MCP runtime. Workers R2 Storage is needed when attaching R2-backed storage. If you need to rotate later: return to **My Profile → API Tokens**, click the existing token → **Roll**, then update **API Token** in the wizard. ### Verify the API token This separates Cloudflare misconfiguration from product issues. Replace `` and `` with your values. ```bash # 1. Token can read accounts (matches the wizard's validation call) curl -sS https://api.cloudflare.com/client/v4/accounts \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" # 2. Token can read the specific account you intend to deploy to curl -sS https://api.cloudflare.com/client/v4/accounts/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" # 3. Optional: token self-verification endpoint # Note: some account-scoped tokens may not return useful results here. curl -sS https://api.cloudflare.com/client/v4/user/tokens/verify \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" ``` `/accounts` and `/accounts/` should return JSON with `"success": true`. If `accounts` returns an empty `result` array, the token is missing **Account Settings: Read** or is scoped to the wrong account. You can also verify with `wrangler` by exporting the token: ```bash export CLOUDFLARE_API_TOKEN= export CLOUDFLARE_ACCOUNT_ID= wrangler whoami ``` --- ## Complete the wizard in the app Use the [Field reference](#field-reference) for definitions. ### Configuration - **Environment Name** — label in your app (for example `cloudflare`). ### Credentials - **API Token** — token **value** from [§ API token](#create-an-api-token). ### Validation Run validation in the UI (Credentials step → **Validate**). It should succeed when the token is valid and can list at least one account. The app derives account details from that result. ### Review & create After validation succeeds, finish creating the environment. --- ## Common issues | Symptom or error | Likely cause | What to do | | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Invalid API token` / HTTP 401 from `tokens/verify` | Wrong token, expired token, or Global API Key pasted instead | Create a new API token (not a Global API Key); copy the value; update the wizard ([§ API token](#create-an-api-token)). | | `No Cloudflare accounts found. Your API token may not have 'Account Settings: Read' permission.` | Token missing **Account Settings: Read** | Edit the token and add **Account → Account Settings: Read**, then re-run validation. | | Validation OK but provisioning fails with `Authentication error` on Workers/Containers/R2 | Token missing **Edit** on one of the required resources | Edit the token and add **Workers Scripts: Edit**, **Containers: Edit**, **Workers R2 Storage: Edit** ([§ API token](#create-an-api-token)). | | Validation says no account found | Token cannot list accounts or is scoped to a different account | Confirm **Account Settings: Read** and that token **Account Resources** include the target account. | | `workers.dev subdomain not configured` during deployment | The account has never enabled a Workers subdomain | Visit **Workers & Pages → Overview** in the dashboard once to provision the subdomain, then retry provisioning/deployment. | | Token expires unexpectedly | TTL was set when the token was created | **My Profile → API Tokens → Roll** the token, paste the new value into the wizard, and consider removing the TTL. | | `Containers` permission not visible when creating the token | Account is not enrolled in Cloudflare Containers | Enable Cloudflare Containers from **Workers & Pages → Containers**, then create the token. | | Pasted the **Global API Key** instead of an API token | Wrong credential type | Create an API token via **My Profile → API Tokens → Create Token**; never use the Global API Key here. | ## Official references - [Cloudflare API token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) - [Wrangler commands](https://developers.cloudflare.com/workers/wrangler/commands/) - [Wrangler configuration](https://developers.cloudflare.com/workers/wrangler/configuration/) - [Cloudflare API reference](https://developers.cloudflare.com/api/) --- ## Appendix: Copy-paste verification script The block below repeats [§ Verify the token](#verify-the-api-token) in one place for convenience. Replace placeholders before running. ```bash export CLOUDFLARE_API_TOKEN= export CLOUDFLARE_ACCOUNT_ID= # Token can list accounts (matches wizard validation) curl -sS https://api.cloudflare.com/client/v4/accounts \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" # Token can read the specific account curl -sS "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID" \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" # Optional: token self-verification endpoint curl -sS https://api.cloudflare.com/client/v4/user/tokens/verify \ -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ -H "Content-Type: application/json" # Optional: confirm with wrangler wrangler whoami ``` After this succeeds, fill the wizard using the [Field reference](#field-reference) and complete [§ Complete the wizard](#complete-the-wizard-in-the-app).