{
  "openapi": "3.0.3",
  "info": {
    "title": "flights-api — Multi-vendor Flight Aggregator",
    "version": "1.0.0",
    "description": "debdab (travzillapro) direct integration + Wasata auth & wallet settlement. Search → revalidate → book → cancel/refund. All money moves in Wasata's wallet ledger keyed by the debdab PNR."
  },
  "servers": [{ "url": "/" }, { "url": "/v2" }],
  "components": {
    "securitySchemes": {
      "wasataBearer": { "type": "http", "scheme": "bearer", "description": "Wasata Sanctum token (from POST /users/login or Wasata directly)." }
    },
    "schemas": {
      "Leg": {
        "type": "object",
        "required": ["from", "to", "departDate"],
        "properties": {
          "from": { "type": "string", "example": "KWI", "description": "Departure airport IATA" },
          "to": { "type": "string", "example": "DXB", "description": "Arrival airport IATA" },
          "departDate": { "type": "string", "format": "date", "example": "2026-08-18" },
          "fromLabel": { "type": "string", "example": "Kuwait City(KWI)" },
          "toLabel": { "type": "string", "example": "Dubai(DXB)" }
        }
      },
      "SearchRequest": {
        "type": "object",
        "required": ["legs"],
        "properties": {
          "legs": { "type": "array", "items": { "$ref": "#/components/schemas/Leg" }, "description": "1 leg = one-way, 2 = round-trip, >2 = multi-city" },
          "adults": { "type": "integer", "minimum": 1, "default": 1 },
          "children": { "type": "integer", "minimum": 0, "default": 0 },
          "infants": { "type": "integer", "minimum": 0, "default": 0 },
          "cabin": { "type": "string", "enum": ["EC", "BC", "FR", "PE"], "default": "EC" },
          "journeyType": { "type": "string", "enum": ["DOM", "INT"] },
          "currency": { "type": "string", "description": "Wallet currency to quote in; required only if you hold >1 wallet" }
        }
      },
      "Passenger": {
        "type": "object",
        "required": ["title", "firstName", "lastName", "type"],
        "properties": {
          "title": { "type": "string", "example": "MR" },
          "firstName": { "type": "string" },
          "lastName": { "type": "string" },
          "type": { "type": "string", "enum": ["ADT", "CHD", "INF"] },
          "dob": { "type": "string", "format": "date" },
          "gender": { "type": "string", "example": "M" },
          "passportNumber": { "type": "string" },
          "passportExpiry": { "type": "string" },
          "nationality": { "type": "string", "example": "KW" },
          "email": { "type": "string" },
          "phone": { "type": "string" },
          "associatedAdultIndex": { "type": "integer", "description": "For INF: 0-based index of the accompanying adult" }
        }
      },
      "CreateBookingRequest": {
        "type": "object",
        "required": ["offerId", "passengers"],
        "properties": {
          "offerId": { "type": "string", "description": "Opaque offer from /search (user-bound, TTL ~15min)" },
          "passengers": { "type": "array", "items": { "$ref": "#/components/schemas/Passenger" } },
          "contact": { "type": "object", "properties": { "email": { "type": "string" }, "mobile": { "type": "string" }, "countryCode": { "type": "string" } } },
          "currency": { "type": "string" },
          "idempotencyNonce": { "type": "string", "description": "Client nonce for double-submit protection" }
        }
      }
    }
  },
  "security": [{ "wasataBearer": [] }],
  "paths": {
    "/health": { "get": { "summary": "Liveness + build marker", "security": [], "responses": { "200": { "description": "OK" } } } },
    "/users/login": {
      "post": {
        "summary": "Proxy to Wasata POST /api/login", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["user_name", "password"], "properties": { "user_name": { "type": "string" }, "password": { "type": "string" } } } } } },
        "responses": { "200": { "description": "{ token, user }" }, "401": { "description": "Bad credentials" } }
      }
    },
    "/users/me": { "get": { "summary": "Cached Wasata session (user/agency/accounts)", "responses": { "200": { "description": "OK" }, "401": { "description": "Unauthenticated" } } } },
    "/users/me/wallets": { "get": { "summary": "Wallets the caller can book & settle in", "responses": { "200": { "description": "OK" } } } },
    "/airports": {
      "get": {
        "summary": "Airport lookup / autocomplete (public)",
        "description": "Static IATA reference used to build a search — debdab exposes no airport or city list endpoint, so this service ships the catalog. Seeded with Libya/Gulf/MENA plus major hubs; unknown codes are still accepted by /search.",
        "security": [],
        "parameters": [
          { "name": "q", "in": "query", "schema": { "type": "string" }, "description": "Match on IATA code, city, airport name, or country", "example": "dub" },
          { "name": "country", "in": "query", "schema": { "type": "string" }, "description": "ISO-2 country filter", "example": "LY" },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "default": 20, "maximum": 100 } }
        ],
        "responses": { "200": { "description": "{ items: [{ code, name, city, country, countryName }], total }" } }
      }
    },
    "/search": {
      "post": {
        "summary": "Multi-leg availability → opaque offers",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SearchRequest" } } } },
        "responses": { "200": { "description": "{ currency, available[], partial, unavailableProviders[] }" }, "400": { "description": "Validation error" }, "422": { "description": "No / ambiguous wallet currency" } }
      }
    },
    "/offers/{id}/revalidate": {
      "post": { "summary": "Reprice an offer before booking", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "{ price, fare, legs, repriced }" }, "410": { "description": "Offer expired" } } }
    },
    "/offers/{id}/farerules": {
      "post": { "summary": "Cancellation/change penalties + rules text", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "{ rulesText, cancellationPenalty, changePenalty, refundable }" } } }
    },
    "/bookings": {
      "post": {
        "summary": "Book (reValidate → bookFlight → charge when confirmed)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateBookingRequest" } } } },
        "responses": { "201": { "description": "Confirmed + settled" }, "202": { "description": "Booked, settlement needs retry / OnRequest" }, "410": { "description": "Offer expired" }, "422": { "description": "Currency gate" } }
      },
      "get": { "summary": "My bookings", "parameters": [{ "name": "page", "in": "query", "schema": { "type": "integer" } }, { "name": "limit", "in": "query", "schema": { "type": "integer" } }, { "name": "state", "in": "query", "schema": { "type": "string" } }, { "name": "lifecycleState", "in": "query", "schema": { "type": "string" } }], "responses": { "200": { "description": "{ items, total, page, limit }" } } }
    },
    "/bookings/{id}": { "get": { "summary": "One booking", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "OK" }, "403": { "description": "Not your booking" }, "404": { "description": "Not found" } } } },
    "/bookings/{id}/settle": { "post": { "summary": "Charge retry / settle an OnRequest-confirmed booking", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Settled" }, "409": { "description": "Not yet confirmed by airline" } } } },
    "/bookings/{id}/cancel": {
      "post": {
        "summary": "Provider cancel → Wasata reversal (void/refund)",
        "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }],
        "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "cancellationFeeOverride": { "type": "number", "description": "owner/admin only" }, "mode": { "type": "string", "enum": ["void", "refund", "reissue"], "description": "owner/admin only" } } } } } },
        "responses": { "200": { "description": "Cancelled / Refunded / RefundPending" } }
      }
    },
    "/bookings/{id}/refund": { "post": { "summary": "Retry a RefundPending reversal", "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], "responses": { "200": { "description": "Refunded" }, "502": { "description": "Reversal failed → RefundPending" } } } },
    "/providers/debdab/sync/bookings": { "post": { "summary": "Trigger booking-state reconcile (superadmin)", "responses": { "200": { "description": "{ scanned, confirmed }" }, "403": { "description": "create-transaction required" } } } }
  }
}
