{
  "openapi": "3.1.0",
  "info": {
    "title": "Luxury Travel Tools calculator API",
    "version": "1",
    "summary": "Free, read-only calculators for the luxury travel industry.",
    "description": "Free calculators from Luxury Travel Tools by Global Lux Agency. No key or sign-up is needed. Requests are rate limited per IP; nothing is stored.\n\nResults are estimates and general guidance, not quotes or tax, legal or financial advice. Show the disclaimer and link to openInBrowser when presenting results to a person.\n\nThe same tools are available over MCP at https://globalluxagency.com/tools/api/mcp.",
    "contact": {
      "name": "Global Lux Agency",
      "url": "https://globalluxagency.com",
      "email": "hello@globalluxagency.com"
    }
  },
  "externalDocs": {
    "url": "https://globalluxagency.com/tools/for-ai-agents",
    "description": "For AI agents and developers"
  },
  "servers": [
    {
      "url": "https://globalluxagency.com/tools/api/calc"
    }
  ],
  "paths": {
    "/yacht-charter-cost-calculator": {
      "get": {
        "operationId": "yachtCharterCostCalculator",
        "summary": "True cost of a crewed yacht charter: fee, charter VAT by cruising area, APA, delivery, local fees and crew tip.",
        "description": "Estimates the all-in cost of a crewed yacht charter from the headline charter fee. Adds the charter tax for the cruising area (sourced default, overridable), APA (advance provisioning allowance), delivery fees, per-guest local fees, other costs and a crew gratuity, and returns totals per guest and per guest per day. It does not fetch live prices or availability.\n\nWeb page: https://globalluxagency.com/tools/yacht-charter-cost-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/yacht-charter-cost-calculator",
          "description": "Yacht Charter True-Cost Calculator"
        },
        "parameters": [
          {
            "name": "charterFee",
            "in": "query",
            "required": true,
            "description": "Headline charter fee as quoted, excluding tax, in the quote currency.",
            "schema": {
              "description": "Headline charter fee as quoted, excluding tax, in the quote currency.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000
            }
          },
          {
            "name": "area",
            "in": "query",
            "required": false,
            "description": "Cruising area: croatia (Croatia), greece (Greece), italy (Italy), france (France & Monaco), spain (Spain & Balearics), montenegro (Montenegro), turkey (Türkiye), bahamas (Bahamas), bvi (British Virgin Islands), usvi (US Virgin Islands).",
            "schema": {
              "description": "Cruising area: croatia (Croatia), greece (Greece), italy (Italy), france (France & Monaco), spain (Spain & Balearics), montenegro (Montenegro), turkey (Türkiye), bahamas (Bahamas), bvi (British Virgin Islands), usvi (US Virgin Islands).",
              "type": "string",
              "enum": [
                "croatia",
                "greece",
                "italy",
                "france",
                "spain",
                "montenegro",
                "turkey",
                "bahamas",
                "bvi",
                "usvi"
              ],
              "default": "croatia"
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Quote currency (ISO 4217). Defaults to the area's usual quote currency.",
            "schema": {
              "description": "Quote currency (ISO 4217). Defaults to the area's usual quote currency.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ]
            }
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Charter length in days.",
            "schema": {
              "description": "Charter length in days.",
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 7
            }
          },
          {
            "name": "guests",
            "in": "query",
            "required": false,
            "description": "Number of guests.",
            "schema": {
              "description": "Number of guests.",
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 10
            }
          },
          {
            "name": "taxRate",
            "in": "query",
            "required": false,
            "description": "Charter tax rate in percent. Defaults to the cruising area's sourced rate; required where no reliable default exists.",
            "schema": {
              "description": "Charter tax rate in percent. Defaults to the cruising area's sourced rate; required where no reliable default exists.",
              "type": "number",
              "minimum": 0,
              "maximum": 100
            }
          },
          {
            "name": "percentOutsideEu",
            "in": "query",
            "required": false,
            "description": "For areas taxed pro rata by time in EU waters: % of charter time outside EU waters (must be provable).",
            "schema": {
              "description": "For areas taxed pro rata by time in EU waters: % of charter time outside EU waters (must be provable).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 0
            }
          },
          {
            "name": "apaPercent",
            "in": "query",
            "required": false,
            "description": "APA as % of the charter fee. Typical range 25–40%; the default of 30% is a modelling choice.",
            "schema": {
              "description": "APA as % of the charter fee. Typical range 25–40%; the default of 30% is a modelling choice.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "tipPercent",
            "in": "query",
            "required": false,
            "description": "Crew gratuity as % of the charter fee (MYBA guideline 5–15%).",
            "schema": {
              "description": "Crew gratuity as % of the charter fee (MYBA guideline 5–15%).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 10
            }
          },
          {
            "name": "deliveryFee",
            "in": "query",
            "required": false,
            "description": "Delivery or redelivery fee, taxed like the charter fee.",
            "schema": {
              "description": "Delivery or redelivery fee, taxed like the charter fee.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000,
              "default": 0
            }
          },
          {
            "name": "perPersonPerDayFee",
            "in": "query",
            "required": false,
            "description": "Local fee per guest per day (e.g. a cruising permit). Defaults to the area's sourced fee when quoting in USD, otherwise 0.",
            "schema": {
              "description": "Local fee per guest per day (e.g. a cruising permit). Defaults to the area's sourced fee when quoting in USD, otherwise 0.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "name": "otherCosts",
            "in": "query",
            "required": false,
            "description": "Anything else outside the APA (flights, transfers, special requests).",
            "schema": {
              "description": "Anything else outside the APA (flights, transfers, special requests).",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/toms-vat-calculator": {
      "get": {
        "operationId": "tomsVatCalculator",
        "summary": "Estimate UK VAT on one travel package under the Tour Operators’ Margin Scheme (TOMS), cost-based method.",
        "description": "Per-package estimate of VAT under the UK Tour Operators’ Margin Scheme (HMRC VAT Notice 709/5). Works out the margin, apportions it between UK and non-UK margin scheme supplies and in-house supplies by cost, and applies the VAT fraction (one-sixth at 20%) to the UK share. The real liability is calculated annually across all packages; this is guidance only, not tax advice.\n\nWeb page: https://globalluxagency.com/tools/toms-vat-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/toms-vat-calculator",
          "description": "UK TOMS VAT Calculator"
        },
        "parameters": [
          {
            "name": "sellingPrice",
            "in": "query",
            "required": true,
            "description": "Total price charged to the customer for the package, including any VAT.",
            "schema": {
              "description": "Total price charged to the customer for the package, including any VAT.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000
            }
          },
          {
            "name": "ukCosts",
            "in": "query",
            "required": false,
            "description": "Bought-in margin scheme supplies enjoyed in the UK, at what you paid including VAT.",
            "schema": {
              "description": "Bought-in margin scheme supplies enjoyed in the UK, at what you paid including VAT.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          },
          {
            "name": "nonUkCosts",
            "in": "query",
            "required": false,
            "description": "Bought-in margin scheme supplies enjoyed outside the UK, at what you paid.",
            "schema": {
              "description": "Bought-in margin scheme supplies enjoyed outside the UK, at what you paid.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          },
          {
            "name": "inHouseCosts",
            "in": "query",
            "required": false,
            "description": "Direct cost of in-house supplies (your own resources), excluding VAT you can reclaim.",
            "schema": {
              "description": "Direct cost of in-house supplies (your own resources), excluding VAT you can reclaim.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          },
          {
            "name": "inHouseTreatment",
            "in": "query",
            "required": false,
            "description": "VAT treatment of the in-house supplies under the normal rules.",
            "schema": {
              "description": "VAT treatment of the in-house supplies under the normal rules.",
              "type": "string",
              "enum": [
                "standard",
                "zero"
              ],
              "default": "standard"
            }
          },
          {
            "name": "vatRate",
            "in": "query",
            "required": false,
            "description": "VAT rate in percent for standard-rated elements. Defaults to the UK standard rate (20%).",
            "schema": {
              "description": "VAT rate in percent for standard-rated elements. Defaults to the UK standard rate (20%).",
              "type": "number",
              "minimum": 0,
              "maximum": 100
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts (VAT returns are in sterling).",
            "schema": {
              "description": "Currency of the amounts (VAT returns are in sterling).",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/payment-timeline-builder": {
      "get": {
        "operationId": "paymentTimelineBuilder",
        "summary": "Turn supplier deposit, balance and cancellation terms into dated payment deadlines and cancellation charges.",
        "description": "Builds a dated payment and cancellation timeline for a trip: deposit, optional instalments and balance (with client due dates set a buffer ahead of the supplier deadlines) and the cancellation charge that applies on each date. Default terms are examples only; pass the terms from the supplier contract.\n\nWeb page: https://globalluxagency.com/tools/payment-timeline-builder",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/payment-timeline-builder",
          "description": "Payment & Cancellation Timeline Builder"
        },
        "parameters": [
          {
            "name": "departureDate",
            "in": "query",
            "required": true,
            "description": "Departure (or service start) date, YYYY-MM-DD.",
            "schema": {
              "description": "Departure (or service start) date, YYYY-MM-DD.",
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "bookingDate",
            "in": "query",
            "required": false,
            "description": "Booking date, YYYY-MM-DD.",
            "schema": {
              "description": "Booking date, YYYY-MM-DD. Defaults to today (UTC).",
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "total",
            "in": "query",
            "required": true,
            "description": "Total cost of the booking.",
            "schema": {
              "description": "Total cost of the booking.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts.",
            "schema": {
              "description": "Currency of the amounts.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          },
          {
            "name": "depositType",
            "in": "query",
            "required": false,
            "description": "Whether depositValue is a percentage of the total or a fixed amount.",
            "schema": {
              "description": "Whether depositValue is a percentage of the total or a fixed amount.",
              "type": "string",
              "enum": [
                "percent",
                "amount"
              ],
              "default": "percent"
            }
          },
          {
            "name": "depositValue",
            "in": "query",
            "required": false,
            "description": "Deposit due at booking (percent or amount; example default 25%).",
            "schema": {
              "description": "Deposit due at booking (percent or amount; example default 25%).",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 25
            }
          },
          {
            "name": "balanceDaysBefore",
            "in": "query",
            "required": false,
            "description": "Balance due this many days before departure.",
            "schema": {
              "description": "Balance due this many days before departure.",
              "type": "integer",
              "minimum": 0,
              "maximum": 1830,
              "default": 84
            }
          },
          {
            "name": "instalments",
            "in": "query",
            "required": false,
            "description": "Optional extra instalments as \"daysBefore:percent\" pairs, e.g. \"120:25\" (up to 6).",
            "schema": {
              "description": "Optional extra instalments as \"daysBefore:percent\" pairs, e.g. \"120:25\" (up to 6).",
              "type": "string",
              "maxLength": 200,
              "pattern": "^\\s*\\d+(\\.\\d+)?\\s*:\\s*\\d+(\\.\\d+)?\\s*(,\\s*\\d+(\\.\\d+)?\\s*:\\s*\\d+(\\.\\d+)?\\s*)*$",
              "default": ""
            }
          },
          {
            "name": "clientBufferDays",
            "in": "query",
            "required": false,
            "description": "Ask the client this many days before each supplier deadline.",
            "schema": {
              "description": "Ask the client this many days before each supplier deadline.",
              "type": "integer",
              "minimum": 0,
              "maximum": 365,
              "default": 7
            }
          },
          {
            "name": "cancellation",
            "in": "query",
            "required": false,
            "description": "Cancellation tiers as \"daysBefore:percentForfeited\" pairs (up to 8), e.g. \"84:25,56:50,29:75,0:100\".",
            "schema": {
              "description": "Cancellation tiers as \"daysBefore:percentForfeited\" pairs (up to 8), e.g. \"84:25,56:50,29:75,0:100\".",
              "type": "string",
              "maxLength": 200,
              "pattern": "^\\s*\\d+(\\.\\d+)?\\s*:\\s*\\d+(\\.\\d+)?\\s*(,\\s*\\d+(\\.\\d+)?\\s*:\\s*\\d+(\\.\\d+)?\\s*)*$",
              "default": "84:25,56:50,29:75,0:100"
            }
          },
          {
            "name": "today",
            "in": "query",
            "required": false,
            "description": "Reference date for flagging deadlines that have passed, YYYY-MM-DD.",
            "schema": {
              "description": "Reference date for flagging deadlines that have passed, YYYY-MM-DD. Defaults to today (UTC).",
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/direct-booking-vs-ota-calculator": {
      "get": {
        "operationId": "directBookingVsOtaCalculator",
        "summary": "Compare what a hotel keeps from OTA and direct bookings, and what shifting room nights to direct is worth.",
        "description": "Compares the net room revenue a hotel keeps per booking from an OTA (after commission and, for pay-at-hotel bookings, card fees) and from a direct booking (after rate discount, booking engine, marketing, per-booking costs and card fees). Returns annual distribution costs by channel, the saving from moving a number of percentage points of room nights from OTAs to direct, and break-even direct costs. Direct-channel costs start at 0: pass your own.\n\nWeb page: https://globalluxagency.com/tools/direct-booking-vs-ota-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/direct-booking-vs-ota-calculator",
          "description": "Direct Booking vs OTA Savings Calculator"
        },
        "parameters": [
          {
            "name": "roomNights",
            "in": "query",
            "required": true,
            "description": "Room nights sold per year, all channels.",
            "schema": {
              "description": "Room nights sold per year, all channels.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000
            }
          },
          {
            "name": "adr",
            "in": "query",
            "required": true,
            "description": "Average daily rate (room revenue per occupied room night).",
            "schema": {
              "description": "Average daily rate (room revenue per occupied room night).",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts.",
            "schema": {
              "description": "Currency of the amounts.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          },
          {
            "name": "nightsPerBooking",
            "in": "query",
            "required": false,
            "description": "Average nights per booking (example default 2.5).",
            "schema": {
              "description": "Average nights per booking (example default 2.5).",
              "type": "number",
              "minimum": 1,
              "maximum": 365,
              "default": 2.5
            }
          },
          {
            "name": "otaSharePercent",
            "in": "query",
            "required": false,
            "description": "Share of room nights booked through OTAs, %. Default: 22.2% (five-star hotels in Europe, 2023).",
            "schema": {
              "description": "Share of room nights booked through OTAs, %. Default: 22.2% (five-star hotels in Europe, 2023).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 22.2
            }
          },
          {
            "name": "otaCommissionPercent",
            "in": "query",
            "required": false,
            "description": "OTA commission, % of booking value, including any visibility programme uplift. Typical 15–30%; the default of 18% is a modelling choice.",
            "schema": {
              "description": "OTA commission, % of booking value, including any visibility programme uplift. Typical 15–30%; the default of 18% is a modelling choice.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 18
            }
          },
          {
            "name": "otaPayment",
            "in": "query",
            "required": false,
            "description": "'ota' = prepaid to the OTA, which pays you net; 'property' = pay at hotel, you take the card payment.",
            "schema": {
              "description": "'ota' = prepaid to the OTA, which pays you net; 'property' = pay at hotel, you take the card payment.",
              "type": "string",
              "enum": [
                "ota",
                "property"
              ],
              "default": "ota"
            }
          },
          {
            "name": "cardFeePercent",
            "in": "query",
            "required": false,
            "description": "Card processing fee, % of the amount charged (example default 1.5%).",
            "schema": {
              "description": "Card processing fee, % of the amount charged (example default 1.5%).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 1.5
            }
          },
          {
            "name": "cardFeeFixed",
            "in": "query",
            "required": false,
            "description": "Card processing fee, fixed amount per booking (example default 0.2, in GBP).",
            "schema": {
              "description": "Card processing fee, fixed amount per booking (example default 0.2, in GBP).",
              "type": "number",
              "minimum": 0,
              "maximum": 1000,
              "default": 0.2
            }
          },
          {
            "name": "directDiscountPercent",
            "in": "query",
            "required": false,
            "description": "Direct-booking rate discount (e.g. member rate), % off the ADR.",
            "schema": {
              "description": "Direct-booking rate discount (e.g. member rate), % off the ADR.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 0
            }
          },
          {
            "name": "bookingEnginePercent",
            "in": "query",
            "required": false,
            "description": "Booking engine fee, % of direct booking value.",
            "schema": {
              "description": "Booking engine fee, % of direct booking value.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 0
            }
          },
          {
            "name": "marketingPercent",
            "in": "query",
            "required": false,
            "description": "Metasearch, paid search and other marketing, % of direct booking value.",
            "schema": {
              "description": "Metasearch, paid search and other marketing, % of direct booking value.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 0
            }
          },
          {
            "name": "directCostPerBooking",
            "in": "query",
            "required": false,
            "description": "Fixed cost per direct booking (cost-per-click spend, per-booking engine fee, perk value).",
            "schema": {
              "description": "Fixed cost per direct booking (cost-per-click spend, per-booking engine fee, perk value).",
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "shiftPoints",
            "in": "query",
            "required": false,
            "description": "Percentage points of room nights moved from OTAs to direct (example default 5).",
            "schema": {
              "description": "Percentage points of room nights moved from OTAs to direct (example default 5).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 5
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/experience-pricing-calculator": {
      "get": {
        "operationId": "experiencePricingCalculator",
        "summary": "Price a tour or experience: cost per guest by group size, break-even group size, net price for a target margin and the rack price after commission.",
        "description": "Prices a private or small-group experience from its costs. Takes fixed costs per departure, variable costs per guest and overhead, then returns the net price per guest that hits a target margin, the public (rack) price that still leaves that net after the agent or OTA commission (grossing up, not adding commission on top), the break-even group size, and a table of cost, price and profit for each group size.\n\nWeb page: https://globalluxagency.com/tools/experience-pricing-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/experience-pricing-calculator",
          "description": "Experience Pricing Calculator"
        },
        "parameters": [
          {
            "name": "fixedCostsPerDeparture",
            "in": "query",
            "required": true,
            "description": "Costs paid once per departure whatever the group size (guide, vehicle, venue hire, permits), in total.",
            "schema": {
              "description": "Costs paid once per departure whatever the group size (guide, vehicle, venue hire, permits), in total.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000
            }
          },
          {
            "name": "variableCostsPerGuest",
            "in": "query",
            "required": true,
            "description": "Costs paid for each guest (tickets, meals, tastings), in total per guest.",
            "schema": {
              "description": "Costs paid for each guest (tickets, meals, tastings), in total per guest.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000
            }
          },
          {
            "name": "overheadPerDeparture",
            "in": "query",
            "required": false,
            "description": "Share of business overheads allocated to each departure.",
            "schema": {
              "description": "Share of business overheads allocated to each departure.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000,
              "default": 0
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts.",
            "schema": {
              "description": "Currency of the amounts.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          },
          {
            "name": "minGuests",
            "in": "query",
            "required": false,
            "description": "Smallest group size to model.",
            "schema": {
              "description": "Smallest group size to model.",
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 2
            }
          },
          {
            "name": "maxGuests",
            "in": "query",
            "required": false,
            "description": "Largest group size to model.",
            "schema": {
              "description": "Largest group size to model.",
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 8
            }
          },
          {
            "name": "pricingGuests",
            "in": "query",
            "required": false,
            "description": "Group size the suggested net price is based on.",
            "schema": {
              "description": "Group size the suggested net price is based on.",
              "type": "integer",
              "minimum": 1,
              "maximum": 60,
              "default": 4
            }
          },
          {
            "name": "targetMarginPercent",
            "in": "query",
            "required": false,
            "description": "Target margin, % of the net price you receive (default 30% is a modelling choice).",
            "schema": {
              "description": "Target margin, % of the net price you receive (default 30% is a modelling choice).",
              "type": "number",
              "minimum": 0,
              "maximum": 95,
              "default": 30
            }
          },
          {
            "name": "commissionPercent",
            "in": "query",
            "required": false,
            "description": "Commission the agent or OTA keeps, % of the public price (default 20% is a modelling choice; see sources for typical ranges).",
            "schema": {
              "description": "Commission the agent or OTA keeps, % of the public price (default 20% is a modelling choice; see sources for typical ranges).",
              "type": "number",
              "minimum": 0,
              "maximum": 95,
              "default": 20
            }
          },
          {
            "name": "netPriceOverride",
            "in": "query",
            "required": false,
            "description": "Your own net price per guest. 0 = use the suggested price.",
            "schema": {
              "description": "Your own net price per guest. 0 = use the suggested price.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/luxury-trip-budget-estimator": {
      "get": {
        "operationId": "luxuryTripBudgetEstimator",
        "summary": "Add up a luxury trip budget by category (stay, flights, transfers, guiding, dining, fees, insurance, gratuities, contingency).",
        "description": "Totals a luxury trip budget from your own quotes and estimates, with a contingency, and returns the total, per person, per night, per person per night and the cost on the ground (excluding flights and insurance), plus each category’s share. It does not supply prices: pass the amounts from your quotes.\n\nWeb page: https://globalluxagency.com/tools/luxury-trip-budget-estimator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/luxury-trip-budget-estimator",
          "description": "Luxury Trip Budget Estimator"
        },
        "parameters": [
          {
            "name": "travellers",
            "in": "query",
            "required": false,
            "description": "Number of travellers.",
            "schema": {
              "description": "Number of travellers.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 2
            }
          },
          {
            "name": "nights",
            "in": "query",
            "required": false,
            "description": "Number of nights.",
            "schema": {
              "description": "Number of nights.",
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 7
            }
          },
          {
            "name": "rooms",
            "in": "query",
            "required": false,
            "description": "Number of rooms.",
            "schema": {
              "description": "Number of rooms.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 1
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts.",
            "schema": {
              "description": "Currency of the amounts.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          },
          {
            "name": "roomRate",
            "in": "query",
            "required": false,
            "description": "Accommodation per room per night, including taxes and service charges.",
            "schema": {
              "description": "Accommodation per room per night, including taxes and service charges.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          },
          {
            "name": "flightPerPerson",
            "in": "query",
            "required": false,
            "description": "Return flights per person.",
            "schema": {
              "description": "Return flights per person.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000,
              "default": 0
            }
          },
          {
            "name": "transfersTotal",
            "in": "query",
            "required": false,
            "description": "Transfers for the whole party.",
            "schema": {
              "description": "Transfers for the whole party.",
              "type": "number",
              "minimum": 0,
              "maximum": 10000000,
              "default": 0
            }
          },
          {
            "name": "guidingPerDay",
            "in": "query",
            "required": false,
            "description": "Private guiding and experiences per day, for the whole party.",
            "schema": {
              "description": "Private guiding and experiences per day, for the whole party.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          },
          {
            "name": "guidingDays",
            "in": "query",
            "required": false,
            "description": "Days with private guiding or experiences (capped at nights + 1).",
            "schema": {
              "description": "Days with private guiding or experiences (capped at nights + 1).",
              "type": "integer",
              "minimum": 0,
              "maximum": 366,
              "default": 0
            }
          },
          {
            "name": "diningPerPersonPerDay",
            "in": "query",
            "required": false,
            "description": "Dining and drinks per person per day, counted for each night.",
            "schema": {
              "description": "Dining and drinks per person per day, counted for each night.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "feesPerPerson",
            "in": "query",
            "required": false,
            "description": "Park, permit and entry fees per person, whole trip.",
            "schema": {
              "description": "Park, permit and entry fees per person, whole trip.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          },
          {
            "name": "insurancePerPerson",
            "in": "query",
            "required": false,
            "description": "Travel insurance per person, whole trip.",
            "schema": {
              "description": "Travel insurance per person, whole trip.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          },
          {
            "name": "gratuitiesTotal",
            "in": "query",
            "required": false,
            "description": "Gratuities for the whole trip.",
            "schema": {
              "description": "Gratuities for the whole trip.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          },
          {
            "name": "contingencyPercent",
            "in": "query",
            "required": false,
            "description": "Contingency, % of every other cost (default 10% is a planning choice, not a benchmark).",
            "schema": {
              "description": "Contingency, % of every other cost (default 10% is a planning choice, not a benchmark).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 10
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/jet-lag-planner": {
      "get": {
        "operationId": "jetLagPlanner",
        "summary": "Day-by-day jet lag plan: when to sleep, seek light and avoid light before and after a flight across time zones.",
        "description": "Builds a jet lag plan from the home and destination time zones, the flight’s local departure and arrival times and the traveller’s usual sleep. Returns the body-clock shift needed, the direction (advance or delay), and for each day before departure, the travel day and the days after arrival: a sleep window, light to seek, light to avoid and an optional caffeine cut-off. Based on published circadian research; general guidance, not medical advice.\n\nWeb page: https://globalluxagency.com/tools/jet-lag-planner",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/jet-lag-planner",
          "description": "Jet Lag Planner"
        },
        "parameters": [
          {
            "name": "homeTimeZone",
            "in": "query",
            "required": true,
            "description": "IANA time zone at home, e.g. Europe/London.",
            "schema": {
              "description": "IANA time zone at home, e.g. Europe/London.",
              "type": "string",
              "maxLength": 64,
              "pattern": "^[A-Za-z][A-Za-z0-9_+-]*(/[A-Za-z0-9_+-]+){0,2}$"
            }
          },
          {
            "name": "destinationTimeZone",
            "in": "query",
            "required": true,
            "description": "IANA time zone at the destination, e.g. Asia/Dubai.",
            "schema": {
              "description": "IANA time zone at the destination, e.g. Asia/Dubai.",
              "type": "string",
              "maxLength": 64,
              "pattern": "^[A-Za-z][A-Za-z0-9_+-]*(/[A-Za-z0-9_+-]+){0,2}$"
            }
          },
          {
            "name": "departure",
            "in": "query",
            "required": true,
            "description": "Departure, local time at home, YYYY-MM-DDTHH:MM.",
            "schema": {
              "description": "Departure, local time at home, YYYY-MM-DDTHH:MM.",
              "type": "string",
              "maxLength": 16,
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
            }
          },
          {
            "name": "arrival",
            "in": "query",
            "required": true,
            "description": "Arrival, local time at the destination, YYYY-MM-DDTHH:MM.",
            "schema": {
              "description": "Arrival, local time at the destination, YYYY-MM-DDTHH:MM.",
              "type": "string",
              "maxLength": 16,
              "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}$"
            }
          },
          {
            "name": "bedtime",
            "in": "query",
            "required": false,
            "description": "Usual bedtime at home, HH:MM (24-hour).",
            "schema": {
              "description": "Usual bedtime at home, HH:MM (24-hour).",
              "type": "string",
              "maxLength": 5,
              "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
              "default": "23:00"
            }
          },
          {
            "name": "wakeTime",
            "in": "query",
            "required": false,
            "description": "Usual wake time at home, HH:MM (24-hour).",
            "schema": {
              "description": "Usual wake time at home, HH:MM (24-hour).",
              "type": "string",
              "maxLength": 5,
              "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
              "default": "07:00"
            }
          },
          {
            "name": "preflightDays",
            "in": "query",
            "required": false,
            "description": "Days before departure to start shifting sleep (0 = none; 2–3 suggested).",
            "schema": {
              "description": "Days before departure to start shifting sleep (0 = none; 2–3 suggested).",
              "type": "integer",
              "minimum": 0,
              "maximum": 7,
              "default": 2
            }
          },
          {
            "name": "strategy",
            "in": "query",
            "required": false,
            "description": "Shift the body clock earlier (advance), later (delay), or let the planner choose (auto).",
            "schema": {
              "description": "Shift the body clock earlier (advance), later (delay), or let the planner choose (auto).",
              "type": "string",
              "enum": [
                "auto",
                "advance",
                "delay"
              ],
              "default": "auto"
            }
          },
          {
            "name": "caffeine",
            "in": "query",
            "required": false,
            "description": "Include a daily caffeine cut-off.",
            "schema": {
              "description": "Include a daily caffeine cut-off.",
              "type": "boolean",
              "default": true
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/travel-agent-commission-calculator": {
      "get": {
        "operationId": "travelAgentCommissionCalculator",
        "summary": "Travel agent commission, host agency split, and markup vs margin for one booking.",
        "description": "Two ways of earning on a booking. mode=commission: from a selling price and commission %, returns the commission, supplier net, margin and equivalent markup. mode=net: from a net cost and a target margin or markup, returns the selling price, profit, margin and markup, plus the price if the percentage were applied on the other basis. Both apply the advisor’s share of a host agency or consortium split. Commission and split defaults are modelling choices, not industry facts; pass the rates in the user’s agreements. Before tax.\n\nWeb page: https://globalluxagency.com/tools/travel-agent-commission-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/travel-agent-commission-calculator",
          "description": "Travel Agent Commission and Markup vs Margin Calculator"
        },
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "description": "commission = supplier pays a % of the selling price; net = you mark up a net rate.",
            "schema": {
              "description": "commission = supplier pays a % of the selling price; net = you mark up a net rate.",
              "type": "string",
              "enum": [
                "commission",
                "net"
              ],
              "default": "commission"
            }
          },
          {
            "name": "sellingPrice",
            "in": "query",
            "required": false,
            "description": "mode=commission: the commissionable price the client pays.",
            "schema": {
              "description": "mode=commission: the commissionable price the client pays.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          },
          {
            "name": "commissionPercent",
            "in": "query",
            "required": false,
            "description": "mode=commission: commission as a % of the selling price. Default 10 is a modelling choice.",
            "schema": {
              "description": "mode=commission: commission as a % of the selling price. Default 10 is a modelling choice.",
              "type": "number",
              "minimum": 0,
              "maximum": 95,
              "default": 10
            }
          },
          {
            "name": "netCost",
            "in": "query",
            "required": false,
            "description": "mode=net: what the supplier charges you.",
            "schema": {
              "description": "mode=net: what the supplier charges you.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000000,
              "default": 0
            }
          },
          {
            "name": "targetBasis",
            "in": "query",
            "required": false,
            "description": "mode=net: whether targetPercent is a margin (on price) or a markup (on cost).",
            "schema": {
              "description": "mode=net: whether targetPercent is a margin (on price) or a markup (on cost).",
              "type": "string",
              "enum": [
                "margin",
                "markup"
              ],
              "default": "margin"
            }
          },
          {
            "name": "targetPercent",
            "in": "query",
            "required": false,
            "description": "mode=net: target margin (max 95) or markup (max 1000) in percent.",
            "schema": {
              "description": "mode=net: target margin (max 95) or markup (max 1000) in percent.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000,
              "default": 20
            }
          },
          {
            "name": "advisorSharePercent",
            "in": "query",
            "required": false,
            "description": "Share of the commission or markup the advisor keeps after a host agency or consortium split; 100 = no split. Default 80 is a modelling choice.",
            "schema": {
              "description": "Share of the commission or markup the advisor keeps after a host agency or consortium split; 100 = no split. Default 80 is a modelling choice.",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 80
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": false,
            "description": "Currency of the amounts.",
            "schema": {
              "description": "Currency of the amounts.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/currency-exposure-calculator": {
      "get": {
        "operationId": "currencyExposureCalculator",
        "summary": "Margin on a trip sold in one currency and paid for in another: at the quoted rate, the rate now and under scenario moves.",
        "description": "For a travel booking sold to the client in one currency (e.g. GBP) with supplier costs in another (e.g. EUR), returns the margin at the quoted rate, at the rate now and when the client currency moves up or down by each scenario percentage, the break-even rate, the margin at risk from the largest adverse move, and an optional comparison with a locked rate. Rates are units of supplier currency per 1 unit of client currency and must be supplied by the caller (no live or reference rates are provided). A deposit or balance already paid or fixed is entered with its rate and is no longer exposed. Not financial or hedging advice.\n\nWeb page: https://globalluxagency.com/tools/currency-exposure-calculator",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/currency-exposure-calculator",
          "description": "Currency Exposure Calculator for Travel Agencies"
        },
        "parameters": [
          {
            "name": "supplierCost",
            "in": "query",
            "required": true,
            "description": "Total supplier cost, in the supplier currency.",
            "schema": {
              "description": "Total supplier cost, in the supplier currency.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000000
            }
          },
          {
            "name": "supplierCurrency",
            "in": "query",
            "required": false,
            "description": "Currency the supplier is paid in.",
            "schema": {
              "description": "Currency the supplier is paid in.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "EUR"
            }
          },
          {
            "name": "clientPrice",
            "in": "query",
            "required": true,
            "description": "Price agreed with the client, in the client currency.",
            "schema": {
              "description": "Price agreed with the client, in the client currency.",
              "type": "number",
              "minimum": 0,
              "maximum": 1000000000
            }
          },
          {
            "name": "clientCurrency",
            "in": "query",
            "required": false,
            "description": "Currency the client pays in.",
            "schema": {
              "description": "Currency the client pays in.",
              "type": "string",
              "enum": [
                "GBP",
                "EUR",
                "USD",
                "CHF",
                "AED",
                "AUD",
                "CAD",
                "SGD",
                "HKD",
                "JPY",
                "ZAR"
              ],
              "default": "GBP"
            }
          },
          {
            "name": "quotedRate",
            "in": "query",
            "required": true,
            "description": "Rate used when quoting: units of supplier currency per 1 unit of client currency (e.g. 1.18 for 1 GBP = 1.18 EUR). Ignored when the currencies match.",
            "schema": {
              "description": "Rate used when quoting: units of supplier currency per 1 unit of client currency (e.g. 1.18 for 1 GBP = 1.18 EUR). Ignored when the currencies match.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "name": "rateNow",
            "in": "query",
            "required": false,
            "description": "Rate now, or the rate you expect to pay at, same convention. Defaults to quotedRate.",
            "schema": {
              "description": "Rate now, or the rate you expect to pay at, same convention. Defaults to quotedRate.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000
            }
          },
          {
            "name": "depositPercent",
            "in": "query",
            "required": false,
            "description": "Share of the supplier cost due as a deposit, %; the rest is the balance (example default 30%).",
            "schema": {
              "description": "Share of the supplier cost due as a deposit, %; the rest is the balance (example default 30%).",
              "type": "number",
              "minimum": 0,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "depositDate",
            "in": "query",
            "required": false,
            "description": "Deposit due date, YYYY-MM-DD (optional).",
            "schema": {
              "description": "Deposit due date, YYYY-MM-DD (optional).",
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "depositFixedRate",
            "in": "query",
            "required": false,
            "description": "Rate at which the deposit was paid or fixed; 0 = still open.",
            "schema": {
              "description": "Rate at which the deposit was paid or fixed; 0 = still open.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "balanceDate",
            "in": "query",
            "required": false,
            "description": "Balance due date, YYYY-MM-DD (optional).",
            "schema": {
              "description": "Balance due date, YYYY-MM-DD (optional).",
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "balanceFixedRate",
            "in": "query",
            "required": false,
            "description": "Rate at which the balance was paid or fixed; 0 = still open.",
            "schema": {
              "description": "Rate at which the balance was paid or fixed; 0 = still open.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "lockedRate",
            "in": "query",
            "required": false,
            "description": "Optional locked rate to compare for the open amount (e.g. a rate offered by your FX provider); 0 = none.",
            "schema": {
              "description": "Optional locked rate to compare for the open amount (e.g. a rate offered by your FX provider); 0 = none.",
              "type": "number",
              "minimum": 0,
              "maximum": 100000,
              "default": 0
            }
          },
          {
            "name": "scenarioMoves",
            "in": "query",
            "required": false,
            "description": "Comma-separated % moves in the client currency, each applied up and down from the rate now (up to 6; example default \"2,5,10\").",
            "schema": {
              "description": "Comma-separated % moves in the client currency, each applied up and down from the rate now (up to 6; example default \"2,5,10\").",
              "type": "string",
              "maxLength": 60,
              "pattern": "^\\s*\\d+(\\.\\d+)?\\s*(,\\s*\\d+(\\.\\d+)?\\s*)*$",
              "default": "2,5,10"
            }
          },
          {
            "name": "today",
            "in": "query",
            "required": false,
            "description": "Reference date for days to each payment, YYYY-MM-DD.",
            "schema": {
              "description": "Reference date for days to each payment, YYYY-MM-DD. Defaults to today (UTC).",
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    },
    "/package-travel-regulations-checker": {
      "get": {
        "operationId": "packageTravelRegulationsChecker",
        "summary": "Classify a UK trip as a package, a linked travel arrangement or neither under the Package Travel Regulations 2018.",
        "description": "Applies the decision tree in the Package Travel and Linked Travel Arrangements Regulations 2018 (as amended by SI 2026/455 from 6 April 2027). Returns the outcome (package, linked-travel-arrangement, neither or outside-scope), the regulation that decides it, the reasoning steps and the organiser or trader duties that follow. Guidance only, not legal advice; it does not judge borderline facts such as whether a tourist service is an essential feature.\n\nWeb page: https://globalluxagency.com/tools/package-travel-regulations-checker",
        "externalDocs": {
          "url": "https://globalluxagency.com/tools/package-travel-regulations-checker",
          "description": "Package Travel Regulations Checker: Is This a Package?"
        },
        "parameters": [
          {
            "name": "rules",
            "in": "query",
            "required": false,
            "description": "Which rules apply, by the date the contract is concluded. \"from-2027-04-06\" applies the 2026 amendments (no linked travel arrangements).",
            "schema": {
              "description": "Which rules apply, by the date the contract is concluded. \"from-2027-04-06\" applies the 2026 amendments (no linked travel arrangements).",
              "type": "string",
              "enum": [
                "before-2027-04-06",
                "from-2027-04-06"
              ],
              "default": "before-2027-04-06"
            }
          },
          {
            "name": "sameTrip",
            "in": "query",
            "required": false,
            "description": "All the services are for the same trip or holiday.",
            "schema": {
              "description": "All the services are for the same trip or holiday.",
              "type": "boolean",
              "default": true
            }
          },
          {
            "name": "transport",
            "in": "query",
            "required": false,
            "description": "Includes carriage of passengers (flight, train, coach, ferry, transfer).",
            "schema": {
              "description": "Includes carriage of passengers (flight, train, coach, ferry, transfer).",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "flight",
            "in": "query",
            "required": false,
            "description": "The transport includes a flight (ATOL pointer). Ignored if transport is false.",
            "schema": {
              "description": "The transport includes a flight (ATOL pointer). Ignored if transport is false.",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "accommodation",
            "in": "query",
            "required": false,
            "description": "Includes accommodation that is not part of the carriage and not residential.",
            "schema": {
              "description": "Includes accommodation that is not part of the carriage and not residential.",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "vehicleHire",
            "in": "query",
            "required": false,
            "description": "Includes car, motor vehicle or motorcycle hire.",
            "schema": {
              "description": "Includes car, motor vehicle or motorcycle hire.",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "otherTourist",
            "in": "query",
            "required": false,
            "description": "Includes other tourist services (excursions, tours, tickets, ski passes, spa treatments).",
            "schema": {
              "description": "Includes other tourist services (excursions, tours, tickets, ski passes, spa treatments).",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "otherSignificant",
            "in": "query",
            "required": false,
            "description": "The other tourist service is a significant proportion of the value (rule of thumb 25%+) or an essential feature. Only relevant with exactly one of transport, accommodation or vehicle hire.",
            "schema": {
              "description": "The other tourist service is a significant proportion of the value (rule of thumb 25%+) or an essential feature. Only relevant with exactly one of transport, accommodation or vehicle hire.",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "otherAfterStart",
            "in": "query",
            "required": false,
            "description": "The other tourist service was selected and bought after the transport, stay or car hire had started (reg. 2(6)(b)).",
            "schema": {
              "description": "The other tourist service was selected and bought after the transport, stay or car hire had started (reg. 2(6)(b)).",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "combination",
            "in": "query",
            "required": false,
            "description": "How the services were put together. One of: single-contract, single-point-of-sale, inclusive-price, sold-as-package, gift-box, data-passed-24h, separate-payment-single-visit, targeted-link-24h, separately-booked.",
            "schema": {
              "description": "How the services were put together. One of: single-contract, single-point-of-sale, inclusive-price, sold-as-package, gift-box, data-passed-24h, separate-payment-single-visit, targeted-link-24h, separately-booked.",
              "type": "string",
              "enum": [
                "single-contract",
                "single-point-of-sale",
                "inclusive-price",
                "sold-as-package",
                "gift-box",
                "data-passed-24h",
                "separate-payment-single-visit",
                "targeted-link-24h",
                "separately-booked"
              ],
              "default": "separately-booked"
            }
          },
          {
            "name": "under24Hours",
            "in": "query",
            "required": false,
            "description": "Covers less than 24 hours with no overnight accommodation (exclusion, reg. 3(2)(a)).",
            "schema": {
              "description": "Covers less than 24 hours with no overnight accommodation (exclusion, reg. 3(2)(a)).",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "notForProfit",
            "in": "query",
            "required": false,
            "description": "Offered occasionally, not for profit, to a limited group (exclusion, reg. 3(2)(b)).",
            "schema": {
              "description": "Offered occasionally, not for profit, to a limited group (exclusion, reg. 3(2)(b)).",
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "generalAgreement",
            "in": "query",
            "required": false,
            "description": "Bought under a general agreement for business travel (exclusion, reg. 3(2)(c)).",
            "schema": {
              "description": "Bought under a general agreement for business travel (exclusion, reg. 3(2)(c)).",
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see details)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "422": {
            "description": "The inputs cannot produce a result (see error)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalcResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: wait a minute"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CalcResponse": {
        "type": "object",
        "description": "Calculation result with the inputs used, sourced defaults, a disclaimer and a link to the same calculation on the website.",
        "properties": {
          "tool": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri"
              },
              "documentation": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          "disclaimer": {
            "type": "string",
            "description": "Show this with the result."
          },
          "inputs": {
            "type": "object",
            "additionalProperties": true,
            "description": "Inputs after defaults and clamping."
          },
          "derived": {
            "type": "object",
            "additionalProperties": true,
            "description": "Values the tool took from sourced defaults."
          },
          "result": {
            "description": "The calculation result (shape depends on the tool)."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "value": {},
                "unit": {
                  "type": "string"
                },
                "confidence": {
                  "type": "string",
                  "enum": [
                    "high",
                    "medium",
                    "low"
                  ]
                },
                "lastVerified": {
                  "type": "string",
                  "format": "date"
                },
                "sources": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string"
                      },
                      "publisher": {
                        "type": "string"
                      },
                      "url": {
                        "type": "string",
                        "format": "uri"
                      }
                    }
                  }
                }
              }
            }
          },
          "openInBrowser": {
            "type": "string",
            "format": "uri",
            "description": "The tool page pre-filled with the supplied inputs."
          },
          "publisher": {
            "type": "string"
          },
          "apiVersion": {
            "type": "string"
          },
          "error": {
            "type": "string"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "tool",
          "disclaimer"
        ]
      }
    }
  }
}