Skip to content
<- Guides
Amazon Ads · Reporting

Launch a DSP Campaign Without Half-Built Entities

How the create-DSP-campaign Skill runs the ordered write sequence on the Amazon Ads MCP, reads back every entity it creates, and activates parent-first, grounded by Amazon Agent Atlas.

Zero eligible DSP tactics is almost always a goal-KPI mismatch, not an outage. Create every entity paused, verify each write, then activate parent-first.

Kuudo
Reviewed by Kuudo Engineering
ChatGPT running the create-DSP-campaign Skill: it resolves the DSP advertiser ID, creates the campaign PAUSED, attaches ASIN conversion tracking, re-queries for eligible tactics, creates one ad group per inventory type and tactic with the enum translated from VIDEO_STV to STREAMING_TV, then activates campaign first and ad groups second.
Every write is read back before the next step depends on it, and nothing goes live until the parent is confirmed enabled.
TL;DR
  • Every write is read back before the next step depends on it. The Amazon Ads API v1 offers no read-after-write guarantee, so the Skill waits 100ms, queries the entity, and retries up to three times at one-second intervals.
  • Zero eligible tactics after conversion tracking lands is almost always a goal-KPI mismatch. PROSPECTING (Brand+) needs REACH, FREQUENCY_AVERAGE, COST_PER_VIDEO_COMPLETION, or VIDEO_COMPLETION_RATE. The Performance+ tactics need ROAS, except MAXIMIZE_PERFORMANCE, which needs COST_PER_DETAIL_PAGE_VIEW or DETAIL_PAGE_VIEW_RATE.
  • The same inventory type is spelled differently at each level. Campaign VIDEO_STV becomes ad group STREAMING_TV, and VIDEO_OLV becomes ONLINE_VIDEO. This is the most common source of INVALID_ARGUMENT on ad-group create.
  • Tactic ad groups accept a six-field allowlist. Passing bid, budgets, or pacing is rejected as extra fields, not as wrong values.
  • Nothing is created live. Every entity is created PAUSED and activated parent-first, campaign then ad groups, and delivery is confirmed in dsp_impressions, dsp_views, and dsp_clicks rather than in a create response.

Launching a DSP (demand-side platform) Performance+ or Brand+ campaign end to end is one ordered 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, grounded by Amazon Agent Atlas where a retrieved rule decides what a step is even allowed to send, and it confirms real delivery in Amazon Marketing Cloud'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.

Why not just ask ChatGPT or Claude to do it? A plain chat hits the same three walls on any Amazon job. It has no access to your data, so it cannot resolve your DSP advertiser ID or ask Amazon which tactics your campaign is actually eligible for. It has no way to take action, so it cannot create a campaign, create an ad group, or flip anything to ENABLED; the most it can do is hand you JSON to paste into an API client yourself. And it runs on generic knowledge, not Amazon's, so it does not know that the inventory type you set on the campaign is spelled differently one level down. What you get is disconnected, generic, manual work that ships silent mistakes. The Amazon Ads MCP supplies your account plus the tools to act on it, Skills supply the ordered workflow, and Atlas supplies the rule book.

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:

{
  "Amazon-Ads-AccountId": "<dspAdvertiserId>",
  "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.

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:

TacticCompatible KPIsGoal
CUSTOMER_ACQUISITION (P+)ROASConversions
REMARKETING (P+)ROASConversions
RETENTION (P+)ROASConversions
MAXIMIZE_PERFORMANCE (P+)COST_PER_DETAIL_PAGE_VIEW, DETAIL_PAGE_VIEW_RATEConversions
PROSPECTING (B+)REACH, FREQUENCY_AVERAGE, COST_PER_VIDEO_COMPLETION, VIDEO_COMPLETION_RATEAwareness

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 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:

IntentCampaign fieldAd group field
DisplayDISPLAYDISPLAY
Streaming TVVIDEO_STVSTREAMING_TV
Online videoVIDEO_OLVONLINE_VIDEO
Live eventsLIVE_EVENTSLIVE_EVENTS
AudioAUDIOAUDIO

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:

{
  "Amazon-Ads-AccountId": "<dspAdvertiserId>",
  "adGroups": [
    {
      "adProduct": "AMAZON_DSP",
      "campaignId": "<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.

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.

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.

What happens next

The launch is the easy half. What makes it durable is that the whole sequence is a Skill 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 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 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, which measures exactly that.

Private beta

Launch your own DSP campaigns without half-built entities

Bring us the DSP launch you want your agent to run. We'll map the create-DSP-campaign Skill, Amazon Ads MCP wiring, Atlas grounding, and private-beta setup with you.

Run this workflow in beta

What you need to run this

MCP
Amazon Ads MCP for the DSP campaign management surface, including the account query, campaign and ad-group creates, conversion tracking, and the activation updates
Skill
create-DSP-campaign Skill, the ordered create, verify, and activate loop for Performance+ and Brand+ tactic campaigns
Atlas collection
amazon_ads, the corpus behind the post-launch delivery check (playbooks: Amazon Marketing Cloud Data Sources; Amazon DSP Views Table; Amazon DSP Clicks Table; Amazon Attributed Events Overview)
Required subscriptions
An Amazon DSP advertiser account reachable from your Amazon Ads profile. The post-launch delivery check additionally needs that DSP account added to your Amazon Marketing Cloud instance.
Last verified
"2026-08-20T00:00:00.000Z"

What success and failure look like

scenariosuccessfailure
Ad-group create returns INVALID_ARGUMENTInventory enum translated to the ad-group spelling and only the six allowlisted fields sentCampaign-level VIDEO_STV sent to the ad group, or bid and pacing included
Eligible tactics come back emptyConversion tracking verified first, then retried, then the goal KPI checked against the wanted tacticThe workflow reads the empty list as an outage and creates nothing
Activation returns NOT_FOUND on a just-created ad groupThe verification read confirmed the ad group before activation was attemptedActivation fired immediately after create and raced the API's eventual consistency
Related reading

Keep exploring this topic

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

Next step

FAQ

Why does my DSP campaign return zero eligible automated targeting tactics?

Two causes, in order. Conversion tracking may not have propagated yet, since eligibility recomputes asynchronously after the products land, so retry three times at one-second intervals first. If it is still empty, the campaign's goal KPI is incompatible with the tactic you want. PROSPECTING needs REACH or another awareness KPI, and the Performance+ tactics need ROAS.

Why does my ad group create fail with INVALID_ARGUMENT?

Almost always extra fields rather than wrong values. Performance+ and Brand+ tactic ad groups accept only adProduct, campaignId, name, state, inventoryType, and targetingSettings.automatedTargetingTactic. Passing bid, budgets, pacing, startDateTime, or creativeRotationType is rejected because those are auto-managed for tactic ad groups.

What is the difference between VIDEO_STV and STREAMING_TV?

Nothing conceptually. They are the same inventory type spelled differently at two levels of the API. The campaign field primaryInventoryTypes takes VIDEO_STV, and the ad group field inventoryType takes STREAMING_TV. VIDEO_OLV becomes ONLINE_VIDEO the same way, while DISPLAY, AUDIO, and LIVE_EVENTS are unchanged.

Why does activation return NOT_FOUND on an entity I just created?

The Amazon Ads API v1 does not guarantee read-after-write consistency, so an update can arrive before the record is visible. The fix is a verification read after every write: wait 100ms, query the entity, and retry up to three times at one-second intervals before depending on it.

Where do I set the budget on a DSP Performance+ campaign?

On the flight, not the campaign. There is no campaign-level budget field. A single budget with a start and end date maps to one flight inside the campaign, and additional flights are a separate update operation afterwards.

How many ASINs can I attach to DSP conversion tracking?

Between 1 and 2,000 products per call, and a campaign can track up to 500,000 products in total. Each entry carries the product ID, a domain derived as AMAZON_ plus the country code, and a product association of FEATURED.

Why do I see no rows for my new campaign in the DSP tables?

The DSP traffic tables cover only the DSP advertising accounts that have been added to your Amazon Marketing Cloud instance. If the advertiser was never added, the campaign delivers normally but returns no rows. Confirm the account is on the instance before treating an empty result as a delivery problem.

Sources