@adcp/sdk 14.0.0 → 14.2.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 (234) hide show
  1. package/dist/lib/adapters/implicit-account-store.d.mts +12 -7
  2. package/dist/lib/adapters/implicit-account-store.d.ts +12 -7
  3. package/dist/lib/adapters/implicit-account-store.js +69 -15
  4. package/dist/lib/adapters/implicit-account-store.mjs +69 -15
  5. package/dist/lib/core/AgentClient.d.mts +1 -0
  6. package/dist/lib/core/AgentClient.d.ts +1 -0
  7. package/dist/lib/core/AgentClient.js +3 -0
  8. package/dist/lib/core/AgentClient.mjs +3 -0
  9. package/dist/lib/core/SingleAgentClient.d.mts +35 -2
  10. package/dist/lib/core/SingleAgentClient.d.ts +35 -2
  11. package/dist/lib/core/SingleAgentClient.js +377 -36
  12. package/dist/lib/core/SingleAgentClient.mjs +387 -38
  13. package/dist/lib/core/TaskExecutor.d.mts +3 -1
  14. package/dist/lib/core/TaskExecutor.d.ts +3 -1
  15. package/dist/lib/core/TaskExecutor.js +17 -12
  16. package/dist/lib/core/TaskExecutor.mjs +17 -12
  17. package/dist/lib/core/account-key.d.mts +3 -0
  18. package/dist/lib/core/account-key.d.ts +3 -0
  19. package/dist/lib/core/account-key.js +41 -0
  20. package/dist/lib/core/account-key.mjs +17 -0
  21. package/dist/lib/core/account-resolution.d.mts +2 -0
  22. package/dist/lib/core/account-resolution.d.ts +2 -0
  23. package/dist/lib/core/buyer-account-registry.d.mts +93 -0
  24. package/dist/lib/core/buyer-account-registry.d.ts +93 -0
  25. package/dist/lib/core/buyer-account-registry.js +602 -0
  26. package/dist/lib/core/buyer-account-registry.mjs +578 -0
  27. package/dist/lib/core/product-cache.d.mts +18 -0
  28. package/dist/lib/core/product-cache.d.ts +18 -0
  29. package/dist/lib/core/product-cache.js +137 -0
  30. package/dist/lib/core/product-cache.mjs +112 -0
  31. package/dist/lib/errors/index.d.mts +40 -1
  32. package/dist/lib/errors/index.d.ts +40 -1
  33. package/dist/lib/errors/index.js +69 -3
  34. package/dist/lib/errors/index.mjs +64 -3
  35. package/dist/lib/governance/authorization.d.mts +17 -1
  36. package/dist/lib/governance/authorization.d.ts +17 -1
  37. package/dist/lib/governance/authorization.js +55 -7
  38. package/dist/lib/governance/authorization.mjs +59 -7
  39. package/dist/lib/governance/index.d.mts +2 -2
  40. package/dist/lib/governance/index.d.ts +2 -2
  41. package/dist/lib/governance/index.js +2 -0
  42. package/dist/lib/governance/index.mjs +3 -1
  43. package/dist/lib/index.d.mts +7 -4
  44. package/dist/lib/index.d.ts +7 -4
  45. package/dist/lib/index.js +29 -0
  46. package/dist/lib/index.mjs +31 -1
  47. package/dist/lib/net/agent-transport-fetch.d.mts +4 -0
  48. package/dist/lib/net/agent-transport-fetch.d.ts +4 -0
  49. package/dist/lib/net/agent-transport-fetch.js +18 -5
  50. package/dist/lib/net/agent-transport-fetch.mjs +17 -5
  51. package/dist/lib/protocols/a2a.js +9 -1
  52. package/dist/lib/protocols/a2a.mjs +9 -1
  53. package/dist/lib/protocols/index.js +9 -2
  54. package/dist/lib/protocols/index.mjs +9 -2
  55. package/dist/lib/protocols/mcp-modern.js +2 -1
  56. package/dist/lib/protocols/mcp-modern.mjs +2 -1
  57. package/dist/lib/protocols/mcp.js +5 -2
  58. package/dist/lib/protocols/mcp.mjs +5 -2
  59. package/dist/lib/protocols/rawResponseCapture.d.mts +6 -0
  60. package/dist/lib/protocols/rawResponseCapture.d.ts +6 -0
  61. package/dist/lib/protocols/rawResponseCapture.js +41 -29
  62. package/dist/lib/protocols/rawResponseCapture.mjs +40 -29
  63. package/dist/lib/protocols/signedRequestRejection.d.mts +9 -0
  64. package/dist/lib/protocols/signedRequestRejection.d.ts +9 -0
  65. package/dist/lib/protocols/signedRequestRejection.js +209 -0
  66. package/dist/lib/protocols/signedRequestRejection.mjs +189 -0
  67. package/dist/lib/protocols/transportDiagnostics.d.mts +1 -0
  68. package/dist/lib/protocols/transportDiagnostics.d.ts +1 -0
  69. package/dist/lib/protocols/transportDiagnostics.js +2 -0
  70. package/dist/lib/protocols/transportDiagnostics.mjs +1 -0
  71. package/dist/lib/registry/types.generated.d.mts +112 -45
  72. package/dist/lib/registry/types.generated.d.ts +112 -45
  73. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  74. package/dist/lib/server/account-provisioning.d.mts +2 -0
  75. package/dist/lib/server/account-provisioning.d.ts +2 -0
  76. package/dist/lib/server/account-provisioning.js +30 -0
  77. package/dist/lib/server/account-provisioning.mjs +6 -0
  78. package/dist/lib/server/account-reference-warnings.d.mts +12 -0
  79. package/dist/lib/server/account-reference-warnings.d.ts +12 -0
  80. package/dist/lib/server/account-reference-warnings.js +48 -0
  81. package/dist/lib/server/account-reference-warnings.mjs +23 -0
  82. package/dist/lib/server/auth-signature.js +1 -0
  83. package/dist/lib/server/auth-signature.mjs +1 -0
  84. package/dist/lib/server/create-adcp-server.d.mts +34 -0
  85. package/dist/lib/server/create-adcp-server.d.ts +34 -0
  86. package/dist/lib/server/create-adcp-server.js +225 -14
  87. package/dist/lib/server/create-adcp-server.mjs +225 -14
  88. package/dist/lib/server/decisioning/account.d.mts +2 -0
  89. package/dist/lib/server/decisioning/account.d.ts +2 -0
  90. package/dist/lib/server/decisioning/runtime/from-platform.js +49 -10
  91. package/dist/lib/server/decisioning/runtime/from-platform.mjs +49 -10
  92. package/dist/lib/server/index.d.mts +2 -2
  93. package/dist/lib/server/index.d.ts +2 -2
  94. package/dist/lib/server/index.js +2 -0
  95. package/dist/lib/server/index.mjs +3 -1
  96. package/dist/lib/signing/agent-resolver/consistency.d.mts +6 -13
  97. package/dist/lib/signing/agent-resolver/consistency.d.ts +6 -13
  98. package/dist/lib/signing/agent-resolver/consistency.js +0 -1
  99. package/dist/lib/signing/agent-resolver/consistency.mjs +0 -1
  100. package/dist/lib/signing/agent-resolver/errors.d.mts +3 -1
  101. package/dist/lib/signing/agent-resolver/errors.d.ts +3 -1
  102. package/dist/lib/signing/agent-resolver/errors.js +6 -0
  103. package/dist/lib/signing/agent-resolver/errors.mjs +6 -0
  104. package/dist/lib/signing/agent-resolver/fetch-helpers.d.mts +11 -0
  105. package/dist/lib/signing/agent-resolver/fetch-helpers.d.ts +11 -0
  106. package/dist/lib/signing/agent-resolver/fetch-helpers.js +37 -2
  107. package/dist/lib/signing/agent-resolver/fetch-helpers.mjs +40 -3
  108. package/dist/lib/signing/agent-resolver/jwks-set.js +24 -3
  109. package/dist/lib/signing/agent-resolver/jwks-set.mjs +24 -3
  110. package/dist/lib/signing/agent-resolver/legacy-brand.d.mts +15 -0
  111. package/dist/lib/signing/agent-resolver/legacy-brand.d.ts +15 -0
  112. package/dist/lib/signing/agent-resolver/legacy-brand.js +60 -0
  113. package/dist/lib/signing/agent-resolver/legacy-brand.mjs +36 -0
  114. package/dist/lib/signing/agent-resolver/operator-authorization.d.mts +13 -0
  115. package/dist/lib/signing/agent-resolver/operator-authorization.d.ts +13 -0
  116. package/dist/lib/signing/agent-resolver/operator-authorization.js +108 -0
  117. package/dist/lib/signing/agent-resolver/operator-authorization.mjs +84 -0
  118. package/dist/lib/signing/agent-resolver/resolve-agent.d.mts +14 -4
  119. package/dist/lib/signing/agent-resolver/resolve-agent.d.ts +14 -4
  120. package/dist/lib/signing/agent-resolver/resolve-agent.js +148 -137
  121. package/dist/lib/signing/agent-resolver/resolve-agent.mjs +157 -139
  122. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.mts +7 -1
  123. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.d.ts +7 -1
  124. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.js +50 -17
  125. package/dist/lib/signing/agent-resolver/resolved-agent-jwks.mjs +50 -17
  126. package/dist/lib/signing/agent-resolver/select-agent.d.mts +14 -15
  127. package/dist/lib/signing/agent-resolver/select-agent.d.ts +14 -15
  128. package/dist/lib/signing/agent-resolver/select-agent.js +98 -13
  129. package/dist/lib/signing/agent-resolver/select-agent.mjs +95 -13
  130. package/dist/lib/signing/brand-jwks.d.mts +29 -75
  131. package/dist/lib/signing/brand-jwks.d.ts +29 -75
  132. package/dist/lib/signing/brand-jwks.js +120 -182
  133. package/dist/lib/signing/brand-jwks.mjs +120 -182
  134. package/dist/lib/signing/errors.d.mts +7 -3
  135. package/dist/lib/signing/errors.d.ts +7 -3
  136. package/dist/lib/signing/errors.js +7 -2
  137. package/dist/lib/signing/errors.mjs +7 -2
  138. package/dist/lib/signing/jwks-https.d.mts +7 -0
  139. package/dist/lib/signing/jwks-https.d.ts +7 -0
  140. package/dist/lib/signing/jwks-https.js +31 -8
  141. package/dist/lib/signing/jwks-https.mjs +31 -8
  142. package/dist/lib/signing/jwks.d.mts +8 -0
  143. package/dist/lib/signing/jwks.d.ts +8 -0
  144. package/dist/lib/signing/middleware.js +2 -1
  145. package/dist/lib/signing/middleware.mjs +2 -1
  146. package/dist/lib/signing/publisher-pins.d.mts +11 -0
  147. package/dist/lib/signing/publisher-pins.d.ts +11 -0
  148. package/dist/lib/signing/publisher-pins.js +125 -0
  149. package/dist/lib/signing/publisher-pins.mjs +101 -0
  150. package/dist/lib/signing/server.d.mts +1 -0
  151. package/dist/lib/signing/server.d.ts +1 -0
  152. package/dist/lib/signing/types.d.mts +5 -0
  153. package/dist/lib/signing/types.d.ts +5 -0
  154. package/dist/lib/signing/verifier.js +50 -5
  155. package/dist/lib/signing/verifier.mjs +50 -5
  156. package/dist/lib/signing/webhook-verifier.d.mts +7 -2
  157. package/dist/lib/signing/webhook-verifier.d.ts +7 -2
  158. package/dist/lib/signing/webhook-verifier.js +42 -2
  159. package/dist/lib/signing/webhook-verifier.mjs +42 -2
  160. package/dist/lib/testing/storyboard/account-policy.d.mts +2 -0
  161. package/dist/lib/testing/storyboard/account-policy.d.ts +2 -0
  162. package/dist/lib/testing/storyboard/account-policy.js +35 -0
  163. package/dist/lib/testing/storyboard/account-policy.mjs +11 -0
  164. package/dist/lib/testing/storyboard/context.js +6 -0
  165. package/dist/lib/testing/storyboard/context.mjs +6 -0
  166. package/dist/lib/testing/storyboard/request-builder.js +12 -2
  167. package/dist/lib/testing/storyboard/request-builder.mjs +12 -2
  168. package/dist/lib/testing/storyboard/runner.js +3 -2
  169. package/dist/lib/testing/storyboard/runner.mjs +3 -2
  170. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  171. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  172. package/dist/lib/types/accept-proposal.d.ts +19 -1
  173. package/dist/lib/types/buy-products.d.ts +19 -1
  174. package/dist/lib/types/check-governance.d.ts +19 -1
  175. package/dist/lib/types/comply-test-controller.d.ts +19 -1
  176. package/dist/lib/types/control-media-buy.d.ts +19 -1
  177. package/dist/lib/types/core.generated.d.mts +14 -1
  178. package/dist/lib/types/core.generated.d.ts +14 -1
  179. package/dist/lib/types/create-media-buy.d.ts +14 -1
  180. package/dist/lib/types/get-media-buys.d.ts +14 -1
  181. package/dist/lib/types/get-products.d.ts +19 -1
  182. package/dist/lib/types/list-products.d.ts +19 -1
  183. package/dist/lib/types/refine-proposals.d.ts +19 -1
  184. package/dist/lib/types/request-proposals.d.ts +19 -1
  185. package/dist/lib/types/schemas.generated.d.ts +12 -3
  186. package/dist/lib/types/schemas.generated.js +4 -1
  187. package/dist/lib/types/schemas.generated.mjs +4 -1
  188. package/dist/lib/types/tools.generated.d.mts +14 -1
  189. package/dist/lib/types/tools.generated.d.ts +14 -1
  190. package/dist/lib/types/update-media-buy.d.ts +14 -1
  191. package/dist/lib/version.d.mts +3 -3
  192. package/dist/lib/version.d.ts +3 -3
  193. package/dist/lib/version.js +3 -3
  194. package/dist/lib/version.mjs +3 -3
  195. package/dist/lib/webhooks/index.d.mts +24 -0
  196. package/dist/lib/webhooks/index.d.ts +24 -0
  197. package/dist/lib/webhooks/index.js +50 -24
  198. package/dist/lib/webhooks/index.mjs +49 -24
  199. package/dist/lib/wholesale-feed-sync/index.d.mts +2 -0
  200. package/dist/lib/wholesale-feed-sync/index.d.ts +2 -0
  201. package/dist/lib/wholesale-feed-sync/index.js +7 -0
  202. package/dist/lib/wholesale-feed-sync/index.mjs +4 -0
  203. package/dist/lib/wholesale-feed-sync/mirror.d.mts +97 -0
  204. package/dist/lib/wholesale-feed-sync/mirror.d.ts +97 -0
  205. package/dist/lib/wholesale-feed-sync/mirror.js +350 -0
  206. package/dist/lib/wholesale-feed-sync/mirror.mjs +322 -0
  207. package/dist/lib/wholesale-feed-sync/sync.d.mts +13 -29
  208. package/dist/lib/wholesale-feed-sync/sync.d.ts +13 -29
  209. package/dist/lib/wholesale-feed-sync/sync.js +208 -281
  210. package/dist/lib/wholesale-feed-sync/sync.mjs +213 -281
  211. package/dist/lib/wholesale-feed-sync/types.d.mts +4 -3
  212. package/dist/lib/wholesale-feed-sync/types.d.ts +4 -3
  213. package/docs/README.md +6 -0
  214. package/docs/TYPE-SUMMARY.md +2 -2
  215. package/docs/guides/BUILD-AN-AGENT.md +2 -2
  216. package/docs/guides/BUYER-QUICKSTART-3.2.md +2 -0
  217. package/docs/guides/BUYER-STORAGE.md +3 -0
  218. package/docs/guides/FIRST-CALL-TO-A-SELLER.md +106 -0
  219. package/docs/guides/SIGNING-GUIDE.md +16 -7
  220. package/docs/guides/account-resolution.md +132 -10
  221. package/docs/llms.txt +3 -2
  222. package/docs/migration-14.0-to-14.1.md +85 -0
  223. package/docs/migration-14.x-rc-worksheet.md +4 -4
  224. package/docs/migration-4.x-to-5.x.md +1 -0
  225. package/docs/migration-agent-resolution-3.3.md +125 -0
  226. package/docs/recipes/verifying-inbound-webhooks.md +60 -15
  227. package/package.json +3 -2
  228. package/skills/adcp-brand.previous/SKILL.md +0 -200
  229. package/skills/adcp-creative.previous/SKILL.md +0 -305
  230. package/skills/adcp-governance.previous/SKILL.md +0 -566
  231. package/skills/adcp-measurement.previous/SKILL.md +0 -136
  232. package/skills/adcp-media-buy.previous/SKILL.md +0 -556
  233. package/skills/adcp-si.previous/SKILL.md +0 -206
  234. package/skills/adcp-signals.previous/SKILL.md +0 -204
@@ -14,8 +14,8 @@
14
14
  * `content-type`, and `content-digest` — `content-digest` is unconditional
15
15
  * for webhooks (vs policy-driven on requests) because every webhook
16
16
  * carries a JSON body.
17
- * - JWKS resolved via the publisher's `brand.json` `agents[]` `jwks_uri`,
18
- * not the request-path capability document.
17
+ * - JWKS resolved via the capabilities-selected operator brand.json;
18
+ * publisher pins narrow its keys for applicable inventory.
19
19
  *
20
20
  * Checklist steps below mirror the 14-step shape in
21
21
  * `docs/building/implementation/security.mdx#verifier-checklist-for-webhooks`
@@ -23,6 +23,7 @@
23
23
  * match the security doc.
24
24
  */
25
25
  import type { JwksResolver } from './jwks';
26
+ import { type PublisherSigningKeyPin } from './publisher-pins';
26
27
  import { type ReplayStore } from './replay';
27
28
  import { type RevocationStore } from './revocation';
28
29
  export declare const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
@@ -42,6 +43,10 @@ export interface WebhookRequestLike {
42
43
  export declare const WEBHOOK_MANDATORY_COMPONENTS: ReadonlyArray<string>;
43
44
  export interface VerifyWebhookOptions {
44
45
  jwks: JwksResolver;
46
+ /** Applicable publishers from the verifier's own media-buy record. */
47
+ publisherPins?: readonly PublisherSigningKeyPin[];
48
+ /** Log the underlying request_signature_* discovery cause locally. */
49
+ onKeyResolutionError?: (error: unknown) => void;
45
50
  replayStore: ReplayStore;
46
51
  revocationStore: RevocationStore;
47
52
  /** Now in seconds since epoch. Defaults to `Date.now() / 1000`. */
@@ -28,11 +28,19 @@ module.exports = __toCommonJS(webhook_verifier_exports);
28
28
  var import_canonicalize = require('./canonicalize.js');
29
29
  var import_content_digest = require('./content-digest.js');
30
30
  var import_errors = require('./errors.js');
31
+ var import_errors2 = require('./agent-resolver/errors.js');
32
+ var import_brand_jwks = require('./brand-jwks.js');
31
33
  var import_parser = require('./parser.js');
32
34
  var import_crypto = require('./crypto.js');
35
+ var import_publisher_pins = require('./publisher-pins.js');
33
36
  var import_replay = require('./replay.js');
34
37
  var import_revocation = require('./revocation.js');
35
38
  var import_types = require('./types.js');
39
+ function keyDiscoveryRetryable(error) {
40
+ if (error instanceof import_errors2.AgentResolverError) return error.recovery === "transient";
41
+ if (error instanceof import_brand_jwks.BrandJsonResolverError) return error.recovery === "transient";
42
+ return true;
43
+ }
36
44
  const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
37
45
  const WEBHOOK_MANDATORY_COMPONENTS = [
38
46
  "@method",
@@ -84,7 +92,22 @@ async function verifyWebhookSignature(request, options) {
84
92
  validateWindow(parsedInput.params.created, parsedInput.params.expires, now);
85
93
  validateCoveredComponents(parsedInput.components);
86
94
  const canonicalWebhookTarget = validateTargetUri(request.url);
87
- const keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
95
+ let keyResolution;
96
+ try {
97
+ keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
98
+ } catch (error) {
99
+ try {
100
+ options.onKeyResolutionError?.(error);
101
+ } catch {
102
+ }
103
+ throw new import_errors.WebhookSignatureError(
104
+ "webhook_signature_key_unknown",
105
+ 7,
106
+ "Signing key discovery or publisher pin check failed.",
107
+ void 0,
108
+ keyDiscoveryRetryable(error)
109
+ );
110
+ }
88
111
  const jwk = keyResolution.jwk;
89
112
  if (!jwk) {
90
113
  throw new import_errors.WebhookSignatureError(
@@ -166,6 +189,23 @@ async function verifyWebhookSignature(request, options) {
166
189
  "Content-Digest header does not match recomputed body hash."
167
190
  );
168
191
  }
192
+ if (options.publisherPins) {
193
+ try {
194
+ await (0, import_publisher_pins.assertPublisherPins)(jwk, options.publisherPins, currentTime);
195
+ } catch (error) {
196
+ try {
197
+ options.onKeyResolutionError?.(error);
198
+ } catch {
199
+ }
200
+ throw new import_errors.WebhookSignatureError(
201
+ "webhook_signature_key_unknown",
202
+ 7,
203
+ "Signing key is outside the applicable publisher pins.",
204
+ void 0,
205
+ keyDiscoveryRetryable(error)
206
+ );
207
+ }
208
+ }
169
209
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
170
210
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
171
211
  }
@@ -189,7 +229,7 @@ async function verifyWebhookSignature(request, options) {
189
229
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
190
230
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
191
231
  }
192
- const agent_url = options.agentUrlForKeyid?.(jwk.kid);
232
+ const agent_url = keyResolution.agentUrl ?? options.agentUrlForKeyid?.(jwk.kid);
193
233
  return {
194
234
  status: "verified",
195
235
  keyid: jwk.kid,
@@ -1,11 +1,19 @@
1
1
  import { buildSignatureBase, canonicalTargetUri, getHeaderValue } from "./canonicalize.mjs";
2
2
  import { contentDigestMatches } from "./content-digest.mjs";
3
3
  import { RequestSignatureError, WebhookSignatureError } from "./errors.mjs";
4
+ import { AgentResolverError } from "./agent-resolver/errors.mjs";
5
+ import { BrandJsonResolverError } from "./brand-jwks.mjs";
4
6
  import { parseSignature, parseSignatureInput } from "./parser.mjs";
5
7
  import { jwkToPublicKey, verifySignature } from "./crypto.mjs";
8
+ import { assertPublisherPins } from "./publisher-pins.mjs";
6
9
  import { InMemoryReplayStore } from "./replay.mjs";
7
10
  import { InMemoryRevocationStore } from "./revocation.mjs";
8
11
  import { ALLOWED_ALGS, CLOCK_SKEW_TOLERANCE_SECONDS, MAX_SIGNATURE_WINDOW_SECONDS } from "./types.mjs";
12
+ function keyDiscoveryRetryable(error) {
13
+ if (error instanceof AgentResolverError) return error.recovery === "transient";
14
+ if (error instanceof BrandJsonResolverError) return error.recovery === "transient";
15
+ return true;
16
+ }
9
17
  const WEBHOOK_SIGNING_TAG = "adcp/webhook-signing/v1";
10
18
  const WEBHOOK_MANDATORY_COMPONENTS = [
11
19
  "@method",
@@ -57,7 +65,22 @@ async function verifyWebhookSignature(request, options) {
57
65
  validateWindow(parsedInput.params.created, parsedInput.params.expires, now);
58
66
  validateCoveredComponents(parsedInput.components);
59
67
  const canonicalWebhookTarget = validateTargetUri(request.url);
60
- const keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
68
+ let keyResolution;
69
+ try {
70
+ keyResolution = options.jwks.resolveWithMetadata ? await options.jwks.resolveWithMetadata(parsedInput.params.keyid) : { jwk: await options.jwks.resolve(parsedInput.params.keyid) };
71
+ } catch (error) {
72
+ try {
73
+ options.onKeyResolutionError?.(error);
74
+ } catch {
75
+ }
76
+ throw new WebhookSignatureError(
77
+ "webhook_signature_key_unknown",
78
+ 7,
79
+ "Signing key discovery or publisher pin check failed.",
80
+ void 0,
81
+ keyDiscoveryRetryable(error)
82
+ );
83
+ }
61
84
  const jwk = keyResolution.jwk;
62
85
  if (!jwk) {
63
86
  throw new WebhookSignatureError(
@@ -139,6 +162,23 @@ async function verifyWebhookSignature(request, options) {
139
162
  "Content-Digest header does not match recomputed body hash."
140
163
  );
141
164
  }
165
+ if (options.publisherPins) {
166
+ try {
167
+ await assertPublisherPins(jwk, options.publisherPins, currentTime);
168
+ } catch (error) {
169
+ try {
170
+ options.onKeyResolutionError?.(error);
171
+ } catch {
172
+ }
173
+ throw new WebhookSignatureError(
174
+ "webhook_signature_key_unknown",
175
+ 7,
176
+ "Signing key is outside the applicable publisher pins.",
177
+ void 0,
178
+ keyDiscoveryRetryable(error)
179
+ );
180
+ }
181
+ }
142
182
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
143
183
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
144
184
  }
@@ -162,7 +202,7 @@ async function verifyWebhookSignature(request, options) {
162
202
  if (keyResolution.operatorAuthorizationValidUntil !== void 0) {
163
203
  assertDelegatedOperatorAuthorizationActive(keyResolution.operatorAuthorizationValidUntil, currentTime());
164
204
  }
165
- const agent_url = options.agentUrlForKeyid?.(jwk.kid);
205
+ const agent_url = keyResolution.agentUrl ?? options.agentUrlForKeyid?.(jwk.kid);
166
206
  return {
167
207
  status: "verified",
168
208
  keyid: jwk.kid,
@@ -0,0 +1,2 @@
1
+ /** Discovery and negotiation must never invent an unprovisioned account. */
2
+ export declare const ACCOUNT_FREE_DISCOVERY_TASKS: ReadonlySet<string>;
@@ -0,0 +1,2 @@
1
+ /** Discovery and negotiation must never invent an unprovisioned account. */
2
+ export declare const ACCOUNT_FREE_DISCOVERY_TASKS: ReadonlySet<string>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var account_policy_exports = {};
20
+ __export(account_policy_exports, {
21
+ ACCOUNT_FREE_DISCOVERY_TASKS: () => ACCOUNT_FREE_DISCOVERY_TASKS
22
+ });
23
+ module.exports = __toCommonJS(account_policy_exports);
24
+ const ACCOUNT_FREE_DISCOVERY_TASKS = /* @__PURE__ */ new Set([
25
+ "get_products",
26
+ "list_products",
27
+ "get_signals",
28
+ "request_proposals",
29
+ "refine_proposals",
30
+ "decline_proposals"
31
+ ]);
32
+ // Annotate the CommonJS export names for ESM import in node:
33
+ 0 && (module.exports = {
34
+ ACCOUNT_FREE_DISCOVERY_TASKS
35
+ });
@@ -0,0 +1,11 @@
1
+ const ACCOUNT_FREE_DISCOVERY_TASKS = /* @__PURE__ */ new Set([
2
+ "get_products",
3
+ "list_products",
4
+ "get_signals",
5
+ "request_proposals",
6
+ "refine_proposals",
7
+ "decline_proposals"
8
+ ]);
9
+ export {
10
+ ACCOUNT_FREE_DISCOVERY_TASKS
11
+ };
@@ -100,15 +100,21 @@ function readCompactMediaBuyFields(data) {
100
100
  const CONTEXT_EXTRACTORS = {
101
101
  sync_accounts(data) {
102
102
  const d = data;
103
+ if (d?.dry_run === true) return {};
103
104
  const accounts = d?.accounts;
104
105
  if (!accounts?.[0]) return {};
105
106
  const first = accounts[0];
107
+ if (first.action === "failed") return {};
106
108
  const extracted = {};
107
109
  if (first.account_id) extracted.account_id = first.account_id;
108
110
  if (first.status) extracted.account_status = first.status;
109
111
  const accountRef = {};
110
112
  if (first.brand) accountRef.brand = first.brand;
111
113
  if (first.operator) accountRef.operator = first.operator;
114
+ for (const field of ["operator_unit", "currency", "timezone", "sandbox"]) {
115
+ if (first[field] !== void 0) accountRef[field] = first[field];
116
+ }
117
+ if (!first.brand && !first.operator && first.account_id) accountRef.account_id = first.account_id;
112
118
  extracted.account = accountRef;
113
119
  return extracted;
114
120
  },
@@ -67,15 +67,21 @@ function readCompactMediaBuyFields(data) {
67
67
  const CONTEXT_EXTRACTORS = {
68
68
  sync_accounts(data) {
69
69
  const d = data;
70
+ if (d?.dry_run === true) return {};
70
71
  const accounts = d?.accounts;
71
72
  if (!accounts?.[0]) return {};
72
73
  const first = accounts[0];
74
+ if (first.action === "failed") return {};
73
75
  const extracted = {};
74
76
  if (first.account_id) extracted.account_id = first.account_id;
75
77
  if (first.status) extracted.account_status = first.status;
76
78
  const accountRef = {};
77
79
  if (first.brand) accountRef.brand = first.brand;
78
80
  if (first.operator) accountRef.operator = first.operator;
81
+ for (const field of ["operator_unit", "currency", "timezone", "sandbox"]) {
82
+ if (first[field] !== void 0) accountRef[field] = first[field];
83
+ }
84
+ if (!first.brand && !first.operator && first.account_id) accountRef.account_id = first.account_id;
79
85
  extracted.account = accountRef;
80
86
  return extracted;
81
87
  },
@@ -24,6 +24,7 @@ __export(request_builder_exports, {
24
24
  hasRequestEnricher: () => hasRequestEnricher
25
25
  });
26
26
  module.exports = __toCommonJS(request_builder_exports);
27
+ var import_account_policy = require('./account-policy.js');
27
28
  var import_node_crypto = require("node:crypto");
28
29
  var import_client = require('../client.js');
29
30
  var import_context = require('./context.js');
@@ -102,6 +103,15 @@ function resolveMediaBuyAccount(fixtureAccount, contextAccount, options) {
102
103
  }
103
104
  return resolvedAccount;
104
105
  }
106
+ function resolveDiscoveryAccount(task, fixtureAccount, contextAccount, options) {
107
+ if (import_account_policy.ACCOUNT_FREE_DISCOVERY_TASKS.has(task) && contextAccount === void 0 && hasUnresolvedContextReference(fixtureAccount))
108
+ return void 0;
109
+ if (import_account_policy.ACCOUNT_FREE_DISCOVERY_TASKS.has(task) && fixtureAccount === void 0 && contextAccount === void 0)
110
+ return void 0;
111
+ if (contextAccount === void 0 && fixtureAccount && typeof fixtureAccount === "object" && "account_id" in fixtureAccount)
112
+ return fixtureAccount;
113
+ return resolveMediaBuyAccount(fixtureAccount, contextAccount, options);
114
+ }
105
115
  const OPERATION_SCOPED_CONTROLLER_SCENARIOS = /* @__PURE__ */ new Set([
106
116
  "force_create_media_buy_arm",
107
117
  "force_get_products_arm",
@@ -193,7 +203,7 @@ const REQUEST_ENRICHERS = {
193
203
  ) : {};
194
204
  const fixtureAccount = fixtureFields.account;
195
205
  const fixtureWholesaleAccount = fixtureFields.buying_mode === "wholesale" && fixtureAccount !== void 0 && !hasUnresolvedContextReference(fixtureAccount) ? fixtureAccount : void 0;
196
- const resolvedAccount = fixtureWholesaleAccount ?? resolveMediaBuyAccount(fixtureAccount, context.account, options);
206
+ const resolvedAccount = fixtureWholesaleAccount ?? resolveDiscoveryAccount("get_products", fixtureAccount, context.account, options);
197
207
  if (fixtureFields.buying_mode === "wholesale") {
198
208
  return {
199
209
  ...fixtureFields,
@@ -471,7 +481,7 @@ const REQUEST_ENRICHERS = {
471
481
  ) : {};
472
482
  const fixtureAccount = fixtureFields.account;
473
483
  const fixtureWholesaleAccount = fixtureFields.discovery_mode === "wholesale" && fixtureAccount !== void 0 && !hasUnresolvedContextReference(fixtureAccount) ? fixtureAccount : void 0;
474
- const account = fixtureWholesaleAccount ?? resolveMediaBuyAccount(fixtureAccount, context.account, options);
484
+ const account = fixtureWholesaleAccount ?? resolveDiscoveryAccount("get_signals", fixtureAccount, context.account, options);
475
485
  if (fixtureFields.discovery_mode === "wholesale") {
476
486
  return { ...fixtureFields, account };
477
487
  }
@@ -1,3 +1,4 @@
1
+ import { ACCOUNT_FREE_DISCOVERY_TASKS } from "./account-policy.mjs";
1
2
  import { createHash } from "node:crypto";
2
3
  import { resolveBrand, resolveAccount } from "../client.mjs";
3
4
  import { injectContext } from "./context.mjs";
@@ -76,6 +77,15 @@ function resolveMediaBuyAccount(fixtureAccount, contextAccount, options) {
76
77
  }
77
78
  return resolvedAccount;
78
79
  }
80
+ function resolveDiscoveryAccount(task, fixtureAccount, contextAccount, options) {
81
+ if (ACCOUNT_FREE_DISCOVERY_TASKS.has(task) && contextAccount === void 0 && hasUnresolvedContextReference(fixtureAccount))
82
+ return void 0;
83
+ if (ACCOUNT_FREE_DISCOVERY_TASKS.has(task) && fixtureAccount === void 0 && contextAccount === void 0)
84
+ return void 0;
85
+ if (contextAccount === void 0 && fixtureAccount && typeof fixtureAccount === "object" && "account_id" in fixtureAccount)
86
+ return fixtureAccount;
87
+ return resolveMediaBuyAccount(fixtureAccount, contextAccount, options);
88
+ }
79
89
  const OPERATION_SCOPED_CONTROLLER_SCENARIOS = /* @__PURE__ */ new Set([
80
90
  "force_create_media_buy_arm",
81
91
  "force_get_products_arm",
@@ -167,7 +177,7 @@ const REQUEST_ENRICHERS = {
167
177
  ) : {};
168
178
  const fixtureAccount = fixtureFields.account;
169
179
  const fixtureWholesaleAccount = fixtureFields.buying_mode === "wholesale" && fixtureAccount !== void 0 && !hasUnresolvedContextReference(fixtureAccount) ? fixtureAccount : void 0;
170
- const resolvedAccount = fixtureWholesaleAccount ?? resolveMediaBuyAccount(fixtureAccount, context.account, options);
180
+ const resolvedAccount = fixtureWholesaleAccount ?? resolveDiscoveryAccount("get_products", fixtureAccount, context.account, options);
171
181
  if (fixtureFields.buying_mode === "wholesale") {
172
182
  return {
173
183
  ...fixtureFields,
@@ -445,7 +455,7 @@ const REQUEST_ENRICHERS = {
445
455
  ) : {};
446
456
  const fixtureAccount = fixtureFields.account;
447
457
  const fixtureWholesaleAccount = fixtureFields.discovery_mode === "wholesale" && fixtureAccount !== void 0 && !hasUnresolvedContextReference(fixtureAccount) ? fixtureAccount : void 0;
448
- const account = fixtureWholesaleAccount ?? resolveMediaBuyAccount(fixtureAccount, context.account, options);
458
+ const account = fixtureWholesaleAccount ?? resolveDiscoveryAccount("get_signals", fixtureAccount, context.account, options);
449
459
  if (fixtureFields.discovery_mode === "wholesale") {
450
460
  return { ...fixtureFields, account };
451
461
  }
@@ -61,6 +61,7 @@ module.exports = __toCommonJS(runner_exports);
61
61
  var import_node_crypto = require("node:crypto");
62
62
  var import_node_fs = require("node:fs");
63
63
  var import_node_path = require("node:path");
64
+ var import_account_policy = require('./account-policy.js');
64
65
  var import_client = require('../client.js');
65
66
  var import_protocols = require('../../protocols/index.js');
66
67
  var import_rawResponseCapture = require('../../protocols/rawResponseCapture.js');
@@ -4291,7 +4292,7 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
4291
4292
  const updatedContext = { ...context };
4292
4293
  (0, import_context.forwardAliasCache)(context, updatedContext);
4293
4294
  const hasData = taskResult?.data !== void 0 && taskResult?.data !== null;
4294
- if (passed && hasData && taskResult) {
4295
+ if (passed && hasData && taskResult && !(effectiveStep.task === "sync_accounts" && request.dry_run === true)) {
4295
4296
  const extracted = (0, import_context.extractContextWithProvenance)(effectiveStep.task, taskResult.data, step.id);
4296
4297
  for (const group of extracted.clearGroups ?? []) {
4297
4298
  if (group.when && !group.when.values.includes(updatedContext[group.when.key])) continue;
@@ -6210,7 +6211,7 @@ function applyBrandInvariant(request, options, taskName, stepFlags) {
6210
6211
  result.account = merged;
6211
6212
  }
6212
6213
  }
6213
- } else if (topAccountOk && taskName !== "list_accounts") {
6214
+ } else if (topAccountOk && taskName !== "list_accounts" && !import_account_policy.ACCOUNT_FREE_DISCOVERY_TASKS.has(taskName ?? "")) {
6214
6215
  result.account = (0, import_client2.resolveAccount)(options);
6215
6216
  }
6216
6217
  return result;
@@ -1,6 +1,7 @@
1
1
  import { createHash, createHmac } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { basename, join } from "node:path";
4
+ import { ACCOUNT_FREE_DISCOVERY_TASKS } from "./account-policy.mjs";
4
5
  import {
5
6
  createTestClient,
6
7
  getOrCreateClientResolution,
@@ -4313,7 +4314,7 @@ async function executeStep(client, step, storyboardId, phaseId, context, allStep
4313
4314
  const updatedContext = { ...context };
4314
4315
  forwardAliasCache(context, updatedContext);
4315
4316
  const hasData = taskResult?.data !== void 0 && taskResult?.data !== null;
4316
- if (passed && hasData && taskResult) {
4317
+ if (passed && hasData && taskResult && !(effectiveStep.task === "sync_accounts" && request.dry_run === true)) {
4317
4318
  const extracted = extractContextWithProvenance(effectiveStep.task, taskResult.data, step.id);
4318
4319
  for (const group of extracted.clearGroups ?? []) {
4319
4320
  if (group.when && !group.when.values.includes(updatedContext[group.when.key])) continue;
@@ -6232,7 +6233,7 @@ function applyBrandInvariant(request, options, taskName, stepFlags) {
6232
6233
  result.account = merged;
6233
6234
  }
6234
6235
  }
6235
- } else if (topAccountOk && taskName !== "list_accounts") {
6236
+ } else if (topAccountOk && taskName !== "list_accounts" && !ACCOUNT_FREE_DISCOVERY_TASKS.has(taskName ?? "")) {
6236
6237
  result.account = resolveAccount(options);
6237
6238
  }
6238
6239
  return result;
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "14.0.0";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "14.2.0";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -197,7 +197,7 @@ export interface UpstreamTrafficQueryResult {
197
197
  */
198
198
  export declare function runValidations(validations: StoryboardValidation[], context: ValidationContext): ValidationResult[];
199
199
  /** Capability semver emitted in run summaries and used for advisory expiry. */
200
- export declare const RUNNER_CAPABILITY_VERSION = "14.0.0";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "14.2.0";
201
201
  /** True when a failed validation contributes to the owning step's grade. */
202
202
  export declare function validationFailsStep(result: ValidationResult): boolean;
203
203
  /**
@@ -145,6 +145,11 @@ export type ActivationKey = {
145
145
  */
146
146
  export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
147
147
 
148
+ /**
149
+ * User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
150
+ */
151
+ export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
152
+
148
153
  /**
149
154
  * Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
150
155
  */
@@ -1083,7 +1088,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
1083
1088
  * Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
1084
1089
  */
1085
1090
  export interface DemographicTargetingIntent {
1086
- age: DemographicAgeRange & {};
1091
+ age: DemographicAgeRange & {
1092
+ /**
1093
+ * User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
1094
+ *
1095
+ * @minItems 1
1096
+ */
1097
+ accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
1098
+ /**
1099
+ * Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
1100
+ *
1101
+ * @minItems 1
1102
+ */
1103
+ accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
1104
+ };
1087
1105
  }
1088
1106
 
1089
1107
  /**
@@ -145,6 +145,11 @@ export type ActivationKey = {
145
145
  */
146
146
  export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
147
147
 
148
+ /**
149
+ * User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
150
+ */
151
+ export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
152
+
148
153
  /**
149
154
  * Age restriction for compliance. Use for legal requirements (alcohol, gambling), not audience targeting.
150
155
  */
@@ -1152,7 +1157,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
1152
1157
  * Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
1153
1158
  */
1154
1159
  export interface DemographicTargetingIntent {
1155
- age: DemographicAgeRange & {};
1160
+ age: DemographicAgeRange & {
1161
+ /**
1162
+ * User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
1163
+ *
1164
+ * @minItems 1
1165
+ */
1166
+ accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
1167
+ /**
1168
+ * Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
1169
+ *
1170
+ * @minItems 1
1171
+ */
1172
+ accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
1173
+ };
1156
1174
  }
1157
1175
 
1158
1176
  /**
@@ -445,6 +445,11 @@ export type ActivationKey = {
445
445
  */
446
446
  export type AdvertiserIndustry = 'automotive' | 'automotive.electric_vehicles' | 'automotive.parts_accessories' | 'automotive.luxury' | 'beauty_cosmetics' | 'beauty_cosmetics.skincare' | 'beauty_cosmetics.fragrance' | 'beauty_cosmetics.haircare' | 'cannabis' | 'cpg' | 'cpg.personal_care' | 'cpg.household' | 'dating' | 'education' | 'education.higher_education' | 'education.online_learning' | 'education.k12' | 'energy_utilities' | 'energy_utilities.renewable' | 'fashion_apparel' | 'fashion_apparel.luxury' | 'fashion_apparel.sportswear' | 'finance' | 'finance.banking' | 'finance.insurance' | 'finance.investment' | 'finance.cryptocurrency' | 'food_beverage' | 'food_beverage.alcohol' | 'food_beverage.restaurants' | 'food_beverage.packaged_goods' | 'gambling_betting' | 'gambling_betting.sports_betting' | 'gambling_betting.casino' | 'gaming' | 'gaming.mobile' | 'gaming.console_pc' | 'gaming.esports' | 'government_nonprofit' | 'government_nonprofit.political' | 'government_nonprofit.charity' | 'healthcare' | 'healthcare.pharmaceutical' | 'healthcare.medical_devices' | 'healthcare.wellness' | 'home_garden' | 'home_garden.furniture' | 'home_garden.home_improvement' | 'media_entertainment' | 'media_entertainment.podcasts' | 'media_entertainment.music' | 'media_entertainment.film_tv' | 'media_entertainment.publishing' | 'media_entertainment.live_events' | 'pets' | 'professional_services' | 'professional_services.legal' | 'professional_services.consulting' | 'real_estate' | 'real_estate.residential' | 'real_estate.commercial' | 'recruitment_hr' | 'retail' | 'retail.ecommerce' | 'retail.department_stores' | 'sports_fitness' | 'sports_fitness.equipment' | 'sports_fitness.teams_leagues' | 'technology' | 'technology.software' | 'technology.hardware' | 'technology.ai_ml' | 'telecom' | 'telecom.mobile_carriers' | 'telecom.internet_providers' | 'transportation_logistics' | 'travel_hospitality' | 'travel_hospitality.airlines' | 'travel_hospitality.hotels' | 'travel_hospitality.cruise' | 'travel_hospitality.tourism';
447
447
 
448
+ /**
449
+ * User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
450
+ */
451
+ export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
452
+
448
453
  /**
449
454
  * Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
450
455
  */
@@ -1585,7 +1590,20 @@ export type DemographicSystem = 'nielsen' | 'nielsen_audio' | 'barb' | 'agf' | '
1585
1590
  * Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
1586
1591
  */
1587
1592
  export interface DemographicTargetingIntent {
1588
- age: DemographicAgeRange & {};
1593
+ age: DemographicAgeRange & {
1594
+ /**
1595
+ * User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
1596
+ *
1597
+ * @minItems 1
1598
+ */
1599
+ accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
1600
+ /**
1601
+ * Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
1602
+ *
1603
+ * @minItems 1
1604
+ */
1605
+ accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
1606
+ };
1589
1607
  }
1590
1608
 
1591
1609
  /**
@@ -486,6 +486,11 @@ export interface AdInventoryConfiguration {
486
486
  supported_formats?: string[];
487
487
  }
488
488
 
489
+ /**
490
+ * User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
491
+ */
492
+ export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
493
+
489
494
  /**
490
495
  * Methods for verifying user age for compliance. Does not include 'inferred' as it is not accepted for regulatory compliance.
491
496
  */
@@ -5526,7 +5531,20 @@ export interface DemographicTargetingCapability {
5526
5531
  * Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
5527
5532
  */
5528
5533
  export interface DemographicTargetingIntent {
5529
- age: DemographicAgeRange & {};
5534
+ age: DemographicAgeRange & {
5535
+ /**
5536
+ * User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
5537
+ *
5538
+ * @minItems 1
5539
+ */
5540
+ accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
5541
+ /**
5542
+ * Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
5543
+ *
5544
+ * @minItems 1
5545
+ */
5546
+ accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
5547
+ };
5530
5548
  }
5531
5549
 
5532
5550
  /**
@@ -129,6 +129,11 @@ export type ActivationKey = {
129
129
  value: string;
130
130
  };
131
131
 
132
+ /**
133
+ * User-level bases that may resolve age for demographic targeting execution. Population estimates are deliberately absent because aggregate composition cannot establish an individual impression's age.
134
+ */
135
+ export type AgeDeterminationBasis = 'verified' | 'declared' | 'inferred';
136
+
132
137
  /**
133
138
  * Age restriction for compliance. Use for legal requirements (alcohol, gambling), not audience targeting.
134
139
  */
@@ -583,7 +588,20 @@ export type DemographicAgeRange = {} & {
583
588
  * Canonical demographic audience targeting intent with optional constraints on how age may be determined. This is distinct from age_restriction: demographics selects an audience, while age_restriction expresses a legal eligibility or verification floor. Fresh create/update targeting MUST compile exactly or be rejected. During get_products, a seller may offer a different configured predicate only through sparse targeting_resolution modifications on a distinguishable product_id; selecting that product accepts the alternative. Sellers never silently broaden, narrow, default, drop, or substitute the basis.
584
589
  */
585
590
  export interface DemographicTargetingIntent {
586
- age: DemographicAgeRange & {};
591
+ age: DemographicAgeRange & {
592
+ /**
593
+ * User-level age determination bases the buyer permits for this predicate. Sellers MUST use only these bases or reject the request; they MUST NOT silently fall back to another basis. When omitted, the buyer imposes no determination-basis constraint beyond product capability and any age_restriction. population_estimate is structurally unavailable because aggregate evidence cannot identify an eligible user.
594
+ *
595
+ * @minItems 1
596
+ */
597
+ accepted_bases?: [AgeDeterminationBasis, ...AgeDeterminationBasis[]];
598
+ /**
599
+ * Verification methods acceptable when verified is an accepted basis. This field narrows verified demographic execution; age_restriction.accepted_methods independently governs legal eligibility and the two sets are intersected when both are present.
600
+ *
601
+ * @minItems 1
602
+ */
603
+ accepted_verification_methods?: [AgeVerificationMethod, ...AgeVerificationMethod[]];
604
+ };
587
605
  }
588
606
 
589
607
  /**