https://adcontextprotocol.org/schemas/v3/signals/get-signals-request.json
Response Schema: https://adcontextprotocol.org/schemas/v3/signals/get-signals-response.json
The get_signals task returns both signal metadata and real-time deployment status across platforms, allowing agents to understand availability and guide the activation process.
Request Parameters
discovery_mode: "wholesale" is an AdCP 3.1+ discovery shape. When the
caller cannot assume a 3.1+ signals surface or wholesale support, clients
should first call get_adcp_capabilities
and confirm that signals.discovery_modes includes "wholesale" before
issuing a wholesale request. If the field is absent or lists only "brief",
treat the agent as brief-only and use signal_spec, signal_refs, or
deprecated signal_ids.Async discovery
discovery_mode: "brief" MAY return submitted when semantic discovery depends on slow provider queries or review that cannot finish before the initial response. Polling get_task_status (legacy tasks/get) with task_id is always valid. If push_notification_config is present and the agent returns submitted, the agent also sends at least the terminal completion/failure notification to that webhook; intermediate progress notifications are optional. discovery_mode: "wholesale" remains a synchronous feed read and reports partial completion via incomplete[], not submitted.
Destination Object
Each deployment target uses atype field to discriminate between platform-based and agent-based deployments:
*
platform is required when type="platform", agent_url is required when type="agent".
Destination filtering: Signals are returned if they are available on any of the requested destinations (OR semantics). Destinations where a signal is not available are omitted from that signal’s response deployments array. A PARTIAL_COVERAGE warning may be included when some destinations don’t support the signal.
Activation Keys: If the authenticated caller has access to any of the destinations in the request, the signal agent will include activation_key fields in the response for those deployments (when is_live: true).
Permission Model: The signal agent determines key inclusion based on the caller’s authentication and authorization. For example:
- A sales agent receives keys for deployments matching its
agent_url - A buyer with credentials for multiple DSP platforms receives keys for all those deployments
- Access is determined by the signal agent’s permission system, not by flags in the request
Filters Object
catalog_types, the deprecated catalog_signals capability flag, and the deprecated signal_id.source: "catalog" value are legacy wire terms. In new prose, read them as provider-published signal definitions in adagents.json signals[]. Existing 3.x agents may continue to accept or emit them for compatibility, but new callers SHOULD use signal_ref and MUST NOT require signals.features.catalog_signals before using the Signals protocol.Response Structure
All AdCP responses include:- message: Human-readable summary of the operation result
- context_id: Session continuity identifier for follow-up requests
- data: Task-specific payload (see Response Data below)
- MCP: Returns complete response as flat JSON
- A2A: Returns as artifacts with message in text part, data in data part
Response Data
get_signals is a discovery and availability surface, not a requirement to
inline every field from the authoritative signal definition. For broad search
results and wholesale feed pages, signal agents SHOULD keep each listing
compact and use stable references plus cacheable disclosure pointers for large
definition resources. Buyers can dereference provider-published definitions
from signal_ref: fetch the provider’s /.well-known/adagents.json (following
authoritative_location when present) and select the matching signals[].id.
Cache the fetched adagents.json document by resolved authoritative URL plus
catalog_etag, HTTP ETag / Last-Modified, or a bounded TTL, then resolve
signal_ref.signal_id within that cached document. Use taxonomy.etag only for
taxonomy documents that have their own freshness validator. These reuse existing
provider-file and taxonomy validators; get_signals does not define a separate
signal-definition validator, and clients must not key signal-definition caches by
validator alone.Definition field inclusion
Buyers that need richer review context in the same call can setfields. This
uses the same response-projection pattern as get_products.fields, rather than
introducing a separate lookup task.
Required identity and activation fields are always included when required by the
response schema. Additional values request optional listing fields or rich
definition metadata inline, including taxonomy, data_sources, methodology,
segmentation_criteria, criteria_url, refresh_cadence, lookback_window,
onboarder, modeling, audience_expansion, device_expansion, countries,
consent_basis, restricted_attributes, policy_categories, art9_basis, and
data_subject_rights.
When consent_basis or art9_basis is projected for another provider’s signal,
the value remains provider-declared signal-definition posture. Sellers and
federating agents MUST NOT substitute their own processing basis for the
provider-declared basis.
Agents SHOULD honor requested fields for exact lookup, refinement, and small
custom-signal result sets when the fields are available. For broad discovery and
wholesale pages, agents MAY still return compact listings with pointers to
provider-published definitions, taxonomy documents, criteria pages, and
disclosure URLs when inlining the requested fields would make the page too
large. This keeps static signals cacheable through adagents.json while letting
custom or brief-specific signals return deeper inline context when useful.
Field Descriptions
- signals: Array of matching signals
- signal_ref: Canonical signal reference. Use
scope: "data_provider"for signals resolved through adagents.jsonsignals[],scope: "signal_source"for source-native signals that are not published in adagents.jsonsignals[], andscope: "product"only in product-contextual responses. New responses SHOULD include this field. - signal_id: Deprecated legacy SignalId object. New clients should read
signal_ref; during the migration window, older responses may include onlysignal_id. - signal_agent_segment_id: Opaque signal handle issued by this signal source. Use it verbatim for
activate_signal; do not treat it as a globally portable signal ID. For package-levelsignal_targeting_groups,signal_refis the buy-time identity and this handle is echoed only when the selected product option exposes it as a separate execution handle. - name: Human-readable signal name
- description: Detailed signal description
- signal_type: Type of signal. One of:
marketplace— resold third-party segment (provider authorization verifiable via the provider’sadagents.json)owned— first-party segment derived from data the signal source directly ownscustom— source-native segment built on demand from models, composites, or buyer inputs (not attributable to a standing upstream provider)
- data_provider: Human-readable source name when applicable. For
scope: "data_provider"signals this is the data provider; forscope: "signal_source"signals it may identify the signal source or proprietary origin. - coverage_percentage: Optional deprecated legacy scalar percentage of audience coverage. Use only as a fallback for clients that do not consume
coverage_forecast. When coverage_forecast is present,coverage_forecastis authoritative for signal-level discovery and this scalar is fallback-only. Ifcoverage_forecastincludes an absent bucket over the same denominator,coverage_percentageshould align with100 * (1 - absent coverage_rate.mid). - coverage_forecast: Optional forecast-shaped availability guidance for the signal.
scopedeclares the denominator,bucket_semanticsdeclares whether returned value buckets areexclusiveoroverlapping, andbucket_completenessdeclares whether the returned buckets are a full denominator partition or a partial histogram. Each point can use akind: "signal"dimension with canonicalsignal_ref,presence: "present"for any present value,presence: "present"plussignal_valuefor a specific value bucket, orpresence: "absent"plussignal_value: nullfor the not-present bucket.metrics.coverage_rateis a 0.0-1.0 fraction of the declared scope. - deployments: Array of destination deployments
- agent_url: URL identifying the destination agent
- account: Account identifier if applicable
- is_live: Whether signal is currently active on this deployment
- activation_key: The key to use for targeting (see Activation Key below). Only present when
is_live=trueand the authenticated caller has access to this deployment. - estimated_activation_duration_minutes: Time to activate if not live
- pricing_options: Array of pricing options for this signal when it has an incremental price. Pass the selected
pricing_option_idinreport_usageor package-levelsignal_targeting_groupsfor billing verification. Omitted when pricing is unavailable to the caller, bundled into the destination product, or has no incremental cost.- pricing_option_id: Unique identifier for this pricing option
- model: Pricing model —
cpm,percent_of_media,flat_fee,per_unit, orcustom model: "cpm"—cpm(number, cost per thousand impressions),currency(ISO 4217)model: "percent_of_media"—percent(0–100),currency(ISO 4217),max_cpm(optional CPM cap: effective charge =min(percent × media_spend_per_mille, max_cpm))model: "flat_fee"—amount(fixed charge),currency(ISO 4217),period(monthly,quarterly,annual, orcampaign)model: "per_unit"—unit(what is counted),unit_price(cost per one unit),currency(ISO 4217)model: "custom"—description(human-readable),metadata(structured parameters),currency(optional). Escape hatch for performance kickers, tiered volume, hybrid formulas, or any construct the standard models cannot express. Buyers SHOULD route custom pricing through operator review before commitment.
- signal_ref: Canonical signal reference. Use
pricing_option_id in report_usage for billing verification; for seller-offered signals selected on a media product, pass it in package-level targeting_overlay.signal_targeting_groups.groups[].signals[].pricing_option_id. If a signal offers multiple models (e.g., CPM and flat fee), choose based on your expected delivery volume and campaign structure.
Response Metadata
Activation Key Object
The activation key represents how to use the signal on a deployment target. It can be either a segment ID or a key-value pair: Segment ID format:Protocol-Specific Examples
The AdCP payload is identical across protocols. Only the request/response wrapper differs.MCP Request - Sales Agent Requesting Signals
A sales agent querying for signals. Because the authenticated caller is wonderstruck.salesagents.com, the signal agent will include activation keys in the response:MCP Response - With Activation Key
Because the authenticated caller matches the deployment target, the response includes the activation key:MCP Response - Multiple Pricing Options
Some signals offer multiple pricing models. The buyer selects one and passes itspricing_option_id in report_usage for direct signal usage or in package-level signal_targeting_groups when the signal is selected on a media buy:
MCP Request - Buyer Querying Multiple DSP Platforms
A buyer checking availability across multiple DSP platforms:MCP Response - Buyer With Multi-Platform Access
A buyer with credentials for both The Trade Desk and Amazon DSP receives keys for both platforms:A2A Request
Natural Language Invocation
Explicit Skill Invocation
A2A Response
A2A returns results as artifacts with the same data structure:Protocol Transport
- MCP: Direct tool call with arguments, returns complete response as flat JSON
- A2A: Skill invocation with input, returns structured artifacts with message and data separated
- Data Consistency: Both protocols contain identical AdCP data structures and version information
Scenarios
All Platforms Discovery
Discover all available deployments across platforms:Response
Message: “Found luxury automotive contextual segment from Peer39 with 15% coverage. Live on Index Exchange and OpenX, pending activation on Pubmatic.” Payload:Response Fields
- context_id (string): Context identifier for session persistence
- signals (array): Array of matching signals
- signal_agent_segment_id (string): Opaque signal handle issued by this signal source. Use it verbatim for
activate_signal; do not treat it as a globally portable signal ID. For media-buy signal groups,signal_refis the buy-time identity and this handle is echoed only when the selected product option exposes it as a separate execution handle. - name (string): Human-readable signal name
- description (string): Detailed signal description
- signal_type (string):
marketplace(resold third-party),owned(signal source first-party data), orcustom(source-native segment built on demand) - data_provider (string, optional): Human-readable source/provider name when applicable
- coverage_percentage (number, optional, deprecated): Legacy scalar estimated reach percentage. Use only as a fallback when
coverage_forecastis absent or unsupported by the client. - coverage_forecast (object, optional): Forecast-shaped coverage breakdown with an explicit denominator and availability points. Use
presence: "present"with omittedsignal_valuefor an aggregate “any present value” bucket; addsignal_valueonly when the row is for a specific value. Usebucket_semantics: "exclusive"when returned buckets do not overlap, or"overlapping"when multi-value signals can make returned rates sum above 1.0. Usebucket_completeness: "complete"only when the returned buckets cover the declared denominator; otherwise use"partial"and buyers must treat omitted share as undisclosed, other, or unsupported buckets.
- signal_agent_segment_id (string): Opaque signal handle issued by this signal source. Use it verbatim for
coverage_forecast as the authoritative field for signal-first discovery: “how much inventory has this signal or its values?” Use product or proposal forecasts with kind: "signal" dimensions as the authoritative surface for product-specific planning: “how does this signal restrict this product’s baseline availability?” Bucket-specific filtering is client-side for now; request filters such as min_coverage_percentage still use the legacy scalar.
- deployments (array): Platform-specific deployment information
- platform (string): Target platform name
- account (string, nullable): Specific account if account-specific
- is_live (boolean): Whether signal is currently active
- activation_key (object): The key to use for targeting. Only present when
is_live=trueand the caller has access. See Activation Key Object above. - estimated_activation_duration_minutes (number, optional): Time to activate if not live
- pricing_options (array): Array of pricing options available for this signal when it has an incremental price. Select one and pass its
pricing_option_idinreport_usageor package-levelsignal_targeting_groups.- pricing_option_id (string): Unique identifier for this pricing option
- model (string): Pricing model —
cpm,percent_of_media,flat_fee,per_unit, orcustom
Error Codes
Discovery Errors
REFERENCE_NOT_FOUND: Referencedsignal_agent_segment_iddoesn’t exist, OR a private signal agent is not visible to this account. The same code is returned whether the resource exists but is unauthorized or truly does not exist — sellers MUST NOT distinguish the two (seeerror.fieldto identify which typed parameter failed to resolve). See the uniform-response MUST in error-handling.mdx.AGENT_ACCESS_DENIED: Authenticated agent’s credentials did not authorize access to this signal agent
Discovery Warnings
PRICING_UNAVAILABLE: Pricing data temporarily unavailable for one or more platformsPARTIAL_COVERAGE: Some requested platforms don’t support this signal typeSTALE_DATA: Some signal metadata may be outdated due to provider refresh delays
Usage Notes
- Authentication-Based Keys: Activation keys are only returned when the authenticated caller matches one of the deployment targets
- Permission Security: The signal agent determines key inclusion based on caller identity, not request flags
- Deployment Status: Check
is_liveto determine if activation is needed - Multiple Deployments: Query multiple deployment targets to check availability across platforms
- Activation Required: If
is_liveis false, use theactivate_signaltask - The message field provides a quick summary of the most relevant findings
Seller-offered signals on media products
A sales agent that owns or is authorized to apply targeting signals exposes buy-time product eligibility throughget_products. It can also expose a broader cross-product signal feed through get_signals when buyers need discovery or activation before package selection:
get_productsdeclares whether a product has package-level signal targeting.products[].included_signalsdescribes non-selectable signals already bundled into or planned into a product. Inlineproducts[].signal_targeting_optionscan carry a product-specific menu, price, activation handle, default, grouping hint, or brief/refine-selected subset; wholesale products can omit inline options and useget_signalsas the selectable signal feed.get_signalsoptionally returns cross-product signal metadata, includingsignal_ref,signal_agent_segment_id, value metadata, and any default or account-scopedpricing_options.create_media_buyselects the signal for a package inpackages[].targeting_overlay.signal_targeting_groups, carrying the selected signalpricing_option_id,signal_ref, and any separate seller execution handle when required.
signal_targeting_options when present and signal_targeting_rules to determine what may be applied on that package. When a product omits inline options but allows signal targeting, use get_signals for candidate discovery and activation metadata, then use get_products.filters.signal_targeting or a product-specific get_products query to confirm the candidate set is selectable and jointly composable for the intended product before calling create_media_buy. When both surfaces include pricing, the product-scoped signal_targeting_options[].pricing_options price is authoritative for that product. For product-local signals exposed on both surfaces, signal_ref.signal_id in the product option MUST match the seller’s get_signals.signals[].signal_ref.signal_id for the same signal. included_signals is descriptive only and does not make a signal selectable.
For media-buy product targeting, product options and package groups use the same signal_ref identity: scope: "product" for product-local signal options, scope: "data_provider" with data_provider_domain for signals defined in published adagents.json signals[], or scope: "signal_source" for source-native custom signals that are not published in adagents.json signals[]. When a provider-published signal is selected, buyers can verify the seller’s authorization by checking the provider’s adagents.json authorized_agents rules for the signal id or tags. The older Signals Protocol signal_id.source shape is deprecated and retained only for backwards compatibility.
Wholesale signals feed
When a consumer (storefront, federated marketplace, registry) needs to mirror a signals agent’s full priced signals feed, setdiscovery_mode: "wholesale" and omit signal_spec / signal_refs / deprecated signal_ids. When the caller cannot assume a 3.1+ signals surface or wholesale support, first check get_adcp_capabilities.signals.discovery_modes for "wholesale". Wholesale enumeration is symmetric with get_products buying_mode: "wholesale": synchronous, paginated, firm-priced, with partial completion declared via incomplete[].
Request
Response
Authorization and provenance
Marketplace signals (signal_type: "marketplace") remain attributable to their upstream data provider. Wholesale enumeration does not collapse provenance:
- Each marketplace signal carries
data_provider. - Consumers SHOULD verify provider authorization via the provider’s
adagents.json— the signals agent’s URL must appear in the provider’s authorization list for that signal class. - Storefronts and registries MAY use wholesale enumeration plus
adagents.jsoncross-reference to materialize a data-publisher signal view: for each known data provider, the set of signals available via authorized signals agents, with prices.
Pricing
pricing_options[] MUST be populated for authenticated callers when the agent has firm standalone signal prices to declare. When account is omitted, the agent returns default rate-card pricing or omits pricing_options[] (in which case the caller MUST re-query with account or use product-scoped pricing from get_products before composing). Unauthenticated callers MAY receive signal metadata without pricing. Signals bundled into a media product or carrying no incremental cost MAY omit pricing_options[].
Capability declaration
Signals agents declare wholesale support inget_adcp_capabilities:
"wholesale" MAY return INVALID_REQUEST for wholesale calls. Because wholesale discovery is AdCP 3.1+, the capability declaration is the canonical support signal when a caller cannot assume a 3.1+ signals surface or wholesale support; callers SHOULD probe before issuing wholesale requests.
Wholesale feed versioning
Even with wholesale enumeration, a consumer mirroring an agent’s signals feed would re-fetch every paginated page on each poll just to detect changes. To avoid that,get_signals supports an opaque wholesale_feed_version token returned on every response. Pass it back via if_wholesale_feed_version on a subsequent call and the agent MAY short-circuit with unchanged: true — no signal payload, no per-page diff.
This is the seller-side wholesale signals feed returned by get_signals. It is not a sync_catalogs feed; sync_catalogs manages buyer-provided campaign input feeds on a seller account.
Unchanged response
Request:unchanged: true, signals[] MUST be omitted and consumers MUST NOT mutate their local wholesale signals mirror.
Wholesale signals feed changed — full payload returned (abbreviated)
test=false
Rules
- Tokens are opaque. No format, no ordering, no inspection.
- A returned
wholesale_feed_versionis scope-keyed viacache_scope. Callers cache(cache_scope, wholesale_feed_version)pairs alongside the(account, filters, discovery_mode, destinations, countries)tuple used. See Cache layering for the two-layer model. pricing_versionis an optional finer-grained token: when present, it changes when prices move butwholesale_feed_versionchanges only when structure/metadata moves. Common for rate-card sweeps that don’t change segment metadata.if_pricing_versionrequiresif_wholesale_feed_version. Pricing has no structural baseline of its own. Sendingif_pricing_versionwithoutif_wholesale_feed_versionis a schema-level error. The agent’s evaluation is two-stage: wholesale feed mismatch returns the full payload; wholesale feed match with pricing mismatch also returns the full payload (so the caller sees updatedpricing_options); both match →unchanged: true.filterscanonicalization. Agents MUST treat thefiltersobject as canonicalized before hashing into thewholesale_feed_versionkeyspace: keys sorted lexicographically, omitted-and-default values treated identically, array values sorted where the filter has set semantics (e.g.,catalog_types,data_providers). Callers that pass equivalent-but-differently-shaped filter objects MUST receive the samewholesale_feed_version. Prevents silent stale-mirror bugs from key-order or default-elision differences. Forward-compat default: new filter fields added in 3.x minor versions MUST declare set-vs-sequence semantics; absent an explicit declaration, the rule defaults to set-semantics.- Pagination interaction. Agents MUST return
wholesale_feed_versionon every paginated page (not only the first) when they declarewholesale_feed_versioning.supported: true; agents that do not declare versioning SHOULD do the same. If the wholesale feed mutates between pages, the new version surfaces on the next page and the caller MUST restart pagination fromcursor: null— the partial pages already received describe a stale version. unchanged: trueand in-progress pagination. A caller mid-pagination MAY sendif_wholesale_feed_versionmatching the version their pages so far were drawn from. If the agent confirmsunchanged: true, the response omitssignals[]and pagination envelope entirely; the caller abandons their in-progress walk under that version. Agents MAY NOT use the conditional-fetch short-circuit to skip individual pages within an active pagination —unchangedis feed-versus-cached-version, not per-page.- Pre-v3.1 agents that ignore
if_wholesale_feed_versionsimply return the full payload — semantically correct, just inefficient.
specs/wholesale-feed-webhooks.md. Wholesale feed webhooks carry the changed signal payload, pricing payload, removal tombstone, or bulk-change summary; get_signals remains the repair and reconciliation read.
Cache layering
Signals agents publish two notional layers: a public layer (the rate-card / structural view) and per-account overlays (account-specific pricing for premium buyers). The conditional-fetch path is layer-aware viacache_scope.
Two-layer cache.
Behavior.
- Requests without
accountalways returncache_scope: "public". Callers cache under the public key. - Requests with
accountreturncache_scope: "public"OR"account"(agent MUST declare; no default)."public": this account prices off the rate card. Caller MAY dedupe — the version and payload are the same as the unauthenticated view."account": this response carries account-specific overrides. Caller caches under the account overlay key.
- Agents MAY downgrade an account from
"account"back to"public"— callers SHOULD interpret this as “this account no longer has overrides” and drop their overlay.
if_wholesale_feed_version. Send the token paired with whichever scope it was returned in. The agent compares against the current version for that scope. If the caller’s token belongs to an "account" scope but the agent responds with cache_scope: "public", that’s the downgrade signal.
Webhook invalidation. Wholesale feed webhook events declare applies_to.scope on *.priced and *.updated payloads. Agents MUST apply the same account/caller authorization predicate used by get_signals discovery_mode: "wholesale" when deciding which subscribers receive signal webhooks:
applies_to: { scope: "public" }→ invalidate the public-layer cache; all account overlays referencing that public version are also stale.applies_to: { scope: "account", account_ids: [...] }→ invalidate only the named accounts’ overlays.applies_to: { scope: "account" }withoutaccount_ids→ seller withholds the affected set; the per-subscriber scope filter routes the event only to subscribers whose principal is in the affected set.
specs/wholesale-feed-webhooks.md §“Cache layering and event scoping” for the full webhook-side spec.
Incomplete array
When the agent cannot complete all work within the caller’stime_budget (or due to internal limits), the response includes incomplete — an array declaring what is missing. Callers can use estimated_wait to decide whether to retry with a larger budget.
Iterative refinement
get_signals supports iterative refinement without a separate mode flag. The combination of signal_spec and signal_refs determines the operation:
To refine previous results, pass back the
signal_ref values from signals you want to keep, and provide an updated signal_spec describing what to change:
Response - Multiple Signals Found
Response - Partial Success with Warnings
Response - No Signals Found
Implementation Guide
Generating Signal Messages
Themessage field should provide actionable insights: