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

# Media buys and packages

> Stage, launch, change and pause the per-seller buys under a campaign, and the packages inside them.

A **media buy** is one AdCP transaction with one seller for one campaign, in the campaign's currency.
A **package** is one product inside that buy, with its own budget, pacing, optional bid price, flight
and targeting overlay.

```text theme={null}
Campaign
└── Media buy   (one per seller)
    └── Package (one per product, or per product per pacing period)
        └── Delivery (impressions, spend, clicks)
```

Media buys don't exist until you stage products onto a campaign. A campaign with nothing staged has
no media buys.

## Stage a buy

Staging puts products on the campaign's **draft** buy for that seller. It contacts no seller.

<Tabs>
  <Tab title="From a proposal">
    ```json theme={null}
    { "fromProposalId": "…", "totalBudget": "25000", "idempotencyKey": "glaze-holiday-proposal-1" }
    ```

    `save_media_buy` with `fromProposalId` stages every product in a seller's proposal and splits the
    budget by the proposal's allocations. Pass `seller:<name>` to use that seller's current proposal.
  </Tab>

  <Tab title="From products">
    ```json theme={null}
    {
      "campaignId": "…",
      "sellerId": 7,
      "products": [
        { "productId": "…", "pricingOptionId": "…", "budget": "10000" }
      ],
      "idempotencyKey": "<the discovery's productQueryId>"
    }
    ```

    `save_media_buy` with `campaignId`, `sellerId` and `products` stages products you picked from
    [product discovery](/buy/product-discovery). Each product names a `pricingOptionId`, a `budget`
    and an optional `bidPrice`.
  </Tab>

  <Tab title="REST">
    `POST /api/v2/buyer/media-buys/batch` with the discovery's `productQueryId`, a `campaign`
    (`{ "campaignId": … }` or `{ "create": { … } }` for a new draft campaign), `selections` and
    `mode`: `stage` contacts no sellers; `execute` sends one create request per seller. `replace: true`
    replaces what is already staged.
  </Tab>
</Tabs>

There is one draft buy per seller on a campaign (per seller and [channel group](/buy/channel-groups)
when the campaign defines groups; then each staging call names its `channelGroupId`): staging again for
the same seller replaces its lines.
The result reports each staged buy, the campaign budget's `allocated` and `unallocated` amounts, and
`productsSkipped` with a reason for each product it couldn't stage, for example "Product is no longer
offered." or a product priced in a currency other than the campaign's. If no budget is left
unallocated, staging is refused: raise the campaign budget or remove a staged buy.

To drop a draft buy, call `save_media_buy` with `mediaBuyId` and `isArchived: true`.

A campaign whose `autonomy.inventorySelection` is `automatic` refuses manual staging (`409 CONFLICT`,
`details.reason: "automatic_inventory_selection"`); its products come from
[auto-select](/buy/autonomy-settings#auto-select-products).

## Launch

Launching the campaign (`POST /api/v2/buyer/campaigns/{id}/execute`, or asking Semi to launch) sends
each draft buy to its seller. See [Campaigns → Launch](/buy/campaign#launch) for the preconditions.
What happens next depends on the seller: an immediate `ACTIVE`, or `PENDING_APPROVAL` while the seller
reviews. The [media buy lifecycle](/concepts/media-buy-lifecycle) has every status and transition.

## Read buys and packages

| What                              | MCP                            | REST (under `/api/v2/buyer`)           |
| --------------------------------- | ------------------------------ | -------------------------------------- |
| One media buy                     | `get` with `kind: "media_buy"` | `GET /media-buys/{id}`                 |
| A buy's packages                  | none                           | `GET /media-buys/{id}/packages`        |
| Status of every buy on a campaign | the campaign workspace         | `GET /campaigns/{id}/media-buy-status` |

A media buy carries `status` (the platform status) and `adcpStatus` (what the seller last reported),
`pendingAt` and `pendingReason` when it's waiting on someone, `errorCode`, `errorOwner` and
`sourceMessage` when it failed, `budget`, `currency`, `startTime` and `endTime`, the seller's own id
(`upstreamMediaBuyId`) once it has one, `pausedBy` (`buyer` or `campaign`) and a `revision`.

A package carries `productId` and `productName`, `pricingOptionId`, `pricingModel`, `rate`, `budget`,
`pacing` (`even`, `asap` or `front_loaded`), `bidPrice`, `startTime`, `endTime`, `targetingOverlay`,
`plannedImpressions`, `pacingPeriod` when the buy was split by [pacing periods](/guides/pacing-periods),
and a `status` of `active`, `paused` or `canceled`.

Budgets are what you set, in the campaign currency. A split of each buy's budget into media and
platform fee isn't shown on buys.

## Change a live buy

`update_media_buy` (`PATCH /api/v2/buyer/media-buys/{id}`) changes the buy's `name` and `endTime`,
and per package the `budget`, `pacing`, `bidPrice` and [targeting overlay](/buy/targeting). Add a
`reason`; it travels with the change. Pass `expectedRevision` to refuse the update if someone else
changed the buy first.

On a live buy the change goes to the seller. If the seller must approve it, the result carries an
[update proposal](/buy/update-proposals) and the buy shows a `pendingChange` until the seller decides.
A buy can have only one pending change at a time. Only draft, active and paused buys can be changed;
on a draft the change applies at once, with no seller involved.

## Pause and resume

* One buy: `save_media_buy` with `mediaBuyId` and `isPaused: true` (or `false` to resume), or
  `POST /media-buys/{id}/pause` and `POST /media-buys/{id}/reactivate`. The rest of the campaign keeps
  running, and the seller is told.
* Every buy on a campaign: pause or reactivate the campaign. Buys paused by the campaign show
  `pausedBy: "campaign"`; a buy you paused on its own stays paused when the campaign reactivates.

## Creatives on a buy

Creatives attach to the campaign (see [Creatives](/buy/creatives)). If a seller accepts a buy before a required creative is attached, the buy
is `ACTIVE` with the operational status `pending_creatives` until you add it.

## Related

* [Campaigns](/buy/campaign)
* [Media buy lifecycle](/concepts/media-buy-lifecycle)
* [Diagnosing a stuck media buy](/guides/diagnosing-stuck-media-buys)
