@adcp/client 4.23.0 → 4.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +23 -9
  2. package/bin/adcp.js +83 -18
  3. package/dist/lib/index.d.ts +2 -4
  4. package/dist/lib/index.d.ts.map +1 -1
  5. package/dist/lib/index.js +16 -12
  6. package/dist/lib/index.js.map +1 -1
  7. package/dist/lib/server/index.d.ts +5 -1
  8. package/dist/lib/server/index.d.ts.map +1 -1
  9. package/dist/lib/server/index.js +10 -1
  10. package/dist/lib/server/index.js.map +1 -1
  11. package/dist/lib/server/postgres-task-store.d.ts +105 -0
  12. package/dist/lib/server/postgres-task-store.d.ts.map +1 -0
  13. package/dist/lib/server/postgres-task-store.js +267 -0
  14. package/dist/lib/server/postgres-task-store.js.map +1 -0
  15. package/dist/lib/server/responses.d.ts +1 -0
  16. package/dist/lib/server/responses.d.ts.map +1 -1
  17. package/dist/lib/server/responses.js +1 -0
  18. package/dist/lib/server/responses.js.map +1 -1
  19. package/dist/lib/server/test-controller.d.ts +88 -0
  20. package/dist/lib/server/test-controller.d.ts.map +1 -0
  21. package/dist/lib/server/test-controller.js +227 -0
  22. package/dist/lib/server/test-controller.js.map +1 -0
  23. package/dist/lib/testing/agent-tester.d.ts +1 -1
  24. package/dist/lib/testing/agent-tester.d.ts.map +1 -1
  25. package/dist/lib/testing/agent-tester.js +13 -1
  26. package/dist/lib/testing/agent-tester.js.map +1 -1
  27. package/dist/lib/testing/compliance/comply.d.ts +2 -0
  28. package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
  29. package/dist/lib/testing/compliance/comply.js +76 -1
  30. package/dist/lib/testing/compliance/comply.js.map +1 -1
  31. package/dist/lib/testing/compliance/index.d.ts +1 -1
  32. package/dist/lib/testing/compliance/index.d.ts.map +1 -1
  33. package/dist/lib/testing/compliance/platform-storyboards.d.ts.map +1 -1
  34. package/dist/lib/testing/compliance/platform-storyboards.js +2 -0
  35. package/dist/lib/testing/compliance/platform-storyboards.js.map +1 -1
  36. package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
  37. package/dist/lib/testing/compliance/storyboard-tracks.js +11 -2
  38. package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
  39. package/dist/lib/testing/compliance/types.d.ts +22 -1
  40. package/dist/lib/testing/compliance/types.d.ts.map +1 -1
  41. package/dist/lib/testing/orchestrator.d.ts.map +1 -1
  42. package/dist/lib/testing/orchestrator.js +5 -1
  43. package/dist/lib/testing/orchestrator.js.map +1 -1
  44. package/dist/lib/testing/scenarios/brand-rights.d.ts +19 -1
  45. package/dist/lib/testing/scenarios/brand-rights.d.ts.map +1 -1
  46. package/dist/lib/testing/scenarios/brand-rights.js +138 -1
  47. package/dist/lib/testing/scenarios/brand-rights.js.map +1 -1
  48. package/dist/lib/testing/scenarios/deterministic.js +7 -7
  49. package/dist/lib/testing/scenarios/deterministic.js.map +1 -1
  50. package/dist/lib/testing/scenarios/index.d.ts +1 -1
  51. package/dist/lib/testing/scenarios/index.d.ts.map +1 -1
  52. package/dist/lib/testing/scenarios/index.js +4 -2
  53. package/dist/lib/testing/scenarios/index.js.map +1 -1
  54. package/dist/lib/testing/scenarios/media-buy.js +4 -4
  55. package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
  56. package/dist/lib/testing/storyboard/loader.d.ts +1 -0
  57. package/dist/lib/testing/storyboard/loader.d.ts.map +1 -1
  58. package/dist/lib/testing/storyboard/loader.js +14 -0
  59. package/dist/lib/testing/storyboard/loader.js.map +1 -1
  60. package/dist/lib/testing/storyboard/request-builder.d.ts.map +1 -1
  61. package/dist/lib/testing/storyboard/request-builder.js +88 -11
  62. package/dist/lib/testing/storyboard/request-builder.js.map +1 -1
  63. package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
  64. package/dist/lib/testing/storyboard/runner.js +83 -5
  65. package/dist/lib/testing/storyboard/runner.js.map +1 -1
  66. package/dist/lib/testing/storyboard/task-map.d.ts +2 -0
  67. package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
  68. package/dist/lib/testing/storyboard/task-map.js +23 -9
  69. package/dist/lib/testing/storyboard/task-map.js.map +1 -1
  70. package/dist/lib/testing/storyboard/types.d.ts +6 -2
  71. package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
  72. package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
  73. package/dist/lib/testing/storyboard/validations.js +21 -4
  74. package/dist/lib/testing/storyboard/validations.js.map +1 -1
  75. package/dist/lib/testing/types.d.ts +1 -1
  76. package/dist/lib/testing/types.d.ts.map +1 -1
  77. package/dist/lib/types/core.generated.d.ts +1 -1
  78. package/dist/lib/types/core.generated.d.ts.map +1 -1
  79. package/dist/lib/types/core.generated.js +1 -1
  80. package/dist/lib/types/schemas.generated.d.ts +16 -13
  81. package/dist/lib/types/schemas.generated.d.ts.map +1 -1
  82. package/dist/lib/types/schemas.generated.js +7 -4
  83. package/dist/lib/types/schemas.generated.js.map +1 -1
  84. package/dist/lib/types/tools.generated.d.ts +14 -5
  85. package/dist/lib/types/tools.generated.d.ts.map +1 -1
  86. package/dist/lib/utils/capabilities.d.ts +2 -2
  87. package/dist/lib/utils/capabilities.d.ts.map +1 -1
  88. package/dist/lib/utils/capabilities.js +9 -3
  89. package/dist/lib/utils/capabilities.js.map +1 -1
  90. package/dist/lib/utils/response-schemas.d.ts.map +1 -1
  91. package/dist/lib/utils/response-schemas.js +9 -0
  92. package/dist/lib/utils/response-schemas.js.map +1 -1
  93. package/docs/llms.txt +10 -2
  94. package/package.json +8 -2
  95. package/skills/adcp/SKILL.md +118 -33
  96. package/skills/build-creative-agent/SKILL.md +221 -0
  97. package/skills/build-generative-seller-agent/SKILL.md +288 -0
  98. package/skills/build-retail-media-agent/SKILL.md +237 -0
  99. package/skills/build-seller-agent/SKILL.md +313 -0
  100. package/skills/build-signals-agent/SKILL.md +203 -0
  101. package/storyboards/campaign_governance_conditions.yaml +2 -2
  102. package/storyboards/campaign_governance_delivery.yaml +1 -1
  103. package/storyboards/campaign_governance_denied.yaml +1 -0
  104. package/storyboards/creative_generative.yaml +317 -0
  105. package/storyboards/creative_sales_agent.yaml +2 -2
  106. package/storyboards/creative_template.yaml +2 -1
  107. package/storyboards/deterministic_testing.yaml +271 -245
  108. package/storyboards/media_buy_catalog_creative.yaml +2 -1
  109. package/storyboards/media_buy_generative_seller.yaml +581 -0
  110. package/storyboards/media_buy_governance_escalation.yaml +1 -1
  111. package/storyboards/media_buy_guaranteed_approval.yaml +14 -14
  112. package/storyboards/media_buy_non_guaranteed.yaml +2 -2
  113. package/storyboards/media_buy_proposal_mode.yaml +5 -5
  114. package/storyboards/media_buy_seller.yaml +10 -10
  115. package/storyboards/media_buy_state_machine.yaml +4 -4
  116. package/storyboards/schema.yaml +2 -1
  117. package/storyboards/signal_marketplace.yaml +3 -0
  118. package/storyboards/signal_owned.yaml +1 -0
  119. package/storyboards/test-kits/acme-outdoor.yaml +118 -0
  120. package/storyboards/test-kits/nova-motors.yaml +134 -0
@@ -260,7 +260,7 @@ phases:
260
260
  expected: |
261
261
  Create the media buy with catalog-driven packages. Return:
262
262
  - media_buy_id
263
- - status: active or confirmed
263
+ - status: active
264
264
  - packages with catalog references preserved
265
265
 
266
266
  sample_request:
@@ -428,6 +428,7 @@ phases:
428
428
  description: "Response matches provide-performance-feedback-response.json schema"
429
429
  - check: field_value
430
430
  path: "success"
431
+ value: true
431
432
  description: "Feedback accepted"
432
433
 
433
434
  - id: check_delivery
@@ -0,0 +1,581 @@
1
+ id: media_buy_generative_seller
2
+ version: "1.0.0"
3
+ title: "Generative seller agent"
4
+ category: media_buy_generative_seller
5
+ summary: "Seller agent that generates creatives from briefs at buy time — no pre-built assets required."
6
+ track: media_buy
7
+ required_tools:
8
+ - get_products
9
+ - create_media_buy
10
+ - list_creative_formats
11
+ - sync_creatives
12
+ - get_media_buy_delivery
13
+
14
+ narrative: |
15
+ You run a generative sell-side platform — an AI ad network, generative DSP, or any system that
16
+ both sells inventory and generates creatives from a brief. The buyer doesn't upload finished
17
+ assets. Instead, they describe what they want via a creative brief, point you at a brand.json,
18
+ and your platform produces finished creatives ready for delivery.
19
+
20
+ This is the media buy seller flow with generative creative capabilities. Your platform handles
21
+ the full lifecycle from brief to reporting, but the creative sync step accepts briefs instead
22
+ of static assets. Your formats declare what brief inputs they accept, and your platform
23
+ generates the creative — potentially asynchronously.
24
+
25
+ A programmatic seller with generative capabilities should also support standard IAB formats
26
+ (display, video, VAST, etc.) for buyers who bring their own assets. A platform that sells
27
+ programmatic inventory but can't accept a pre-built VAST tag or display banner is incoherent.
28
+ The generative capability is additive to standard programmatic creative acceptance.
29
+
30
+ This storyboard focuses on the generative creative delta. Governance agent registration and
31
+ proposal refinement work identically to the base media_buy_seller storyboard — see that
32
+ storyboard for those phases.
33
+
34
+ agent:
35
+ interaction_model: media_buy_seller
36
+ capabilities:
37
+ - sells_media
38
+ - accepts_briefs
39
+ - supports_generation
40
+ - supports_guaranteed
41
+ - supports_non_guaranteed
42
+ examples:
43
+ - "OpenAds"
44
+ - "AI ad networks"
45
+ - "Generative DSPs"
46
+
47
+ caller:
48
+ role: buyer_agent
49
+ example: "Scope3 (DSP)"
50
+
51
+ prerequisites:
52
+ description: |
53
+ The caller needs a brand identity hosted at the brand's domain (brand.json) or resolvable
54
+ via AgenticAdvertising.org. The brand.json provides visual identity — logos, colors, fonts,
55
+ tone — that the generative seller uses to produce on-brand creatives. The test kit provides
56
+ a sample brand with campaign parameters.
57
+ test_kit: "test-kits/acme-outdoor.yaml"
58
+
59
+ phases:
60
+ - id: account_setup
61
+ title: "Account setup"
62
+ narrative: |
63
+ Before buying anything, the buyer establishes an account relationship with your platform.
64
+ This is the same handshake as any media buy seller — the buyer identifies the brand and
65
+ operator, and you provision the account.
66
+
67
+ steps:
68
+ - id: sync_accounts
69
+ title: "Establish account relationship"
70
+ narrative: |
71
+ The buyer registers their brand and operator with your platform. Your platform should
72
+ resolve the brand via AgenticAdvertising.org to pull brand identity for creative
73
+ generation. If the brand domain doesn't resolve, return an error — don't generate
74
+ creatives for unknown brands.
75
+ task: sync_accounts
76
+ schema_ref: "account/sync-accounts-request.json"
77
+ response_schema_ref: "account/sync-accounts-response.json"
78
+ doc_ref: "/accounts/tasks/sync_accounts"
79
+ stateful: true
80
+ expected: |
81
+ Return the account with:
82
+ - account_id: your platform's identifier for this relationship
83
+ - action: created or updated
84
+ - status: active (instant approval) or pending_approval (requires human review)
85
+ - account_scope: operator, brand, operator_brand, or agent
86
+ - setup: URL and message if pending_approval
87
+
88
+ sample_request:
89
+ accounts:
90
+ - brand:
91
+ domain: "acmeoutdoor.com"
92
+ operator: "pinnacle-agency.com"
93
+ billing: "operator"
94
+ payment_terms: "net_30"
95
+
96
+ validations:
97
+ - check: response_schema
98
+ description: "Response matches sync-accounts-response.json schema"
99
+ - check: field_present
100
+ path: "accounts[0].account_id"
101
+ description: "Account has a platform-assigned ID"
102
+ - check: field_present
103
+ path: "accounts[0].status"
104
+ description: "Account has a status (active or pending_approval)"
105
+
106
+ - id: format_discovery
107
+ title: "Format discovery"
108
+ narrative: |
109
+ The buyer discovers what your platform can accept. A generative seller's format catalog
110
+ has two parts: generative formats that accept briefs as inputs, and standard IAB formats
111
+ that accept pre-built assets.
112
+
113
+ Generative formats declare brief asset slots. Standard formats declare image, video, or
114
+ HTML asset slots. The buyer uses this to decide whether to send a brief or upload finished
115
+ assets for each package.
116
+
117
+ A platform selling programmatic inventory should support both. Generative-only is fine for
118
+ a pure creative agent, but a seller that takes media buys needs to accept pre-built assets
119
+ from buyers who already have creatives.
120
+
121
+ steps:
122
+ - id: list_formats
123
+ title: "Discover creative formats"
124
+ narrative: |
125
+ The buyer asks what formats your platform supports. Your response should include both
126
+ generative formats (accepting brief inputs) and standard IAB formats (accepting image,
127
+ video, VAST, etc.).
128
+
129
+ A programmatic seller that only returns generative formats means the buyer can never
130
+ bring their own assets. If you sell inventory, you should accept pre-built creatives
131
+ too — the generative capability is additive.
132
+ task: list_creative_formats
133
+ schema_ref: "creative/list-creative-formats-request.json"
134
+ response_schema_ref: "creative/list-creative-formats-response.json"
135
+ doc_ref: "/creative/task-reference/list_creative_formats"
136
+ comply_scenario: creative_lifecycle
137
+ stateful: false
138
+ expected: |
139
+ Return your format catalog. It should include:
140
+
141
+ Generative formats:
142
+ - format_id with your agent_url and a unique id (e.g., "display_300x250_generative")
143
+ - Asset slots accepting brief asset types
144
+ - Render dimensions for the output
145
+ - Description indicating this is a generative format
146
+
147
+ Standard formats:
148
+ - Standard IAB display formats (300x250, 728x90, etc.)
149
+ - Video formats (VAST, pre-roll, etc.) if applicable
150
+ - Asset slots accepting image, video, html, etc.
151
+
152
+ Both types should be present for a programmatic seller. Buyers who already
153
+ have creatives need to be able to upload them directly.
154
+
155
+ sample_request: {}
156
+
157
+ validations:
158
+ - check: response_schema
159
+ description: "Response matches list-creative-formats-response.json schema"
160
+ - check: field_present
161
+ path: "formats"
162
+ description: "Response contains a formats array"
163
+ - check: field_present
164
+ path: "formats[0].format_id.agent_url"
165
+ description: "Each format has a format_id with agent_url"
166
+
167
+ - id: product_discovery
168
+ title: "Product discovery"
169
+ narrative: |
170
+ The buyer sends a natural-language brief describing what they want to buy. Your platform
171
+ interprets the brief against your inventory and returns products with pricing, delivery
172
+ forecasts, and creative format requirements.
173
+
174
+ Products from a generative seller should reference generative format IDs — telling the
175
+ buyer that this product accepts a brief rather than requiring pre-built assets. Products
176
+ may also reference standard formats for buyers who prefer to supply their own creatives.
177
+
178
+ steps:
179
+ - id: get_products_brief
180
+ title: "Send a brief"
181
+ narrative: |
182
+ The buyer describes what they want. Your platform returns products. Each product's
183
+ creative_format_ids should reference formats from the catalog — some generative,
184
+ some standard. The buyer uses this to decide the creative approach per product.
185
+ task: get_products
186
+ schema_ref: "media-buy/get-products-request.json"
187
+ response_schema_ref: "media-buy/get-products-response.json"
188
+ doc_ref: "/media-buy/task-reference/get_products"
189
+ comply_scenario: full_sales_flow
190
+ stateful: false
191
+ expected: |
192
+ Return products matching the brief. Each product should include:
193
+ - product_id: unique identifier
194
+ - name and description
195
+ - delivery_type: guaranteed or non_guaranteed
196
+ - pricing_models: available pricing options
197
+ - forecast: estimated impressions, reach
198
+ - creative_format_ids: formats this product accepts (including generative formats)
199
+
200
+ sample_request:
201
+ buying_mode: "brief"
202
+ brief: "Premium display and video inventory on outdoor lifestyle content. Q2 flight, $50K budget. Adults 25-54, US. We want your platform to generate the creatives from our brand brief."
203
+ brand:
204
+ domain: "acmeoutdoor.com"
205
+ account:
206
+ brand:
207
+ domain: "acmeoutdoor.com"
208
+ operator: "pinnacle-agency.com"
209
+
210
+ validations:
211
+ - check: response_schema
212
+ description: "Response matches get-products-response.json schema"
213
+ - check: field_present
214
+ path: "products"
215
+ description: "Response contains a products array"
216
+ - check: field_present
217
+ path: "products[0].product_id"
218
+ description: "Each product has a product_id"
219
+
220
+ - id: create_buy
221
+ title: "Create the media buy"
222
+ narrative: |
223
+ The buyer commits to specific products with budgets and flight dates. This is the same
224
+ create_media_buy flow as any seller — the generative aspect doesn't change the buy
225
+ creation. The buy may return pending_creatives status, indicating the buyer needs to
226
+ sync creatives (via brief) before the campaign can go live.
227
+
228
+ steps:
229
+ - id: create_media_buy
230
+ title: "Create a media buy"
231
+ narrative: |
232
+ The buyer commits to products. The response may include pending_creatives status
233
+ for packages that require creative sync before activation.
234
+ task: create_media_buy
235
+ schema_ref: "media-buy/create-media-buy-request.json"
236
+ response_schema_ref: "media-buy/create-media-buy-response.json"
237
+ doc_ref: "/media-buy/task-reference/create_media_buy"
238
+ comply_scenario: create_media_buy
239
+ stateful: true
240
+ expected: |
241
+ Process the media buy request and return one of:
242
+ - completed: buy confirmed, packages may be pending_creatives
243
+ - working: platform is processing (poll for status)
244
+ - submitted: long-running async (approval workflow, IO signing)
245
+ - input-required: need more information
246
+
247
+ sample_request:
248
+ account:
249
+ brand:
250
+ domain: "acmeoutdoor.com"
251
+ operator: "pinnacle-agency.com"
252
+ brand:
253
+ domain: "acmeoutdoor.com"
254
+ start_time: "2026-04-01T00:00:00Z"
255
+ end_time: "2026-06-30T23:59:59Z"
256
+ packages:
257
+ - product_id: "outdoor_display_q2"
258
+ budget: 30000
259
+ pricing_option_id: "cpm_guaranteed"
260
+ - product_id: "outdoor_video_q2"
261
+ budget: 20000
262
+ pricing_option_id: "cpm_standard"
263
+ push_notification_config:
264
+ url: "https://buyer.example/webhooks/adcp"
265
+ authentication:
266
+ scheme: "HMAC-SHA256"
267
+
268
+ validations:
269
+ - check: response_schema
270
+ description: "Response matches create-media-buy-response.json schema"
271
+
272
+ - id: creative_brief_sync
273
+ title: "Creative brief sync"
274
+ narrative: |
275
+ This is where a generative seller diverges from the standard flow. Instead of the buyer
276
+ uploading finished assets, they send a creative brief through sync_creatives. The brief
277
+ describes campaign messaging, tone, audience, and compliance requirements. The format_id
278
+ points to a generative format that accepts brief asset types.
279
+
280
+ Your platform resolves the brand via AgenticAdvertising.org (using the account's brand
281
+ domain), pulls brand.json for visual identity, and generates the creative. This may be
282
+ synchronous (accepted immediately) or asynchronous (pending_review while generation
283
+ completes). The buyer polls or waits for a webhook to know when the creative is ready.
284
+
285
+ steps:
286
+ - id: sync_creatives_brief
287
+ title: "Send creative brief via sync_creatives"
288
+ narrative: |
289
+ The buyer sends a creative brief through sync_creatives, using a generative format_id
290
+ that accepts brief asset types. The account's brand domain tells your platform where
291
+ to resolve brand identity for generation.
292
+
293
+ Your platform should actually read the operator and brand from the request — not
294
+ ignore them. If the brand domain is invalid or unresolvable, return a rejection
295
+ with a clear error rather than generating with defaults.
296
+ task: sync_creatives
297
+ schema_ref: "creative/sync-creatives-request.json"
298
+ response_schema_ref: "creative/sync-creatives-response.json"
299
+ doc_ref: "/creative/task-reference/sync_creatives"
300
+ comply_scenario: creative_sync
301
+ stateful: true
302
+ expected: |
303
+ Accept the creative brief and begin generation:
304
+ - Per-creative action: created
305
+ - Per-creative status: pending_review (generation in progress) or accepted (instant)
306
+ - The brand domain from the account should be resolved via AgenticAdvertising.org
307
+ - If the brand domain is invalid, reject with a clear error
308
+ - If the brief references a non-generative format, reject with format mismatch error
309
+
310
+ sample_request:
311
+ account:
312
+ brand:
313
+ domain: "acmeoutdoor.com"
314
+ operator: "pinnacle-agency.com"
315
+ creatives:
316
+ - creative_id: "gen_display_summer_sale"
317
+ name: "Summer Sale - Generated Display 300x250"
318
+ format_id:
319
+ agent_url: "https://your-platform.example.com"
320
+ id: "display_300x250_generative"
321
+ assets:
322
+ brief:
323
+ name: "Acme Outdoor Summer Sale Q2"
324
+ objective: "conversion"
325
+ tone: "Bold, adventurous, urgent"
326
+ audience: "Active adults 25-54, outdoor enthusiasts"
327
+ territory: "Summer gear clearance"
328
+ messaging:
329
+ headline: "Summer Sale — 40% Off All Gear"
330
+ tagline: "Gear up for less"
331
+ cta: "Shop Now"
332
+ key_messages:
333
+ - "40% off all trail running and hiking gear"
334
+ - "Free shipping on orders over $75"
335
+ compliance:
336
+ required_disclosures:
337
+ - text: "Sale ends June 30, 2026. Exclusions apply."
338
+ position: "footer"
339
+ jurisdictions: ["US"]
340
+ prohibited_claims:
341
+ - "Best price guaranteed"
342
+ - creative_id: "gen_video_summer_sale"
343
+ name: "Summer Sale - Generated Video 30s"
344
+ format_id:
345
+ agent_url: "https://your-platform.example.com"
346
+ id: "video_30s_generative"
347
+ assets:
348
+ brief:
349
+ name: "Acme Outdoor Summer Sale Q2 - Video"
350
+ objective: "awareness"
351
+ tone: "Cinematic, energetic"
352
+ audience: "Active adults 25-54"
353
+ territory: "Summer adventure"
354
+ messaging:
355
+ headline: "Your Next Adventure Starts Here"
356
+ tagline: "Acme Outdoor — Gear up for less"
357
+ cta: "Shop the Sale"
358
+ key_messages:
359
+ - "40% off all gear this summer"
360
+ compliance:
361
+ required_disclosures:
362
+ - text: "Sale ends June 30, 2026. Exclusions apply."
363
+ position: "audio"
364
+ jurisdictions: ["US"]
365
+ assignments:
366
+ - creative_id: "gen_display_summer_sale"
367
+ package_id: "outdoor_display_q2"
368
+ - creative_id: "gen_video_summer_sale"
369
+ package_id: "outdoor_video_q2"
370
+
371
+ validations:
372
+ - check: response_schema
373
+ description: "Response matches sync-creatives-response.json schema"
374
+ - check: field_present
375
+ path: "creatives[0].action"
376
+ description: "Each creative has an action (created/updated)"
377
+ - check: field_present
378
+ path: "creatives[0].status"
379
+ description: "Each creative has a status (pending_review or accepted)"
380
+
381
+ - id: sync_creatives_standard
382
+ title: "Sync standard creatives alongside generative"
383
+ narrative: |
384
+ The buyer sends a pre-built creative using a standard format — verifying that this
385
+ programmatic seller also accepts traditional asset uploads alongside generative briefs.
386
+ task: sync_creatives
387
+ schema_ref: "creative/sync-creatives-request.json"
388
+ response_schema_ref: "creative/sync-creatives-response.json"
389
+ doc_ref: "/creative/task-reference/sync_creatives"
390
+ comply_scenario: creative_sync
391
+ stateful: true
392
+ expected: |
393
+ Accept the standard creative:
394
+ - Per-creative action: created
395
+ - Per-creative status: accepted or pending_review
396
+ - Standard asset validation (dimensions, file size, mime type)
397
+
398
+ sample_request:
399
+ account:
400
+ brand:
401
+ domain: "acmeoutdoor.com"
402
+ operator: "pinnacle-agency.com"
403
+ creatives:
404
+ - creative_id: "static_display_300x250"
405
+ name: "Trail Pro 3000 - Pre-built Display 300x250"
406
+ format_id:
407
+ agent_url: "https://your-platform.example.com"
408
+ id: "display_300x250"
409
+ assets:
410
+ image:
411
+ url: "https://cdn.pinnacle-agency.example/trail-pro-300x250.png"
412
+ format: "png"
413
+ width: 300
414
+ height: 250
415
+
416
+ validations:
417
+ - check: response_schema
418
+ description: "Response matches sync-creatives-response.json schema"
419
+ - check: field_present
420
+ path: "creatives[0].action"
421
+ description: "Standard creative has an action"
422
+
423
+ - id: sync_creatives_invalid_brand
424
+ title: "Reject brief with invalid brand"
425
+ narrative: |
426
+ The buyer sends a brief referencing a brand domain that doesn't exist in
427
+ AgenticAdvertising.org. The seller should reject the creative rather than
428
+ generating with unknown brand identity.
429
+ task: sync_creatives
430
+ schema_ref: "creative/sync-creatives-request.json"
431
+ response_schema_ref: "creative/sync-creatives-response.json"
432
+ doc_ref: "/creative/task-reference/sync_creatives"
433
+ comply_scenario: creative_sync
434
+ stateful: true
435
+ expected: |
436
+ Reject the creative with a clear error:
437
+ - Per-creative status: rejected
438
+ - Validation error indicating the brand domain could not be resolved
439
+ - The seller should not generate creatives for unknown brands
440
+
441
+ sample_request:
442
+ account:
443
+ brand:
444
+ domain: "nonexistent-brand-xyz.example"
445
+ operator: "pinnacle-agency.com"
446
+ creatives:
447
+ - creative_id: "gen_invalid_brand"
448
+ name: "Invalid Brand Test"
449
+ format_id:
450
+ agent_url: "https://your-platform.example.com"
451
+ id: "display_300x250_generative"
452
+ assets:
453
+ brief:
454
+ name: "Test brief"
455
+ objective: "awareness"
456
+ tone: "Neutral"
457
+ messaging:
458
+ headline: "Test"
459
+ cta: "Click"
460
+
461
+ validations:
462
+ - check: response_schema
463
+ description: "Response matches sync-creatives-response.json schema"
464
+ - check: field_present
465
+ path: "creatives[0].status"
466
+ description: "Creative has a status (expected: rejected)"
467
+
468
+ - id: creative_generation_lifecycle
469
+ title: "Creative generation lifecycle"
470
+ narrative: |
471
+ If creative generation is asynchronous, the buyer monitors progress. The seller
472
+ transitions creatives through processing → pending_review → approved as generation
473
+ completes. The comply_test_controller can force these transitions for deterministic
474
+ testing via force_creative_status.
475
+
476
+ The buyer polls by re-calling sync_creatives with the same creatives. Because
477
+ sync_creatives has upsert semantics, re-sending the same brief is idempotent — the
478
+ seller returns the current status without restarting generation.
479
+
480
+ steps:
481
+ - id: check_creative_status
482
+ title: "Poll creative generation status"
483
+ narrative: |
484
+ The buyer re-sends the same creatives via sync_creatives. Because sync has upsert
485
+ semantics, this is idempotent — the seller returns updated statuses without
486
+ restarting generation. If the creative was pending_review after the initial sync,
487
+ it should transition to approved once generation completes. In testing, the
488
+ comply_test_controller forces this transition via force_creative_status.
489
+ task: sync_creatives
490
+ schema_ref: "creative/sync-creatives-request.json"
491
+ response_schema_ref: "creative/sync-creatives-response.json"
492
+ doc_ref: "/creative/task-reference/sync_creatives"
493
+ comply_scenario: creative_sync
494
+ stateful: true
495
+ expected: |
496
+ Return updated creative statuses:
497
+ - Generative creatives should transition from pending_review to approved
498
+ - Approved creatives should include generated assets (image URLs, VAST tags, etc.)
499
+ - The generated output should reflect the brand identity from the original brief
500
+
501
+ sample_request:
502
+ account:
503
+ brand:
504
+ domain: "acmeoutdoor.com"
505
+ operator: "pinnacle-agency.com"
506
+ creatives:
507
+ - creative_id: "gen_display_summer_sale"
508
+ name: "Summer Sale - Generated Display 300x250"
509
+ format_id:
510
+ agent_url: "https://your-platform.example.com"
511
+ id: "display_300x250_generative"
512
+ assets:
513
+ brief:
514
+ name: "Acme Outdoor Summer Sale Q2"
515
+ objective: "conversion"
516
+ tone: "Bold, adventurous, urgent"
517
+ messaging:
518
+ headline: "Summer Sale — 40% Off All Gear"
519
+ cta: "Shop Now"
520
+ - creative_id: "gen_video_summer_sale"
521
+ name: "Summer Sale - Generated Video 30s"
522
+ format_id:
523
+ agent_url: "https://your-platform.example.com"
524
+ id: "video_30s_generative"
525
+ assets:
526
+ brief:
527
+ name: "Acme Outdoor Summer Sale Q2 - Video"
528
+ objective: "awareness"
529
+ tone: "Cinematic, energetic"
530
+ messaging:
531
+ headline: "Your Next Adventure Starts Here"
532
+ cta: "Shop the Sale"
533
+
534
+ validations:
535
+ - check: response_schema
536
+ description: "Response matches sync-creatives-response.json schema"
537
+ - check: field_present
538
+ path: "creatives[0].status"
539
+ description: "Each creative has a current status"
540
+
541
+ - id: delivery_monitoring
542
+ title: "Delivery and reporting"
543
+ narrative: |
544
+ The campaign is live. Delivery monitoring is identical to any media buy seller —
545
+ the generative aspect doesn't change how delivery is reported. The buyer monitors
546
+ impressions, clicks, spend, and pacing.
547
+
548
+ steps:
549
+ - id: get_delivery
550
+ title: "Check delivery metrics"
551
+ narrative: |
552
+ The buyer requests delivery data for the active media buy. Reporting is the same
553
+ regardless of whether creatives were generated or uploaded.
554
+ task: get_media_buy_delivery
555
+ schema_ref: "media-buy/get-media-buy-delivery-request.json"
556
+ response_schema_ref: "media-buy/get-media-buy-delivery-response.json"
557
+ doc_ref: "/media-buy/task-reference/get_media_buy_delivery"
558
+ comply_scenario: reporting_flow
559
+ stateful: true
560
+ expected: |
561
+ Return delivery metrics for the media buy:
562
+ - Per-package delivery: impressions, clicks, spend, completion rates
563
+ - Daily breakdown if requested
564
+ - Pacing information
565
+ - Budget utilization
566
+
567
+ sample_request:
568
+ account:
569
+ brand:
570
+ domain: "acmeoutdoor.com"
571
+ operator: "pinnacle-agency.com"
572
+ media_buy_ids:
573
+ - "mb_acme_q2_2026"
574
+ include_package_daily_breakdown: true
575
+
576
+ validations:
577
+ - check: response_schema
578
+ description: "Response matches get-media-buy-delivery-response.json schema"
579
+ - check: field_present
580
+ path: "media_buy_deliveries"
581
+ description: "Response contains media buy delivery data"
@@ -350,7 +350,7 @@ phases:
350
350
  expected: |
351
351
  Confirm the media buy with governance approval:
352
352
  - media_buy_id: your platform's identifier
353
- - status: confirmed or active
353
+ - status: active
354
354
  - confirmed_at: timestamp
355
355
  - governance_context: echoed back confirming governance was validated
356
356
  - packages: confirmed line items