openapi: 3.1.0
info:
  title: Tell & Go Travel Agent API
  description: |
    AI-native luxury travel API for agent platforms (ChatGPT, Claude, Perplexity).
    Search 300+ curated luxury properties across Maldives, Seychelles, Mauritius, and Sri Lanka.
    Full search-to-book lifecycle with natural language support.
  version: 2.0.0
  contact:
    name: Tell & Go API Support
    email: api@tellandgo.com
    url: https://tellandgo.com
  license:
    name: Proprietary
    url: https://tellandgo.com/terms

servers:
  - url: https://api.tellandgo.com/api/v1/agent
    description: Production

security:
  - apiKey: []

paths:
  /health:
    get:
      operationId: healthCheck
      summary: API health check
      description: Returns API status and available features. No authentication required.
      security: []
      responses:
        "200":
          description: API is healthy
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum: [healthy]
                  version:
                    type: string
                  timestamp:
                    type: string
                    format: date-time
                  features:
                    type: array
                    items:
                      type: string

  /search:
    post:
      operationId: searchProperties
      summary: Search luxury properties
      description: |
        Natural language property search across 300+ luxury resorts.
        Supports structured constraints (dates, budget, guests) alongside free-text queries.
        Returns ranked results with match explanations.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SearchRequest"
      responses:
        "200":
          description: Search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SearchResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /properties/{id}:
    get:
      operationId: getProperty
      summary: Get property details
      description: Returns full property information including amenities, dining, activities, and images.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Property ID
      responses:
        "200":
          description: Property details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PropertyDetailResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /availability:
    post:
      operationId: checkAvailability
      summary: Check property availability
      description: Check real-time availability and room rates for specific dates and guest configuration.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AvailabilityRequest"
      responses:
        "200":
          description: Availability result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AvailabilityResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /chat:
    post:
      operationId: chat
      summary: Conversational property search
      description: |
        Multi-turn conversational search with preference extraction and follow-up questions.
        Maintains session context across turns for increasingly refined recommendations.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChatRequest"
      responses:
        "200":
          description: Chat response with optional property recommendations
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChatResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /quote:
    post:
      operationId: createQuote
      summary: Create a pricing quote
      description: |
        Creates a time-limited price guarantee for a specific property and room.
        The quote ID is required for booking. Quotes typically expire within 30 minutes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuoteRequest"
      responses:
        "200":
          description: Quote created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QuoteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /prebook:
    post:
      operationId: prebookQuote
      summary: Verify or lock a quote before payment
      description: |
        Live inventory verification step. Call this after quote selection and before
        creating a booking/payment. Quotes that require final verification return
        locked, while quotes without a prebook phase return `not_required`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrebookRequest"
      responses:
        "200":
          description: Quote verified or prebook not required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PrebookResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "409":
          description: Price changed or prebook required action
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /quotes/{id}/prebook:
    post:
      operationId: prebookQuoteById
      summary: Verify or lock a quote before payment
      description: |
        Runs the final verification step for a quote before payment.
        Quotes that require verification are locked before payment. If live pricing
        reports a price increase or decrease, this endpoint returns HTTP 409 with
        structured price-change details so the caller can refresh and ask the user
        to confirm before payment. Quotes that do not require prebook return
        status `not_required`.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Quote ID returned by POST /quote.
      responses:
        "200":
          description: Quote verified or prebook not required
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/PrebookResponse"
        "409":
          description: Price changed or prebook required action
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /book:
    post:
      operationId: createBooking
      summary: Create a booking
      description: |
        Creates a booking from a valid quote. Quotes that require prebook must be
        locked before payment. Provide at least one valid adult lead guest per
        booked room. Do not send placeholder guests for children or unnamed
        occupants; the quote already stores the occupancy. Returns a
        Stripe-hosted checkout URL for payment by default. `stripe_checkout`
        is a hosted browser payment flow, not a headless server-to-server
        payment call: redirect the customer browser to the returned
        `payment.url`. After payment, Tell&Go confirms the booking server-side
        from the Stripe webhook and supplier status checks. API consumers can
        read final status with GET /bookings/{id} and may optionally register
        partner webhooks for async status events.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BookingRequest"
      responses:
        "201":
          description: Booking created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BookingResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /bookings:
    get:
      operationId: listBookings
      summary: List partner bookings
      description: Returns paginated list of bookings created by this partner.
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [pending_payment, confirmed, cancelled, completed]
          description: Filter by booking status
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: Booking list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BookingListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /bookings/{id}:
    get:
      operationId: getBooking
      summary: Get booking status
      description: Returns current status and details of a specific booking.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Booking details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BookingResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: cancelBooking
      summary: Cancel a booking
      description: Cancels a booking. Cancellation policies may apply.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Cancellation reason
      responses:
        "200":
          description: Booking cancelled
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BookingResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /flights/search:
    post:
      operationId: searchFlights
      summary: Search for flights
      description: |
        Search available flights by origin, destination, dates, and passengers.
        Supports IATA codes or city/country names for origin and destination.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FlightSearchRequest"
      responses:
        "200":
          description: Flight search results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FlightSearchResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /flights/quote:
    post:
      operationId: createFlightQuote
      summary: Create a flight quote
      description: |
        Revalidates a flight offer and creates a time-limited price-guaranteed quote.
        The quote ID is required for booking. Quotes expire within 15 minutes.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FlightQuoteRequest"
      responses:
        "200":
          description: Flight quote created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FlightQuoteResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "410":
          description: Offer no longer available
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /flights/book:
    post:
      operationId: bookFlight
      summary: Book a flight
      description: |
        Creates a flight booking from a valid quote. Returns a hosted checkout session
        URL for payment. After payment, the booking is confirmed automatically via webhook.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/FlightBookRequest"
      responses:
        "201":
          description: Flight booking created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FlightBookResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: Quote not found or expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /flights/bookings/{id}:
    get:
      operationId: getFlightBooking
      summary: Get flight booking status
      description: Returns current status and details of a specific flight booking.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Booking ID
      responses:
        "200":
          description: Flight booking details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FlightBookingStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: cancelFlightBooking
      summary: Cancel a flight booking
      description: |
        Cancels a flight booking. Initiates payment refund and airline
        cancellation. Cancellation policies may apply.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Booking ID
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                reason:
                  type: string
                  description: Cancellation reason
      responses:
        "200":
          description: Flight booking cancelled
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /bundles/book:
    post:
      operationId: bookBundle
      summary: Book a hotel + flight bundle
      description: |
        Creates a bundle booking combining a hotel quote and a flight quote.
        A 5% bundle discount is applied automatically. Returns a single hosted
        checkout URL for the combined payment. After payment, both the
        hotel and flight are confirmed automatically via webhook.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BundleBookRequest"
      responses:
        "201":
          description: Bundle booking created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BundleBookResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          description: Hotel or flight quote not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "410":
          description: Quote expired
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /bundles/{id}:
    get:
      operationId: getBundleStatus
      summary: Get bundle booking status
      description: Returns combined hotel and flight status for a bundle booking.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: Booking ID
      responses:
        "200":
          description: Bundle booking details
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BundleStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /webhooks/endpoints:
    get:
      operationId: listWebhookEndpoints
      summary: List webhook endpoints
      description: Requires the `webhooks:write` scope.
      responses:
        "200":
          description: Webhook endpoints
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"
    post:
      operationId: createWebhookEndpoint
      summary: Create a webhook endpoint
      description: Requires the `webhooks:write` scope. The signing secret is returned only on creation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointCreateRequest"
      responses:
        "201":
          description: Webhook endpoint created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointCreateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"

  /webhooks/endpoints/{id}:
    patch:
      operationId: updateWebhookEndpoint
      summary: Update a webhook endpoint
      description: Requires the `webhooks:write` scope.
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEndpointUpdateRequest"
      responses:
        "200":
          description: Webhook endpoint updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"
        "404":
          $ref: "#/components/responses/NotFound"
    delete:
      operationId: deleteWebhookEndpoint
      summary: Disable a webhook endpoint
      description: Requires the `webhooks:write` scope.
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      responses:
        "200":
          description: Webhook endpoint disabled
          content:
            application/json:
              schema:
                type: object
                required: [success]
                properties:
                  success:
                    type: boolean
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"
        "404":
          $ref: "#/components/responses/NotFound"

  /webhooks/endpoints/{id}/test:
    post:
      operationId: testWebhookEndpoint
      summary: Send a test webhook event
      description: Requires the `webhooks:write` scope.
      parameters:
        - $ref: "#/components/parameters/WebhookEndpointId"
      responses:
        "200":
          description: Test delivery result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"
        "404":
          $ref: "#/components/responses/NotFound"

  /webhook-deliveries:
    get:
      operationId: listWebhookDeliveries
      summary: List recent webhook delivery attempts
      description: Requires the `webhooks:write` scope.
      responses:
        "200":
          description: Recent delivery attempts
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookDeliveryListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/InsufficientScope"

components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: "API key (format: tg_live_* for production, tg_test_* for sandbox)"

  parameters:
    WebhookEndpointId:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Webhook endpoint ID

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Authentication required or invalid
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    InsufficientScope:
      description: API key lacks the required scope
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: string
        message:
          type: string
        details:
          description: Structured error details. Prebook price changes include quote_id, prebook status/direction, previous/current pricing, and refresh_required.
        request_id:
          type: string
        retryable:
          type: boolean

    WebhookEvent:
      type: string
      enum:
        - booking.created
        - payment.succeeded
        - payment.failed
        - booking.confirmed
        - booking.failed
        - booking.cancelled
        - refund.updated
        - webhook.test

    WebhookEndpoint:
      type: object
      required: [id, url, events, active, created_at, updated_at]
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        description:
          type: string
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEvent"
        active:
          type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    WebhookEndpointCreateRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          format: uri
          description: HTTPS endpoint URL.
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEvent"
        description:
          type: string

    WebhookEndpointUpdateRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEvent"
        description:
          type: string
          nullable: true
        active:
          type: boolean

    WebhookEndpointCreateResponse:
      type: object
      required: [success, endpoint, secret]
      properties:
        success:
          type: boolean
        endpoint:
          $ref: "#/components/schemas/WebhookEndpoint"
        secret:
          type: string
          description: Webhook signing secret returned once when the endpoint is created.

    WebhookEndpointResponse:
      type: object
      required: [success, endpoint]
      properties:
        success:
          type: boolean
        endpoint:
          $ref: "#/components/schemas/WebhookEndpoint"

    WebhookEndpointListResponse:
      type: object
      required: [success, endpoints]
      properties:
        success:
          type: boolean
        endpoints:
          type: array
          items:
            $ref: "#/components/schemas/WebhookEndpoint"

    WebhookDelivery:
      type: object
      required: [id, endpoint_id, event, status, attempts, created_at]
      properties:
        id:
          type: string
        endpoint_id:
          type: string
        event:
          $ref: "#/components/schemas/WebhookEvent"
        status:
          type: string
          enum: [delivered, failed]
        status_code:
          type: integer
        error:
          type: string
        attempts:
          type: integer
        created_at:
          type: string
          format: date-time

    WebhookDeliveryResponse:
      type: object
      required: [success, delivery]
      properties:
        success:
          type: boolean
        delivery:
          $ref: "#/components/schemas/WebhookDelivery"

    WebhookDeliveryListResponse:
      type: object
      required: [success, deliveries]
      properties:
        success:
          type: boolean
        deliveries:
          type: array
          items:
            $ref: "#/components/schemas/WebhookDelivery"
        retry_after:
          type: integer

    SearchRequest:
      type: object
      description: Live-priced accommodation search. Every valid request must include dates, residency, and occupancy; identical RateHawk SERP requests may be served from the 5-minute supplier cache.
      required: [query, residency, constraints]
      properties:
        query:
          type: string
          description: Natural language search query
          example: "Overwater villa in the Maldives for a honeymoon in March"
        residency:
          type: string
          minLength: 2
          maxLength: 2
          example: US
          description: Required ISO 3166-1 alpha-2 citizenship/residency code. Passed to live supplier searches as residency.
        session_id:
          type: string
        constraints:
          type: object
          required: [dates]
          oneOf:
            - required: [guests]
            - required: [rooms]
          properties:
            destination:
              type: string
              example: "Maldives"
            budget_max:
              type: number
            budget_min:
              type: number
            currency:
              type: string
              default: USD
            dates:
              type: object
              required: [check_in, check_out]
              properties:
                check_in:
                  type: string
                  format: date
                check_out:
                  type: string
                  format: date
            guests:
              type: object
              properties:
                adults:
                  type: integer
                  minimum: 1
                children:
                  type: array
                  items:
                    type: integer
                  description: Array of children's ages
            rooms:
              type: array
              description: Per-room occupancy for multi-room live pricing. Each room has adults and optional child ages in children.
              items:
                type: object
                required: [adults]
                properties:
                  adults:
                    type: integer
                    minimum: 1
                  children:
                    type: array
                    items:
                      type: integer
                    description: Child ages for this room
            amenities:
              type: array
              items:
                type: string
            activities:
              type: array
              items:
                type: string
        limit:
          type: integer
          default: 20
          maximum: 50
        offset:
          type: integer
          default: 0

    SearchResponse:
      type: object
      properties:
        success:
          type: boolean
        properties:
          type: array
          items:
            $ref: "#/components/schemas/PropertyResult"
        pagination:
          $ref: "#/components/schemas/Pagination"
        booking_url:
          type: string
          format: uri
        meta:
          type: object
          properties:
            query_id:
              type: string
            processing_time_ms:
              type: number

    PropertyResult:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        slug:
          type: string
        location:
          type: object
          properties:
            country:
              type: string
            area:
              type: string
            coordinates:
              type: object
              properties:
                lat:
                  type: number
                lng:
                  type: number
        pricing:
          type: object
          properties:
            price_per_night:
              type: number
            total_price:
              type: number
            currency:
              type: string
            is_available:
              type: boolean
        match:
          type: object
          properties:
            score:
              type: number
              minimum: 0
              maximum: 1
            explanation:
              type: string
        rating:
          type: number
        rating_label:
          type: string
        type:
          type: string
        images:
          type: array
          items:
            type: object
            properties:
              url:
                type: string
                format: uri
              caption:
                type: string
        highlights:
          type: array
          items:
            type: string
        amenities:
          type: array
          items:
            type: string

    PropertyDetailResponse:
      type: object
      properties:
        success:
          type: boolean
        property:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            slug:
              type: string
            description:
              type: string
            location:
              type: object
              properties:
                country:
                  type: string
                area:
                  type: string
                address:
                  type: string
                coordinates:
                  type: object
                  properties:
                    lat:
                      type: number
                    lng:
                      type: number
            highlights:
              type: array
              items:
                type: string
            amenities:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  category:
                    type: string
            dining:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  cuisine:
                    type: string
                  description:
                    type: string
            activities:
              type: array
              items:
                type: string
            images:
              type: array
              items:
                type: object
                properties:
                  url:
                    type: string
                    format: uri
                  caption:
                    type: string
            rating:
              type: number
            rating_label:
              type: string
            type:
              type: string
            check_in_time:
              type: string
            check_out_time:
              type: string
        booking_url:
          type: string
          format: uri

    AvailabilityRequest:
      type: object
      required: [property_id, check_in, check_out, residency]
      description: Provide either guests for a single-room/simple request or rooms for multi-room occupancy.
      properties:
        property_id:
          type: string
        check_in:
          type: string
          format: date
        check_out:
          type: string
          format: date
        guests:
          type: object
          description: Single-room/simple occupancy object. For multi-room requests, send rooms instead.
          properties:
            adults:
              type: integer
              minimum: 1
              description: Required only when rooms is omitted.
            children:
              type: array
              items:
                type: integer
              description: Array of child ages.
        rooms:
          type: array
          description: Per-room occupancy for multi-room availability searches. Required when guests is omitted.
          items:
            type: object
            required: [adults]
            properties:
              adults:
                type: integer
                minimum: 1
              children:
                type: array
                items:
                  type: integer
                description: Child ages for this room.

    AvailabilityResponse:
      type: object
      properties:
        success:
          type: boolean
        available:
          type: boolean
        rooms:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              description:
                type: string
              max_occupancy:
                type: integer
              rates:
                type: array
                items:
                  type: object
                  properties:
                    rate_plan:
                      type: string
                    price_per_night:
                      type: number
                    total_price:
                      type: number
                    currency:
                      type: string
                    meal_plan:
                      type: string
                    cancellation_policy:
                      type: string
                    cancellation_policy_timezone:
                      type: string
                      example: UTC+0
                      description: Timezone used for timestamps inside cancellation_policy.
                    offer_id:
                      type: string
                      description: Opaque live-rate offer ID. Pass as quote.room_id to quote this exact rate.
        hotel_policies:
          $ref: "#/components/schemas/HotelPolicies"
        booking_url:
          type: string
          format: uri

    ChatRequest:
      type: object
      required: [message]
      properties:
        message:
          type: string
          description: User message
          example: "I'm looking for a romantic overwater villa in the Maldives for our honeymoon"
        session_id:
          type: string
          description: Session ID for multi-turn conversations
        context:
          type: object
          properties:
            previous_searches:
              type: array
              items:
                type: string
            selected_properties:
              type: array
              items:
                type: string
            collected_preferences:
              type: object
              properties:
                travel_style:
                  type: array
                  items:
                    type: string
                interests:
                  type: array
                  items:
                    type: string
                budget:
                  type: object
                  properties:
                    min:
                      type: number
                    max:
                      type: number
                dates:
                  type: object
                  properties:
                    check_in:
                      type: string
                      format: date
                    check_out:
                      type: string
                      format: date
                destination:
                  type: string
                guests:
                  type: object
                  properties:
                    adults:
                      type: integer
                    children:
                      type: array
                      items:
                        type: integer
        include_pricing:
          type: boolean
          default: false
        max_properties:
          type: integer
          default: 5

    ChatResponse:
      type: object
      properties:
        success:
          type: boolean
        response:
          type: object
          properties:
            text:
              type: string
              description: Chat response text
            intent:
              type: string
              enum:
                [
                  greeting,
                  search,
                  recommendation,
                  comparison,
                  clarification,
                  booking_intent,
                  general_info,
                  farewell,
                  narrowing,
                ]
            confidence:
              type: number
            extracted_preferences:
              type: object
              properties:
                destination:
                  type: string
                dates:
                  type: object
                  properties:
                    check_in:
                      type: string
                    check_out:
                      type: string
                budget:
                  type: object
                  properties:
                    min:
                      type: number
                    max:
                      type: number
                interests:
                  type: array
                  items:
                    type: string
        properties:
          type: array
          items:
            $ref: "#/components/schemas/PropertyResult"
        follow_ups:
          type: array
          items:
            type: object
            properties:
              question:
                type: string
              type:
                type: string
                enum: [clarification, preference, narrowing, upsell]
              options:
                type: array
                items:
                  type: object
                  properties:
                    label:
                      type: string
                    value:
                      type: string
        session:
          type: object
          properties:
            id:
              type: string
            updated_context:
              type: object

    QuoteRequest:
      type: object
      required: [property_id, check_in, check_out, residency]
      description: Provide either guests for a single-room/simple quote or rooms for multi-room occupancy.
      properties:
        property_id:
          type: string
        room_id:
          type: string
          description: Optional room code or opaque offer_id returned by /availability.
        check_in:
          type: string
          format: date
        check_out:
          type: string
          format: date
        residency:
          type: string
          description: ISO 3166-1 alpha-2 citizenship/residency code for live pricing.
        guests:
          type: object
          description: Single-room/simple occupancy object. For multi-room quotes, send rooms instead.
          properties:
            adults:
              type: integer
              minimum: 1
              description: Required only when rooms is omitted.
            children:
              type: array
              items:
                type: integer
              description: Array of child ages.
        rooms:
          type: array
          description: Per-room occupancy for multi-room quotes. Required when guests is omitted.
          items:
            type: object
            required: [adults]
            properties:
              adults:
                type: integer
                minimum: 1
              children:
                type: array
                items:
                  type: integer
                description: Child ages for this room.
        rate_plan:
          type: string

    QuoteResponse:
      type: object
      properties:
        success:
          type: boolean
        quote:
          type: object
          properties:
            id:
              type: string
            expires_at:
              type: string
              format: date-time
            property:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
            room:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
            dates:
              type: object
              properties:
                check_in:
                  type: string
                  format: date
                check_out:
                  type: string
                  format: date
                nights:
                  type: integer
            pricing:
              type: object
              properties:
                price_per_night:
                  type: number
                subtotal:
                  type: number
                taxes_and_fees:
                  type: number
                total:
                  type: number
                currency:
                  type: string
                non_included_taxes:
                  type: array
                  description: Taxes/fees not included in the online total and payable directly at check-in.
                  items:
                    $ref: "#/components/schemas/NonIncludedTax"
            cancellation:
              type: object
              properties:
                policy:
                  type: string
                deadline:
                  type: string
                  format: date-time
                  description: Cancellation deadline in UTC.
                refundable:
                  type: boolean
                timezone:
                  type: string
                  example: UTC+0
                  description: Timezone used for cancellation deadline and policy timestamps.
            meal_plan:
              type: string
            prebook:
              type: object
              properties:
                required_before_payment:
                  type: boolean
                status:
                  type: string
                  enum:
                    [
                      not_required,
                      not_started,
                      required,
                      locked,
                      price_changed,
                      failed,
                    ]
                expires_at:
                  type: string
                  format: date-time
                price_changed:
                  type: boolean
            hotel_policies:
              $ref: "#/components/schemas/HotelPolicies"
        quotes:
          type: array
          description: All currently available rates from one live availability lookup, each with its own quote ID.
          items:
            type: object
        booking_url:
          type: string
          format: uri

    PrebookRequest:
      type: object
      required: [quote_id]
      properties:
        quote_id:
          type: string
          description: Quote ID returned by POST /quote.

    PrebookResponse:
      type: object
      properties:
        success:
          type: boolean
        quote_id:
          type: string
        prebook:
          type: object
          properties:
            required_before_payment:
              type: boolean
            status:
              type: string
              enum: [not_required, locked]
            expires_at:
              type: string
              format: date-time
            price_changed:
              type: boolean
            original_book_hash:
              type: string
            book_hash:
              type: string
        pricing:
          type: object
          properties:
            total:
              type: number
            currency:
              type: string

    NonIncludedTax:
      type: object
      properties:
        name:
          type: string
        amount:
          type: string
        currency_code:
          type: string
        payable_at:
          type: string
          enum: [check_in]
        note:
          type: string

    HotelPolicies:
      type: object
      description: Supplier-neutral hotel policy metadata. Field names are stable across suppliers; raw supplier-specific policy keys are not exposed.
      properties:
        extra_info:
          description: Optional free-text policy notes such as local taxes, fees, or other important hotel information.
          oneOf:
            - type: string
            - type: object
            - type: array
        structured:
          type: object
          description: Optional structured policy data normalized under supplier-neutral field names.
          additionalProperties: true

    BookingRequest:
      type: object
      required: [quote_id, guests]
      properties:
        quote_id:
          type: string
        guests:
          type: array
          description: |
            Guest details. Provide at least one valid adult lead guest per booked
            room. Remaining occupants from the quote are not auto-populated into
            supplier guest names.
          minItems: 1
          items:
            type: object
            required: [first_name, last_name]
            properties:
              first_name:
                type: string
              last_name:
                type: string
              email:
                type: string
                format: email
              phone:
                type: string
              is_primary:
                type: boolean
              is_child:
                type: boolean
                description: Optional; only set when providing accurate child details.
              age:
                type: number
                description: Required when is_child is true.
        special_requests:
          type: string
        payment_method:
          type: string
          enum: [stripe_checkout, redirect]
          default: stripe_checkout
          description: |
            Payment method. `stripe_checkout` (default) returns a Stripe-hosted payment URL.
            `redirect` (deprecated) returns a URL to our checkout page.
        success_url:
          type: string
          format: uri
          description: Optional HTTPS URL to return the payer to after Stripe-hosted checkout succeeds. Tell&Go appends booking_id and session_id when missing.
        cancel_url:
          type: string
          format: uri
          description: Optional HTTPS URL to return the payer to if Stripe-hosted checkout is cancelled. Tell&Go appends booking_id and payment=cancelled when missing.

    BookingResponse:
      type: object
      properties:
        success:
          type: boolean
        booking:
          type: object
          properties:
            id:
              type: string
            status:
              type: string
              enum: [pending_payment, confirmed, cancelled, completed]
            reference:
              type: string
            confirmation_number:
              type: string
            property:
              type: object
              properties:
                id:
                  type: string
                name:
                  type: string
            room:
              type: object
              properties:
                name:
                  type: string
            dates:
              type: object
              properties:
                check_in:
                  type: string
                  format: date
                check_out:
                  type: string
                  format: date
                nights:
                  type: integer
            guests:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  is_primary:
                    type: boolean
            payment:
              type: object
              properties:
                method:
                  type: string
                  enum: [stripe_checkout, redirect]
                  description: Payment method used
                status:
                  type: string
                  enum: [pending, completed, failed, refunded]
                amount:
                  type: number
                currency:
                  type: string
                url:
                  type: string
                  format: uri
                  description: Payment URL. For hosted checkout, this is a hosted payment page.
                session_id:
                  type: string
                  description: Hosted checkout session ID (only for hosted checkout)
                expires_at:
                  type: string
                  format: date-time
            created_at:
              type: string
              format: date-time

    BookingListResponse:
      type: object
      properties:
        success:
          type: boolean
        bookings:
          type: array
          items:
            $ref: "#/components/schemas/BookingResponse/properties/booking"
        pagination:
          $ref: "#/components/schemas/Pagination"

    FlightSearchRequest:
      type: object
      required: [origin, destination, departure_date, passengers]
      properties:
        origin:
          type: string
          description: Origin airport IATA code or city/country name
          example: "LHR"
        destination:
          type: string
          description: Destination airport IATA code or city/country name
          example: "MLE"
        departure_date:
          type: string
          format: date
          description: Departure date (YYYY-MM-DD)
          example: "2026-04-15"
        return_date:
          type: string
          format: date
          description: Return date for round trips (YYYY-MM-DD)
        passengers:
          type: array
          minItems: 1
          items:
            type: object
            properties:
              type:
                type: string
                enum: [adult, child, infant_without_seat]
                default: adult
          description: List of passengers with type
          example: [{ "type": "adult" }, { "type": "adult" }]
        cabin_class:
          type: string
          enum: [economy, premium_economy, business, first]
          description: Preferred cabin class

    FlightSearchResponse:
      type: object
      properties:
        success:
          type: boolean
        flights:
          type: array
          items:
            $ref: "#/components/schemas/FlightResult"
        search_id:
          type: string
          nullable: true
        meta:
          type: object
          properties:
            origin:
              type: string
            destination:
              type: string
            message:
              type: string
            result_count:
              type: integer

    FlightResult:
      type: object
      properties:
        offer_id:
          type: string
          description: Unique offer ID (use for quoting)
        airline:
          type: object
          properties:
            name:
              type: string
            iata_code:
              type: string
            logo_url:
              type: string
              format: uri
        slices:
          type: array
          items:
            $ref: "#/components/schemas/FlightSlice"
        price:
          type: object
          properties:
            total:
              type: number
            currency:
              type: string
        cabin_class:
          type: string
        passengers:
          type: array
          items:
            type: object
            properties:
              type:
                type: string
              count:
                type: integer
        seats_remaining:
          type: integer
        conditions:
          type: object
          properties:
            refundable:
              type: boolean
            changeable:
              type: boolean
        expires_at:
          type: string
          format: date-time

    FlightSlice:
      type: object
      properties:
        origin:
          type: string
          description: Origin IATA code
        destination:
          type: string
          description: Destination IATA code
        duration:
          type: string
          description: Duration in ISO 8601 format
        segments:
          type: array
          items:
            $ref: "#/components/schemas/FlightSegment"

    FlightSegment:
      type: object
      properties:
        carrier:
          type: string
          description: Airline name
        carrier_code:
          type: string
          description: IATA airline code
        flight_number:
          type: string
        origin:
          type: string
        destination:
          type: string
        departure_time:
          type: string
          format: date-time
        arrival_time:
          type: string
          format: date-time
        cabin_class:
          type: string

    FlightQuoteRequest:
      type: object
      required: [offer_id]
      properties:
        offer_id:
          type: string
          description: Offer ID from flight search results

    FlightQuoteResponse:
      type: object
      properties:
        success:
          type: boolean
        quote:
          type: object
          properties:
            id:
              type: string
              description: Quote ID (use for booking)
            offer_id:
              type: string
            expires_at:
              type: string
              format: date-time
            airline:
              type: object
              properties:
                name:
                  type: string
                iata_code:
                  type: string
                logo_url:
                  type: string
                  format: uri
            slices:
              type: array
              items:
                type: object
                properties:
                  origin:
                    type: string
                  destination:
                    type: string
                  departureTime:
                    type: string
                    format: date-time
                  arrivalTime:
                    type: string
                    format: date-time
                  duration:
                    type: string
                  segments:
                    type: array
                    items:
                      type: object
                      properties:
                        carrier:
                          type: string
                        carrierCode:
                          type: string
                        flightNumber:
                          type: string
                        origin:
                          type: string
                        destination:
                          type: string
                        departureTime:
                          type: string
                          format: date-time
                        arrivalTime:
                          type: string
                          format: date-time
                        cabinClass:
                          type: string
            passengers:
              type: array
              items:
                type: object
                properties:
                  id:
                    type: string
                  type:
                    type: string
            pricing:
              type: object
              properties:
                total:
                  type: number
                currency:
                  type: string
                base:
                  type: number
                taxes:
                  type: number
            cabin_class:
              type: string
            conditions:
              type: object
              properties:
                refundable:
                  type: boolean
                changeable:
                  type: boolean
            seats_remaining:
              type: integer

    FlightPassenger:
      type: object
      required: [first_name, last_name, email, phone, born_on, gender]
      properties:
        title:
          type: string
          enum: [mr, ms, mrs, miss, dr]
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        phone:
          type: string
          description: Phone number with country code
          example: "+12025550100"
        born_on:
          type: string
          format: date
          description: Date of birth (YYYY-MM-DD)
        gender:
          type: string
          enum: [m, f]
        document_type:
          type: string
          enum: [passport, national_identity_card]
        document_number:
          type: string
        document_expiry:
          type: string
          format: date
          description: Document expiry date (YYYY-MM-DD)
        nationality:
          type: string
          description: ISO 3166-1 alpha-2 country code

    FlightBookRequest:
      type: object
      required: [quote_id, passengers]
      properties:
        quote_id:
          type: string
          description: Quote ID from flight quote
        passengers:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/FlightPassenger"

    FlightBookResponse:
      type: object
      properties:
        success:
          type: boolean
        booking:
          type: object
          properties:
            id:
              type: string
            flight_booking_id:
              type: string
            status:
              type: string
              enum: [pending_payment, confirmed, cancelled]
            reference:
              type: string
            airline:
              type: object
              properties:
                name:
                  type: string
                iata_code:
                  type: string
                logo_url:
                  type: string
                  format: uri
            slices:
              type: array
              items:
                $ref: "#/components/schemas/FlightSlice"
            passengers:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  type:
                    type: string
            total:
              type: object
              properties:
                amount:
                  type: number
                currency:
                  type: string
            payment:
              type: object
              properties:
                method:
                  type: string
                url:
                  type: string
                  format: uri
                  description: Hosted checkout URL
                session_id:
                  type: string
                  description: Hosted checkout session ID
                expires_at:
                  type: string
                  format: date-time
            created_at:
              type: string
              format: date-time

    FlightBookingStatusResponse:
      type: object
      properties:
        success:
          type: boolean
        booking:
          type: object
          properties:
            id:
              type: string
            flight_booking_id:
              type: string
            status:
              type: string
              enum: [pending_payment, confirmed, cancelled, completed]
            reference:
              type: string
            confirmation_number:
              type: string
            airline:
              type: object
              properties:
                name:
                  type: string
                iata_code:
                  type: string
            slices:
              type: array
              items:
                $ref: "#/components/schemas/FlightSlice"
            segments:
              type: array
              items:
                type: object
                properties:
                  origin:
                    type: string
                  destination:
                    type: string
                  departure_at:
                    type: string
                    format: date-time
                  arrival_at:
                    type: string
                    format: date-time
                  airline:
                    type: string
                  flight_number:
                    type: string
                  cabin_class:
                    type: string
            passengers:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  email:
                    type: string
                    format: email
            total:
              type: object
              properties:
                amount:
                  type: number
                currency:
                  type: string
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time

    BundleBookRequest:
      type: object
      required: [hotel_quote_id, flight_quote_id, guests, passengers]
      properties:
        hotel_quote_id:
          type: string
          description: Quote ID from hotel quote endpoint
        flight_quote_id:
          type: string
          description: Quote ID from flight quote endpoint
        guests:
          type: array
          minItems: 1
          items:
            type: object
            required: [first_name, last_name]
            properties:
              first_name:
                type: string
              last_name:
                type: string
              email:
                type: string
                format: email
              phone:
                type: string
              is_primary:
                type: boolean
          description: Hotel guests
        passengers:
          type: array
          minItems: 1
          items:
            $ref: "#/components/schemas/FlightPassenger"
          description: Flight passengers
        special_requests:
          type: string

    BundleBookResponse:
      type: object
      properties:
        success:
          type: boolean
        booking:
          type: object
          properties:
            id:
              type: string
            flight_booking_id:
              type: string
            status:
              type: string
              enum: [pending_payment, confirmed, cancelled]
            reference:
              type: string
            hotel:
              type: object
              properties:
                property_name:
                  type: string
                room_name:
                  type: string
                check_in:
                  type: string
                  format: date
                check_out:
                  type: string
                  format: date
                nights:
                  type: integer
            flight:
              type: object
              properties:
                airline:
                  type: object
                  properties:
                    name:
                      type: string
                    iata_code:
                      type: string
                    logo_url:
                      type: string
                      format: uri
                slices:
                  type: array
                  items:
                    type: object
                    properties:
                      origin:
                        type: string
                      destination:
                        type: string
                      departureTime:
                        type: string
                        format: date-time
                      arrivalTime:
                        type: string
                        format: date-time
                      duration:
                        type: string
            guests:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  is_primary:
                    type: boolean
            passengers:
              type: array
              items:
                type: object
                properties:
                  name:
                    type: string
                  type:
                    type: string
            pricing:
              type: object
              properties:
                hotel_subtotal:
                  type: number
                flight_subtotal:
                  type: number
                bundle_discount:
                  type: number
                  description: "5% bundle discount applied"
                total:
                  type: number
                currency:
                  type: string
            payment:
              type: object
              properties:
                method:
                  type: string
                url:
                  type: string
                  format: uri
                  description: Hosted checkout URL for combined payment
                session_id:
                  type: string
                expires_at:
                  type: string
                  format: date-time
            created_at:
              type: string
              format: date-time

    BundleStatusResponse:
      type: object
      properties:
        success:
          type: boolean
        booking:
          type: object
          properties:
            id:
              type: string
            flight_booking_id:
              type: string
            status:
              type: string
              enum: [pending_payment, confirmed, cancelled, completed]
            reference:
              type: string
            confirmation_number:
              type: string
            hotel:
              type: object
              properties:
                property_name:
                  type: string
                room_name:
                  type: string
                check_in:
                  type: string
                  format: date
                check_out:
                  type: string
                  format: date
                nights:
                  type: integer
                status:
                  type: string
            flight:
              type: object
              properties:
                airline:
                  type: object
                  properties:
                    name:
                      type: string
                    iata_code:
                      type: string
                segments:
                  type: array
                  items:
                    type: object
                    properties:
                      origin:
                        type: string
                      destination:
                        type: string
                      departure_at:
                        type: string
                        format: date-time
                      arrival_at:
                        type: string
                        format: date-time
                      airline:
                        type: string
                      flight_number:
                        type: string
                      cabin_class:
                        type: string
                status:
                  type: string
            pricing:
              type: object
              properties:
                hotel_subtotal:
                  type: number
                flight_subtotal:
                  type: number
                bundle_discount:
                  type: number
                total:
                  type: number
                currency:
                  type: string
            created_at:
              type: string
              format: date-time
            updated_at:
              type: string
              format: date-time

    Pagination:
      type: object
      properties:
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer
        has_more:
          type: boolean
