Skip to content

Commit f9bf815

Browse files
authored
feat: add policies[] to cart, checkout, catalog, and order (#572)
* add policies[] to cart, checkout, and catalog Introduce `policies[]`, a core response field carrying business rules about the items — return/refund terms, warranty, and custom policies — on cart, checkout, and catalog (search, lookup, get_product) responses. Entries target items with RFC 9535 JSONPath via `applies_to` (omitted = whole response; most-specific match wins). Key design decisions: - Core, not an extension: policies are facts the business expresses directly; reading them requires no capability negotiation. - Cross-surface: the same field applies on catalog, cart, and checkout responses. - Open, reverse-DNS `type` vocabulary (`dev.ucp.shopping.policy.refund`, `.warranty`; custom types in the author's own domain). Platforms tolerate unknown types — no closed enum, no central registry, no namespace collisions. - Open envelope + discriminated overlay: the base validates any policy; `refund` layers structured fields (`window` xor `final_sale`, mutually exclusive) via an allOf if/then. Adding a well-known overlay is additive. - `type` + `description` are the required floor (description MUST, stated in prose) so any policy is presentable without modeling its type. No `id`: policies are referenced positionally and by `url`, matching `discount`. - Disclosure lives in messages, not policies — a policy never forces display. When a term must be shown, the business emits a `messages[]` warning with `presentation: "disclosure"`, linked to its policy by `code == type` at the same target. One forcing channel, consistent with existing Prop 65 notices. - Complementary to `links[]`: policies are the structured layer, `links[]` the always-present fallback; a policy omitting `url` resolves the `links[]` entry of the corresponding type. * define policies[] resolution & precedence rules A response can carry several policies of the same `type` — a store-wide default plus per-item overrides. A reader needs one deterministic answer to "which policy governs this node?", or the same document renders differently across platforms. The initial cut left that as a one-line "most-specific match wins" and embedded a refund sketch in the type. This commit makes the resolution rule precise and pulls the refund domain body out to its own proposal, so the generic container can stand on its own as infrastructure. `applies_to` binds a policy to nodes by RFC 9535 JSONPath, relative to the response root — the convention `messages[].path` and `discount.allocations[].path` already use. RFC 9535 also gives us the two instruments precedence needs, so we don't build a parallel system: - a Normalized Path (Section 2.7) is a node's canonical identity: `$.products[0].variants[3]` is the segment sequence `products, 0, variants, 3`, and the root `$` is the empty sequence; - a singular query (Section 2.3.5.1) is an expression that names exactly one node using only name and index selectors, as opposed to a filter, wildcard, or slice, which match a set. A direct id-reference would need neither, but it also can't express the thing policy resolution is mostly about: cascade. A target covers the node it names and everything nested under it, so `$.products[0]` governs a product and all its variants in a single entry, while `$.products[0].variants[3]` overrides just that variant. An id names one item; the cascade would have to be enumerated node by node. For a given `type`, among the policies whose target covers a node: 1. A target covers a node when the node it matches is that node or an ancestor of it — equivalently, when the matched node's Normalized Path is a prefix of the target's. That prefix length is its depth (a Response-wide policy targets the root `$`, so depth 0). 2. Score each policy by its deepest covering match as `(depth, precision)`: depth first; precision is 1 for a singular query and 0 for a Set match. 3. The greatest score governs — deepest wins, and at equal depth a singular query outranks a Set match. So a variant-level target (depth 4) beats a product-level one (depth 2) beats a response-wide default (depth 0); and where two reach the same node at the same depth, `$.products[0]` (singular) beats `$.products[?@.category=='electronics']` (a set). Depth first is deliberate: ranking selector kinds regardless of nesting would let a wildcard on a variant lose to an index on the product, inverting the cascade. And we stop at the singular-vs-set distinction because that is the only ordering RFC 9535 defines. When two same-`type` policies cover a node at the same depth and precision — two overlapping Set matches, or two Response-wide entries — the outcome is undefined. This is model-independent: naming the node by an id would collide the same way, because the author has said two contradictory things about one node. A Business MUST NOT publish such a collision; a Platform MUST surface a warning rather than resolve it silently. * bind policy disclosures to the governing policy The disclosure pairing said a disclosure pairs with "the policy" at its `path` node, joined by `code == type`. With precedence now defined, that's ambiguous when several policies of the same `type` cover the node. Tie the pairing to precedence instead: a disclosure pairs with the policy that governs its node — the one precedence selects — which is at most one, so the pairing is unambiguous. Naming the governing policy also makes two authoring drifts nameable, since `messages[]` and `policies[]` are authored separately: - Contradiction — the notice disagrees with its policy. They are two statements about one node, so a disclosure's content MUST agree with the policy it pairs with. - Dangling — `code` names a `type` no policy covers at the node, so the pairing resolves to nothing. The notice still displays, so this is a SHOULD: requiring a backing policy would couple the two arrays and forbid force-displaying a notice without a structured policy. Neither carries a Platform MUST-warn. That stays reserved for precedence collisions — the case that is both machine-detectable and leaves resolution undefined. A contradiction is free text on the message side, and a dangling pairing is benign. * feat: add policies[] to order, decouple refund - Add `policies[]` to `order` as a read-only snapshot of the policies that applied at checkout (`ucp_request: omit`), mirroring how `order.attribution` snapshots checkout data. Completes the catalog -> cart -> checkout -> order lifecycle; returns are post-purchase, so the order is the durable home for their terms. - Remove the refund `allOf` overlay from `policy.json` and delete the now-empty `policy_refund.json`. Core stays a pure generic container; the refund type attaches its body via extension composition (#257). - Type `policy.type` via `$ref` to `reverse_domain_name.json`, so the reverse-DNS format is enforced rather than described only in prose. - State in Precedence that a Platform MUST NOT merge same-type policy bodies: a consumer cannot infer combine-vs-replace semantics, so it resolves a single governing policy by structure alone. Composition is authored into the most-specific policy, not resolved at read time. - Collapse the disclosure-pairing passage to one paragraph plus two numbered rules (editorial). * docs: rename refund policy type to return, soften collisions - Rename the well-known type `dev.ucp.shopping.policy.refund` to `.return`. schema.org models this as MerchantReturnPolicy (return is the head concept; refundType is one outcome facet), so `refund` named the policy after a facet rather than the container. The refund / exchange / credit outcomes live in the type body defined by #257. - Soften the collision rule: a Platform SHOULD (was MUST) flag an authoring collision rather than resolve it silently, treatment left to the Platform. A MUST-surface obligation was inconsistent with policy presentation being optional and had no defined audience. The Business MUST NOT publish a collision is unchanged.
1 parent 7be6f3d commit f9bf815

11 files changed

Lines changed: 343 additions & 1 deletion

File tree

docs/specification/cart.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,12 @@ are estimates; accurate taxes are computed at checkout.
286286
### Link
287287

288288
{{ schema_fields('types/link', 'checkout') }}
289+
290+
### Policy
291+
292+
Policies (return/refund terms, warranty, and the like) that apply to the items
293+
in this cart. JSONPath targets in `applies_to` are relative to
294+
this response root (e.g., `$.line_items[0]`). See
295+
[Policies](overview.md#policies) for the full model.
296+
297+
{{ schema_fields('types/policy', 'cart') }}

docs/specification/catalog/index.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,15 @@ as the first element. Platforms SHOULD treat the first element as featured.
150150

151151
{{ schema_fields('types/rating', 'catalog') }}
152152

153+
### Policy
154+
155+
Policies (return/refund terms, warranty, and the like) that apply to the
156+
products in a catalog response. JSONPath targets in `applies_to` are
157+
relative to the response root — `$.products[N]` for search and batch lookup,
158+
`$.product` for get_product. See [Policies](../overview.md#policies) for the full model.
159+
160+
{{ schema_fields('types/policy', 'catalog') }}
161+
153162
## Actions
154163

155164
Catalog Search, batch Lookup, and successful Get Product responses can include

docs/specification/checkout.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,9 @@ downgrading to a notice.
573573
* **SHOULD** use the `path` field to associate disclosures with the
574574
relevant component in the response.
575575
* **SHOULD** provide a `code` that identifies the disclosure category
576-
(e.g., `prop65`, `allergens`, `energy_label`).
576+
(e.g., `prop65`, `allergens`, `energy_label`). When the disclosure renders a
577+
structured `policies[]` entry, the `code` **MUST** equal that policy's `type`
578+
to link the two (see [Policies](overview.md#policies)).
577579
* **SHOULD** provide `image_url` when the disclosure has an associated
578580
visual element (e.g., warning symbol, energy class label).
579581
* **SHOULD** provide `url` when a reference link is available for the
@@ -945,6 +947,15 @@ Businesses **MAY** define custom types for domain-specific needs. Platforms
945947
**SHOULD** handle unknown types gracefully by displaying them using the `title`
946948
field or omitting them.
947949

950+
### Policy
951+
952+
Policies (return/refund terms, warranty, and the like) that apply to the items
953+
in this checkout. JSONPath targets in `applies_to` are relative to
954+
this response root (e.g., `$.line_items[0]`). See
955+
[Policies](overview.md#policies) for the full model.
956+
957+
{{ schema_fields('types/policy', 'checkout') }}
958+
948959
### Message
949960

950961
{{ schema_fields('message', 'checkout') }}

docs/specification/order.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ Businesses MAY surface a snapshot of the originating checkout's
9494
`order.attribution`. See [Attribution](overview.md#attribution) for the
9595
underlying contract.
9696

97+
### Policies
98+
99+
Businesses MAY surface a snapshot of the policies that applied at checkout on
100+
the order. See [Policies](overview.md#policies) for the underlying contract.
101+
97102
### Adjustments
98103

99104
**Adjustments** are post-order events that exist independently of

docs/specification/overview.md

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2252,6 +2252,235 @@ Detailed definitions for endpoints, schemas, and valid extensions for each
22522252
capability are provided in their respective specification files. Extensions are
22532253
typically versioned and defined alongside their parent capability.
22542254

2255+
## Policies
2256+
2257+
A policy is a business rule — return terms, warranty, subscription
2258+
terms, and the like — that applies to the items in a response at the time of
2259+
purchase, carried in a core `policies[]` array alongside `messages[]` and
2260+
`links[]`.
2261+
2262+
### Policy types
2263+
2264+
A Business publishes well-known and custom policies. Every policy carries a
2265+
`type` drawn from an open, reverse-DNS vocabulary.
2266+
2267+
| Well-known type | Description |
2268+
| :-- | :-- |
2269+
| `dev.ucp.shopping.policy.return` | Return terms. |
2270+
| `dev.ucp.shopping.policy.warranty` | Warranty terms. |
2271+
2272+
A Business **MAY** define custom types in its own domain (e.g.,
2273+
`com.example.policy.price_match`) and **MAY** add type-specific fields that a
2274+
Platform modeling that `type` can read for structured context. Because the
2275+
vocabulary is open, a Platform **MUST** tolerate unknown `type` values,
2276+
presenting the policy from its `description` (see
2277+
[Presenting policies](#presenting-policies)).
2278+
2279+
### Targeting
2280+
2281+
`applies_to` is an array of RFC 9535 JSONPath expressions, evaluated relative to
2282+
the **embedding response root** — the same convention `messages[].path` uses.
2283+
The root differs by surface: `$.line_items[N]` on cart, checkout, and order,
2284+
`$.products[N]` on catalog search and lookup, `$.product` on get_product. A
2285+
policy targets nodes in one of three forms:
2286+
2287+
- **Singular query** — an expression naming exactly one node using only name
2288+
and index selectors ([RFC 9535
2289+
§2.3.5.1](https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1)), e.g.
2290+
`$.line_items[2]`.
2291+
- **Set match** — a filter, wildcard, or slice matching a set of nodes, e.g.
2292+
`$.products[?@.category=='electronics']`.
2293+
- **Response-wide** — an omitted `applies_to`; the policy applies to the entire
2294+
response. This is the common case: a single site-wide policy is one entry with
2295+
no targeting, never repeated per item.
2296+
2297+
A target covers the node it names **and everything nested under it** — a policy
2298+
on `$.products[0]` covers the product and all its variants, while one on
2299+
`$.products[0].variants[3]` covers only that variant. To give one variant a
2300+
different term, a Business names it directly; the narrower target wins where the
2301+
two overlap (see Precedence, below).
2302+
2303+
### Precedence
2304+
2305+
Policies of **different** `type` are independent: each applies on its own, so a
2306+
single node can carry a warranty policy and a price-match policy at once.
2307+
2308+
Policies of the **same** `type` can contest a node. When they do, exactly one
2309+
governs and **replaces** the rest — a Platform **MUST NOT** merge their bodies.
2310+
Merging would mean inferring whether policies combine or replace, which a
2311+
Platform cannot read from the data; resolution selects one governing policy by
2312+
structure alone. When terms genuinely stack, the Business folds them into the
2313+
most-specific policy — composition is authored, not resolved. Resolving which
2314+
one governs is a **longest-prefix match** against the response in hand.
2315+
2316+
Every node has a canonical identity: its **Normalized Path** ([RFC 9535
2317+
§2.7](https://www.rfc-editor.org/rfc/rfc9535#section-2.7)), the sequence of
2318+
segments locating it from the root — `$.products[0].variants[3]` is `products`,
2319+
`0`, `variants`, `3`, and the root `$` is the empty sequence. A target
2320+
**covers** a node when a node it matches is that node or an ancestor of it —
2321+
equivalently, when the matched node's Normalized Path is a prefix of the target
2322+
node's. The length of that prefix is its **depth**.
2323+
2324+
To resolve which policy of a given `type` governs a node:
2325+
2326+
1. Take the same-`type` policies with a target covering the node. A
2327+
Response-wide policy (omitted `applies_to`) targets the root `$`, so it
2328+
covers every node at depth 0. If none cover the node, no policy of that
2329+
`type` governs it.
2330+
2. Score each by its **deepest** covering match as the pair `(depth,
2331+
precision)`: *depth* is that match's prefix length; *precision* is `1`
2332+
when the target is a **singular query** ([RFC 9535
2333+
§2.3.5.1](https://www.rfc-editor.org/rfc/rfc9535#section-2.3.5.1) — name and
2334+
index selectors only, so it names a single node) and `0` when it is a **Set
2335+
match** (filter, wildcard, or slice). If several covering matches share the
2336+
greatest depth, take the greatest precision among them.
2337+
3. The policy with the greatest score governs — **depth first, then
2338+
precision**.
2339+
4. When the greatest score is shared, the outcome is **undefined**. A tie
2340+
requires two policies to cover the node at the same depth and precision;
2341+
since a node has one ancestor at each depth, they contest the *same* node —
2342+
two overlapping Set matches, or two Response-wide entries of one `type`. This
2343+
is an authoring error, not an artifact of path targeting: naming the node by
2344+
an id would collide the same way. A Business **MUST NOT** publish such a
2345+
collision. Because resolution is undefined, a Platform **SHOULD** flag the
2346+
ambiguity rather than resolve it silently; the treatment is left to the
2347+
Platform.
2348+
2349+
### Absent vs. empty
2350+
2351+
When `policies[]` is absent or empty for a given response, the Platform
2352+
**SHOULD** refer to the general policy resources in `links[]` (e.g.,
2353+
`refund_policy`, or per-variant `seller.links` in catalog).
2354+
2355+
### Presenting policies
2356+
2357+
Policies describe the business rules applied to the items. A Platform **MAY**
2358+
reason over them for its own decisions — eligibility, a computed return
2359+
deadline. So that a policy is always presentable — even by a Platform that does
2360+
not model its `type` — a Business **MUST** provide a `description`, a
2361+
human-readable summary a Platform **MAY** surface to the Buyer. Presenting a
2362+
policy is optional.
2363+
2364+
When a Business **requires** a policy to be shown to the Buyer — a final-sale
2365+
item, a regulatory notice — it **MUST** emit a `messages[]` warning that:
2366+
2367+
- sets `presentation: "disclosure"`, so the Platform displays the content and
2368+
cannot hide or dismiss it (see
2369+
[Warning Presentation](checkout.md#warning-presentation));
2370+
- sets `path` to the item the notice concerns; and
2371+
- sets `code` to the policy's `type`, linking the notice to its policy.
2372+
2373+
The warning is type-agnostic: the Platform shows its content without
2374+
understanding the policy behind it, so one channel handles everything from
2375+
final-sale terms to regulatory notices.
2376+
2377+
A disclosure pairs with the policy that **governs** its `path` node — the one
2378+
[Precedence](#precedence) selects when several policies of the same `type` cover
2379+
that node. Precedence yields at most one, so the pairing is unambiguous. Two
2380+
rules apply:
2381+
2382+
1. A disclosure's content **MUST** agree with the policy it pairs with — the
2383+
notice and the policy are two statements about the same node.
2384+
2. A disclosure **SHOULD** resolve to a governing policy: when its `code` names
2385+
a `type` no policy covers at that node, the notice still displays, but
2386+
nothing structured stands behind it.
2387+
2388+
For example, an engraved line item is final sale. A response-wide return policy
2389+
applies to the whole cart, but the item-scoped final-sale policy governs line 2,
2390+
so the disclosure on that line pairs with it — notice and policy agree:
2391+
2392+
<!-- ucp:example schema=shopping/checkout target=$.policies -->
2393+
```json
2394+
[
2395+
{
2396+
"type": "dev.ucp.shopping.policy.return",
2397+
"description": { "plain": "Free 30-day returns from delivery." }
2398+
},
2399+
{
2400+
"type": "dev.ucp.shopping.policy.return",
2401+
"description": { "plain": "This engraved item is final sale and cannot be returned." },
2402+
"applies_to": ["$.line_items[2]"],
2403+
"url": "https://example.com/returns#final-sale"
2404+
}
2405+
]
2406+
```
2407+
2408+
<!-- ucp:example schema=shopping/checkout target=$.messages -->
2409+
```json
2410+
[
2411+
{
2412+
"type": "warning",
2413+
"code": "dev.ucp.shopping.policy.return",
2414+
"path": "$.line_items[2]",
2415+
"presentation": "disclosure",
2416+
"content": "This engraved item is final sale and cannot be returned."
2417+
}
2418+
]
2419+
```
2420+
2421+
### Relationship to `links[]`
2422+
2423+
`links[]` and `policies[]` are complementary. `links[]` is the always-present
2424+
fallback — a labeled URL, response-wide, usually one per type. `policies[]` is
2425+
the structured layer when available — typed, with optional per-item
2426+
`applies_to` targeting and multiple entries (a response-wide default plus
2427+
overrides).
2428+
2429+
### Examples
2430+
2431+
A site-wide warranty with a per-item override, both the same `type`. On line
2432+
item 2 the singular query governs, overriding the Response-wide default; every
2433+
other line item keeps it:
2434+
2435+
<!-- ucp:example schema=shopping/checkout target=$.policies -->
2436+
```json
2437+
[
2438+
{
2439+
"type": "dev.ucp.shopping.policy.warranty",
2440+
"description": { "plain": "1-year limited warranty on all items." }
2441+
},
2442+
{
2443+
"type": "dev.ucp.shopping.policy.warranty",
2444+
"description": { "plain": "3-year extended warranty on this item." },
2445+
"applies_to": ["$.line_items[2]"]
2446+
}
2447+
]
2448+
```
2449+
2450+
A Set match overridden by a singular query, again the same `type`. Product 0 is
2451+
an electronics item, so both entries reach it; the singular query governs
2452+
there, while other electronics keep the 2-year term:
2453+
2454+
<!-- ucp:example schema=shopping/catalog_search op=search direction=response target=$.policies -->
2455+
```json
2456+
[
2457+
{
2458+
"type": "dev.ucp.shopping.policy.warranty",
2459+
"description": { "plain": "2-year warranty on electronics." },
2460+
"applies_to": ["$.products[?@.category=='electronics']"]
2461+
},
2462+
{
2463+
"type": "dev.ucp.shopping.policy.warranty",
2464+
"description": { "plain": "5-year manufacturer-certified warranty on this item." },
2465+
"applies_to": ["$.products[0]"]
2466+
}
2467+
]
2468+
```
2469+
2470+
A custom `type` a Platform does not model is still presentable from its
2471+
`description`:
2472+
2473+
<!-- ucp:example schema=shopping/catalog_search op=search direction=response target=$.policies -->
2474+
```json
2475+
[
2476+
{
2477+
"type": "com.example.policy.price_match",
2478+
"description": { "plain": "We match a competitor's lower price for 14 days after purchase." },
2479+
"applies_to": ["$.products[0]"]
2480+
}
2481+
]
2482+
```
2483+
22552484
## Security
22562485

22572486
### Transport Security

source/schemas/shopping/cart.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@
122122
"description": "Optional merchant links (policies, FAQs).",
123123
"ucp_request": "omit"
124124
},
125+
"policies": {
126+
"type": "array",
127+
"items": {
128+
"$ref": "types/policy.json"
129+
},
130+
"description": "Policies (e.g., return/refund terms) that apply to the items in this cart. `applies_to` targets are relative to the response root; when absent or empty, refer to the URLs in `links[]`.",
131+
"ucp_request": "omit"
132+
},
125133
"continue_url": {
126134
"type": "string",
127135
"format": "uri",

source/schemas/shopping/catalog_lookup.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,13 @@
8686
"$ref": "../common/types/message.json"
8787
},
8888
"description": "Errors, warnings, or informational messages about the requested items."
89+
},
90+
"policies": {
91+
"type": "array",
92+
"items": {
93+
"$ref": "types/policy.json"
94+
},
95+
"description": "Policies (e.g., return/refund terms) that apply to the products in this response. `applies_to` targets are relative to the response root; when absent or empty, refer to the URLs in `links[]`."
8996
}
9097
}
9198
},
@@ -180,6 +187,13 @@
180187
"$ref": "../common/types/message.json"
181188
},
182189
"description": "Warnings or informational messages about the product (e.g., price recently changed, limited availability)."
190+
},
191+
"policies": {
192+
"type": "array",
193+
"items": {
194+
"$ref": "types/policy.json"
195+
},
196+
"description": "Policies (e.g., return/refund terms) that apply to this product. `applies_to` targets are relative to the response root; when absent or empty, refer to the URLs in `links[]`."
183197
}
184198
}
185199
}

source/schemas/shopping/catalog_search.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@
6161
"$ref": "../common/types/message.json"
6262
},
6363
"description": "Errors, warnings, or informational messages about the search results."
64+
},
65+
"policies": {
66+
"type": "array",
67+
"items": {
68+
"$ref": "types/policy.json"
69+
},
70+
"description": "Policies (e.g., return/refund terms) that apply to the products in these search results. `applies_to` targets are relative to the response root; when absent or empty, refer to the URLs in `links[]`."
6471
}
6572
}
6673
}

source/schemas/shopping/checkout.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@
106106
"description": "Links to be displayed by the platform (Privacy Policy, TOS). Mandatory for legal compliance.",
107107
"ucp_request": "omit"
108108
},
109+
"policies": {
110+
"type": "array",
111+
"items": {
112+
"$ref": "types/policy.json"
113+
},
114+
"description": "Policies (e.g., return/refund terms) that apply to the items in this checkout. `applies_to` targets are relative to the response root; when absent or empty, refer to the URLs in `links[]`.",
115+
"ucp_request": "omit"
116+
},
109117
"expires_at": {
110118
"type": "string",
111119
"format": "date-time",

source/schemas/shopping/order.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@
9696
"$ref": "types/totals.json",
9797
"description": "Different totals for the order."
9898
},
99+
"policies": {
100+
"type": "array",
101+
"items": {
102+
"$ref": "types/policy.json"
103+
},
104+
"description": "Snapshot of the policies that applied to the items at checkout, captured on the order as a durable record. `applies_to` targets are relative to the response root.",
105+
"ucp_request": "omit"
106+
},
99107
"messages": {
100108
"type": "array",
101109
"items": {

0 commit comments

Comments
 (0)