Developer Hub
这是自动生成的译文

订单问题

预订问答功能有助于收集更多旅客信息。

抢先体验版

本文档是仅面向特定合作伙伴的早期访问预览计划的一部分。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

>> 请参阅集成流程:步骤 1 了解使用方法

**用途:**返回所有可用数据类型模型的技术参考目录。

示例请求:

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
        }
      ]
    }
  ]
}
该页面有帮助吗?
我们该如何改进这些内容?
感谢您帮助我们改进!