@adcp/sdk 16.0.0-beta.7 → 16.0.0-beta.8

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 (75) hide show
  1. package/README.md +17 -0
  2. package/dist/lib/conformance/index.d.mts +2 -0
  3. package/dist/lib/conformance/index.d.ts +2 -0
  4. package/dist/lib/conformance/index.js +3 -0
  5. package/dist/lib/conformance/index.mjs +2 -0
  6. package/dist/lib/conformance/property-lists.d.mts +14 -0
  7. package/dist/lib/conformance/property-lists.d.ts +14 -0
  8. package/dist/lib/conformance/property-lists.js +45 -0
  9. package/dist/lib/conformance/property-lists.mjs +21 -0
  10. package/dist/lib/conformance/runConformance.js +20 -1
  11. package/dist/lib/conformance/runConformance.mjs +20 -1
  12. package/dist/lib/conformance/types.d.mts +3 -0
  13. package/dist/lib/conformance/types.d.ts +3 -0
  14. package/dist/lib/core/SingleAgentClient.d.mts +13 -3
  15. package/dist/lib/core/SingleAgentClient.d.ts +13 -3
  16. package/dist/lib/core/SingleAgentClient.js +109 -21
  17. package/dist/lib/core/SingleAgentClient.mjs +112 -22
  18. package/dist/lib/identifiers.d.mts +53 -0
  19. package/dist/lib/identifiers.d.ts +53 -0
  20. package/dist/lib/identifiers.js +212 -0
  21. package/dist/lib/identifiers.mjs +163 -0
  22. package/dist/lib/index.d.mts +3 -1
  23. package/dist/lib/index.d.ts +3 -1
  24. package/dist/lib/index.js +2 -0
  25. package/dist/lib/index.mjs +1 -0
  26. package/dist/lib/media-buy/index.d.mts +1 -1
  27. package/dist/lib/media-buy/index.d.ts +1 -1
  28. package/dist/lib/media-buy/property-policy.d.mts +15 -1
  29. package/dist/lib/media-buy/property-policy.d.ts +15 -1
  30. package/dist/lib/media-buy/property-policy.js +135 -3
  31. package/dist/lib/media-buy/property-policy.mjs +134 -3
  32. package/dist/lib/registry/index.d.mts +12 -0
  33. package/dist/lib/registry/index.d.ts +12 -0
  34. package/dist/lib/registry/index.js +159 -5
  35. package/dist/lib/registry/index.mjs +156 -4
  36. package/dist/lib/registry/types.d.mts +23 -0
  37. package/dist/lib/registry/types.d.ts +23 -0
  38. package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
  39. package/dist/lib/server/decisioning/specialisms/lists.d.mts +4 -4
  40. package/dist/lib/server/decisioning/specialisms/lists.d.ts +4 -4
  41. package/dist/lib/server/index.d.mts +3 -0
  42. package/dist/lib/server/index.d.ts +3 -0
  43. package/dist/lib/server/index.js +8 -1
  44. package/dist/lib/server/index.mjs +4 -0
  45. package/dist/lib/server/property-lists.d.mts +36 -0
  46. package/dist/lib/server/property-lists.d.ts +36 -0
  47. package/dist/lib/server/property-lists.js +154 -0
  48. package/dist/lib/server/property-lists.mjs +129 -0
  49. package/dist/lib/server/targeting-helpers.d.mts +2 -0
  50. package/dist/lib/server/targeting-helpers.d.ts +2 -0
  51. package/dist/lib/server/targeting-helpers.js +28 -1
  52. package/dist/lib/server/targeting-helpers.mjs +18 -1
  53. package/dist/lib/signing/parser.js +2 -2
  54. package/dist/lib/signing/parser.mjs +2 -2
  55. package/dist/lib/signing/server.d.mts +1 -1
  56. package/dist/lib/signing/server.d.ts +1 -1
  57. package/dist/lib/signing/server.mjs +5 -1
  58. package/dist/lib/signing/webhook-verifier.d.mts +11 -0
  59. package/dist/lib/signing/webhook-verifier.d.ts +11 -0
  60. package/dist/lib/signing/webhook-verifier.js +11 -3
  61. package/dist/lib/signing/webhook-verifier.mjs +11 -3
  62. package/dist/lib/supply-path/verify.js +12 -2
  63. package/dist/lib/supply-path/verify.mjs +2 -2
  64. package/dist/lib/testing/storyboard/validations.d.mts +1 -1
  65. package/dist/lib/testing/storyboard/validations.d.ts +1 -1
  66. package/dist/lib/version.d.mts +3 -3
  67. package/dist/lib/version.d.ts +3 -3
  68. package/dist/lib/version.js +3 -3
  69. package/dist/lib/version.mjs +3 -3
  70. package/docs/TYPE-SUMMARY.md +2 -2
  71. package/docs/guides/PROPERTY-LISTS.md +264 -0
  72. package/docs/guides/SIGNING-GUIDE.md +25 -0
  73. package/docs/llms.txt +2 -2
  74. package/docs/migration-14.x-rc-worksheet.md +4 -4
  75. package/package.json +1 -1
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
  var verify_exports = {};
20
30
  __export(verify_exports, {
@@ -25,7 +35,6 @@ module.exports = __toCommonJS(verify_exports);
25
35
  var import_abort = require('../protocols/abort.js');
26
36
  var import_publisher_property_selector = require('../discovery/publisher-property-selector.js');
27
37
  var import_validation = require('./validation.js');
28
- var import_registry = require('../registry/index.js');
29
38
  var import_fetch_evidence = require('./fetch-evidence.js');
30
39
  var import_evaluate = require('./evaluate.js');
31
40
  var import_validation2 = require('./validation.js');
@@ -34,7 +43,8 @@ const MAX_PROPERTY_SELECTOR_EXPANSIONS = 4096;
34
43
  async function verifySupplyPath(request, options = { source: "authoritative" }) {
35
44
  const normalized = (0, import_validation2.validateSupplyPathRequest)(request);
36
45
  if (options.source === "registry") {
37
- const result = await (options.registry ?? new import_registry.RegistryClient()).verifySupplyPath(normalized);
46
+ const registry = options.registry ?? new (await import("../registry/index.js")).RegistryClient();
47
+ const result = await registry.verifySupplyPath(normalized);
38
48
  (0, import_validation2.assertRegistrySupplyPathResult)(result, normalized);
39
49
  return result;
40
50
  }
@@ -4,7 +4,6 @@ import {
4
4
  expandPublisherPropertySelector
5
5
  } from "../discovery/publisher-property-selector.mjs";
6
6
  import { domain, records, strings } from "./validation.mjs";
7
- import { RegistryClient } from "../registry/index.mjs";
8
7
  import { SupplyPathEvidenceSession } from "./fetch-evidence.mjs";
9
8
  import {
10
9
  isUnqualifiedPropertySelector,
@@ -20,7 +19,8 @@ const MAX_PROPERTY_SELECTOR_EXPANSIONS = 4096;
20
19
  async function verifySupplyPath(request, options = { source: "authoritative" }) {
21
20
  const normalized = validateSupplyPathRequest(request);
22
21
  if (options.source === "registry") {
23
- const result = await (options.registry ?? new RegistryClient()).verifySupplyPath(normalized);
22
+ const registry = options.registry ?? new (await import("../registry/index.mjs")).RegistryClient();
23
+ const result = await registry.verifySupplyPath(normalized);
24
24
  assertRegistrySupplyPathResult(result, normalized);
25
25
  return result;
26
26
  }
@@ -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 = "16.0.0-beta.7";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.8";
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 = "16.0.0-beta.7";
200
+ export declare const RUNNER_CAPABILITY_VERSION = "16.0.0-beta.8";
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
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "16.0.0-beta.7";
4
+ export declare const LIBRARY_VERSION = "16.0.0-beta.8";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "16.0.0-beta.7";
36
+ readonly library: "16.0.0-beta.8";
37
37
  readonly adcp: "3.2.5";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.1.25", "3.1.26", "3.1.27", "3.1.28", "3.1.29", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4", "3.2.5"];
39
- readonly generatedAt: "2026-10-17T13:58:28.444Z";
39
+ readonly generatedAt: "2026-10-18T01:21:23.594Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * AdCP SDK library version
3
3
  */
4
- export declare const LIBRARY_VERSION = "16.0.0-beta.7";
4
+ export declare const LIBRARY_VERSION = "16.0.0-beta.8";
5
5
  /**
6
6
  * AdCP specification version this library is built for
7
7
  */
@@ -33,10 +33,10 @@ export type AdcpVersion = (typeof COMPATIBLE_ADCP_VERSIONS)[number];
33
33
  * Full version information
34
34
  */
35
35
  export declare const VERSION_INFO: {
36
- readonly library: "16.0.0-beta.7";
36
+ readonly library: "16.0.0-beta.8";
37
37
  readonly adcp: "3.2.5";
38
38
  readonly compatibleVersions: readonly ["v2.5", "v2.6", "v3", "3.0.0", "3.0", "3.0.1", "3.0.2", "3.0.3", "3.0.4", "3.0.5", "3.0.6", "3.0.7", "3.0.8", "3.0.9", "3.0.10", "3.0.11", "3.0.12", "3.0.13", "3.0.14", "3.0.15", "3.0.16", "3.0.17", "3.0.18", "3.0.19", "3.0.20", "3.0.21", "3.0.22", "3.0.23", "3.0.24", "3.0.25", "3.1.0", "3.1", "3.1.1", "3.1.2", "3.1.3", "3.1.4", "3.1.5", "3.1.6", "3.1.7", "3.1.8", "3.1.9", "3.1.10", "3.1.11", "3.1.12", "3.1.13", "3.1.14", "3.1.15", "3.1.16", "3.1.17", "3.1.18", "3.1.19", "3.1.20", "3.1.21", "3.1.22", "3.1.23", "3.1.24", "3.1.25", "3.1.26", "3.1.27", "3.1.28", "3.1.29", "3.2.1", "3.2", "3.2.2", "3.2.3", "3.2.4", "3.2.5"];
39
- readonly generatedAt: "2026-10-17T13:58:28.444Z";
39
+ readonly generatedAt: "2026-10-18T01:21:23.594Z";
40
40
  };
41
41
  /**
42
42
  * Get the AdCP specification version this library is built for
@@ -31,7 +31,7 @@ __export(version_exports, {
31
31
  toReleasePrecisionVersion: () => toReleasePrecisionVersion
32
32
  });
33
33
  module.exports = __toCommonJS(version_exports);
34
- const LIBRARY_VERSION = "16.0.0-beta.7";
34
+ const LIBRARY_VERSION = "16.0.0-beta.8";
35
35
  const ADCP_VERSION = "3.2.5";
36
36
  const ADCP_MAJOR_VERSION = 3;
37
37
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -104,10 +104,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
104
104
  "3.2.5"
105
105
  ];
106
106
  const VERSION_INFO = {
107
- library: "16.0.0-beta.7",
107
+ library: "16.0.0-beta.8",
108
108
  adcp: "3.2.5",
109
109
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
110
- generatedAt: "2026-10-17T13:58:28.444Z"
110
+ generatedAt: "2026-10-18T01:21:23.594Z"
111
111
  };
112
112
  function getAdcpVersion() {
113
113
  return ADCP_VERSION;
@@ -1,4 +1,4 @@
1
- const LIBRARY_VERSION = "16.0.0-beta.7";
1
+ const LIBRARY_VERSION = "16.0.0-beta.8";
2
2
  const ADCP_VERSION = "3.2.5";
3
3
  const ADCP_MAJOR_VERSION = 3;
4
4
  const COMPATIBLE_ADCP_VERSIONS = [
@@ -71,10 +71,10 @@ const COMPATIBLE_ADCP_VERSIONS = [
71
71
  "3.2.5"
72
72
  ];
73
73
  const VERSION_INFO = {
74
- library: "16.0.0-beta.7",
74
+ library: "16.0.0-beta.8",
75
75
  adcp: "3.2.5",
76
76
  compatibleVersions: COMPATIBLE_ADCP_VERSIONS,
77
- generatedAt: "2026-10-17T13:58:28.444Z"
77
+ generatedAt: "2026-10-18T01:21:23.594Z"
78
78
  };
79
79
  function getAdcpVersion() {
80
80
  return ADCP_VERSION;
@@ -1,7 +1,7 @@
1
1
  # AdCP Type Summary
2
2
 
3
- > Generated at: 2026-10-17
4
- > @adcp/sdk v16.0.0-beta.7
3
+ > Generated at: 2026-10-18
4
+ > @adcp/sdk v16.0.0-beta.8
5
5
 
6
6
  Curated reference of the types that matter for using the AdCP client. For full generated types see `src/lib/types/tools.generated.ts` and `src/lib/types/core.generated.ts`.
7
7
 
@@ -0,0 +1,264 @@
1
+ # Property lists: matching, registry contribution, and serving
2
+
3
+ ## Buyer enforcement
4
+
5
+ `validation.productPropertyPolicy` reads `get_products.filters.property_list`
6
+ and the deprecated top-level `property_list`. When both are present they must
7
+ have the same `agent_url`, `list_id`, and `auth_token`; conflicting references
8
+ fail closed with `property_list_conflicting_references`. A malformed nested
9
+ reference never falls back to the top-level field. The same reference is kept
10
+ for polling, tracking, and webhook completions.
11
+
12
+ These location and mixed-identifier fixes ship together. As before, request
13
+ list enforcement defaults to enabled. Set `enforceRequestPropertyList: false`
14
+ to disable local resolution, or `productPropertyPolicy: false` to disable the
15
+ policy entirely. Resolution errors (including a non-MCP list URL) fail closed.
16
+
17
+ Lists may mix domains, app bundles, and CTV store IDs. Non-domain types no
18
+ longer cause an unconditional response failure. A product's
19
+ `publisher_properties` contains publisher selectors, not app IDs: `property_ids`
20
+ are opaque names scoped to a publisher. Supply complete catalogs to match
21
+ their real identifiers:
22
+
23
+ ```typescript
24
+ import { AdCPClient, type ProductPolicyResolvedProperty } from '@adcp/sdk';
25
+
26
+ declare const loadPublisherCatalog: (
27
+ domain: string
28
+ ) => Promise<readonly ProductPolicyResolvedProperty[] | undefined>;
29
+
30
+ const client = new AdCPClient(agents, {
31
+ validation: {
32
+ productPropertyPolicy: {
33
+ enforceRequestPropertyList: true,
34
+ unknownSelectorBehavior: 'flag',
35
+ resolvePublisherProperties: loadPublisherCatalog,
36
+ },
37
+ },
38
+ });
39
+ ```
40
+
41
+ The resolver receives normalized, untrusted publisher hostnames and an optional
42
+ `{ signal }` argument. It returns the complete
43
+ catalog of `{ property_id?, identifiers: [{ type, value }], tags? }` records
44
+ from your publisher metadata store or discovery layer. Alternatively provide
45
+ `publisherProperties: { [publisherDomain]: catalog }` directly. Keys use the exact
46
+ normalized publisher hostname, including `www.` when present; catalogs are not
47
+ shared across host aliases. When catalogs are configured, missing or failed
48
+ lookups mean unknown metadata; `[]` means a known empty catalog. This hook does
49
+ not initiate any additional registry or publisher lookups unless you implement
50
+ them. Identifier type and value must match exactly for non-domain identifiers;
51
+ bundle casing is preserved. `all`, `by_id`, and `by_tag` selectors are resolved
52
+ against these records. Products with `property_targeting_allowed: true` may
53
+ retain a proven eligible subset; other products must satisfy the policy for
54
+ every selected property.
55
+
56
+ Catalog loading is limited to 100 distinct lookups per response, 10 seconds
57
+ per lookup, and a 10-second total budget by default (`maxPublisherLookups`,
58
+ `publisherLookupTimeoutMs`, `publisherLookupBudgetMs`).
59
+ Overflow and failed/timed-out lookups stay unknown even when a domain matches.
60
+ Literal IP and localhost inputs do not reach the hook. For network discovery,
61
+ use the SDK's SSRF-safe networking utilities to protect DNS resolution too;
62
+ catalog identity/ownership verification remains the catalog provider's job.
63
+
64
+ Products whose membership cannot be judged follow `unknownSelectorBehavior`
65
+ (`allow`, `flag`, or `reject`). Request-derived policies remain strict by
66
+ default, so the default unknown behavior is `reject`; choose `flag` explicitly
67
+ to retain uncertain products. Known disallowed properties still reject even
68
+ when another property is unknown. Diagnostics appear under
69
+ `metadata.productPropertyPolicy`, including per-product
70
+ `unresolved_property_identifiers` / `identifier_types` and
71
+ `request_property_list.unsupported_identifier_types` for unrecognized types.
72
+
73
+ ## Identifier normalization and registration
74
+
75
+ `normalizePropertyIdentifier(type, value)` (also exported as
76
+ `validatePropertyIdentifier`) returns either `{ ok: true, value, rewrite? }`
77
+ or `{ ok: false, error: IdentifierValidationError }`. Named normalizers cover
78
+ domain, subdomain, iOS/Android bundles, Apple/Google/Roku/Samsung store IDs,
79
+ Fire TV ASINs, and Apple TV/generic bundles. Each normalizer validates too.
80
+
81
+ Domain normalization accepts HTTP(S) URLs and reports their reduction to a
82
+ hostname, lowercases IDNs, and preserves wildcards. It preserves `www.` and
83
+ `m.` because removing those labels would broaden an explicit allowlist.
84
+ `subdomain` rejects wildcards. Platform names preserve case; numeric IDs stay
85
+ strings without losing precision. Formats for app/store identifiers are SDK
86
+ input hygiene rules, not assertions of registration, ownership, or existence.
87
+ Opaque identifier families retain their spelling (including station names
88
+ with spaces). Use these opt-in helpers before storing or matching values:
89
+
90
+ ```typescript
91
+ import { normalizePropertyIdentifier } from '@adcp/sdk';
92
+
93
+ const checked = normalizePropertyIdentifier('domain', submittedValue);
94
+ if (!checked.ok) throw checked.error;
95
+ await identifierStore.save({ type: 'domain', value: checked.value });
96
+ // checked.rewrite, when present, records { from, to } for your review/audit UI.
97
+ ```
98
+
99
+ `validatePublisherDomain(value)` is deliberately stricter: a publisher
100
+ registration must already be a lowercase bare hostname. URLs, wildcards,
101
+ ports, whitespace, trailing dots, malformed labels, and IP literals reject
102
+ with `IdentifierValidationError.code === 'invalid_publisher_domain'`.
103
+ `RegistryClient.saveProperty()` always performs this validation before HTTP.
104
+ `saveProperties()` uses the same check for every record; failed records retain
105
+ the existing `error` string and expose the typed error as `cause`.
106
+
107
+ ## Bulk catalog contribution
108
+
109
+ `resolveIdentifiers()` in default `resolve` mode is the primary bulk catalog
110
+ registration path: it contributes provenance, creates missing entries, and
111
+ returns stable `property_rid` handles. Use `mode: 'lookup'` for read-only lookup.
112
+ A RID is a join handle and does not grant advertising authorization.
113
+
114
+ ```typescript
115
+ import { RegistryClient, isRegistryRequestError } from '@adcp/sdk';
116
+
117
+ const registry = new RegistryClient({ apiKey: process.env.ADCP_REGISTRY_API_KEY });
118
+ const outcomes = await registry.resolveIdentifiersAll({
119
+ identifiers,
120
+ provenance: { type: 'agency_allowlist', context: campaignId },
121
+ // Omit mode to register/contribute. Use mode: 'lookup' for a pure read.
122
+ }, {
123
+ batchSize: 10_000,
124
+ maxRequestBytes: 1024 * 1024,
125
+ concurrency: 2,
126
+ maxRetries: 3,
127
+ maxRetryDelayMs: 60_000,
128
+ });
129
+
130
+ for (const outcome of outcomes) {
131
+ if (outcome.success) useCatalogHandle(outcome.identifier, outcome.result.property_rid);
132
+ else if (isRegistryRequestError(outcome.error)) {
133
+ reportRegistryFailure(outcome.index, outcome.error.status, outcome.error.retryAfterMs);
134
+ } else reportInputFailure(outcome.index, outcome.error);
135
+ }
136
+ ```
137
+
138
+ The helper caps each request at 10,000 identifiers and the configured UTF-8
139
+ JSON byte limit, including provenance. It limits concurrency, honors
140
+ `retryAfterMs` on HTTP 429, and returns an outcome for each input in input order,
141
+ including duplicates. Oversized individual items, missing/malformed response
142
+ entries, and failed batches become per-input errors; successful batches remain
143
+ available. Inputs should already use canonical identifier spelling so registry
144
+ results can be correlated by type and value. Empty input returns `[]`.
145
+
146
+ Only rate limits are retried automatically. A server delay exceeding
147
+ `maxRetryDelayMs` stops pending requests and returns the typed 429 (including
148
+ its `retryAfterMs`) for callers to reschedule, without retrying early.
149
+ Network and 5xx failures may have
150
+ already contributed activity: identifier-to-RID resolution is idempotent, but
151
+ the activity log is additive. The single-call API guards the 10,000-item limit;
152
+ use the helper for byte-based splitting. `/api/registry/resolve` accepts responses
153
+ up to 16 MiB by default; explicit `RegistryClientConfig.maxBodyBytes` still wins.
154
+
155
+ Use `saveProperty()` when publishing or updating hosted publisher
156
+ configuration (names, property identities, tags), rather than contributing a
157
+ large identifier catalog. `saveProperties()` is a bounded client-side fan-out
158
+ over that endpoint, intended for configuration updates. Its results are keyed
159
+ by publisher domain; submit one configuration per domain.
160
+
161
+ Registry HTTP failures use the exported `RegistryRequestError` /
162
+ `isRegistryRequestError` contract, with `status`, `method`, `retryAfterMs`, and
163
+ bounded untrusted `details`. Do not parse `error.message` for status or retry
164
+ timing. Input validation and transport failures are separate errors.
165
+
166
+ ## Serve buyer-issued references over MCP
167
+
168
+ `registerPropertyListTool` registers a real MCP `get_property_list` tool using
169
+ the official MCP server. The reference's `auth_token` must authorize its exact
170
+ `list_id`. It arrives in `Authorization: Bearer` or `x-adcp-auth`, never as a
171
+ tool argument. The verifier runs before storage is read, on every page.
172
+
173
+ ```typescript
174
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
175
+ import { serve } from '@adcp/sdk';
176
+ import { registerPropertyListTool } from '@adcp/sdk/server';
177
+
178
+ serve(() => {
179
+ const server = new McpServer({ name: 'Buyer property lists', version: '1.0.0' });
180
+ registerPropertyListTool(server, {
181
+ verifyToken: ({ listId, token }) => listStore.verifyListToken(listId, token),
182
+ loadPage: (request, principal) => listStore.loadAuthorizedPage(request, principal),
183
+ });
184
+ return server;
185
+ }, {
186
+ path: '/property-lists/mcp',
187
+ publicUrl: 'https://buyer.example/property-lists/mcp',
188
+ authenticate: listEndpointAuthenticator,
189
+ });
190
+ ```
191
+
192
+ Use your list endpoint's authenticator to validate list tokens globally and
193
+ return `{ principal, token }`; the per-tool verifier still checks exact list
194
+ access. Mount this read-only endpoint separately if your normal platform auth
195
+ only accepts account bearers. Its proxy/auth middleware must allow the list
196
+ token through. Ordinary MCP initialization needs no list ID. The helper also
197
+ supports raw HTTP headers when a transport does not populate `authInfo`.
198
+ It denies conflicting credentials and does not expose verifier/storage errors.
199
+
200
+ `loadPage` receives a verified principal and the validated request, including
201
+ `resolve` and `pagination`. Return `{ list, identifiers, pagination?,
202
+ resolved_at?, cache_valid_until? }`. The list includes at least `list_id` and
203
+ `name`; `identifiers` must be an array when resolving, including `[]` for an
204
+ empty list. For `resolve: false`, return metadata without resolving identifiers.
205
+ Honor the requested page limit (default 1,000; maximum 10,000). Provide a
206
+ nonempty continuation cursor only when `has_more` is true, and bind cursors to
207
+ the list, principal, and catalog revision. `list.property_count` is the total
208
+ property count, not the current page's identifier count. Return coherent
209
+ `cache_valid_until` timestamps across pages. The helper adds completion status
210
+ and echoes request context on success and errors.
211
+
212
+ Issue references whose `agent_url` points at this MCP endpoint. A plain REST
213
+ URL returning JSON does not implement `get_property_list`.
214
+
215
+ ## Signed change notifications and certification
216
+
217
+ Use your configured SDK `WebhookEmitter` (or the emitter from
218
+ `createPostgresWebhookRuntime`) so signing, bounded retries, durable delivery
219
+ bindings, and crash recovery use the same infrastructure as other webhooks:
220
+
221
+ ```typescript
222
+ import { sendPropertyListChanged } from '@adcp/sdk/server';
223
+
224
+ const result = await sendPropertyListChanged(
225
+ webhooks.emitter.forTenantScope(trustedTenantId),
226
+ {
227
+ url: subscriber.webhookUrl,
228
+ delivery_id: persistedDeliveryId,
229
+ notification: {
230
+ list_id: changedListId,
231
+ resolved_at: resolvedAt.toISOString(),
232
+ change_summary: { properties_added: added, properties_removed: removed, total_properties: total },
233
+ },
234
+ },
235
+ );
236
+ ```
237
+
238
+ Retain the same delivery ID and exact notification for retries. Each distinct
239
+ event/subscriber delivery needs its own ID. The helper selects RFC 9421,
240
+ includes the compatibility marker `signature: 'rfc9421'`, and lets the emitter
241
+ bind a stable cryptographic `idempotency_key`. It sends a summary; receivers
242
+ refresh through `get_property_list`. Configure production delivery storage,
243
+ recovery, signing keys, and per-attempt subscription authorization on the
244
+ emitter; see [the runtime reference](../TYPE-SUMMARY.md#postgresql-webhook-runtime).
245
+
246
+ Before certifying a buyer, resolve references it actually issued:
247
+
248
+ ```typescript
249
+ import { runPropertyListConformance, runConformance } from '@adcp/sdk/conformance';
250
+
251
+ const report = await runPropertyListConformance(issuedReferences);
252
+ if (report.totalFailures) throw new Error('Buyer-issued property lists are not resolvable');
253
+
254
+ // Or include them in the main certification report/failure count:
255
+ const fullReport = await runConformance(buyerAgentUrl, {
256
+ propertyListReferences: issuedReferences,
257
+ });
258
+ ```
259
+
260
+ Certification disables caches, validates each resolved page, checks list
261
+ identity, rejects failure envelopes/oversized pages/cursor cycles, and uses
262
+ the same MCP resolver as sellers. Reports identify inputs by index and never
263
+ echo the reference token, URL, or remote exception. Empty lists are valid;
264
+ unresolvable references fail the report rather than silently certifying.
@@ -503,6 +503,31 @@ app.post('/webhook', async (req, res) => {
503
503
  });
504
504
  ```
505
505
 
506
+ Webhook-v1 uses unpadded Base64URL for `Signature` throughout AdCP 3.x,
507
+ including 3.2. Its URL canonicalization follows AdCP 3.2 independently of
508
+ that encoding. The 3.2 request-signing migration to padded standard Base64
509
+ does not change the webhook-v1 profile. Python senders should use
510
+ `adcp.signing.webhook_signer.sign_webhook` rather than a generic
511
+ `sign_request` call with `signing_profile_version="3.2"` and the webhook tag.
512
+ See the [protocol's binary-value encoding rules](https://github.com/adcontextprotocol/adcp/blob/v3.2.5/docs/building/by-layer/L1/security.mdx#adcp-rfc-9421-profile).
513
+
514
+ When a known peer uses a generic RFC 9421 signer that emits standard padded
515
+ Base64, configure that receiver route explicitly:
516
+
517
+ ```typescript
518
+ await verifyWebhookSignature(req, {
519
+ jwks,
520
+ replayStore,
521
+ revocationStore,
522
+ signatureEncoding: 'rfc8941-base64',
523
+ });
524
+ ```
525
+
526
+ `createWebhookVerifier` accepts the same option. Each mode strictly parses
527
+ its configured encoding; neither retries the other encoding. Select this
528
+ option from trusted integration configuration, independently of incoming
529
+ headers or webhook payloads.
530
+
506
531
  ## Step 6: Sign Outbound Webhooks (Seller)
507
532
 
508
533
  Configure `createAdcpServerFromPlatform` with a webhook signing key:
package/docs/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # Ad Context Protocol (AdCP)
2
2
 
3
- > Generated at: 2026-10-17
4
- > Library: @adcp/sdk v16.0.0-beta.7
3
+ > Generated at: 2026-10-18
4
+ > Library: @adcp/sdk v16.0.0-beta.8
5
5
  > AdCP major version: 3
6
6
  > Canonical URL: https://adcontextprotocol.github.io/adcp-client/llms.txt
7
7
  > Note: the `Library` stamp reflects the package.json version at doc-generation time. The narrative below describes the surface that lands on the next-published minor — including any 6.7 helpers documented here ahead of the release tag.
@@ -16,8 +16,8 @@ pin using `ADCP_VERSION` inside that tarball.
16
16
 
17
17
  | Fact | Value |
18
18
  | --- | --- |
19
- | Checkout `package.json` version | `@adcp/sdk@16.0.0-beta.7` |
20
- | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@16.0.0-beta.7 dist.integrity` |
19
+ | Checkout `package.json` version | `@adcp/sdk@16.0.0-beta.8` |
20
+ | npm integrity | registry-derived after publication; run `npm view @adcp/sdk@16.0.0-beta.8 dist.integrity` |
21
21
  | Node.js runtime | `^20.19.0 || >=22.12.0` |
22
22
  | Default AdCP wire release | `3.2.5` |
23
23
  | Maintained wire releases | `v2.5`, `v2.6`, `v3`, `3.0.0`, `3.0`, `3.0.1`, `3.0.2`, `3.0.3`, `3.0.4`, `3.0.5`, `3.0.6`, `3.0.7`, `3.0.8`, `3.0.9`, `3.0.10`, `3.0.11`, `3.0.12`, `3.0.13`, `3.0.14`, `3.0.15`, `3.0.16`, `3.0.17`, `3.0.18`, `3.0.19`, `3.0.20`, `3.0.21`, `3.0.22`, `3.0.23`, `3.0.24`, `3.0.25`, `3.1.0`, `3.1`, `3.1.1`, `3.1.2`, `3.1.3`, `3.1.4`, `3.1.5`, `3.1.6`, `3.1.7`, `3.1.8`, `3.1.9`, `3.1.10`, `3.1.11`, `3.1.12`, `3.1.13`, `3.1.14`, `3.1.15`, `3.1.16`, `3.1.17`, `3.1.18`, `3.1.19`, `3.1.20`, `3.1.21`, `3.1.22`, `3.1.23`, `3.1.24`, `3.1.25`, `3.1.26`, `3.1.27`, `3.1.28`, `3.1.29`, `3.2.1`, `3.2`, `3.2.2`, `3.2.3`, `3.2.4`, `3.2.5` |
@@ -26,8 +26,8 @@ pin using `ADCP_VERSION` inside that tarball.
26
26
  For a checkout at the published release commit, install the exact package version:
27
27
 
28
28
  ```bash
29
- npm install --save-exact '@adcp/sdk@16.0.0-beta.7'
30
- npm view '@adcp/sdk@16.0.0-beta.7' dist.integrity
29
+ npm install --save-exact '@adcp/sdk@16.0.0-beta.8'
30
+ npm view '@adcp/sdk@16.0.0-beta.8' dist.integrity
31
31
  ```
32
32
 
33
33
  ### Required and optional peers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcp/sdk",
3
- "version": "16.0.0-beta.7",
3
+ "version": "16.0.0-beta.8",
4
4
  "description": "AdCP SDK — client, server, and compliance harnesses for the AdContext Protocol (MCP + A2A)",
5
5
  "workspaces": [
6
6
  ".",