openapi: 3.1.0
info:
  title: ALLFLYGHTS API
  version: 1.0.0
  description: |
    **A geo-based flight search API and MCP service for developers and AI engines.**

    ALLFLYGHTS helps applications discover real flight routes based on places,
    not just airport codes. Give it a city, town, or airport, and it returns
    structured route options built from the airports that actually serve that
    location — always providing multiple airport-pair options when the search
    is geo-based. The result is a clean, fast foundation for geo-based flight
    search, route intelligence, and automated travel systems.

    ## What it is

    This is a route intelligence layer for:

    - flight search applications
    - travel discovery tools
    - AI agents and MCP clients
    - aviation data and network analysis

    ## What it is not

    This is not a booking engine or a price comparison service. Version 1 focuses
    on route structure only — airports, airlines, and distances — keeping the
    service fast, predictable, and easy to build on. Pricing and bookings are
    planned for a future release.

    ## Available formats

    - **REST API** for direct integration
    - **MCP** for AI agents and automated workflows

    ## How it works

    1. **`GET /locations/search`** — find cities and airports, then get their IDs
    2. **`POST /search/routes`** — search route options using those IDs

    ## Typical flow

    ```
    1. User wants to fly from Tolna to Cambridge
    2. Call /locations/search?q=Tolna → pick the city, save its geonameid
    3. Call /locations/search?q=Cambridge → pick the city, save its geonameid
    4. POST /search/routes with both geonameids → get 13 route options
    ```

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

security:
  - apiKey: []

tags:
  - name: Locations
    description: City and airport lookup
  - name: Search
    description: Flight route search across multiple airports

paths:

  # ═══════════════════════════════════════════════
  #  LOCATION SEARCH
  # ═══════════════════════════════════════════════
  /locations/search:
    get:
      operationId: searchLocations
      summary: Search cities and airports by name
      description: |
        Returns up to 10 matching cities and airports for the given query.

        The client chooses which result to use for a flight search,
        then passes the `geonameid` as `cityId` in `/search/routes`.

        Results include both cities (towns, villages) and airports in a single list.
        Use the `isAirport` flag to distinguish:
        - `isAirport: false` → a city/town → the search engine will find nearby airports
        - `isAirport: true` → a specific airport → the search engine uses that exact airport

        Query is trimmed before matching. Empty queries return an empty list.
      tags:
        - Locations
      parameters:
        - name: q
          in: query
          required: true
          description: "City, town, or airport name to search for"
          schema:
            type: string
            minLength: 1
          examples:
            small_town:
              value: "Tolna"
              summary: Hungarian town
            city_with_country:
              value: "Cambridge"
              summary: Ambiguous — returns multiple Cambridges
            airport_name:
              value: "Heathrow"
              summary: Finds London Heathrow Airport
            partial_match:
              value: "Buda"
              summary: Prefix match — returns Budapest and others
      responses:
        '200':
          description: List of matching cities and airports (max 10)
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/City'
              examples:
                tolna_result:
                  summary: Search for "Tolna"
                  value:
                    - geonameid: 48217
                      name: "Tolna"
                      country: "Hungary"
                      lat: 46.4271
                      lon: 18.7929
                      population: 11284
                      iata: null
                      isAirport: false
                      stateCode: null
                heathrow_result:
                  summary: Search for "Heathrow"
                  value:
                    - geonameid: 2647216
                      name: "Heathrow"
                      country: "United Kingdom"
                      lat: 51.4700
                      lon: -0.4543
                      population: 0
                      iata: "LHR"
                      isAirport: true
                      stateCode: "ENG"
                cambridge_multi:
                  summary: Search for "Cambridge" (multiple matches)
                  value:
                    - geonameid: 12845
                      name: "Cambridge"
                      country: "United Kingdom"
                      lat: 52.2053
                      lon: 0.1218
                      population: 158434
                      iata: null
                      isAirport: false
                      stateCode: "ENG"
                    - geonameid: 4931972
                      name: "Cambridge"
                      country: "United States"
                      lat: 42.3751
                      lon: -71.1056
                      population: 118403
                      iata: null
                      isAirport: false
                      stateCode: "MA"
        '400':
          description: Invalid query
        '401':
          description: Missing or invalid API key
        '429':
          description: Rate limit exceeded

  # ═══════════════════════════════════════════════
  #  FLIGHT ROUTE SEARCH
  # ═══════════════════════════════════════════════
  /search/routes:
    post:
      operationId: searchRoutes
      summary: Search flight routes between locations
      description: |
        Submit one or more legs (origin → destination pairs) and receive all possible
        airport combinations with airlines and distances.

        **Search types (by number of legs):**
        - 1 leg = one-way
        - 2 legs (same cities reversed) = return
        - N legs = multi-city or open jaw

        **Each leg's `from` and `to` independently support:**
        - `sameCountry: true` — restrict airports to that location's country
        - `fromDate` / `toDate` — flexible date window (YYYY-MM-DD strings)
        - Cities (geo-based multi-airport discovery) or airports (fixed IATA)

        **Response:** `Flight` objects — route structure without pricing.
        Pricing will be added in a future release.

        **Performance:** typically ~50ms for 1-leg searches, up to 2-3s for complex
        multi-stop searches.
      tags:
        - Search
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FlightsRequest'
            examples:

              one_way:
                summary: "One-way: Tolna, HU → Cambridge, GB"
                value:
                  legs:
                    - from:
                        cityId: 48217
                        sameCountry: false
                      to:
                        cityId: 12845
                        sameCountry: false
                        fromDate: "2026-04-25"
                        toDate: "2026-05-02"

              return_trip:
                summary: "Return: Cork ↔ Innsbruck"
                value:
                  legs:
                    - from:
                        cityId: 29001
                      to:
                        cityId: 55420
                        fromDate: "2026-06-15"
                        tripType: "return"

              multi_city:
                summary: "Multi-city: 3 legs"
                value:
                  legs:
                    - from:
                        cityId: 11234
                      to:
                        cityId: 33456
                        fromDate: "2026-07-01"
                    - from:
                        cityId: 33456
                      to:
                        cityId: 67890
                        fromDate: "2026-07-05"
                    - from:
                        cityId: 67890
                      to:
                        cityId: 48217
                        fromDate: "2026-07-10"

              airport_to_city:
                summary: "Fixed airport → geo-based city"
                value:
                  legs:
                    - from:
                        cityId: 2647216
                      to:
                        cityId: 55420
                        fromDate: "2026-09-01"

      responses:
        '200':
          description: Route combinations found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FlightsResponse'
              example:
                searchId: "s_20260425_48217_12845"
                flights:
                  - id: "r001"
                    segments:
                      - departure:
                          iata: "OSI"
                          name: "Osijek"
                          city: "Osijek"
                          countryCode: "HR"
                        arrival:
                          iata: "STN"
                          name: "Stansted"
                          city: "London"
                          countryCode: "GB"
                        options:
                          - airline:
                              name: "Ryanair"
                              iata: "FR"
                        durationMinutes: null
                    departureAirportDistance: 106
                    destinationAirportDistance: 35
                  - id: "r002"
                    segments:
                      - departure:
                          iata: "BUD"
                          name: "Budapest Ferenc Liszt International Airport"
                          city: "Budapest"
                          countryCode: "HU"
                        arrival:
                          iata: "STN"
                          name: "Stansted"
                          city: "London"
                          countryCode: "GB"
                        options:
                          - airline:
                              name: "Ryanair UK"
                              iata: "RK"
                          - airline:
                              name: "Ryanair"
                              iata: "FR"
                        durationMinutes: null
                    departureAirportDistance: 117
                    destinationAirportDistance: 35
                  - id: "r003"
                    segments:
                      - departure:
                          iata: "BUD"
                          name: "Budapest Ferenc Liszt International Airport"
                          city: "Budapest"
                          countryCode: "HU"
                        arrival:
                          iata: "LHR"
                          name: "Heathrow"
                          city: "London"
                          countryCode: "GB"
                        options:
                          - airline:
                              name: "British Airways"
                              iata: "BA"
                        durationMinutes: null
                    departureAirportDistance: 117
                    destinationAirportDistance: 90
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid API key
        '429':
          description: Rate limit exceeded

# ═════════════════════════════════════════════════
#  COMPONENTS
# ═════════════════════════════════════════════════
components:

  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Get your API key at https://allflyghts.com/developers

  schemas:

    # ── LOCATION MODEL ───────────────────────────

    City:
      type: object
      description: |
        A location result from /locations/search — either a city/town or an airport.
        Use `geonameid` as `cityId` when calling /search/routes.
      required:
        - geonameid
        - name
        - country
        - lat
        - lon
        - population
        - isAirport
      properties:
        geonameid:
          type: integer
          description: "GeoNames ID — use this as cityId in search requests"
          example: 48217
        name:
          type: string
          example: "Tolna"
        country:
          type: string
          example: "Hungary"
        lat:
          type: number
          format: double
          example: 46.4271
        lon:
          type: number
          format: double
          example: 18.7929
        population:
          type: integer
          format: int64
          example: 11284
        iata:
          type: string
          nullable: true
          description: "IATA code — present only when isAirport is true"
          example: null
        isAirport:
          type: boolean
          description: "True = airport, false = city/town/village"
          example: false
        stateCode:
          type: string
          nullable: true
          description: "State or region code where applicable"
          example: null

    # ── REQUEST MODELS ───────────────────────────

    FlightsRequest:
      type: object
      required: [legs]
      properties:
        legs:
          type: array
          description: |
            Origin→destination pairs. 1 leg = one-way, 2 legs = return,
            N legs = multi-city / open jaw.
          minItems: 1
          items:
            $ref: '#/components/schemas/FlightRequestLeg'
        airlineGroupedPrices:
          type: boolean
          nullable: true
          description: "Optional flag — reserved for future use when pricing is added"

    FlightRequestLeg:
      type: object
      required: [from, to]
      properties:
        from:
          $ref: '#/components/schemas/FlightRoute'
        to:
          $ref: '#/components/schemas/FlightRoute'

    FlightRoute:
      type: object
      required: [cityId]
      properties:
        cityId:
          type: integer
          description: "GeoNames ID from /locations/search (the geonameid field)"
          example: 48217
        sameCountry:
          type: boolean
          default: false
          description: "Restrict airport search to this location's country only"
        fromDate:
          type: string
          description: "Earliest departure date (YYYY-MM-DD)"
          example: "2026-04-25"
        toDate:
          type: string
          description: "Latest departure date (YYYY-MM-DD) — defines flexible date window"
          example: "2026-05-02"
        tripType:
          type: string
          enum: [one-way, return]
          description: "Trip type for this leg"

    # ── RESPONSE MODELS ──────────────────────────

    FlightsResponse:
      type: object
      required: [flights, searchId]
      properties:
        flights:
          type: array
          items:
            $ref: '#/components/schemas/Flight'
        searchId:
          type: string
          example: "s_20260425_48217_12845"

    Flight:
      type: object
      description: "A route combination — airports, segments, airlines, distances. Pricing fields are present but currently always null."
      required:
        - id
        - segments
        - departureAirportDistance
        - destinationAirportDistance
      properties:
        id:
          type: string
          example: "r001"
        segments:
          type: array
          items:
            $ref: '#/components/schemas/FlightSegment'
        departureAirportDistance:
          type: integer
          description: "Km from searched origin location to the departure airport"
          example: 106
        destinationAirportDistance:
          type: integer
          description: "Km from the arrival airport to the searched destination location"
          example: 35
        totalPrice:
          type: number
          format: double
          nullable: true
          description: "Total price for the route. Reserved for future use — currently always null."
          example: null
        durationMinutes:
          type: integer
          nullable: true
          description: "Total route duration in minutes (if known)"
          example: null

    FlightSegment:
      type: object
      required: [departure, arrival, options]
      properties:
        departure:
          $ref: '#/components/schemas/AirportStop'
        arrival:
          $ref: '#/components/schemas/AirportStop'
        options:
          type: array
          description: "Airlines operating this segment"
          items:
            $ref: '#/components/schemas/SegmentOption'
        durationMinutes:
          type: integer
          nullable: true
          description: "Segment duration in minutes (if known)"

    AirportStop:
      type: object
      required: [iata]
      properties:
        iata:
          type: string
          example: "OSI"
        name:
          type: string
          nullable: true
          example: "Osijek"
        city:
          type: string
          nullable: true
          example: "Osijek"
        countryCode:
          type: string
          nullable: true
          example: "HR"

    SegmentOption:
      type: object
      required: [airline]
      properties:
        airline:
          $ref: '#/components/schemas/Carrier'
        departureTime:
          type: string
          nullable: true
          example: "08:30"
        arrivalTime:
          type: string
          nullable: true
          example: "10:15"
        flightNumber:
          type: string
          nullable: true
          example: "FR1234"
        distanceKm:
          type: integer
          nullable: true
        price:
          type: number
          nullable: true
          description: "Reserved for future use — currently always null"

    Carrier:
      type: object
      required: [name]
      properties:
        name:
          type: string
          example: "Ryanair"
        iata:
          type: string
          nullable: true
          example: "FR"

    # ── ERROR MODEL ──────────────────────────────

    Error:
      type: object
      properties:
        error:
          type: boolean
          default: true
        code:
          type: string
          enum:
            - INVALID_REQUEST
            - CITY_NOT_FOUND
            - RATE_LIMITED
            - INTERNAL_ERROR
        message:
          type: string
          example: "City ID 99999 not found"
