> ## 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.

# Channel groups

> Express composite inventory intent on a campaign, compile it into separate media buys, and report by the same grouping.

A **channel group** is one coherent inventory selection inside a campaign. It combines AdCP inventory
dimensions without inventing new channel values. Mobile web display, for example, is:

```json theme={null}
{ "channels": ["display"], "propertyTypes": ["website"], "deviceTypes": ["mobile"] }
```

It is not `channel: "mobile_web"`: `mobile_web` isn't an AdCP media channel.

<Warning>
  Semicola uses the channel group id to split and label media buys. It doesn't filter discovery by a
  group, check selected products against the group's definition, or turn the definition into package
  targeting. Check the discovery results and the seller's product details before you assign a
  `channelGroupId`.
</Warning>

Within one dimension, values are alternatives (OR). Across dimensions, they combine (AND). A group with
`channels: ["ctv", "olv"]` and `deviceTypes: ["ctv", "mobile"]` allows either channel and either device.
For pairwise logic such as "CTV on TV devices, or online video on mobile", make two groups.

## Groups split media buys

One AdCP media buy can't carry arbitrary combinations of channel, property type, device and format, so
Semicola compiles a campaign by this rule:

```text theme={null}
channel group × seller × settlement currency = one media buy
```

If a campaign has `mobile-web-display` and `ctv` groups, products in each become separate draft buys
even with the same seller. They stay in one campaign.

## Save preset groups

Pass `channelGroups` to `save_campaign`. A preset is shorthand: Semicola expands it and saves the full
definition with its preset version, so a later preset change never alters an existing campaign.

```json theme={null}
{
  "advertiserId": 12,
  "name": "Streaming launch",
  "budget": { "total": 50000, "currency": "USD" },
  "channelGroups": [
    { "channelGroupId": "mobile-web-display", "presetId": "mobile_web_display" },
    { "channelGroupId": "ctv", "presetId": "ctv" }
  ],
  "idempotencyKey": "streaming-launch-2027-02"
}
```

| Preset               | Saved definition                                                                                                                                                                                                                              |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `display`            | `channels: ["display"]`                                                                                                                                                                                                                       |
| `olv`                | `channels: ["olv"]`                                                                                                                                                                                                                           |
| `ctv`                | `channels: ["ctv"]`                                                                                                                                                                                                                           |
| `mobile_web_display` | `channels: ["display"]`, `propertyTypes: ["website"]`, `deviceTypes: ["mobile"]`, and the display `formatKinds` (`image`, `html5`, `display_tag`, `image_carousel`, `native_in_feed`, `sponsored_placement`, `responsive_creative`, `custom`) |
| `mobile_web_olv`     | `channels: ["olv"]`, `propertyTypes: ["website"]`, `deviceTypes: ["mobile"]`                                                                                                                                                                  |

The simple presets make no property, device or format assumption: `ctv` means the AdCP CTV channel, not
only a TV set or only a CTV app. A preset group's `name` defaults to the preset's name ("Mobile web
display", "CTV"…); pass `name` to change it.

## Define your own group

```json theme={null}
{
  "channelGroupId": "premium-streaming-video",
  "name": "Premium streaming video",
  "inventory": {
    "channels": ["ctv", "olv"],
    "propertyTypes": ["ctv_app", "website"],
    "deviceTypes": ["ctv", "desktop"],
    "formatKinds": ["video_hosted", "video_vast"]
  }
}
```

`channels` is required and takes AdCP channel values only; put an alias such as "mobile web" in the
group's name, not in `channels`. A `channelGroupId` is 1–64 letters, digits, dots, dashes or
underscores, unique within the campaign.

Over REST (`POST` or `PUT /api/v2/buyer/campaigns`), send the saved shape; there's no preset shorthand:

```json theme={null}
{
  "channelGroups": [
    {
      "channelGroupId": "mobile-web-display",
      "name": "Mobile web display",
      "source": { "kind": "preset", "presetId": "mobile_web_display", "presetVersion": 1 },
      "inventory": {
        "channels": ["display"],
        "propertyTypes": ["website"],
        "deviceTypes": ["mobile"],
        "formatKinds": ["image", "html5", "display_tag", "image_carousel", "native_in_feed", "sponsored_placement", "responsive_creative", "custom"]
      }
    }
  ]
}
```

A custom group's `source` is `{ "kind": "custom" }`. The media-buy batch's `campaign.create` takes the
same saved shape.

## Assign products to a group

When a campaign defines groups, every staging call names one:

* `save_media_buy` (products or a proposal): `channelGroupId` applies to everything staged in that call.
* `POST /api/v2/buyer/media-buys/batch`: `channelGroupId` on each selection.

A missing or unknown `channelGroupId` is refused with the campaign's group ids, and a campaign without
groups refuses one. Each draft buy records the `channelGroupId` and `channelGroupName` that created it.
[Auto-select](/buy/autonomy-settings#auto-select-products) assigns each product to the first group
whose channels it carries.

Groups are immutable once used:

* A group a staged buy references can't be removed or redefined (`409 CONFLICT`: "Add a new group
  instead."). Add a new group.
* Define groups before the campaign's first buy is staged: adding groups to a campaign that already has
  an ungrouped buy is refused (`409 CONFLICT`).

## Report by group

Use `get_delivery` with the `channel_group` dimension:

```json theme={null}
{
  "report": "campaign_delivery",
  "metrics": ["impressions", "spend", "clicks"],
  "dimensions": ["channel_group"],
  "range": { "startDate": "2026-09-01", "endDate": "2026-09-30" },
  "filters": { "campaignId": "cmp_…" }
}
```

Add `filters.channelGroupId` to read one group. Buys staged without a group roll up under a `null`
channel group; they're never guessed from names or products.

Channel groups answer "which inventory selection did the buyer ask for?" Campaign
[targeting](/buy/targeting) answers "who, where or when should it reach?" Keep them separate even when
both mention a dimension such as device.

## Related

* [Media buys and packages](/buy/media-buys-and-packages)
* [Autonomy and auto-select](/buy/autonomy-settings)
* [Reporting](/buy/reporting)
