{
  "openapi": "3.1.0",
  "info": {
    "title": "Tixxly AI API",
    "description": "Search and book tours, experiences, and activities worldwide via Tixxly's\npublic HTTP API. This document is the canonical machine-readable reference\nfor ChatGPT Actions, Gemini function calling, Perplexity tools, and the\nTixxly MCP server.\n\n## Authentication\n\nSend your vendor API key in the `x-api-key` header. Keys are created in the\nTixxly backoffice under **AI Connections** (feed / MCP key). Without a key,\nmany read endpoints still respond, but results are not vendor-scoped.\nWrite/booking endpoints do not require a key for the lean reserve flow.\n\nObtain a key: sign in at https://vendors.tixxly.ai → AI Connections → create\nor copy your feed/MCP API key. Contact help@tixxly.ai if you need platform access.\n\n## Rate limits\n\nFair-use limits apply. Sustained bursts may receive HTTP 429. Prefer\npagination (`page` / `limit`) on the feed and cache availability for short\nwindows. Contact help@tixxly.ai for higher limits on Infrastructure plans.\n\n## Booking flow\n\n1. `searchExperiences` — browse activities (`GET /openai/feed`)\n2. `getExperienceDetail` — full product info (optional; availability also returns key data)\n3. `checkAvailability` — time slots, pricingCategories, and places\n4. `getBookingOptions` — form fields; include pickupPlaceId/dropoffPlaceId only when places[] was non-empty\n5. `createReservation` — submit answers; returns `payment_url`\n6. `getReservation` — optional status lookup after payment\n\n### Places rule\n\n- When `places[]` is a **non-empty** array from detail or availability: **required**\n  positive `pickupPlaceId` and `dropoffPlaceId` from `places[].id`.\n- When `places[]` is empty, null, or absent: **omit** both fields — never send `0` or `null`.\n\n### Answers rule\n\n- `answers` / `pickupAnswers` are arrays of `{ questionId: string, values: string[] }`.\n- Include `answers` only when booking_options returned activity/option questions.\n- Include `pickupAnswers` only when `pickupQuestions` was non-empty.\n- `questionId` is the numeric ID string from booking_options — never the human label.\n\nHuman-readable docs: https://tixxly.ai/api\n",
    "version": "2.3.0",
    "contact": {
      "name": "Tixxly Engineering",
      "email": "help@tixxly.ai",
      "url": "https://tixxly.ai/api"
    },
    "termsOfService": "https://tixxly.ai/en/terms",
    "license": {
      "name": "Proprietary",
      "url": "https://tixxly.ai/en/terms"
    }
  },
  "externalDocs": {
    "description": "Tixxly API docs (Redoc) and agent surface",
    "url": "https://tixxly.ai/api"
  },
  "servers": [
    {
      "url": "https://api.tixxly.ai",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Destinations",
      "description": "Public destination index"
    },
    {
      "name": "Suppliers",
      "description": "Tour and activity supplier directory"
    },
    {
      "name": "Experiences",
      "description": "Search and product detail"
    },
    {
      "name": "Availability",
      "description": "Real-time availability and booking form fields"
    },
    {
      "name": "Bookings",
      "description": "Reservations and status"
    },
    {
      "name": "Vendors",
      "description": "Public vendor storefront profiles"
    },
    {
      "name": "Analytics",
      "description": "Optional event tracking"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/v1/directory/destinations": {
      "get": {
        "operationId": "listDestinations",
        "tags": [
          "Destinations"
        ],
        "summary": "List destinations",
        "description": "Returns cities currently published in the directory, sorted by product\ncount descending. Includes country codes and optional geo coordinates.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Destination list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DestinationsResponse"
                },
                "examples": {
                  "immmersivHub": {
                    "summary": "Sample destinations",
                    "value": {
                      "destinations": [
                        {
                          "slug": "marrakech",
                          "name": "Marrakech",
                          "country": {
                            "code": "ma",
                            "name": "Morocco"
                          },
                          "geo": {
                            "latitude": 31.6295,
                            "longitude": -7.9811
                          },
                          "productCount": 12
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/directory/suppliers": {
      "get": {
        "operationId": "listSuppliers",
        "tags": [
          "Suppliers"
        ],
        "summary": "List suppliers",
        "description": "Lists verified tour and activity suppliers with Trust Score, product\ncount, and country coverage. Sorted by Trust Score desc, then product count.\n",
        "security": [],
        "responses": {
          "200": {
            "description": "Supplier roster",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppliersResponse"
                },
                "examples": {
                  "sample": {
                    "summary": "Includes Immmersiv",
                    "value": {
                      "suppliers": [
                        {
                          "username": "immmersiv",
                          "name": "Immmersiv",
                          "verified": true,
                          "productCount": 21,
                          "countries": [
                            {
                              "code": "ma",
                              "name": "Morocco"
                            }
                          ],
                          "cities": [
                            {
                              "slug": "marrakech",
                              "name": "Marrakech",
                              "country": {
                                "code": "ma",
                                "name": "Morocco"
                              }
                            }
                          ],
                          "trustScore": {
                            "score": 86,
                            "band": "high"
                          },
                          "storefrontUrl": "https://ai.immmersiv.eu",
                          "liveSince": "2025-06-01"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/directory/suppliers/{username}": {
      "get": {
        "operationId": "getSupplier",
        "tags": [
          "Suppliers"
        ],
        "summary": "Get supplier detail",
        "description": "Supplier profile including Trust Score breakdown and published products.\n",
        "security": [],
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "immmersiv"
          }
        ],
        "responses": {
          "200": {
            "description": "Supplier profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SupplierDetailResponse"
                },
                "examples": {
                  "immmersiv": {
                    "summary": "Immmersiv profile",
                    "value": {
                      "supplier": {
                        "username": "immmersiv",
                        "name": "Immmersiv",
                        "verified": true,
                        "productCount": 21,
                        "countries": [
                          {
                            "code": "ma",
                            "name": "Morocco"
                          }
                        ],
                        "trustScore": {
                          "score": 86,
                          "band": "high"
                        },
                        "storefrontUrl": "https://ai.immmersiv.eu",
                        "products": []
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/openai/feed": {
      "get": {
        "operationId": "searchExperiences",
        "tags": [
          "Experiences"
        ],
        "summary": "Search travel experiences",
        "description": "Returns paginated bookable experiences. Use each product `id` as\n`experienceId` in subsequent calls. With an `x-api-key` (MCP group),\nresults are scoped to that vendor.\n",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "description": "Free-text search (e.g. \"desert tour\")",
            "schema": {
              "type": "string"
            },
            "example": "desert tour"
          },
          {
            "name": "location",
            "in": "query",
            "description": "Country, region, or city",
            "schema": {
              "type": "string"
            },
            "example": "Morocco"
          },
          {
            "name": "category",
            "in": "query",
            "description": "Category filter (adventure, sightseeing, food, culture, water, nature)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vendor_id",
            "in": "query",
            "description": "Restrict to a numeric vendor id",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1,
              "minimum": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Product feed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FeedResponse"
                },
                "examples": {
                  "morocco": {
                    "summary": "Morocco search",
                    "value": {
                      "total": 1,
                      "products": [
                        {
                          "id": "10042",
                          "content": {
                            "title": "Sahara Desert Day Trip from Marrakech",
                            "subtitle": "Camel ride and berber lunch"
                          },
                          "taxonomy": {
                            "brand": "Immmersiv"
                          },
                          "offers": {
                            "price": 89,
                            "currency": "EUR"
                          }
                        }
                      ],
                      "merchant": {
                        "name": "Tixxly"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/experiences/{experienceId}": {
      "get": {
        "operationId": "getExperienceDetail",
        "tags": [
          "Experiences"
        ],
        "summary": "Get full experience details",
        "description": "Returns content, offers, pricingCategories, places, and vendor.\nIf `places` is a non-empty array, ask the user to choose a location and\nsend `places[].id` as pickupPlaceId and dropoffPlaceId in booking-options\nand reserve. If places is empty or absent, omit those fields.\n",
        "parameters": [
          {
            "name": "experienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "10042"
          }
        ],
        "responses": {
          "200": {
            "description": "Experience details with pricing and places",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExperienceDetail"
                },
                "examples": {
                  "sample": {
                    "summary": "Experience with places",
                    "value": {
                      "success": true,
                      "data": {
                        "id": "10042",
                        "content": {
                          "title": "Sahara Desert Day Trip from Marrakech"
                        },
                        "image": "https://cdn.example.com/sahara.jpg",
                        "vendor": {
                          "id": 12,
                          "name": "Immmersiv"
                        },
                        "pricingCategories": [
                          {
                            "id": 501,
                            "fullTitle": "Adult"
                          }
                        ],
                        "places": [
                          {
                            "id": 9001,
                            "name": "Jemaa el-Fnaa Meeting Point"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/experiences/{experienceId}/availability": {
      "get": {
        "operationId": "checkAvailability",
        "tags": [
          "Availability"
        ],
        "summary": "Check availability for dates",
        "description": "Returns availability slots (max 10), pricingCategories, and places.\nApply the places rule before calling booking-options or reserve.\n",
        "security": [],
        "parameters": [
          {
            "name": "experienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "startDate",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-15"
          },
          {
            "name": "endDate",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-09-20"
          }
        ],
        "responses": {
          "200": {
            "description": "Availability slots",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AvailabilityResponse"
                },
                "examples": {
                  "sample": {
                    "summary": "Open slots",
                    "value": {
                      "success": true,
                      "data": {
                        "availabilities": [
                          {
                            "startTimeId": 7001,
                            "date": "2026-09-15",
                            "startTime": "08:00",
                            "availabilityCount": 8,
                            "soldOut": false,
                            "rates": [
                              {
                                "rateId": 3001,
                                "prices": [
                                  {
                                    "categoryId": 501,
                                    "title": "Adult",
                                    "amount": 89,
                                    "currency": "EUR"
                                  }
                                ]
                              }
                            ]
                          }
                        ],
                        "pricingCategories": [
                          {
                            "id": 501,
                            "fullTitle": "Adult"
                          }
                        ],
                        "places": [
                          {
                            "id": 9001,
                            "name": "Jemaa el-Fnaa Meeting Point"
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/experiences/{experienceId}/booking-options": {
      "post": {
        "operationId": "getBookingOptions",
        "tags": [
          "Availability"
        ],
        "summary": "Get required booking form fields",
        "description": "Returns questions for contact, passengers, and pickup. Collect all answers\nusing `questionId` values (not labels).\n\nWhen places[] from detail/availability was non-empty, you MUST include\npositive pickupPlaceId and dropoffPlaceId. When places was empty or\nabsent, omit both fields entirely.\n",
        "security": [],
        "parameters": [
          {
            "name": "experienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BookingOptionsRequest"
              },
              "examples": {
                "withPlace": {
                  "summary": "With pickup place",
                  "value": {
                    "activityBookings": [
                      {
                        "activityId": 10042,
                        "startTimeId": 7001,
                        "date": "2026-09-15",
                        "rateId": 3001,
                        "pickupPlaceId": 9001,
                        "dropoffPlaceId": 9001,
                        "passengers": [
                          {
                            "pricingCategoryId": 501
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Form fields grouped by contact, passenger, and pickup",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BookingOptionsRes"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/experiences/{experienceId}/reserve": {
      "post": {
        "operationId": "createReservation",
        "tags": [
          "Bookings"
        ],
        "summary": "Create booking and get payment link",
        "description": "Submit collected contact and passenger answers. Returns `payment_url`.\nAlways present payment_url as a markdown link: [Complete Payment](url).\n\nInclude pickupPlaceId/dropoffPlaceId only when places[] applied.\nInclude answers / pickupAnswers only when booking_options returned those\nquestion groups.\n",
        "security": [],
        "parameters": [
          {
            "name": "experienceId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateReservationRequest"
              },
              "examples": {
                "sample": {
                  "summary": "Two adults",
                  "value": {
                    "user_email": "traveller@example.com",
                    "directBooking": {
                      "mainContactDetails": [
                        {
                          "questionId": "1",
                          "values": [
                            "Ada"
                          ]
                        },
                        {
                          "questionId": "2",
                          "values": [
                            "Lovelace"
                          ]
                        },
                        {
                          "questionId": "3",
                          "values": [
                            "traveller@example.com"
                          ]
                        },
                        {
                          "questionId": "4",
                          "values": [
                            "+447821015382"
                          ]
                        }
                      ],
                      "activityBookings": [
                        {
                          "activityId": 10042,
                          "startTimeId": 7001,
                          "date": "2026-09-15",
                          "rateId": 3001,
                          "pickupPlaceId": 9001,
                          "dropoffPlaceId": 9001,
                          "passengers": [
                            {
                              "pricingCategoryId": 501,
                              "passengerDetails": []
                            }
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Reservation created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReservationResponse"
                },
                "examples": {
                  "created": {
                    "summary": "Payment handoff",
                    "value": {
                      "success": true,
                      "data": {
                        "reservation_id": "res_abc123",
                        "payment_url": "https://stores.tixxly.ai/en/v/immmersiv/book/order/res_abc123",
                        "amount": 178,
                        "currency": "EUR"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/reservations/{reservationId}": {
      "get": {
        "operationId": "getReservation",
        "tags": [
          "Bookings"
        ],
        "summary": "Get booking status",
        "description": "Lookup reservation status, payment state, and pricing after createReservation.\n",
        "security": [],
        "parameters": [
          {
            "name": "reservationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "res_abc123"
          }
        ],
        "responses": {
          "200": {
            "description": "Reservation status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReservationStatusResponse"
                },
                "examples": {
                  "pending": {
                    "summary": "Awaiting payment",
                    "value": {
                      "success": true,
                      "data": {
                        "reservation_id": "res_abc123",
                        "status": "pending",
                        "payment_status": "requires_payment_method",
                        "amount": 178,
                        "currency": "EUR",
                        "pricing": {
                          "subtotal": 178,
                          "service_fee": 0,
                          "total": 178,
                          "currency": "EUR"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vendors/{username}": {
      "get": {
        "operationId": "getVendor",
        "tags": [
          "Vendors"
        ],
        "summary": "Get vendor information",
        "security": [],
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "immmersiv"
          }
        ],
        "responses": {
          "200": {
            "description": "Vendor profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vendor"
                },
                "examples": {
                  "immmersiv": {
                    "value": {
                      "vendor_id": 12,
                      "company_name": "Immmersiv",
                      "business_type": "tours",
                      "username": "immmersiv"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vendors/{username}/products": {
      "get": {
        "operationId": "getVendorProducts",
        "tags": [
          "Vendors"
        ],
        "summary": "List vendor products",
        "security": [],
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "immmersiv"
          }
        ],
        "responses": {
          "200": {
            "description": "Vendor products (empty if Stripe onboarding incomplete)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/analytics/track": {
      "post": {
        "operationId": "trackEvent",
        "tags": [
          "Analytics"
        ],
        "summary": "Track an analytics event",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackEventRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event accepted"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Vendor feed/MCP API key from the Tixxly backoffice (AI Connections).\nOptional on many public read endpoints; scopes feed results when present.\n"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid request",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            },
            "example": {
              "success": false,
              "message": "Validation failed",
              "code": "VALIDATION_ERROR"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            },
            "example": {
              "success": false,
              "message": "Unauthorized",
              "code": "UNAUTHORIZED"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but not allowed (e.g. tier gate)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            },
            "example": {
              "success": false,
              "message": "Not found",
              "code": "NOT_FOUND"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            },
            "example": {
              "success": false,
              "message": "Too many requests",
              "code": "RATE_LIMITED"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorBody"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorBody": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "message": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "details": {
            "type": "object",
            "additionalProperties": true
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "Country": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 (lowercase)"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Geo": {
        "type": "object",
        "properties": {
          "latitude": {
            "type": "number"
          },
          "longitude": {
            "type": "number"
          }
        }
      },
      "Destination": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "country": {
            "$ref": "#/components/schemas/Country"
          },
          "geo": {
            "$ref": "#/components/schemas/Geo"
          },
          "productCount": {
            "type": "integer"
          }
        }
      },
      "DestinationsResponse": {
        "type": "object",
        "properties": {
          "destinations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Destination"
            }
          }
        }
      },
      "TrustScore": {
        "type": "object",
        "properties": {
          "score": {
            "type": "number"
          },
          "band": {
            "type": "string",
            "description": "Qualitative band (e.g. high, medium, low)"
          }
        }
      },
      "SupplierSummary": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "verified": {
            "type": "boolean"
          },
          "productCount": {
            "type": "integer"
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Country"
            }
          },
          "cities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "slug": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "country": {
                  "$ref": "#/components/schemas/Country"
                }
              }
            }
          },
          "trustScore": {
            "$ref": "#/components/schemas/TrustScore"
          },
          "storefrontUrl": {
            "type": "string",
            "format": "uri"
          },
          "liveSince": {
            "type": "string",
            "format": "date"
          },
          "verifiedAt": {
            "type": "string",
            "format": "date"
          }
        }
      },
      "SuppliersResponse": {
        "type": "object",
        "properties": {
          "suppliers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplierSummary"
            }
          }
        }
      },
      "ProductSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "fromPrice": {
            "type": "object",
            "properties": {
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          }
        }
      },
      "SupplierDetailResponse": {
        "type": "object",
        "properties": {
          "supplier": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SupplierSummary"
              },
              {
                "type": "object",
                "properties": {
                  "products": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/ProductSummary"
                    }
                  }
                }
              }
            ]
          }
        }
      },
      "FeedResponse": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "products": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Tixxly product ID — use everywhere"
                },
                "content": {
                  "type": "object",
                  "properties": {
                    "title": {
                      "type": "string"
                    },
                    "subtitle": {
                      "type": "string"
                    }
                  }
                },
                "taxonomy": {
                  "type": "object",
                  "properties": {
                    "brand": {
                      "type": "string"
                    },
                    "attributes": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "value": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "offers": {
                  "type": "object",
                  "properties": {
                    "price": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "merchant": {
            "type": "object",
            "additionalProperties": true
          },
          "pagination": {
            "type": "object",
            "properties": {
              "page": {
                "type": "integer"
              },
              "total": {
                "type": "integer"
              },
              "has_next": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "ExperienceDetail": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "external_id": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "content": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "subtitle": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "taxonomy": {
                "type": "object",
                "properties": {
                  "category_path": {
                    "type": "string"
                  },
                  "brand": {
                    "type": "string"
                  },
                  "attributes": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "value": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              },
              "compliance": {
                "type": "object",
                "properties": {
                  "warnings": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "age_restriction": {
                    "type": "string"
                  }
                }
              },
              "offers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "offer_id": {
                      "type": "string"
                    },
                    "price": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "availability": {
                      "type": "string"
                    }
                  }
                }
              },
              "image": {
                "type": "string",
                "format": "uri",
                "description": "Display as markdown: ![title](image_url)"
              },
              "vendor": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              },
              "pricingCategories": {
                "type": "array",
                "description": "Ticket types. Use id as pricingCategoryId in booking.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "fullTitle": {
                      "type": "string"
                    },
                    "minAge": {
                      "type": "integer"
                    },
                    "maxAge": {
                      "type": "integer"
                    },
                    "dependent": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "places": {
                "type": "array",
                "description": "Pickup/dropoff locations. When non-empty, use id as\npickupPlaceId/dropoffPlaceId. When empty or absent, omit those fields.\n",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "AvailabilityResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "image": {
                "type": "string",
                "format": "uri",
                "description": "Display as markdown: ![title](image_url)"
              },
              "availabilities": {
                "type": "array",
                "description": "Time slots (max 10)",
                "items": {
                  "type": "object",
                  "properties": {
                    "startTimeId": {
                      "type": "integer",
                      "description": "Use in booking-options and reserve"
                    },
                    "date": {
                      "type": "string"
                    },
                    "startTime": {
                      "type": "string"
                    },
                    "availabilityCount": {
                      "type": "integer"
                    },
                    "soldOut": {
                      "type": "boolean"
                    },
                    "rates": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "rateId": {
                            "type": "integer",
                            "description": "Use in booking-options and reserve"
                          },
                          "prices": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "categoryId": {
                                  "type": "integer"
                                },
                                "title": {
                                  "type": "string"
                                },
                                "amount": {
                                  "type": "number"
                                },
                                "currency": {
                                  "type": "string"
                                },
                                "minParticipantsRequired": {
                                  "type": "integer"
                                },
                                "maxParticipantsRequired": {
                                  "type": "integer"
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "pricingCategories": {
                "type": "array",
                "description": "Ticket types. Use id as pricingCategoryId.",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "fullTitle": {
                      "type": "string"
                    },
                    "minAge": {
                      "type": "integer"
                    },
                    "maxAge": {
                      "type": "integer"
                    }
                  }
                }
              },
              "places": {
                "type": "array",
                "description": "Pickup/dropoff locations. If non-empty, ask user to choose one\nand send as pickupPlaceId AND dropoffPlaceId. If empty or absent, omit both.\n",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "BookingOptionsRequest": {
        "type": "object",
        "required": [
          "activityBookings"
        ],
        "properties": {
          "activityBookings": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "activityId",
                "startTimeId",
                "date",
                "passengers"
              ],
              "properties": {
                "activityId": {
                  "type": "integer",
                  "description": "Tixxly product ID"
                },
                "startTimeId": {
                  "type": "integer",
                  "description": "From checkAvailability slot"
                },
                "date": {
                  "type": "string",
                  "description": "YYYY-MM-DD"
                },
                "rateId": {
                  "type": "integer",
                  "description": "From checkAvailability slot rates[].rateId"
                },
                "pickupPlaceId": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Required when places[] was non-empty. Use places[].id.\nOmit entirely when places[] was empty or absent — never 0 or null.\n"
                },
                "dropoffPlaceId": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Required when places[] was non-empty. Use places[].id.\nOmit entirely when places[] was empty or absent — never 0 or null.\n"
                },
                "passengers": {
                  "type": "array",
                  "description": "One entry per passenger",
                  "items": {
                    "type": "object",
                    "required": [
                      "pricingCategoryId"
                    ],
                    "properties": {
                      "pricingCategoryId": {
                        "type": "integer",
                        "description": "From getExperienceDetail pricingCategories[].id"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "BookingOptionsRes": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "options": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "questions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Question"
                      }
                    }
                  }
                }
              },
              "questions": {
                "type": "object",
                "properties": {
                  "mainContactDetails": {
                    "type": "array",
                    "description": "Collect answers for reserve mainContactDetails",
                    "items": {
                      "$ref": "#/components/schemas/Question"
                    }
                  },
                  "activityBookings": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "questions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Question"
                          }
                        },
                        "passengers": {
                          "type": "array",
                          "description": "Collect answers for reserve passengers[].passengerDetails",
                          "items": {
                            "type": "object",
                            "properties": {
                              "passengerDetails": {
                                "type": "array",
                                "items": {
                                  "$ref": "#/components/schemas/Question"
                                }
                              }
                            }
                          }
                        },
                        "pickupQuestions": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Question"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Question": {
        "type": "object",
        "description": "When dataFormat is PHONE_NUMBER, values must use international format with\ncountry code (e.g. +44123456789). Always include the + prefix and country code.\n",
        "properties": {
          "questionId": {
            "type": "string",
            "description": "Numeric ID string — use in answers, never the label"
          },
          "label": {
            "type": "string"
          },
          "dataType": {
            "type": "string"
          },
          "dataFormat": {
            "type": "string",
            "description": "PHONE_NUMBER = must be +countrycode format"
          },
          "required": {
            "type": "boolean"
          },
          "selectFromOptions": {
            "type": "boolean"
          },
          "answerOptions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Answer": {
        "type": "object",
        "required": [
          "questionId",
          "values"
        ],
        "properties": {
          "questionId": {
            "type": "string",
            "description": "Numeric question ID string from booking_options"
          },
          "values": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateReservationRequest": {
        "type": "object",
        "required": [
          "user_email",
          "directBooking"
        ],
        "properties": {
          "user_email": {
            "type": "string",
            "format": "email"
          },
          "directBooking": {
            "type": "object",
            "required": [
              "activityBookings",
              "mainContactDetails"
            ],
            "properties": {
              "activityBookings": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "activityId",
                    "startTimeId",
                    "date",
                    "passengers"
                  ],
                  "properties": {
                    "activityId": {
                      "type": "integer",
                      "description": "Tixxly product ID"
                    },
                    "startTimeId": {
                      "type": "integer",
                      "description": "From checkAvailability slot"
                    },
                    "date": {
                      "type": "string",
                      "description": "YYYY-MM-DD"
                    },
                    "rateId": {
                      "type": "integer",
                      "description": "From checkAvailability slot rates[].rateId"
                    },
                    "pickupPlaceId": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Required when places[] was non-empty. Omit when places empty/absent.\n"
                    },
                    "dropoffPlaceId": {
                      "type": "integer",
                      "minimum": 1,
                      "description": "Required when places[] was non-empty. Omit when places empty/absent.\n"
                    },
                    "answers": {
                      "type": "array",
                      "description": "Activity/option answers from booking_options. Omit when that\nquestion group was empty.\n",
                      "items": {
                        "$ref": "#/components/schemas/Answer"
                      }
                    },
                    "pickupAnswers": {
                      "type": "array",
                      "description": "Pickup answers when pickupQuestions was non-empty. Omit otherwise.\n",
                      "items": {
                        "$ref": "#/components/schemas/Answer"
                      }
                    },
                    "passengers": {
                      "type": "array",
                      "description": "One per passenger with filled passengerDetails",
                      "items": {
                        "type": "object",
                        "required": [
                          "pricingCategoryId",
                          "passengerDetails"
                        ],
                        "properties": {
                          "pricingCategoryId": {
                            "type": "integer"
                          },
                          "passengerDetails": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/Answer"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "mainContactDetails": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Answer"
                }
              }
            }
          }
        }
      },
      "ReservationResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "reservation_id": {
                "type": "string"
              },
              "payment_url": {
                "type": "string",
                "format": "uri",
                "description": "Always display as markdown link [Complete Payment](url)"
              },
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              }
            }
          }
        }
      },
      "ReservationStatusResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "data": {
            "type": "object",
            "properties": {
              "reservation_id": {
                "type": "string"
              },
              "booking_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "payment_status": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "amount": {
                "type": "number"
              },
              "currency": {
                "type": "string"
              },
              "pricing": {
                "type": "object",
                "properties": {
                  "subtotal": {
                    "type": "number"
                  },
                  "service_fee": {
                    "type": "number"
                  },
                  "total": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "Vendor": {
        "type": "object",
        "properties": {
          "vendor_id": {
            "type": "integer"
          },
          "company_name": {
            "type": "string"
          },
          "business_type": {
            "type": "string"
          },
          "username": {
            "type": "string"
          }
        }
      },
      "TrackEventRequest": {
        "type": "object",
        "required": [
          "event_type",
          "vendor_id"
        ],
        "properties": {
          "event_type": {
            "type": "string",
            "enum": [
              "storefront_view",
              "product_view",
              "booking_click",
              "checkout_start",
              "payment_attempt",
              "booking_confirmed"
            ]
          },
          "vendor_id": {
            "type": "integer"
          },
          "product_id": {
            "type": "integer"
          },
          "source": {
            "type": "string",
            "default": "openai"
          }
        }
      }
    }
  }
}
