订单问题
预订问答功能有助于收集更多旅客信息。
抢先体验版
本文档是仅面向特定合作伙伴的早期访问预览计划的一部分。Beta 测试计划将于 2026 年第三季度启动,并于 2027 年全面推出。
如果您有兴趣成为测试合作伙伴,请联系您的客户经理。
概述
预订问题功能能够以灵活的方式收集额外的旅行者数据(身高、体重、旅行证件信息、饮食限制等)。
主要优点
- **灵活:**无需 API 版本控制即可添加新的数据类型
- Type-safe: JSON schema 确保结构正确
- 可发现性: 能够通过目录端点查询可用类型
- 已筛选: 您只会看到您可以支持的活动。
- 本地化: 问题支持多种语言
文档术语表
- 解决方案架构
核心概念、通用数据类型模型、活动需求、合作伙伴能力和结构化答案提交。 - API 端点
数据类型目录、活动筛选和价格检查端点,用于公开预订问题。 - 条件问题
父/子问题的工作原理以及驱动显示/隐藏逻辑的运算符。 - 集成流程
End-to-end 从发现到预订提交的步骤。 - 数据类型示例
一个示例数据类型,展示了其模式定义和答案结构。 - 本地化
如何在保持 ID 不变的情况下以多种语言返回问题和选项。 - 验证
Client-side 和 server-side 验证规则和错误响应格式。
解决方案架构
核心概念
通 用数据类型模型
可重用的数据类型模型表示信息类别(需要时可以添加更多类别),e.g。测量、选择、地址和电话。
如需查看 data-types 的完整目录,请致电 GET /experiences/booking-questions/data-types
活动要求
活动会在预订问卷中说明所需的数据类型,具体如下:
- 问题的唯一字符串 ID
- 问题文本(本地化)
- 问题文本描述(本地化)
- 数据类型引用
- 无论是 per-traveler 还是 per-booking
- 验证约束(最小值/最大值、长度等)
- 适用于哪些车票
- 为给定的父问题定义子问题
- 可选条件逻辑(取决于其他答案的问题)
合作伙伴能力
您可以使用搜索/内容端点上的 supported_booking_data_types筛选参数声明您支持的数据类型。此筛选功能仅显示您可以处理的活动。
- 要动态处理任何类型,请发送。****
supported_booking_data_types=* - 如需支持无预订问题,请发送
supported_booking_data_types=none。
结构化答案提交
答案嵌套得当:
- Per-traveler 答案—在每个旅行者对象的
booking_question_answers数组中。 - Per-booking 回答—在预订级别的
booking_question_answers数组中。
API 端点
获取数据类型目录
终点: GET /experiences/booking-questions/data-types
查询参数: ?date_updated_start
**用途:**返回所有可用数据类型模型的技术参考目录。
示例请求:
GET /experiences/booking-questions/data-types响应示例:
[
{
"date_added": "2026-07-14T11:30:08.104575+02:00",
"date_updated": "2026-07-14T11:30:08.104575+02:00",
"description": "Used for collecting any measurement with a numeric value and unit. Common examples include height (in/cm/foot/meter), weight (lb/kg/stone), shoe size (us_men/us_women/eu/uk/cm), helmet size (in/cm), waist size (in/cm), and chest size (in/cm). The allowed_units field specifies which units are valid for this measurement.",
"name": "Measurement",
"schema_definition": {
"properties": {
"unit": {
"description": "Unit of measurement - must be one of the allowed_options specified for the question",
"type": "string"
},
"value": {
"description": "Numeric value as string to preserve precision",
"pattern": "^[0-9]+(\\.[0-9]+)?$",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"type": "object"
},
"type": "measurement"
},
]活动筛选
查询参数: supported_booking_data_types
适用于: /experiences/activities/content 和 /experiences/activities/availability端点
用途: 筛选结果,仅包含必填问题使用指定数据类型的活动。
示例:
GET /experiences/activities/content
?activity_id=123&activity_id=456
&language=en-US
&supported_booking_data_types=measurement
&supported_booking_data_types=selection
&supported_booking_data_types=text- 要处理所有类型并显示所有活动,请使用
supported_booking_data_types=*。接下来,您将负责处理价格查询和预订中的所有问题类型。 - 要处理非预订相关问题,请使用
supported_booking_data_types=none。这将仅返回没有预订问题的活动。
价格查询(包含预订问题)
终点: GET /experiences/activities/{activity_id}/price-check
目的: 执行价格检查并回复有关活动的预订问题(如有)。
示例请求:
GET /experiences/activities/12345/price-check?token=abc123&tickets=...响应示例:
{
"status": "available",
"offer_pricing": {
"total": {
"value": "150.00",
"currency": "USD"
}
},
"booking_questions": [
{
"id": "height_1",
"type": "measurement",
"question": "What is your height?",
"description": "Required for safety equipment fitting",
"applies_to": "per_traveler",
"ticket_ids": ["12346","12345"]
"allowed_options": [
{ "id": "in", "label": "in" },
{ "id": "cm", "label": "cm" }
]
},
{
"id": "weight_1",
"type": "measurement",
"question": "What is your weight?",
"description": "Required for zipline weight limits",
"applies_to": "per_traveler",
"ticket_ids": ["12346","12345"]
"allowed_options": [
{ "id": "lb", "label": "lb" },
{ "id": "kg", "label": "kg" }
]
}
],
"links": {
"create": {
"method": "POST",
"href": "/itineraries/activity"
}
}
}注意: 如果您在之前的搜索/内容调用中使用 supported_booking_data_types筛选了活动,则所有返回的问题都将使用您支持的数据类型。
条件句
问题可以根据先前问题的答案而定。这有助于根据用户选择收集不同的信息。
如何操作
- 父问题—一个问题(通常是
selection类型),其答案决定后续内容。 - 子问题—包含引用父问题的
conditional字段的问题。 - 双向关系—父级在其
children数组中列出子级;子级使用完整的条件逻辑引用父级。
示例:联系方式选择
父问题:
{
"id": "contact_method_1",
"type": "selection",
"question": "How would you prefer to be contacted?",
"applies_to": "per_traveler",
"ticket_ids": ["12346","12345"]
"children": ["contact_phone_1", "contact_whatsapp_1", "contact_email_1"],
"allow_multiple": false,
"allowed_options": [
{ "id": "phone", "label": "Phone Call" },
{ "id": "whatsapp", "label": "WhatsApp" },
{ "id": "email", "label": "Email" },
{ "id": "no_contact", "label": "No Contact" }
]
}条件子问题:
[
{
"id": "contact_phone_1",
"type": "phone",
"question": "What is your phone number?",
"applies_to": "per_traveler",
"conditional": {
"parent_question_id": "contact_method_1",
"show_when": {
"operator": "equals",
"field": "selected",
"values": ["phone"]
}
}
},
{
"id": "contact_whatsapp_1",
"type": "text",
"question": "What is your WhatsApp username?",
"applies_to": "per_traveler",
"conditional": {
"parent_question_id": "contact_method_1",
"show_when": {
"operator": "equals",
"field": "selected",
"values": ["whatsapp"]
}
}
},
{
"id": "contact_email_1",
"type": "email",
"question": "What is your email address?",
"applies_to": "per_traveler",
"conditional": {
"parent_question_id": "contact_method_1",
"show_when": {
"operator": "equals",
"field": "selected",
"values": ["email"]
}
}
}
]条件运算符
| 操作员 | 含义 |
|---|---|
contains | 字段包含任何指定的值 |
equals | 字段与任何指定值完全匹配 |
not_contains | 字段不包含任何指定的值 |
not_equals | 字段值与任何指定值都不匹配。 |
集成流程
第一步:发现阶段
调用数据类型目录以确定支持哪些类型:
GET /experiences/booking-questions/data-types?date_updated_start=2026-02-01如果date_updated_start如果提供了指定日期,则仅返回在给定日期或之后添加的数据类型。
步骤 2:使用筛选功能进行搜索
筛选活动,仅显示包含匹配问题的活动:
GET /experiences/activities/content
?activity_id=123&activity_id=456&activity_id=789
&language=en-US
&supported_booking_data_types=measurement
&supported_booking_data_types=selection
&supported_booking_data_types=text此筛选条件仅包含必填问题使用指定数据类型的活动。
步骤三:价格核查
在价格查询过程中,您会收到预订相关问题的回复:
GET /experiences/activities/12345/price-check?token=abc123&tickets=...答复包括booking_questions数组(如果有)。所有问题均使用您筛选器中的数据类型。
第四步:收集答案
构建用于收集答案的用户界面:
- 为每种数据类型渲染合适的输 入
- 使用数据类型目录中的架构验证 client-side
- 对于 per-traveler 问题,请收集每位旅行者的答案。
- 对于 per-booking 问题,请一次性收集。
- 处理条件问题(根据父问题的答案显示/隐藏)
第五步:提交预订
请在预订申请中提交答案:
POST /itineraries/activity{
"primary_traveler": [
{
"given_name": "John",
"family_name": "Doe",
"booking_question_answers": [
{
"question_id": "height_1",
"answer": {
"value": "72",
"unit": "in"
}
},
{
"question_id": "weight_1",
"answer": {
"value": "180",
"unit": "lb"
}
}
]
}
],
"booking_question_answers": [
{
"question_id": "emergency_contact_1",
"answer": {
"value": "Jane Doe, +1-555-1234"
}
}
]
}数据类型示例
尺寸(尺寸测量)
模式定义:
{
"type": "object",
"required": ["value", "unit"],
"properties": {
"value": {
"type": "string",
"pattern": "^[0-9]+(\\.[0-9]+)?$"
},
"unit": {
"type": "string"
}
}
}答案示例:
{
"value": "72",
"unit": "in"
}本地化
价格查询功能通过 language 参数支持多种语言。
英文示例:
GET /experiences/activities/12345/price-check?token=...&language=en-US{
"id": "dietary_1",
"type": "selection",
"question": "Dietary preferences (optional)",
"description": "Please share any dietary restrictions or allergies."
"applies_to": "per_traveler",
"allow_multiple": false
"ticket_ids": ["12345", "12346"]
"options": [
{ "id": "vegetarian", "label": "Vegetarian" },
{ "id": "vegan", "label": "Vegan" }
]
}西班牙语示例:
同样的问题也适用于language=es-ES……
GET /experiences/activities/12345/price-check?token=...&language=es-ES{
"id": "dietary_1",
"type": "selection",
"question": "Preferencias alimentarias (opcional)",
"description": "Por favor, indica las restricciones alimentarias o alergias. "
"applies_to": "per_traveler",
"allow_multiple": false
"ticket_ids": ["12345", "12346"]
"options": [
{ "id": "vegetarian", "label": "Vegetariano" },
{ "id": "vegan", "label": "Vegano" }
]
}注意: 为了确保答案提交的一致性,问题 ID 和选项 ID 在不同语言中保持不变。
验证
Client-side 验证
合作伙伴应使用数据类型目录中的模式验证答案 client-side,以确保结构正确。
Server-side 验证
预订终端验证以下内容:
- 所有必填问题均有答案
- 答案结构与数据类型模式匹配
- 值符合验证约束
- 条件性问题得到了恰当的回答。
- 每位旅客都会收到 Per-traveler 问题
- Per-booking 问题仅提供一次
错误响应
{
"type": "validation_failed",
"message": "One or more booking question answers are invalid",
"errors": [
{
"type": "missing_required_answer",
"message": "Required question 'height_1' not answered for traveler 1",
"fields": [
{
"path": "$.travelers[0].booking_question_answers",
"value": null
}
]
}
]
}