Skip to content

Commit e39985d

Browse files
bokelleyclaude
andauthored
feat: clarify signal activation execution paths, add signal agent storyboards (#1949) (#1966)
Addresses the signal activation spec gaps surfaced during A1 certification (supersedes #1944, #1945, #1946). Clarifies when to use platform vs agent destinations and adds testing storyboards for signal agent builders. Spec docs: - activate_signal.mdx: add "Using Activation Keys" execution path section - overview.mdx: add sales agent activation pattern to walkthrough - specification.mdx: add destination type selection conformance language Signal agent storyboards: - signal_marketplace.yaml: marketplace/catalog agent (4 phases: discovery, verification, platform activation, agent activation) - signal_owned.yaml: owned/first-party agent (3 phases: discovery, platform activation, agent activation) - test-kits/nova-motors.yaml: signal test kit with marketplace + owned signals Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ec2f8a8 commit e39985d

8 files changed

Lines changed: 736 additions & 5 deletions

File tree

docs/signals/overview.mdx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,26 @@ Each activation call specifies the destination platform and account:
144144

145145
The signal agent pushes segment membership to each platform. Sam gets back deployment IDs he can reference when building media buys.
146146

147+
### Buying through a sales agent
148+
149+
Sam also wants to run a sponsored article campaign through Wonderstruck, a premium publisher with its own sales agent. Instead of activating the signal on a specific DSP, Sam activates it on the sales agent directly — Wonderstruck handles its own DSP coordination:
150+
151+
```json
152+
{
153+
"$schema": "https://adcontextprotocol.org/schemas/v3/signals/activate-signal-request.json",
154+
"signal_agent_segment_id": "shopgrid_new_to_brand",
155+
"pricing_option_id": "po_shopgrid_retail_cpm",
156+
"destinations": [
157+
{
158+
"type": "agent",
159+
"agent_url": "https://wonderstruck.salesagents.example"
160+
}
161+
]
162+
}
163+
```
164+
165+
The sales agent records the activation internally. When Sam later calls `create_media_buy` through Wonderstruck, the signal-based targeting is already in place — Sam doesn't need to know which DSP Wonderstruck uses behind the scenes.
166+
147167
## Step 5: Build the campaign
148168

149169
<img src="/images/walkthrough/signals-05-campaign-targeting.png" alt="Sam views three targeting layers stacking on a large screen — location, audience, and purchase data overlap and glow where they intersect" style={{ width: '100%', borderRadius: '12px', marginBottom: '1rem' }} />

docs/signals/specification.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,14 @@ Signal activation is typically asynchronous:
174174

175175
Orchestrators MUST NOT assume immediate availability after activation request.
176176

177+
### Destination Type Selection
178+
179+
The `activate_signal` request supports two destination types. The choice depends on the buyer's execution path:
180+
181+
- Orchestrators buying through a Sales Agent SHOULD use `type: "agent"` destinations with the SA's URL. The SA handles downstream platform coordination — which DSP it uses is an implementation detail.
182+
- Orchestrators buying directly on a DSP SHOULD use `type: "platform"` destinations. The orchestrator is responsible for ensuring the activation platform matches where campaigns will run.
183+
- Signal agents MUST support both destination types per the destination schema.
184+
177185
## Schema Reference
178186

179187
| Schema | Description |

docs/signals/tasks/activate_signal.mdx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,16 @@ The activation key represents how to use the signal on a deployment target. It c
128128
}
129129
```
130130

131+
### Using Activation Keys
132+
133+
The activation key tells the buyer how to reference the signal on the destination. The execution path depends on the destination type:
134+
135+
**Platform destinations** — The `activation_key` contains a `segment_id`, a platform-native identifier. The signal agent pushed segment data to the DSP; the buyer references this key when configuring campaign targeting on that platform. The buyer is responsible for ensuring the activation platform matches where it will run campaigns.
136+
137+
**Agent destinations** — The `activation_key` confirms the signal is live on the sales agent. The SA records the activation internally and applies signal-based targeting when fulfilling media buys through `create_media_buy`. The buyer does not need to know which DSP the SA uses — downstream platform coordination is the SA's responsibility.
138+
139+
**Choosing a destination type**: Use `type: "platform"` when buying directly on a DSP. Use `type: "agent"` when buying through a Sales Agent — the SA coordinates its own DSP targeting as an implementation detail.
140+
131141
## Protocol-Specific Examples
132142

133143
The AdCP payload is identical across protocols. Only the request/response wrapper differs.

docs/storyboards/schema.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
# id: string (unique identifier, e.g., "creative_template")
1414
# version: string (semver, e.g., "1.0.0")
1515
# title: string (human-readable title)
16-
# category: enum (creative_template | creative_ad_server | creative_sales_agent | creative_generative | media_buy_seller | media_buy_guaranteed_approval | media_buy_non_guaranteed | media_buy_proposal_mode | media_buy_governance_escalation | media_buy_catalog_creative)
16+
# category: enum (creative_template | creative_ad_server | creative_sales_agent | creative_generative | media_buy_seller | media_buy_guaranteed_approval | media_buy_non_guaranteed | media_buy_proposal_mode | media_buy_governance_escalation | media_buy_catalog_creative | signal_marketplace | signal_owned)
1717
# summary: string (one-line description for listings)
1818
# narrative: string (paragraph explaining the overall flow)
1919
#
2020
# agent:
21-
# interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller)
22-
# capabilities: string[] (AdCP capability flags: supports_transformation, has_creative_library, supports_generation, sells_media, accepts_briefs, supports_guaranteed, supports_non_guaranteed)
21+
# interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller | marketplace_catalog | owned_signals)
22+
# capabilities: string[] (AdCP capability flags: supports_transformation, has_creative_library, supports_generation, sells_media, accepts_briefs, supports_guaranteed, supports_non_guaranteed, catalog_signals)
2323
# examples: string[] (real-world examples: "Celtra", "Innovid")
2424
#
2525
# caller:
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
id: signal_marketplace
2+
version: "1.0.0"
3+
title: "Marketplace signal agent"
4+
category: signal_marketplace
5+
summary: "Signal agent that resells third-party data provider signals with verifiable catalog provenance."
6+
7+
narrative: |
8+
You operate a signal marketplace — an intermediary that aggregates audience data from
9+
multiple third-party providers and makes it available to buyers through a single interface.
10+
Think LiveRamp Data Marketplace, Oracle Data Cloud, or Lotame.
11+
12+
Your agent searches across catalogs published by data providers in their adagents.json
13+
files. Buyers discover signals through natural language queries, verify provenance by
14+
checking the data provider's catalog directly, and activate signals on DSPs or sales
15+
agents for campaign targeting.
16+
17+
The key property of marketplace signals: provenance is independently verifiable. Each
18+
signal traces back to a data_provider_domain whose adagents.json lists your agent as
19+
authorized. Buyers can (and should) verify this before spending.
20+
21+
This storyboard walks through discovery, verification, and both activation patterns —
22+
activating directly on a DSP (buyer manages targeting) and activating on a sales agent
23+
(SA handles downstream coordination).
24+
25+
agent:
26+
interaction_model: marketplace_catalog
27+
capabilities:
28+
- catalog_signals
29+
examples:
30+
- "LiveRamp Data Marketplace"
31+
- "Oracle Data Cloud"
32+
- "Lotame"
33+
34+
caller:
35+
role: buyer_agent
36+
example: "Pinnacle Agency (buyer)"
37+
38+
prerequisites:
39+
description: |
40+
The buyer has a campaign brief with targeting objectives. The test kit provides
41+
sample signal definitions, pricing options, and destination configurations that
42+
match the training agent's signal providers.
43+
test_kit: "test-kits/nova-motors.yaml"
44+
45+
phases:
46+
- id: discovery
47+
title: "Signal discovery"
48+
narrative: |
49+
The buyer describes what they need in natural language. Your agent searches
50+
across all authorized data provider catalogs and returns matching signals with
51+
pricing, coverage estimates, and value types.
52+
53+
This is where marketplace agents earn their keep — the buyer doesn't need to
54+
know which providers exist or what taxonomies they use. One query, many sources.
55+
56+
steps:
57+
- id: search_by_spec
58+
title: "Discover signals from a campaign brief"
59+
narrative: |
60+
The buyer's platform translates a campaign brief into a get_signals call.
61+
Your agent searches catalogs from every authorized data provider and returns
62+
what matches — automotive intent from one provider, geo data from another,
63+
retail purchase history from a third.
64+
task: get_signals
65+
schema_ref: "signals/get-signals-request.json"
66+
response_schema_ref: "signals/get-signals-response.json"
67+
doc_ref: "/signals/tasks/get_signals"
68+
stateful: false
69+
expected: |
70+
Return matching signals from multiple data providers. Each signal must include:
71+
- signal_agent_segment_id for activation
72+
- signal_id with source, data_provider_domain, and id
73+
- name, description, and value_type (binary, categorical, or numeric)
74+
- coverage_percentage (audience reach estimate)
75+
- pricing_options with at least one pricing model
76+
- signal_type: "marketplace"
77+
78+
sample_request:
79+
signal_spec: "In-market EV buyers with high purchase propensity, near auto dealerships"
80+
81+
validations:
82+
- check: response_schema
83+
description: "Response matches get-signals-response.json schema"
84+
- check: field_present
85+
path: "signals[0].signal_agent_segment_id"
86+
description: "Each signal has a signal_agent_segment_id"
87+
- check: field_present
88+
path: "signals[0].signal_id.data_provider_domain"
89+
description: "Each signal traces to a data provider domain"
90+
- check: field_present
91+
path: "signals[0].pricing_options"
92+
description: "Each signal has pricing options"
93+
- check: field_present
94+
path: "signals[0].coverage_percentage"
95+
description: "Each signal has a coverage estimate"
96+
97+
- id: search_by_ids
98+
title: "Look up specific signals by ID"
99+
narrative: |
100+
The buyer already knows which signals they want — maybe from a previous
101+
campaign or a provider's documentation. They pass signal_ids directly
102+
instead of a natural language query.
103+
task: get_signals
104+
schema_ref: "signals/get-signals-request.json"
105+
response_schema_ref: "signals/get-signals-response.json"
106+
doc_ref: "/signals/tasks/get_signals"
107+
stateful: false
108+
expected: |
109+
Return the exact signals requested, with full metadata and pricing.
110+
If a signal_id doesn't exist, omit it from results — don't error.
111+
112+
sample_request:
113+
signal_ids:
114+
- source: "catalog"
115+
data_provider_domain: "tridentauto.example"
116+
id: "likely_ev_buyers"
117+
- source: "catalog"
118+
data_provider_domain: "meridiangeo.example"
119+
id: "competitor_visitors"
120+
121+
validations:
122+
- check: response_schema
123+
description: "Response matches schema"
124+
- check: field_present
125+
path: "signals"
126+
description: "Response contains a signals array"
127+
128+
- id: verification
129+
title: "Catalog verification"
130+
narrative: |
131+
Before activating third-party data, the buyer verifies provenance. They fetch
132+
the data provider's adagents.json directly and confirm the signal exists and
133+
your agent is authorized. This independent check is outside the AdCP protocol —
134+
but your agent must return the metadata that makes it possible.
135+
136+
This phase tests that your get_signals responses include verifiable provenance
137+
data: signal_id.source is "catalog" and signal_id.data_provider_domain points
138+
to a real domain whose adagents.json the buyer can fetch independently.
139+
140+
steps:
141+
- id: verify_provenance_metadata
142+
title: "Confirm signals carry verifiable provenance"
143+
narrative: |
144+
The buyer looks up a specific signal by ID and checks that the response
145+
includes the metadata needed for independent verification — source is
146+
"catalog" and data_provider_domain points to a fetchable adagents.json.
147+
The actual HTTP fetch of adagents.json is the buyer's responsibility, but
148+
your agent must provide the domain to fetch from.
149+
task: get_signals
150+
schema_ref: "signals/get-signals-request.json"
151+
response_schema_ref: "signals/get-signals-response.json"
152+
doc_ref: "/signals/data-providers"
153+
stateful: false
154+
expected: |
155+
Return the requested signal with verifiable provenance metadata:
156+
- signal_id.source is "catalog"
157+
- signal_id.data_provider_domain matches a real domain
158+
The buyer will independently fetch that domain's adagents.json to confirm
159+
your agent is listed in authorized_agents.
160+
161+
sample_request:
162+
signal_ids:
163+
- source: "catalog"
164+
data_provider_domain: "shopgrid.example"
165+
id: "new_to_brand"
166+
167+
validations:
168+
- check: field_value
169+
path: "signals[0].signal_id.source"
170+
description: "Signal source is 'catalog' (verifiable via adagents.json)"
171+
- check: field_present
172+
path: "signals[0].signal_id.data_provider_domain"
173+
description: "Data provider domain is present for independent verification"
174+
175+
- id: platform_activation
176+
title: "Activate on a DSP"
177+
narrative: |
178+
The buyer activates a signal directly on a DSP platform. The signal agent pushes
179+
segment data to the platform, and the buyer gets back a segment_id they can
180+
reference when configuring campaign targeting.
181+
182+
Use platform destinations when the buyer is managing DSP campaigns directly —
183+
not through a sales agent.
184+
185+
steps:
186+
- id: activate_on_platform
187+
title: "Activate signal on a DSP"
188+
narrative: |
189+
The buyer selects a signal and a DSP destination. The signal agent pushes
190+
the segment to the platform. This is typically asynchronous — the initial
191+
response shows is_live: false with an estimated duration, and the buyer
192+
polls for completion.
193+
task: activate_signal
194+
schema_ref: "signals/activate-signal-request.json"
195+
response_schema_ref: "signals/activate-signal-response.json"
196+
doc_ref: "/signals/tasks/activate_signal"
197+
stateful: true
198+
expected: |
199+
Return a deployment with:
200+
- type: "platform" matching the requested destination
201+
- is_live: false initially (async activation)
202+
- estimated_activation_duration_minutes
203+
After polling, the deployment should show:
204+
- is_live: true
205+
- activation_key with type: "segment_id" and a platform-native segment ID
206+
- deployed_at timestamp
207+
208+
sample_request:
209+
signal_agent_segment_id: "trident_likely_ev_buyers"
210+
pricing_option_id: "po_trident_ev_cpm"
211+
destinations:
212+
- type: "platform"
213+
platform: "the-trade-desk"
214+
account: "agency-123-ttd"
215+
216+
validations:
217+
- check: response_schema
218+
description: "Response matches activate-signal-response.json schema"
219+
- check: field_present
220+
path: "deployments[0].type"
221+
description: "Deployment includes type"
222+
- check: field_value
223+
path: "deployments[0].type"
224+
description: "Deployment type is 'platform'"
225+
226+
- id: agent_activation
227+
title: "Activate on a sales agent"
228+
narrative: |
229+
The buyer activates a signal on a sales agent instead of a DSP. This is the
230+
right pattern when the buyer is purchasing media through the SA — the SA handles
231+
its own DSP coordination.
232+
233+
The buyer doesn't need to know which DSP the SA uses. The activation key confirms
234+
the signal is live on the SA, and the SA applies targeting when fulfilling media
235+
buys through create_media_buy.
236+
237+
steps:
238+
- id: activate_on_agent
239+
title: "Activate signal on a sales agent"
240+
narrative: |
241+
The buyer activates a signal with the sales agent's URL as the destination.
242+
Agent activations are typically synchronous — the SA records the activation
243+
immediately and returns a key_value activation key.
244+
task: activate_signal
245+
schema_ref: "signals/activate-signal-request.json"
246+
response_schema_ref: "signals/activate-signal-response.json"
247+
doc_ref: "/signals/tasks/activate_signal"
248+
stateful: true
249+
expected: |
250+
Return a deployment with:
251+
- type: "agent" matching the requested destination
252+
- agent_url matching the SA's URL
253+
- is_live: true (sync activation)
254+
- activation_key with type: "key_value"
255+
- deployed_at timestamp
256+
257+
The SA records the activation internally. When the buyer later calls
258+
create_media_buy through this SA, signal-based targeting is already
259+
in place.
260+
261+
sample_request:
262+
signal_agent_segment_id: "shopgrid_new_to_brand"
263+
pricing_option_id: "po_shopgrid_retail_cpm"
264+
destinations:
265+
- type: "agent"
266+
agent_url: "https://wonderstruck.salesagents.example"
267+
268+
validations:
269+
- check: response_schema
270+
description: "Response matches activate-signal-response.json schema"
271+
- check: field_present
272+
path: "deployments[0].activation_key"
273+
description: "Deployment includes activation key"
274+
- check: field_value
275+
path: "deployments[0].type"
276+
description: "Deployment type is 'agent'"

0 commit comments

Comments
 (0)