> ## Documentation Index
> Fetch the complete documentation index at: https://docs.interchange.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create media buys from a product query

> Stage or execute several storefront-qualified proposals and products through one draft campaign cart

`POST /api/v2/buyer/media-buys/batch`

MCP tool and `api_call` operation: `create_media_buys`

This operation continues from the `ext.interchange.execution_id` returned by
[`get_products`](/v2/buyer/discovery/multi-storefront-get-products). It can
select inventory from several storefronts in one request. Interchange creates
or updates one DRAFT campaign cart, then prepares one or more media buys grouped
for bilateral seller execution.

## Request

```json theme={null}
{
  "productQueryId": "query_123",
  "campaign": { "campaignId": "campaign_123" },
  "mode": "execute",
  "replace": false,
  "debug": false,
  "selections": [
    {
      "kind": "proposal",
      "proposalId": "sfp1:42:...",
      "totalBudget": 40000
    },
    {
      "kind": "product",
      "productId": "sf1:57:...",
      "pricingOptionId": "cpm-standard",
      "budget": 10000,
      "bidPrice": 8.5
    }
  ]
}
```

| Field            | Required | Description                                                                                                          |
| ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `productQueryId` | Yes      | Exact `execution_id` returned by `get_products`.                                                                     |
| `campaign`       | Yes      | `{campaignId}` for an existing cart, or `{create: {...}}` containing a complete discovery-mode campaign create body. |
| `mode`           | Yes      | `stage` prepares DRAFT media buys without seller calls; `execute` also dispatches them.                              |
| `selections`     | Yes      | One to 100 qualified proposal/product selections.                                                                    |
| `replace`        | No       | Replaces the existing cart selections before applying the batch. Defaults to `false`.                                |
| `debug`          | No       | Includes execution diagnostics on failures when `mode` is `execute`. Defaults to `false`.                            |

Proposal selections distribute `totalBudget` using the seller's allocations.
Omit `totalBudget` only when the proposal carries a recommended total. Product
selections are for proposal-less products and may include `pricingOptionId`,
`budget`, `bidPrice`, and `targetingOverlay`.

When creating the campaign inline, do not set `productIds`; `selections` is the
only cart mutation in this request. If `discoveryId` is present, it must equal
`productQueryId`. The campaign and product query must belong to the same
advertiser. New selections can only be staged into a DRAFT campaign; an ACTIVE
campaign is accepted only for an unchanged execution retry.

Proposal selection also requires the durable accepted-proposal set written by
the current `get_products` flow. If an older execution predates that set, run
`get_products` again; Interchange will not infer acceptance from the raw seller
response.

## Choose stage or execute

Use `mode: "execute"` as a one-call shortcut when the selected products can be
submitted without reviewing the generated media buys. Interchange prepares the
batch first, records that exact request as prepared, and then contacts sellers.

Use `mode: "stage"` when the media buys need review or customization:

1. Call `create_media_buys` with `mode: "stage"`.
2. Inspect the returned DRAFT media buys and update per-buy creatives, flight
   dates, pacing, or optimization goals as needed.
3. Call `execute_campaign` for the returned campaign. Alternatively, repeat
   `create_media_buys` with the same product query, campaign, selections, and
   `replace` value, changing only `mode` to `"execute"`.

The final execute submits those same DRAFT media buys. It does not reconcile
the product query again or discard the intervening changes. Repeating an
unchanged stage request is also a no-op. If preparation failed, the request is
not marked prepared and an unchanged retry attempts preparation again.

## Response

```json theme={null}
{
  "productQueryId": "query_123",
  "campaignId": "campaign_123",
  "campaignCreated": false,
  "mode": "execute",
  "campaignStatus": "ACTIVE",
  "queryProductsSelected": 4,
  "proposalsApplied": 1,
  "mediaBuysStaged": 0,
  "mediaBuysExecuted": 2,
  "mediaBuyRefs": [
    { "mediaBuyId": "mb_123", "status": "ACTIVE" },
    { "mediaBuyId": "mb_456", "status": "ACTIVE" }
  ],
  "mediaBuyRefsTruncated": false,
  "success": true,
  "noOp": false
}
```

External seller mutations cannot be atomic. `success: false` includes an
`errors` entry for each failed media buy; successful storefront buys remain
successful. Retry the unchanged request to dispatch only work that is still
DRAFT. The operation records a fingerprint on the campaign cart, so a completed
product query cannot be replayed with a different selection. `mediaBuyRefs`
returns lightweight IDs and statuses for up to 100 current buys; when
`mediaBuyRefsTruncated` is true, use `get_campaign` to drill into the cart.

## Errors

* `400 VALIDATION_ERROR` — unqualified IDs, an advertiser/campaign mismatch,
  a non-DRAFT cart, invalid campaign terms, a completed query replayed with
  different selections, or selections that cannot be priced/funded.
* `403 ACCESS_DENIED` — the caller cannot access the advertiser or campaign.
* `404 NOT_FOUND` — the product query, proposal, product, or campaign does not
  exist in the authenticated customer.
* `409 CONFLICT` — campaign budget, settlement, or seller state conflicts with
  the requested batch.

<Card title="Get products across storefronts" href="/v2/buyer/discovery/multi-storefront-get-products" icon="network-wired">
  Retrieve and refine the qualified proposals/products used by this operation
</Card>
