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 carrieswholesale_feed_versionandpricing_version. Send them back asif_wholesale_feed_versionandif_pricing_versionand the storefront answersunchanged: truewhen nothing changed.discovery_mode: "brief"(the default) looks signals up. Sendsignal_refsto read particular signals,signal_specto search by words, or both. Asignal_specmatch is a word match against each signal’s name, description, provider and categories, ranked by how many of your words it contains. Asignal_speccan be up to 500 characters, and a request can look up at most 100signal_refsandsignal_idstogether; a larger request is refused withINVALID_REQUEST.
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: anaccount_idmust 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 asignal_ref that names the storefront as the source:
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 thesignal_ref in a package’s
targeting_overlay.signal_targeting_groups on create_media_buy or
update_media_buy:
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 samesignal_ref that get_signals returns.
included_signalsare 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 inext.apostra.data_provider. Each other included signal carries its provider indata_provider, asget_signalsdoes.signal_targeting_allowed: truemeans you can add signals to a package of the product throughtargeting_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 withinvalid_signal_targeting_groups. On a product with both a fixed and an auction price, choose the auction one to add signals.signal_targeting_optionslists up to 20 signals from that inventory that you can add, widest reach first: highestcoverage_percentage, then by name. The list does not depend on your brief.get_signalsis the full menu: a package can target any signal it serves for that inventory, subject to the checks below. Each option carries itssignal_ref, thesignal_agent_segment_idget_signalsserves,name,description,data_providerand value type. Today the options are the publisher’s own data that costs nothing, so they carry nopricing_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_groupnames the ad-server targeting it uses (audience_segment,custom_key_valueorgam_custom_key), andsignal_targeting_ruleslists those groups. Ananygroup can’t mix them. An option that combines several targeting criteria has aselection_groupof its own: it can’t be an alternative to any other signal.
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 anany 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 anany 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_valueorgam_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.
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 withsearch, the same
read a buyer’s own agent makes through the Apostra MCP server or HTTP API:
filter.sellerAccountIdnames the seller by its Account id, thesellerAccountIdon a seller or product result. One search reads one seller.filter.advertiserIdnames the advertiser whose account with that seller is used. Apostra asks the seller’s storefrontget_signalswith 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.querysearches by words, assignal_specdoes in brief mode. Without aqueryyou get the seller’s whole list, sorted by name.filter.dataProviderkeeps 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 inquery.limitsets the page size, up to 100 (50 by default). For the next page, send the returnednextCursorascursor, with the same query and filters.
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.
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.