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

# Browse what an ad-server source offers

> Read an ad-server source's capabilities, creative formats and publisher properties with get, and browse its ad units, placements and segments with search, before you author a product.

Before you author a [wholesale product](https://docs.apostra.com/v3/seller-product-creation.md) on an ad-server
source, you need to know what the source supports, which creative formats and
publisher properties it can carry, and which ad units, placements or segments you
can sell. V3 reads each of these without a tool of its own: three `include` values
on `get` for the small facts of one source, and one `search` kind for the long,
paged list of selectors.

Every read on this page needs a source with a managed ad-server connection, such
as Google Ad Manager or FreeWheel. Pass the source's own `sourceId`, as
`search({ "kind": "inventory_source" })` returns it. You never pass an ad-server
connection id.

A read changes nothing. Browsing a selector does not create, import or reserve
anything: it gives you the values a product's `inventory` is written from.

## Read the source's capabilities, formats and properties

Call `get` with `kind: "inventory_source"`, the source's `id` and any of
`capabilities`, `creativeFormats` and `publisherProperties`:

```json theme={null}
{
  "kind": "inventory_source",
  "id": "SOURCE_ID",
  "include": ["capabilities", "creativeFormats", "publisherProperties"]
}
```

A `get` that names none of the three returns what it returned before.

If the source has no managed ad-server connection, or the connection cannot answer,
that include is left out of the result and listed in `unavailableIncludes` with
its reason. The other includes you asked for are still returned. A source that is
not yours is not found, as for any `get`.

### `capabilities`

What the ad-server adapter declares. These are the adapter's own facts, not AdCP's,
so the names are the adapter's, in camelCase.

| Field | Meaning |
| - | - |
| `adapter` | The adapter's name, such as `google_ad_manager`. `unknown` when the connection declares none. |
| `selectorTypes` | The kinds of selector you can browse and sell. Each has a `selectorType` (what you pass to `search` as `filter.selectorType`), a `label` and `supportsParentFilter`, which says whether you can list the children of one selector. |
| `selectionRequirements` | Rules a product must meet. Each names the `selectorTypes` it covers, `minSelected` and a `message`. |
| `pricingModels` | The pricing models the ad server supports. |
| `targetingDimensions` | The targeting dimensions it supports. |
| `optimizationGoals` | The optimization goals it supports. |
| `settlementCurrency` | The currency the ad server settles prices in, or `null` when none is known. Price a product in this currency. |

A list the adapter does not state is empty.

### `creativeFormats`

The creative formats the source can carry, as AdCP format declarations. A format is
named by `format_kind` and `params`, and has no agent URL. The source's older format
list, which names formats by agent URL and id, is not served.

| Field | Meaning |
| - | - |
| `format_option_id` | The option's id. |
| `format_kind` | An AdCP canonical format kind, such as `image` or `video_hosted`. |
| `params` | The kind's parameters, such as `width` and `height`. |
| `display_name` | The format's name. |
| `publisher_domain` | Present only when the source states one. An ad server's own formats are product-local, and AdCP says a product-local option leaves `publisher_domain` out. Today no ad-server adapter states one. |
| `dimensions`, `assetTypes`, `requirements` | The adapter's own facts about the format. They are read-only. |

To use a format in a product, send its `format_option_id`, `format_kind` and
`params` back in `formatOptions` on `save_wholesale_product`, unchanged. Do not send
`display_name`, `dimensions`, `assetTypes` or `requirements`: a save does not take
them.

### `publisherProperties`

The properties the source can sell, and the selectors the ad server has already
decided are valid. The result has two lists, `properties` and `selectors`.

A property uses AdCP's `Property` names: `publisher_domain`, `property_id`,
`identifiers` (a list of `type` and `value`) and `tags`. AdCP also requires
`property_type` and `name`. The ad server does not always state them, so a property
carries only what the source stated. The ad server's display text for a property is
`label`, kept beside the AdCP fields and read-only. It is not AdCP's `name`.

A selector uses AdCP's `PublisherPropertySelector`: `selection_type` (`all`,
`by_id` or `by_tag`), `publisher_domain`, `property_ids` and `property_tags`. It is
the shape `inventory.publisher_properties` takes on `save_wholesale_product`, so
send one back unchanged. Its `label` is the ad server's own text and is read-only.

Apostra returns these as stored and does not check them against the AdCP schema
when you read them. The check happens when you save a product.

## Browse the source's selectors

Selectors are the ad units, placements, segments and other items the ad server
declares. A list can run to thousands, so it is a search, not an include:

```json theme={null}
{
  "kind": "inventory_source_item",
  "sourceId": "SOURCE_ID",
  "filter": { "selectorType": "ad_unit", "parentId": "PARENT_ID" },
  "query": "homepage"
}
```

* `sourceId` and `filter.selectorType` are required. A search without either is
  refused. Take `selectorType` from the `selectorTypes` that `capabilities` lists.
* `query` narrows the list by name. `filter.parentId` lists the children of one
  selector.
* `filter.mediaType` is `display` or `video`, and `filter.environmentType` is
  `BROWSER` or `VIDEO_PLAYER`. Both are optional. Any other value is refused with
  `VALIDATION_ERROR`.
* `limit` is at most 100. When more rows match, the answer has `nextCursor`. Send it
  back as `cursor` with the same `sourceId` and filters. If the ad server's cursor is
  longer than 2048 characters, it is not returned: the answer has
  `nextCursorTooLong: true` and `truncated: true` instead. Narrow the search with
  `query`, `filter.parentId`, `filter.mediaType` or `filter.environmentType`.
* The kind is explicit-only. A search with no `kind` never reads selectors, and
  `get` does not read a selector.

Each row has the ad server's own facts:

| Field | Meaning |
| - | - |
| `selectorType` | Which selector type the row is. |
| `externalId` | The ad server's id. Use it as `external_id` in `inventory.execution.selectors`. |
| `name`, `path`, `parentId`, `status` | The ad server's name, its path of ancestor names, its parent's `externalId` and its status. Each is `null` when the ad server sends none. |
| `truncated` | Present, and `true`, only when a value on the row was cut. The ad server controls these strings, so each one (`selectorType`, `externalId`, `name`, `parentId`, `status` and each `path` entry) is cut at 512 characters and ends with "…", and `path` keeps at most 32 entries. A cut `externalId` cannot be sent back in a product. A row with no cut value has no `truncated` field. |

## Related

* [Create and manage seller products](https://docs.apostra.com/v3/seller-product-creation.md) uses these values in
  `save_wholesale_product`.
* [Sellers on the V3 tool surface](https://docs.apostra.com/v3/tool-reference.md) lists every `get` kind and include a seller
  account reads.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.