{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://usp-protocol.dev/schemas/services/booking_questions.json",
  "title": "USP Booking Questions Extension",
  "description": "Catalog-first, typed, business-enforced intake and consent for dev.usp-protocol.services.booking_questions. Canonical home of every booking-questions shape. The catalog Service, Booking, registry service hit, and paid-booking BookingContext reach these definitions through optional thin references; the root composes the buyer-input Action type into UCP checkout for paid UCP-Native bookings. Optional properties stay schema-valid when the extension is not negotiated; negotiation alone determines whether they carry semantics. See specification.md Section 11.4.",
  "requires": {
    "dev.usp-protocol.services.catalog": ">=2026-08-20",
    "dev.usp-protocol.services.bookings": ">=2026-08-20"
  },
  "allOf": [
    {
      "$ref": "https://ucp.dev/latest/schemas/shopping/checkout.json"
    },
    {
      "$ref": "#/$defs/dev.ucp.shopping.checkout"
    }
  ],
  "$defs": {
    "dev.ucp.shopping.checkout": {
      "type": "object",
      "description": "Extension properties composed into the UCP checkout root for a paid UCP-Native booking. Contributes the booking-questions buyer-input Action type to UCP's response-only actions map. Request-side submissions, the assessment token, and response-side set state ride on the paid-bookings BookingContext through thin references from paid_bookings.json, so this composition only declares the Action type.",
      "properties": {
        "actions": {
          "type": "object",
          "description": "UCP response-only actions map. This extension declares exactly one Action type key.",
          "properties": {
            "dev.usp-protocol.services.booking_questions.buyer_input": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BuyerInputAction"
              },
              "description": "Outstanding booking-questions buyer-input Actions. Emitted only when this extension is active for checkout in the negotiated intersection."
            }
          }
        }
      }
    },
    "BookingQuestionLabel": {
      "title": "BookingQuestionLabel",
      "description": "Buyer-facing label. Reuses the catalog Description object; plain is quoted verbatim by platforms and capped at 300 characters.",
      "allOf": [
        {
          "$ref": "catalog.json#/$defs/Description"
        },
        {
          "properties": {
            "plain": {
              "type": "string",
              "minLength": 1,
              "maxLength": 300
            }
          }
        }
      ]
    },
    "BookingQuestionText": {
      "title": "BookingQuestionText",
      "description": "Buyer-facing help or description text. Reuses the catalog Description object; plain is capped at 2000 characters.",
      "allOf": [
        {
          "$ref": "catalog.json#/$defs/Description"
        },
        {
          "properties": {
            "plain": {
              "type": "string",
              "minLength": 1,
              "maxLength": 2000
            }
          }
        }
      ]
    },
    "BookingQuestionNoticeContent": {
      "title": "BookingQuestionNoticeContent",
      "description": "Full notice or disclosure content. Reuses the catalog Description object. A notice MUST be available to the buyer in full; publishing only a truncated or read-more form of required disclosure content is forbidden.",
      "allOf": [
        {
          "$ref": "catalog.json#/$defs/Description"
        },
        {
          "properties": {
            "plain": {
              "type": "string",
              "minLength": 1
            }
          }
        }
      ]
    },
    "BookingQuestionConditionLeaf": {
      "type": "object",
      "title": "BookingQuestionConditionLeaf",
      "description": "A leaf condition over one earlier question in the same set. An unanswered or inapplicable reference makes answered false and every other operation false. contains is valid only for multiple-choice answers; gt, gte, lt, and lte are valid only for numeric, date, time, and date-time answers. References may name only earlier questions in the same set.",
      "additionalProperties": false,
      "required": [
        "question",
        "op"
      ],
      "properties": {
        "question": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$",
          "description": "Identifier of an earlier question in the same set."
        },
        "op": {
          "type": "string",
          "enum": [
            "equals",
            "in",
            "contains",
            "answered",
            "gt",
            "gte",
            "lt",
            "lte"
          ]
        },
        "value": {
          "description": "Comparison operand. Absent for answered; an array of candidate values for in; otherwise a single value of the referenced answer's type."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "op": {
                "const": "answered"
              }
            }
          },
          "then": {
            "not": {
              "required": [
                "value"
              ]
            }
          },
          "else": {
            "required": [
              "value"
            ]
          }
        },
        {
          "if": {
            "properties": {
              "op": {
                "const": "in"
              }
            }
          },
          "then": {
            "properties": {
              "value": {
                "type": "array",
                "minItems": 1
              }
            }
          }
        }
      ]
    },
    "BookingQuestionConditionDepth1": {
      "title": "BookingQuestionConditionDepth1",
      "description": "A condition with at most one combinator level.",
      "oneOf": [
        {
          "$ref": "#/$defs/BookingQuestionConditionLeaf"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "all"
          ],
          "properties": {
            "all": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionLeaf"
              }
            }
          },
          "description": "True when every member condition is true. Innermost combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "any"
          ],
          "properties": {
            "any": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionLeaf"
              }
            }
          },
          "description": "True when at least one member condition is true. Innermost combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "not"
          ],
          "properties": {
            "not": {
              "$ref": "#/$defs/BookingQuestionConditionLeaf"
            }
          },
          "description": "True when the member condition is false. Innermost combinator level."
        }
      ]
    },
    "BookingQuestionConditionDepth2": {
      "title": "BookingQuestionConditionDepth2",
      "description": "A condition with at most two combinator levels.",
      "oneOf": [
        {
          "$ref": "#/$defs/BookingQuestionConditionLeaf"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "all"
          ],
          "properties": {
            "all": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionDepth1"
              }
            }
          },
          "description": "True when every member condition is true. Second combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "any"
          ],
          "properties": {
            "any": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionDepth1"
              }
            }
          },
          "description": "True when at least one member condition is true. Second combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "not"
          ],
          "properties": {
            "not": {
              "$ref": "#/$defs/BookingQuestionConditionDepth1"
            }
          },
          "description": "True when the member condition is false. Second combinator level."
        }
      ]
    },
    "BookingQuestionCondition": {
      "title": "BookingQuestionCondition",
      "description": "A condition built from leaf operations and the all, any, and not combinators. Maximum combinator nesting depth is 3, enforced by the Depth1/Depth2 ladder; all and any contain at least one condition.",
      "oneOf": [
        {
          "$ref": "#/$defs/BookingQuestionConditionLeaf"
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "all"
          ],
          "properties": {
            "all": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionDepth2"
              }
            }
          },
          "description": "True when every member condition is true. Outermost combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "any"
          ],
          "properties": {
            "any": {
              "type": "array",
              "minItems": 1,
              "items": {
                "$ref": "#/$defs/BookingQuestionConditionDepth2"
              }
            }
          },
          "description": "True when at least one member condition is true. Outermost combinator level."
        },
        {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "not"
          ],
          "properties": {
            "not": {
              "$ref": "#/$defs/BookingQuestionConditionDepth2"
            }
          },
          "description": "True when the member condition is false. Outermost combinator level."
        }
      ]
    },
    "BookingQuestionRequired": {
      "title": "BookingQuestionRequired",
      "description": "Whether an applicable item must be completed. A boolean, or a condition that makes the item required only when it is true. required: true on a boolean question is satisfied by both true and false; only schema const: true forces an affirmative value.",
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "$ref": "#/$defs/BookingQuestionCondition"
        }
      ]
    },
    "BookingQuestionDateBound": {
      "title": "BookingQuestionDateBound",
      "description": "Inclusive date bound: either an absolute RFC 3339 full-date, or a signed ISO 8601 duration (ISO 8601-2 sign extension) added to the request date in the business's time zone. P0D is the request date; -P18Y is 18 years before it. No keyword such as today is defined.",
      "type": "string",
      "anyOf": [
        {
          "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$",
          "format": "date"
        },
        {
          "pattern": "^-?P(?=\\d)(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?$"
        }
      ]
    },
    "BookingQuestionOption": {
      "type": "object",
      "title": "BookingQuestionOption",
      "description": "One choice option. const is the stable option identifier the platform submits; title is the plain-text label the platform quotes verbatim. title stays a JSON Schema string so the answer schema remains a valid MCP elicitation primitive.",
      "additionalProperties": false,
      "required": [
        "const",
        "title"
      ],
      "properties": {
        "const": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "title": {
          "type": "string",
          "minLength": 1,
          "maxLength": 300
        },
        "image_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://",
          "description": "Optional HTTPS image illustrating the option. The option title MUST still convey its meaning on its own."
        },
        "exclusive": {
          "type": "boolean",
          "description": "When true, selecting this option together with any other option is booking_question_answer_invalid (for example a none-of-the-above option)."
        },
        "localized": {
          "type": "object",
          "description": "Per-locale override of this option's title only. Keys are BCP 47 language tags. const never changes.",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 300
              }
            }
          }
        }
      }
    },
    "BookingQuestionStringSchema": {
      "type": "object",
      "title": "BookingQuestionStringSchema",
      "description": "Free-text or formatted string answer. pattern is forbidden: a business that needs a specific text format describes it in the question description and restates it in any booking_question_answer_invalid message. A string without a format MUST declare maxLength.",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "const": "string"
        },
        "minLength": {
          "type": "integer",
          "minimum": 0,
          "maximum": 5000
        },
        "maxLength": {
          "type": "integer",
          "minimum": 1,
          "maximum": 5000
        },
        "format": {
          "type": "string",
          "enum": [
            "email",
            "uri",
            "date",
            "time",
            "date-time",
            "phone"
          ],
          "description": "phone is an E.164 number. A time question MUST NOT be routed to an MCP elicitation form, whose primitives stop at email, uri, date, and date-time."
        },
        "allowed_country_codes": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[1-9][0-9]{0,2}$"
          },
          "description": "Optional E.164 country calling codes (digits without +) a phone answer must start with. Valid only with format phone."
        },
        "not_before": {
          "$ref": "#/$defs/BookingQuestionDateBound"
        },
        "not_after": {
          "$ref": "#/$defs/BookingQuestionDateBound"
        }
      },
      "allOf": [
        {
          "if": {
            "not": {
              "required": [
                "format"
              ]
            }
          },
          "then": {
            "required": [
              "maxLength"
            ]
          }
        },
        {
          "if": {
            "not": {
              "required": [
                "format"
              ],
              "properties": {
                "format": {
                  "const": "phone"
                }
              }
            }
          },
          "then": {
            "not": {
              "required": [
                "allowed_country_codes"
              ]
            }
          }
        },
        {
          "if": {
            "not": {
              "required": [
                "format"
              ],
              "properties": {
                "format": {
                  "enum": [
                    "date",
                    "date-time"
                  ]
                }
              }
            }
          },
          "then": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "not_before"
                  ]
                },
                {
                  "required": [
                    "not_after"
                  ]
                }
              ]
            }
          }
        }
      ]
    },
    "BookingQuestionNumberSchema": {
      "type": "object",
      "title": "BookingQuestionNumberSchema",
      "description": "Integer or number answer with optional inclusive bounds. A rating is an integer question with a rating rendering hint.",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "enum": [
            "integer",
            "number"
          ]
        },
        "minimum": {
          "type": "number"
        },
        "maximum": {
          "type": "number"
        }
      }
    },
    "BookingQuestionBooleanSchema": {
      "type": "object",
      "title": "BookingQuestionBooleanSchema",
      "description": "Boolean answer. With const: true it is an explicit acknowledgment, which satisfies a requirement only through the negotiated buyer-input Action contract; the same bare value outside that context has no acceptance semantics.",
      "additionalProperties": false,
      "required": [
        "type"
      ],
      "properties": {
        "type": {
          "const": "boolean"
        },
        "const": {
          "const": true
        }
      }
    },
    "BookingQuestionSingleChoiceSchema": {
      "type": "object",
      "title": "BookingQuestionSingleChoiceSchema",
      "description": "Single choice using the MCP-aligned string oneOf of {const, title} options. Option const values MUST be unique within the question.",
      "additionalProperties": false,
      "required": [
        "type",
        "oneOf"
      ],
      "properties": {
        "type": {
          "const": "string"
        },
        "oneOf": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/$defs/BookingQuestionOption"
          }
        }
      }
    },
    "BookingQuestionMultipleChoiceSchema": {
      "type": "object",
      "title": "BookingQuestionMultipleChoiceSchema",
      "description": "Multiple choice using items.anyOf of {const, title} options, so every option keeps its title. uniqueItems MUST be true. A bare array of option identifiers is not a conforming shape.",
      "additionalProperties": false,
      "required": [
        "type",
        "items",
        "uniqueItems"
      ],
      "properties": {
        "type": {
          "const": "array"
        },
        "items": {
          "type": "object",
          "additionalProperties": false,
          "required": [
            "anyOf"
          ],
          "properties": {
            "anyOf": {
              "type": "array",
              "minItems": 1,
              "maxItems": 50,
              "items": {
                "$ref": "#/$defs/BookingQuestionOption"
              }
            }
          }
        },
        "minItems": {
          "type": "integer",
          "minimum": 0
        },
        "maxItems": {
          "type": "integer",
          "minimum": 1
        },
        "uniqueItems": {
          "const": true
        }
      }
    },
    "BookingQuestionAnswerSchema": {
      "title": "BookingQuestionAnswerSchema",
      "description": "Restricted JSON Schema subset for one answer. Regex patterns, remote references, nested objects, patternProperties, and conditional keywords are not permitted: every variant forbids keywords it does not list.",
      "oneOf": [
        {
          "$ref": "#/$defs/BookingQuestionStringSchema"
        },
        {
          "$ref": "#/$defs/BookingQuestionNumberSchema"
        },
        {
          "$ref": "#/$defs/BookingQuestionBooleanSchema"
        },
        {
          "$ref": "#/$defs/BookingQuestionSingleChoiceSchema"
        },
        {
          "$ref": "#/$defs/BookingQuestionMultipleChoiceSchema"
        }
      ]
    },
    "BookingQuestionAnswerValue": {
      "title": "BookingQuestionAnswerValue",
      "description": "A typed answer value: string, number, boolean, or array of option identifiers. Never an object.",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "number"
        },
        {
          "type": "boolean"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      ]
    },
    "BookingQuestionSection": {
      "type": "object",
      "title": "BookingQuestionSection",
      "description": "Groups the following items until the next section. A false when makes every grouped item inapplicable. A section MUST group at least one item.",
      "additionalProperties": false,
      "required": [
        "kind",
        "id",
        "title"
      ],
      "properties": {
        "kind": {
          "const": "section"
        },
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "title": {
          "$ref": "#/$defs/BookingQuestionLabel"
        },
        "description": {
          "$ref": "#/$defs/BookingQuestionText"
        },
        "when": {
          "$ref": "#/$defs/BookingQuestionCondition"
        },
        "localized": {
          "type": "object",
          "description": "Per-locale overrides of this section's own title and description.",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "title": {
                "$ref": "#/$defs/BookingQuestionLabel"
              },
              "description": {
                "$ref": "#/$defs/BookingQuestionText"
              }
            }
          }
        }
      }
    },
    "BookingQuestionNotice": {
      "type": "object",
      "title": "BookingQuestionNotice",
      "description": "Display-only content. A disclosure is shown under the deterministic display rules or handed off to the business surface; it never carries an answer value.",
      "additionalProperties": false,
      "required": [
        "kind",
        "id",
        "content"
      ],
      "properties": {
        "kind": {
          "const": "notice"
        },
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "content": {
          "$ref": "#/$defs/BookingQuestionNoticeContent"
        },
        "presentation": {
          "type": "string",
          "enum": [
            "notice",
            "disclosure"
          ],
          "default": "notice"
        },
        "when": {
          "$ref": "#/$defs/BookingQuestionCondition"
        },
        "localized": {
          "type": "object",
          "description": "Per-locale overrides of this notice's content.",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "content": {
                "$ref": "#/$defs/BookingQuestionNoticeContent"
              }
            }
          }
        }
      }
    },
    "BookingQuestion": {
      "type": "object",
      "title": "BookingQuestion",
      "description": "A typed question. default is a buyer-editable suggestion only and is forbidden on an acknowledgment (schema const: true) and on any marketing-consent question.",
      "additionalProperties": false,
      "required": [
        "kind",
        "id",
        "label",
        "schema",
        "required"
      ],
      "properties": {
        "kind": {
          "const": "question"
        },
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "label": {
          "$ref": "#/$defs/BookingQuestionLabel"
        },
        "description": {
          "$ref": "#/$defs/BookingQuestionText"
        },
        "placeholder": {
          "type": "string",
          "maxLength": 300
        },
        "rendering": {
          "type": "string",
          "enum": [
            "text",
            "textarea",
            "rating",
            "radio",
            "dropdown",
            "checkbox",
            "tags",
            "image_choice"
          ],
          "description": "Optional presentation hint. It never changes validation."
        },
        "required": {
          "$ref": "#/$defs/BookingQuestionRequired"
        },
        "when": {
          "$ref": "#/$defs/BookingQuestionCondition"
        },
        "collection": {
          "type": "string",
          "enum": [
            "in_band",
            "platform_surface",
            "business_surface"
          ],
          "default": "in_band"
        },
        "sensitive": {
          "type": "boolean",
          "default": false,
          "description": "Sensitive answers are never echoed, logged, or quoted in messages, and are never routed to an MCP elicitation form."
        },
        "default": {
          "$ref": "#/$defs/BookingQuestionAnswerValue"
        },
        "maps_to": {
          "type": "string",
          "enum": [
            "notes"
          ],
          "description": "notes: this question is the sole surface for the booking's notes content; core notes is not separately requested or merged into it. At most one question per set."
        },
        "consent_purpose": {
          "type": "string",
          "enum": [
            "marketing"
          ],
          "description": "Marks a channel-specific marketing-consent question. It stays distinct from core buyer.consent.marketing and is never defaulted."
        },
        "consent_channel": {
          "type": "string",
          "enum": [
            "email",
            "sms",
            "phone",
            "post",
            "messaging",
            "other"
          ]
        },
        "schema": {
          "$ref": "#/$defs/BookingQuestionAnswerSchema"
        },
        "localized": {
          "type": "object",
          "description": "Per-locale overrides of this question's own text. Overrides never change id, option const values, type, constraints, or conditions.",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "label": {
                "$ref": "#/$defs/BookingQuestionLabel"
              },
              "description": {
                "$ref": "#/$defs/BookingQuestionText"
              },
              "placeholder": {
                "type": "string",
                "maxLength": 300
              }
            }
          }
        }
      },
      "allOf": [
        {
          "if": {
            "required": [
              "schema"
            ],
            "properties": {
              "schema": {
                "required": [
                  "const"
                ]
              }
            }
          },
          "then": {
            "not": {
              "required": [
                "default"
              ]
            }
          }
        },
        {
          "if": {
            "required": [
              "consent_purpose"
            ]
          },
          "then": {
            "not": {
              "required": [
                "default"
              ]
            }
          }
        },
        {
          "if": {
            "required": [
              "consent_channel"
            ]
          },
          "then": {
            "required": [
              "consent_purpose"
            ]
          }
        }
      ]
    },
    "BookingQuestionExternalTask": {
      "type": "object",
      "title": "BookingQuestionExternalTask",
      "description": "Business-hosted work (upload, signature, image markup) completed through a session-specific continue_url. Never represented as an answer value.",
      "additionalProperties": false,
      "required": [
        "kind",
        "id",
        "label",
        "task_type",
        "required",
        "collection"
      ],
      "properties": {
        "kind": {
          "const": "external_task"
        },
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "label": {
          "$ref": "#/$defs/BookingQuestionLabel"
        },
        "description": {
          "$ref": "#/$defs/BookingQuestionText"
        },
        "task_type": {
          "type": "string",
          "enum": [
            "file",
            "signature",
            "markup"
          ]
        },
        "required": {
          "$ref": "#/$defs/BookingQuestionRequired"
        },
        "when": {
          "$ref": "#/$defs/BookingQuestionCondition"
        },
        "collection": {
          "const": "business_surface"
        },
        "localized": {
          "type": "object",
          "description": "Per-locale overrides of this task's own text.",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "label": {
                "$ref": "#/$defs/BookingQuestionLabel"
              },
              "description": {
                "$ref": "#/$defs/BookingQuestionText"
              }
            }
          }
        }
      }
    },
    "BookingQuestionItem": {
      "title": "BookingQuestionItem",
      "description": "One ordered form item, discriminated by kind.",
      "oneOf": [
        {
          "$ref": "#/$defs/BookingQuestionSection"
        },
        {
          "$ref": "#/$defs/BookingQuestionNotice"
        },
        {
          "$ref": "#/$defs/BookingQuestion"
        },
        {
          "$ref": "#/$defs/BookingQuestionExternalTask"
        }
      ]
    },
    "BookingQuestionSet": {
      "type": "object",
      "title": "BookingQuestionSet",
      "description": "One business-defined form with its own purpose, timing, frequency, revision, and subject. Item identifiers are unique within the set; conditions reference only earlier questions in the same set.",
      "additionalProperties": false,
      "required": [
        "id",
        "revision",
        "purpose",
        "completion_timing",
        "frequency",
        "items"
      ],
      "properties": {
        "id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "revision": {
          "type": "string",
          "minLength": 1,
          "description": "Opaque form revision echoed in submissions. A changed revision invalidates prior once_per_buyer and once_per_buyer_per_service submissions for this set id."
        },
        "title": {
          "$ref": "#/$defs/BookingQuestionLabel"
        },
        "description": {
          "$ref": "#/$defs/BookingQuestionText"
        },
        "language": {
          "type": "string",
          "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
          "description": "BCP 47 language tag of the default text."
        },
        "localized": {
          "type": "object",
          "description": "Per-locale overrides of this set's own title and description. Missing locales or members fall back to the default text (specification Section 3.5).",
          "propertyNames": {
            "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$"
          },
          "additionalProperties": {
            "type": "object",
            "additionalProperties": false,
            "minProperties": 1,
            "properties": {
              "title": {
                "$ref": "#/$defs/BookingQuestionLabel"
              },
              "description": {
                "$ref": "#/$defs/BookingQuestionText"
              }
            }
          }
        },
        "purpose": {
          "type": "string",
          "enum": [
            "eligibility",
            "configuration",
            "intake"
          ],
          "description": "Why the information is collected. configuration answers may change slot duration through the assessment token; they never change price math."
        },
        "completion_timing": {
          "type": "string",
          "enum": [
            "preflight",
            "before_confirmation",
            "before_service"
          ]
        },
        "completion_requirement": {
          "type": "string",
          "enum": [
            "required",
            "optional"
          ],
          "default": "required",
          "description": "Valid only with purpose intake. eligibility and configuration sets are implicitly required."
        },
        "frequency": {
          "type": "string",
          "enum": [
            "every_booking",
            "once_per_buyer",
            "once_per_buyer_per_service"
          ]
        },
        "validity": {
          "type": "string",
          "pattern": "^P(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(T(\\d+H)?(\\d+M)?(\\d+S)?)?$",
          "description": "ISO 8601 duration from completion after which a once_per_buyer or once_per_buyer_per_service submission must be redone."
        },
        "subject": {
          "type": "string",
          "enum": [
            "buyer",
            "recipient"
          ],
          "default": "buyer",
          "description": "recipient means the effective recipient: the booking's recipient when present, otherwise the buyer."
        },
        "due_at": {
          "type": "string",
          "format": "date-time",
          "description": "RFC 3339 deadline. Valid only with completion_timing before_service."
        },
        "items": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
            "$ref": "#/$defs/BookingQuestionItem"
          }
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "purpose": {
                "const": "configuration"
              }
            }
          },
          "then": {
            "properties": {
              "completion_timing": {
                "const": "preflight"
              }
            }
          }
        },
        {
          "if": {
            "properties": {
              "completion_timing": {
                "const": "preflight"
              }
            }
          },
          "then": {
            "properties": {
              "purpose": {
                "enum": [
                  "eligibility",
                  "configuration"
                ]
              }
            }
          }
        },
        {
          "if": {
            "required": [
              "completion_requirement"
            ]
          },
          "then": {
            "properties": {
              "purpose": {
                "const": "intake"
              }
            }
          }
        },
        {
          "if": {
            "required": [
              "due_at"
            ]
          },
          "then": {
            "properties": {
              "completion_timing": {
                "const": "before_service"
              }
            }
          }
        }
      ]
    },
    "BookingQuestionSets": {
      "type": "array",
      "title": "BookingQuestionSets",
      "description": "Every active booking question set for one service, at most 20.",
      "maxItems": 20,
      "items": {
        "$ref": "#/$defs/BookingQuestionSet"
      }
    },
    "RequiredBuyerFields": {
      "type": "array",
      "title": "RequiredBuyerFields",
      "description": "Core Buyer member names this service requires even though they are not booking questions. Declared once per service, never inside a set. The business validates them against the request's buyer object and reports an absent one as booking_question_answer_missing.",
      "uniqueItems": true,
      "minItems": 1,
      "items": {
        "type": "string",
        "enum": [
          "first_name",
          "last_name",
          "email",
          "phone_number"
        ]
      }
    },
    "BookingQuestionSubmission": {
      "type": "object",
      "title": "BookingQuestionSubmission",
      "description": "Answers for one set and revision. collected_via is a platform provenance claim only and is never independently sufficient evidence that the buyer saw or accepted anything.",
      "additionalProperties": false,
      "required": [
        "set_id",
        "revision",
        "answers"
      ],
      "properties": {
        "set_id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "revision": {
          "type": "string",
          "minLength": 1
        },
        "answers": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z0-9_.-]{1,64}$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/BookingQuestionAnswerValue"
          },
          "description": "Map of question id to typed value. External tasks never appear here."
        },
        "collected_via": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z0-9_.-]{1,64}$"
          },
          "additionalProperties": {
            "type": "string",
            "enum": [
              "conversation",
              "platform_surface"
            ]
          }
        },
        "action_id": {
          "type": "string",
          "minLength": 1,
          "description": "Required when the submission processes an outstanding booking-questions buyer-input Action."
        }
      }
    },
    "BookingQuestionSubmissions": {
      "type": "array",
      "title": "BookingQuestionSubmissions",
      "description": "Request-side submissions on eligibility checks, booking create and update, and UCP checkout create and update. On UCP Update Checkout the platform resends every still-applicable submission.",
      "maxItems": 20,
      "items": {
        "$ref": "#/$defs/BookingQuestionSubmission"
      }
    },
    "BookingQuestionAssessmentToken": {
      "type": "string",
      "title": "BookingQuestionAssessmentToken",
      "description": "Opaque, short-lived token returned only with an eligible preflight decision. Scoped by the business to the service, applicable set revisions, buyer context when available, and calling platform. Contains no platform-readable answers and is not evidence of acceptance, identity, age, medical status, or legal capacity.",
      "minLength": 1,
      "maxLength": 4096
    },
    "BookingQuestionSetRef": {
      "type": "object",
      "title": "BookingQuestionSetRef",
      "description": "A set identifier with its live revision.",
      "additionalProperties": false,
      "required": [
        "set_id",
        "revision"
      ],
      "properties": {
        "set_id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "revision": {
          "type": "string",
          "minLength": 1
        }
      }
    },
    "BookingQuestionState": {
      "type": "object",
      "title": "BookingQuestionState",
      "description": "Extension-owned state of one set on a booking. before_service sets live here, never in core Booking.actions. Sensitive answers are never echoed.",
      "additionalProperties": false,
      "required": [
        "set_id",
        "revision",
        "status",
        "completion_timing"
      ],
      "properties": {
        "set_id": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_.-]{1,64}$"
        },
        "revision": {
          "type": "string",
          "minLength": 1
        },
        "status": {
          "type": "string",
          "enum": [
            "pending",
            "completed",
            "expired",
            "waived",
            "extended",
            "under_review",
            "rejected"
          ],
          "description": "On a required set, pending, expired, under_review, and rejected are unresolved for gating. On an optional set no status blocks confirmation, payment, or the in_progress transition."
        },
        "completion_timing": {
          "type": "string",
          "enum": [
            "preflight",
            "before_confirmation",
            "before_service"
          ]
        },
        "due_at": {
          "type": "string",
          "format": "date-time"
        },
        "valid_until": {
          "type": "string",
          "format": "date-time",
          "description": "Present when the set declares validity and status is completed or extended."
        },
        "message": {
          "type": "string",
          "minLength": 1,
          "maxLength": 2000,
          "description": "Buyer-safe reason. Required when status is rejected. Never echoes a sensitive answer."
        },
        "continue_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://",
          "description": "Session-specific business URL for pending business-surface work."
        },
        "answers": {
          "type": "object",
          "propertyNames": {
            "pattern": "^[A-Za-z0-9_.-]{1,64}$"
          },
          "additionalProperties": {
            "$ref": "#/$defs/BookingQuestionAnswerValue"
          },
          "description": "Non-sensitive accepted answer echo."
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "status": {
                "const": "rejected"
              }
            }
          },
          "then": {
            "required": [
              "message"
            ]
          }
        },
        {
          "if": {
            "required": [
              "valid_until"
            ]
          },
          "then": {
            "properties": {
              "status": {
                "enum": [
                  "completed",
                  "extended"
                ]
              }
            }
          }
        }
      ]
    },
    "BookingQuestionStates": {
      "type": "array",
      "title": "BookingQuestionStates",
      "description": "Response-side set state on a Booking, a booking webhook payload, and a paid-booking BookingContext.",
      "maxItems": 20,
      "items": {
        "$ref": "#/$defs/BookingQuestionState"
      }
    },
    "BookingEligibilityCheckRequest": {
      "type": "object",
      "title": "BookingEligibilityCheckRequest",
      "description": "Body of the privileged preflight eligibility check. The service is addressed by the operation. Omitting recipient means the buyer is the recipient.",
      "properties": {
        "buyer": {
          "$ref": "booking.json#/$defs/Buyer"
        },
        "recipient": {
          "$ref": "booking.json#/$defs/Buyer"
        },
        "booking_question_submissions": {
          "$ref": "#/$defs/BookingQuestionSubmissions"
        }
      }
    },
    "BookingEligibilityCheckResult": {
      "type": "object",
      "title": "BookingEligibilityCheckResult",
      "description": "Business decision for a preflight eligibility check. A decision is a business result, not a protocol error. Buyer-safe reasons travel in the response envelope's messages[].",
      "additionalProperties": false,
      "required": [
        "decision"
      ],
      "properties": {
        "decision": {
          "type": "string",
          "enum": [
            "eligible",
            "ineligible",
            "requires_input"
          ]
        },
        "required_sets": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/BookingQuestionSetRef"
          },
          "description": "Sets and live revisions still needed when decision is requires_input."
        },
        "assessment_token": {
          "$ref": "#/$defs/BookingQuestionAssessmentToken"
        },
        "expires_at": {
          "type": "string",
          "format": "date-time"
        }
      },
      "allOf": [
        {
          "if": {
            "properties": {
              "decision": {
                "const": "eligible"
              }
            }
          },
          "then": {
            "required": [
              "assessment_token",
              "expires_at"
            ]
          },
          "else": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "assessment_token"
                  ]
                },
                {
                  "required": [
                    "expires_at"
                  ]
                }
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "decision": {
                "const": "requires_input"
              }
            }
          },
          "then": {
            "required": [
              "required_sets"
            ],
            "properties": {
              "required_sets": {
                "minItems": 1
              }
            }
          }
        }
      ]
    },
    "BookingQuestionsSummary": {
      "type": "object",
      "title": "BookingQuestionsSummary",
      "description": "Compact registry-hit summary for compatibility assessment. No registry search filter is defined over it.",
      "additionalProperties": false,
      "required": [
        "set_count",
        "has_preflight_eligibility",
        "needs_platform_surface",
        "needs_business_surface",
        "has_before_service_requirements"
      ],
      "properties": {
        "set_count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 20
        },
        "has_preflight_eligibility": {
          "type": "boolean"
        },
        "needs_platform_surface": {
          "type": "boolean"
        },
        "needs_business_surface": {
          "type": "boolean"
        },
        "has_before_service_requirements": {
          "type": "boolean"
        }
      }
    },
    "BuyerInputActionConfig": {
      "type": "object",
      "title": "BuyerInputActionConfig",
      "description": "Extension-owned config for the buyer-input Action. The Platform displays the exact business-authored content of the named set revisions verbatim, preserves disclosure associations, never preselects an acknowledgment, requires an explicit buyer response, and submits it with the Action id without agent inference or rewriting.",
      "additionalProperties": false,
      "required": [
        "mode",
        "sets",
        "presentation"
      ],
      "properties": {
        "mode": {
          "type": "string",
          "enum": [
            "collect_answers",
            "acknowledge"
          ]
        },
        "sets": {
          "type": "array",
          "minItems": 1,
          "items": {
            "$ref": "#/$defs/BookingQuestionSetRef"
          }
        },
        "item_ids": {
          "type": "array",
          "minItems": 1,
          "uniqueItems": true,
          "items": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_.-]{1,64}$"
          },
          "description": "Optional subset of item identifiers this Action covers. When absent, every applicable item of the named sets is covered."
        },
        "presentation": {
          "const": "verbatim",
          "description": "Fixed value. A single-valued enum so a later revision can add presentation modes and an unrecognized value fails closed."
        }
      }
    },
    "BuyerInputAction": {
      "type": "object",
      "title": "BuyerInputAction",
      "description": "One outstanding booking-questions buyer-input Action instance. In UCP checkout it is a value under the response-only actions map key dev.usp-protocol.services.booking_questions.buyer_input; in a Standalone or free UCP-Native Booking the same id and config ride on a core Booking.actions entry whose type is that key. continue_url is the optional business-surface fallback.",
      "required": [
        "id",
        "config"
      ],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1
        },
        "config": {
          "$ref": "#/$defs/BuyerInputActionConfig"
        },
        "continue_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://"
        }
      }
    }
  }
}
