Developer Hub
此為系統自動產生的翻譯

預訂問題

「預訂問題」功能有助於蒐集旅客的額外詳細資料

搶先體驗預覽版

本文件屬於一項僅限特定合作夥伴參與的「搶先體驗」預覽計畫。Beta 測試計畫將於 2026 年第三季啟動,並於 2027 年正式推出。

如果您有興趣成為 Beta 合作夥伴,請聯絡您的客戶經理。

簡介

「預訂問題」功能可讓您以靈活且 type-safe 的方式,收集旅客的額外資料 (e.g. 身高、體重、旅行證件資訊、飲食限制等)。

主要優點

  • 彈性: 無需 API 版本控制即可新增資料類型
  • Type-safe: JSON 模式可確保結構正確
  • 可發現性: 可透過目錄端點查詢可用類型
  • 已篩選: 您只會看到您可以支持的活動
  • 本地化: 問題支援多種語言

文件術語表

  • 解決方案架構
    核心概念、通用資料類型模型、活動需求、合作夥伴能力,以及結構化答案提交。
  • API 端點
    「資料類型目錄」、「活動篩選」及「價格查詢」端點會提供預訂相關的問題。
  • 條件式問題
    父子問題的運作方式,以及驅動顯示/隱藏邏輯的運算子。
  • 整合流程
    End-to-end 從發現到預訂提交的各步驟。
  • 資料類型範例
    一個展示其資料結構定義與答案結構的資料類型範例。
  • 本地化
    如何在保持 ID 不變的情況下,以多種語言返回問題和選項。
  • 驗證
    Client-side 及 server-side 中的驗證規則與錯誤回應格式。

解決方案架構

核心概念

泛型資料型別模型

可重複使用的資料類型模型代表資訊類別 (必要時可新增更多類別),e.g. 包含「測量」、「選項」、「地址」及「電話號碼」。

>> 進一步了解資料類型模型

如需查閱《data-types》的完整目錄,請致電 GET /experiences/booking-questions/data-types

活動要求

活動會透過預訂問題來聲明所需的数据類型,並指定:

  • 該問題的唯一字串識別碼
  • 問題內容 (本地化)
  • 問題文字的說明 (已在地化)
  • 資料型別參考
  • 無論是採用 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該欄位與任何指定值均不符

整合流程

步驟 1:探索階段

呼叫資料類型目錄以確定哪些類型受到支援:

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

此功能會篩選結果,僅包含其必答問題使用指定資料類型的活動。

步驟 3:價格確認

在價格查詢過程中,您會在回覆中收到關於預訂的問題:

GET /experiences/activities/12345/price-check?token=abc123&tickets=...

回應中包含一個booking_questions 陣列 (如有)。所有問題均採用您篩選條件中的資料類型。

步驟 4:彙整答案

建立用於收集答案的用戶介面:

  • 針對每種資料類型渲染適當的輸入
  • 使用資料類型目錄中的架構來驗證 client-side
  • 關於「per-traveler」的問題,請為每位旅客彙整答案
  • 關於 per-booking 的問題,請集中蒐集一次
  • 處理條件式問題 (根據父問題的答案顯示/隱藏)

步驟 5:提交預訂

請隨預訂請求一併提交答案:

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" }
  ]
}

註:為確保答案提交的一致性, 中的題目編號與選項編號在各語言版本中均保持不變。

驗證

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
        }
      ]
    }
  ]
}
這個頁面對您有幫助嗎?
我們能如何改善內容?
感謝您協助我們進行改善!