import {z} from 'zod' export type AgentFlightPassengers = {adults: number, children?: number | undefined, infants_in_seat?: number | undefined, infants_on_lap?: number | undefined}; export type AgentFlightQuoteRequest = {origin: string, destination: string, departure_date: string, return_date?: (string | null) | undefined, passengers: AgentFlightPassengers, airline?: (string | null) | undefined, max_stops?: (number | null) | undefined, cabin?: ('economy' | 'premium_economy' | 'business' | 'first') | undefined, currency?: 'USD' | undefined}; export type CreateApiConvAgentFlightSearchesArgs = {prefetch_departure?: (Array | null) | undefined, cached?: boolean | undefined, 'X-Axel-Quote-Session': string, 'X-Axel-Flight-Source'?: (string | null) | undefined, 'X-Axel-Flight-Source-Signature'?: (string | null) | undefined, 'CF-Connecting-IP'?: (string | null) | undefined, body: AgentFlightQuoteRequest}; export type CreateApiConvAgentFlightSearchesOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const agentFlightPassengers = z.object({adults: z.number().int().gte(1).lte(9), children: z.number().int().gte(0).lte(8).optional(), infants_in_seat: z.number().int().gte(0).lte(8).optional(), infants_on_lap: z.number().int().gte(0).lte(8).optional()}); export const agentFlightQuoteRequest = z.object({origin: z.string().min(1).max(120), destination: z.string().min(1).max(120), departure_date: z.string(), return_date: z.string().nullable().optional(), passengers: agentFlightPassengers, airline: z.string().nullable().optional(), max_stops: z.number().int().gte(0).lte(2).nullable().optional(), cabin: z.enum(["economy", "premium_economy", "business", "first"]).optional(), currency: z.literal("USD").optional()}); export const flightShareSegment = z.object({origin: z.string().nullable().optional(), destination: z.string().nullable().optional(), departure_date: z.string().nullable().optional(), departure_time: z.string().nullable().optional(), arrival_date: z.string().nullable().optional(), arrival_time: z.string().nullable().optional(), flight_number: z.string().nullable().optional(), duration_minutes: z.number().int().gte(1).nullable().optional(), layover_after_minutes: z.number().int().gte(0).nullable().optional()}); export const flightShareLeg = z.object({airline_name: z.string().nullable().optional(), airline_code: z.string().nullable().optional(), origin: z.string(), destination: z.string(), departure_date: z.string(), departure_time: z.string().nullable().optional(), arrival_date: z.string().nullable().optional(), arrival_time: z.string().nullable().optional(), duration_minutes: z.number().int().gte(1).nullable().optional(), stops: z.number().int().gte(0), connection_airports: z.array(z.string()).nullable().optional(), flight_numbers: z.array(z.string()).optional(), segments: z.array(flightShareSegment).optional()}); export const fareBaggageQuote = z.object({bag_type: z.enum(["personal_item", "carry_on", "checked", "checked_first", "checked_second", "unknown"]).optional(), quantity: z.number().int().nullable().optional(), is_included: z.boolean().optional(), is_restriction: z.boolean().optional(), fee_amount: z.string().nullable().optional(), fee_currency: z.string().nullable().optional(), fee_basis: z.enum(["round_trip", "one_way", "per_segment", "unknown"]).optional(), pax_basis: z.enum(["per_pax", "total", "unknown"]).optional(), fee_amount_per_direction: z.string().nullable().optional(), source_key: z.enum(["together", "departing", "returning", "unknown"]).optional(), raw_text: z.string()}); export const fareBaggagePolicy = z.object({source: z.enum(["serpapi_google_flights", "unknown"]).optional(), quotes: z.array(fareBaggageQuote).optional()}); export const farePolicyFact = z.object({category: z.enum(["personal_item", "carry_on", "checked_bags", "seat_selection", "changes", "refunds", "seat_upgrade", "same_day_changes", "priority_boarding", "wifi", "meals", "lounge", "entertainment", "rewards"]), state: z.enum(["included", "fee", "not_included", "not_available", "conditional", "unknown"]).optional(), quantity: z.number().int().gte(0).nullable().optional(), evidence: z.array(z.string()).optional(), details: z.array(z.string()).optional(), reason: z.enum(["not_provided", "conflicting_evidence", "conditional", "varies_by_leg"]).nullable().optional()}); export const farePolicy = z.object({facts: z.array(farePolicyFact).optional(), unclassified: z.array(z.string()).optional()}); export const fareTake = z.object({id: z.string(), text: z.string(), emphasis: z.string().nullable().optional()}); export const money = z.object({amount: z.number().int(), currency: z.string()}); export const agentFlightCabin = z.object({origin: z.string().nullable().optional(), destination: z.string().nullable().optional(), marketing_carrier: z.string().nullable().optional(), operating_carrier: z.string().nullable().optional(), flight_number: z.string().nullable().optional(), cabin: z.string().nullable().optional(), cabin_label: z.string().nullable().optional(), confirmed: z.boolean().optional(), inferred: z.boolean().optional(), source: z.string().nullable().optional()}); export const agentFlightFareOption = z.object({baggage: z.array(z.string()).optional(), baggage_policy: fareBaggagePolicy.nullable().optional(), features: z.array(z.string()).optional(), fare_policy: farePolicy.nullable().optional(), fare_take: fareTake.nullable().optional(), fare_name: z.string(), book_with: z.string().nullable().optional(), fare_family_id: z.string().nullable().optional(), fallback_label_used: z.boolean().nullable().optional(), comparison_price_total: money.nullable().optional(), non_member_price_total: money.nullable().optional(), member_price_total: money.nullable().optional(), separate_tickets: z.boolean().optional(), flight_cabins: z.array(agentFlightCabin).optional(), cabin_check: z.string().nullable().optional(), requested_cabin: z.string().nullable().optional()}); export const agentFlightSearchOption = z.object({option_id: z.string(), summary: flightShareLeg, return_summary: flightShareLeg.nullable().optional(), verified_cabin: z.string().nullable().optional(), cached_fare_options: z.array(agentFlightFareOption).optional(), quality_rank: z.number().int().gte(1).nullable().optional(), quality_flags: z.array(z.string()).optional(), quality_sort_key: z.array(z.unknown()).nullable().optional(), starting_member_price_total: money.nullable().optional(), starting_non_member_price_total: money.nullable().optional(), member_price_total: money.nullable().optional(), non_member_price_total: money.nullable().optional(), airline_price_total: money.nullable().optional()}); export const agentFlightSearchOptionsResponse = z.object({stage: z.enum(["outbound", "return"]), session_id: z.string(), request: agentFlightQuoteRequest, options: z.array(agentFlightSearchOption), supplemental_status: z.enum(["pending", "complete", "unavailable"]).nullable().optional(), ready_return_options: z.record(z.string(), z.array(agentFlightSearchOption)).optional(), selected_outbound: flightShareLeg.nullable().optional(), selected_outbound_id: z.string().nullable().optional(), origin_airports: z.array(z.string()).optional(), destination_airports: z.array(z.string()).optional(), empty_reason: z.enum(["departs_too_soon", "no_axel_price", "filtered_out", "no_flights", "beyond_fare_horizon"]).nullable().optional(), fare_horizon_opens_on: z.string().nullable().optional(), provider_results: z.number().int().gte(0).nullable().optional(), hidden_departs_too_soon: z.number().int().gte(0).nullable().optional(), min_departure_lead_minutes: z.number().int().gte(0).nullable().optional()}); export const flightShareTarget = z.object({type: z.literal("flight_share").optional()}); export const travelDateWindow = z.object({start: z.string(), end: z.string()}); export const travelIntent = z.object({willingness: z.enum(["definite", "price_dependent"]).nullable().optional(), date_flexibility: z.enum(["fixed", "flexible"]).nullable().optional(), fixed_commitment: z.boolean().nullable().optional(), departure_windows: z.array(travelDateWindow).optional(), return_windows: z.array(travelDateWindow).optional(), departure_weekdays: z.array(z.number().int().gte(0).lte(6)).optional(), return_weekdays: z.array(z.number().int().gte(0).lte(6)).optional(), stay_nights: z.number().int().gte(1).lte(365).nullable().optional(), stay_nights_min: z.number().int().gte(1).lte(365).nullable().optional(), stay_nights_max: z.number().int().gte(1).lte(365).nullable().optional(), origin_airport_codes: z.array(z.string()).nullable().optional(), destination_airport_codes: z.array(z.string()).nullable().optional(), depart_after: z.string().nullable().optional(), depart_before: z.string().nullable().optional(), return_depart_after: z.string().nullable().optional(), return_depart_before: z.string().nullable().optional(), arrive_by: z.string().nullable().optional(), return_by: z.string().nullable().optional(), arrival_buffer_minutes: z.number().int().gte(0).lte(10080).nullable().optional(), return_buffer_minutes: z.number().int().gte(0).lte(10080).nullable().optional(), airport_flexibility: z.enum(["exact", "nearby"]).nullable().optional(), budget_target_cents: z.number().int().gte(0).lte(100000000).nullable().optional(), budget_limit_cents: z.number().int().gte(0).lte(100000000).nullable().optional(), currency: z.literal("USD").optional(), note: z.string().max(300).nullable().optional()}); export const flightSearchLegChoices = z.object({stops: z.enum(["nonstop", "one", "twoPlus"]).nullable().optional(), airline_only: z.string().max(100).nullable().optional(), avoided_airlines: z.array(z.string()).optional()}); export const flightFarePreferences = z.object({seat_selection: z.boolean().nullable().optional(), carry_on: z.boolean().nullable().optional(), exclude_basic_economy: z.boolean().nullable().optional(), checked_bags: z.number().int().gte(0).lte(2).nullable().optional(), changes: z.boolean().nullable().optional(), refunds: z.boolean().nullable().optional()}); export const flightSearchChoices = z.object({airline: z.string().nullable().optional(), max_stops: z.number().int().gte(0).lte(2).nullable().optional(), outbound: flightSearchLegChoices.nullable().optional(), returning: flightSearchLegChoices.nullable().optional(), fare_preferences: flightFarePreferences.nullable().optional()}); export const flightSampleHistoryPoint = z.object({date: z.string(), price_cents: z.number().int().gt(0)}); export const flightSampleHistory = z.object({source: z.literal("google_flights").optional(), currency: z.literal("USD").optional(), passengers: z.literal(1).optional(), price_history: z.array(flightSampleHistoryPoint), historical_low_cents: z.number().int().nullable().optional(), historical_high_cents: z.number().int().nullable().optional(), typical_low_cents: z.number().int().nullable().optional(), typical_high_cents: z.number().int().nullable().optional()}); export const flightShareSnapshot = z.object({price_context: flightSampleHistory.nullable().optional(), fare_label: z.string().nullable().optional(), passenger_count: z.number().int().gte(1), legs: z.array(flightShareLeg), axel_price_total: money, comparison_price_total: money.nullable().optional(), ticketed_passenger_count: z.number().int().gte(1).nullable().optional(), axel_price_per_person: money.nullable().optional(), comparison_price_per_person: money.nullable().optional(), non_member_price_total: money.nullable().optional()}); export const agentQuotePriceHold = z.object({held_total_cents: z.number().int().gt(0), currency: z.literal("USD").optional(), armed_at: z.string(), expires_at: z.string(), basis: z.literal("free").optional(), source: z.enum(["assistant", "generated_offer"]).optional(), selected_fare: agentFlightFareOption.nullable().optional()}); export const agentFlightQuoteResolveResponse = z.object({target: flightShareTarget.optional(), share_kind: z.literal("agent_quote").optional(), quote_id: z.string(), source_observation_id: z.string().nullable().optional(), search_session_id: z.string().nullable().optional(), trip_reference: z.string().nullable().optional(), travel_intent: travelIntent.nullable().optional(), search_choices: flightSearchChoices.nullable().optional(), request: agentFlightQuoteRequest, snapshot: flightShareSnapshot, checked_at: z.string(), fare_options: z.array(agentFlightFareOption).optional(), fare_options_started_at: z.string().nullable().optional(), fare_options_retrieved_at: z.string().nullable().optional(), fare_options_incomplete: z.boolean().nullable().optional(), fare_auto_retry_attempted: z.boolean().nullable().optional(), fare_options_status: z.enum(["pending", "ready", "unavailable"]).nullable().optional(), membership_required: z.literal(true).optional(), membership_terms: z.string().optional(), non_member_booking: z.string().optional(), price_hold: agentQuotePriceHold.nullable().optional()}); export const agentFlightSearchQuoteResponse = z.object({stage: z.literal("quote").optional(), session_id: z.string(), checks_remaining: z.number().int().gte(0).lte(5).nullable().optional(), url: z.string(), share: agentFlightQuoteResolveResponse}); export const agentFlightSearchResponse = z.discriminatedUnion("stage", [agentFlightSearchOptionsResponse, agentFlightSearchQuoteResponse]); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Search * * POST /conv/agent-flight-searches */ export const createApiConvAgentFlightSearches = async (args: CreateApiConvAgentFlightSearchesArgs, options: CreateApiConvAgentFlightSearchesOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") for (const item of args.prefetch_departure ?? []) query.append("prefetch_departure", String(item)) if (args.cached != null) query.append("cached", String(args.cached)) if (args['X-Axel-Quote-Session'] != null) headers.set("X-Axel-Quote-Session", String(args['X-Axel-Quote-Session'])) if (args['X-Axel-Flight-Source'] != null) headers.set("X-Axel-Flight-Source", String(args['X-Axel-Flight-Source'])) if (args['X-Axel-Flight-Source-Signature'] != null) headers.set("X-Axel-Flight-Source-Signature", String(args['X-Axel-Flight-Source-Signature'])) if (args['CF-Connecting-IP'] != null) headers.set("CF-Connecting-IP", String(args['CF-Connecting-IP'])) const url = `${baseUrl.replace(/\/+$/, '')}/conv/agent-flight-searches${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(agentFlightSearchResponse, await res.json()) };