Skip to main content
A storefront on Apostra lists the signals its inventory can target, such as audience segments from a data provider, through AdCP get_signals. You can then target one of those signals on a package in create_media_buy. Most of this page is for anyone who calls a storefront’s AdCP endpoint directly, with their own agent or SDK. If you buy through Apostra, see Searching a seller’s signals in Apostra.

What a storefront returns

A storefront returns the signals its inventory sources report to Apostra. It keeps them in a catalog that Apostra refreshes in the background, the same way it keeps the catalog of products. Each signal carries: The get_signals answer names none of the storefront’s inventory sources, their accounts or their own ids for a signal. When the storefront’s signal catalog holds a signal, a product from get_products names it in its signal options and included signals with the same signal_ref and signal_agent_segment_id that get_signals returns. A product can still carry an inventory source’s own reference: when that source’s signals could not be refreshed or were for one account only, or for a signal get_signals does not list. You can target such a reference on a buy as it is. When some of a seller’s signals could not be read from its inventory sources, or a new inventory source’s signals have not been read yet, the answer carries incomplete with scope signals: the list may be missing some, so ask again later. An inventory source that does not offer signals at all leaves the answer complete.

Two ways to ask

get_signals has two modes.
  • discovery_mode: "wholesale" returns every signal you can use, sorted by name, in pages of up to 100 (50 by default). The answer carries wholesale_feed_version and pricing_version. Send them back as if_wholesale_feed_version and if_pricing_version and the storefront answers unchanged: true when nothing changed.
  • discovery_mode: "brief" (the default) looks signals up. Send signal_refs to read particular signals, signal_spec to search by words, or both. A signal_spec match is a word match against each signal’s name, description, provider and categories, ranked by how many of your words it contains. A signal_spec can be up to 500 characters, and a request can look up at most 100 signal_refs and signal_ids together; a larger request is refused with INVALID_REQUEST.
Both modes take filters (catalog_types, data_providers, max_cpm, max_percent, min_coverage_percentage), countries and destinations. A signal that names no countries is not limited by country. destinations matches when it names the storefront’s own endpoint. Each of destinations, countries, data_providers and catalog_types takes at most 250 values; a longer list is refused with INVALID_REQUEST. Page through with pagination.cursor. A cursor belongs to the answer it came from: if the storefront’s signals change while you page, the next page is refused with INVALID_REQUEST and you start again without a cursor. get_signals needs AdCP 3.1 or later. A request pinned to 3.0 is refused with VERSION_UNSUPPORTED, because AdCP 3.0 requires a price and coverage figure on every signal, which a source need not report. On 3.1 and later, get_adcp_capabilities lists signals with signals.discovery_modes: ["brief", "wholesale"].

What an account changes

account is optional.
  • Without an account you get every signal the storefront’s live inventory sources report, as a public answer (cache_scope: "public"). A source whose own signal list was for one account only is left out.
  • With an account you get only the signals from the inventory sources that account can buy through (cache_scope: "account"). The feed and pricing versions belong to that account’s view. The account is checked the way product discovery checks it: an account_id must be one you hold, and a brand and operator are accepted only as another name for an account you already synced. See Discovering products on a storefront needs an account.

The signal reference

Most signals carry a signal_ref that names the storefront as the source:
The signal_id stays the same for as long as the source keeps reporting the signal and Apostra keeps the key it is derived with. If Apostra changes that key, every storefront’s signal_ids change at once: a buy that sends an old reference is refused with invalid_signal_targeting_groups, and you call get_signals again for the current references. The signal_id reveals nothing about the source. A signal the source names by its data provider’s published reference (scope: "data_provider", with data_provider_domain) keeps that reference, so the same provider signal reads the same on every seller that carries it. A storefront reference names the storefront, not the provider, so you cannot check the seller’s right to resell a provider’s signal through that provider’s adagents.json.

Targeting a signal on a media buy

Send the signal_ref in a package’s targeting_overlay.signal_targeting_groups on create_media_buy or update_media_buy:
The storefront sends the signal to the inventory source that sells the package’s product, as that source’s own reference. A signal must come from the same inventory source as the product. If it does not, or the storefront does not know the reference, the request is refused at once with INVALID_REQUEST and the reason invalid_signal_targeting_groups, before any seller approval, and nothing is sent to the source. Send the signal_source_url exactly as the storefront returned it: the same reference under another address for the storefront is refused. Call get_signals again for current references and send the package again. When the signal has pricing_options, AdCP has you send the pricing_option_id of the price you choose in the signal’s entry. On a product a storefront puts together for your brief, priced signals are not yet buyable; see below. The create_media_buy answer echoes each package’s signal_targeting_groups as you sent them, with the storefront’s references. On update_media_buy, a package’s targeting_overlay changes only the dimensions you send, as AdCP defines: leave signal_targeting_groups out to keep the package’s signals, or send null to clear them.

Signals on a storefront’s own products

A product a storefront puts together for your brief lists its signals in AdCP fields, with the same signal_ref that get_signals returns.
  • included_signals are signals built into the product. They describe what the product is, its price already covers them, and you do not select them. A product with exact age targeting lists its age first, with its data provider in ext.apostra.data_provider. Each other included signal carries its provider in data_provider, as get_signals does.
  • signal_targeting_allowed: true means you can add signals to a package of the product through targeting_overlay.signal_targeting_groups. A product says so when the inventory it is sold from runs on Apostra’s embedded sales agent and the product is not guaranteed. A fixed-price (guaranteed) package takes no added signals for now: its delivery was forecast without them, and a narrow audience could keep it from delivering. A buy or update that adds signals to a package whose pricing option is fixed-price is refused at once with invalid_signal_targeting_groups. On a product with both a fixed and an auction price, choose the auction one to add signals.
  • signal_targeting_options lists up to 20 signals from that inventory that you can add, widest reach first: highest coverage_percentage, then by name. The list does not depend on your brief. get_signals is the full menu: a package can target any signal it serves for that inventory, subject to the checks below. Each option carries its signal_ref, the signal_agent_segment_id get_signals serves, name, description, data_provider and value type. Today the options are the publisher’s own data that costs nothing, so they carry no pricing_options. A third party’s data is not offered while its data cost is unknown, and neither is an age signal.
  • An option you can only use to exclude an audience says allowed_targeting_modes: ["exclude"].
  • Each option’s selection_group names the ad-server targeting it uses (audience_segment, custom_key_value or gam_custom_key), and signal_targeting_rules lists those groups. An any group can’t mix them. An option that combines several targeting criteria has a selection_group of its own: it can’t be an alternative to any other signal.
If the storefront could not read all of an inventory source’s signals when it answered, or had not read a new source’s signals yet, a product from that inventory lists none of them, rather than a partial list: its included_signals list at most its age, and it has no signal_targeting_options. It still says signal_targeting_allowed: true, and get_signals is the menu: its answer carries incomplete with scope signals whenever the catalog read was partial.

Priced signals are not yet buyable here

On a product a storefront puts together, a signal with a price is not yet buyable: the seller’s ad server does not yet bill a selected signal’s price on its line item. A buy that selects one in an any group is refused at once with INVALID_REQUEST and the reason invalid_signal_targeting_groups, before any seller approval. A priced signal in a none group only excludes an audience and is not charged, so it is accepted, unless the signal can only exclude. Choose a signal from the product’s signal_targeting_options and send the package again.

Groups the seller’s ad server can apply

Signals in an any group are alternatives, and the seller’s ad server can make alternatives only of audience segments, or of values of one custom-targeting key. A buy is refused at once with invalid_signal_targeting_groups, before any seller approval, when an any group:
  • mixes audience segments with custom-targeting key signals (custom_key_value or gam_custom_key);
  • holds values of more than one custom-targeting key;
  • holds a signal that combines several targeting criteria together with other signals; or
  • holds a signal that can only exclude.
Values of one key, such as two key=value signals on the same key, can be alternatives in one any group. A group with a single signal takes any signal except one that can only exclude. A none group excludes each of its signals separately, so it takes any mix. A signal_ref that names more than one of the seller’s signals is refused the same way: send the signal’s signal_agent_segment_id with it.

Searching a seller’s signals in Apostra

A buyer account in Apostra reads one seller’s signals with search, the same read a buyer’s own agent makes through the Apostra MCP server or HTTP API:
  • filter.sellerAccountId names the seller by its Account id, the sellerAccountId on a seller or product result. One search reads one seller.
  • filter.advertiserId names the advertiser whose account with that seller is used. Apostra asks the seller’s storefront get_signals with that account, so you see only the signals from the inventory sources the account can buy through (see What an account changes). When your account has only one active advertiser, it is filled in for you.
  • query searches by words, as signal_spec does in brief mode. Without a query you get the seller’s whole list, sorted by name.
  • filter.dataProvider keeps only signals from these data providers. It matches the provider’s name exactly, ignoring case, so copy the name from an earlier result. To search by part of a provider’s name, such as “Polk”, put it in query.
  • limit sets the page size, up to 100 (50 by default). For the next page, send the returned nextCursor as cursor, with the same query and filters.
Each result is the signal as the storefront returns it, without deployments. The search is refused, never answered with an empty list, when:
  • your advertiser has no account with the seller yet, or the account needs attention: open Media Partners to see where it stands;
  • the seller is a platform such as Meta or Snap, which does not offer a signal catalog through Apostra. That does not mean it has no signals.
To target a signal, put its signal_ref unchanged in targetingOverlay.signal_targeting_groups on that seller’s product in save_media_buy, with its value_type (a binary signal takes value: true). A signal can be targeted only on products of the inventory source that reports it; any other pairing is refused with invalid_signal_targeting_groups, as described in Targeting a signal on a media buy.