type TripPurpose = 'honeymoon' | 'special_occasion' | 'business' | 'ski' | 'beach' | 'city_break' | 'family_holiday' | 'graduation' | 'concert_festival' | 'sports_event' | 'spring_break'; declare const TRIP_PURPOSES: readonly TripPurpose[]; interface TripPurposeOptions { tripPurpose?: TripPurpose | null; tripPurposes?: ReadonlyArray | null; } declare function normalizeTripPurposes({ tripPurpose, tripPurposes }: TripPurposeOptions): TripPurpose[]; declare function getPrimaryTripPurpose(options: TripPurposeOptions): TripPurpose | undefined; /** * LetsFG Personalized Flight Ranking Engine * * Open-source implementation of the scoring algorithm that powers letsfg.co. * Scores each offer across 9 dimensions with personalization weights that shift * based on trip context and purpose. Pure TypeScript — no external dependencies, * safe to run in both Node and browser. * * Usage: * import { rankOffers, type RankingContext } from 'letsfg/ranking' * const ranked = rankOffers(offers, { tripContext: 'family', requireBag: true }) * // ranked[0].offer is the best pick, ranked[0].heroFacts explains why * * Companion modules in the same package: * - trip-purpose.ts — TripPurpose type + normalization helpers * - offer-details.ts — extractOfferDetailSignals, getOfferDetailBadges, etc. */ interface RankOfferSegment { layover_minutes?: number; airline?: string; flight_number?: string; aircraft?: string; origin?: string; destination?: string; } interface RankOffer { id: string; price: number; /** Display total in the user's chosen currency (ticket + fee + ancillaries). * When provided, this is used for all price scoring and penalty calculations * instead of `price`. Callers should populate this so the ranking reflects * exactly what the user will pay. */ displayPrice?: number; google_flights_price?: number; currency: string; airline: string; origin?: string; destination?: string; departure_time: string; arrival_time: string; duration_minutes: number; stops: number; segments?: RankOfferSegment[]; inbound?: { departure_time?: string; arrival_time?: string; origin?: string; destination?: string; stops?: number; /** Inbound-leg duration in minutes. Required for round-trip scoring to * see the return leg in `scoreDuration`. When absent the inbound is * treated as zero-duration (best case for the offer). */ duration_minutes?: number; segments?: RankOfferSegment[]; }; ancillaries?: { cabin_bag?: { included?: boolean; price?: number; currency?: string; description?: string; }; checked_bag?: { included?: boolean; price?: number; currency?: string; description?: string; }; seat_selection?: { included?: boolean; price?: number; currency?: string; description?: string; }; }; conditions?: { refund_before_departure?: 'allowed' | 'not_allowed' | 'allowed_with_fee' | 'unknown'; change_before_departure?: 'allowed' | 'not_allowed' | 'allowed_with_fee' | 'unknown'; [key: string]: string | undefined; }; /** Quality tag set by upstream validateOfferBatch when the offer's intrinsic * data is untrustworthy (date drift from search criteria, asymmetric leg * durations, etc.). The ranker uses this as the highest-priority hero gate: * a suspect offer can never be hero unless every offer in the pool is suspect. * Rank-not-filter — suspect offers still appear as runner-ups. */ quality?: 'suspect' | string; } interface RankingContext { tripContext?: 'solo' | 'couple' | 'family' | 'group' | 'business_traveler'; tripPurpose?: TripPurpose; tripPurposes?: ReadonlyArray; travelerCount?: number; depTimePref?: 'early_morning' | 'morning' | 'afternoon' | 'evening' | 'night' | 'red_eye'; retTimePref?: 'early_morning' | 'morning' | 'afternoon' | 'evening' | 'night' | 'red_eye'; arrivalTimePref?: 'morning' | 'afternoon' | 'evening'; requireBag?: boolean; requireSeat?: boolean; requireMeals?: boolean; requireCancellation?: boolean; preferredAirline?: string; preferQuickFlight?: boolean; /** User said "direct" or "nonstop" — strongly boost stops weight but don't filter. * If no directs exist, 1-stop will naturally float to the top over 3-stop. */ preferDirect?: boolean; /** User explicitly asked for cheapest / lowest price — price dominates all other factors. */ preferCheapest?: boolean; /** User asked for "1 stop" / "max 2 stops" / "at most 1 connection" — a soft cap * on the number of stops. Offers exceeding the cap are gated out of the HERO slot * (rank-not-filter: they still appear as runner-ups, just below within-cap offers). * When no offer satisfies the cap it is relaxed like the other gates. preferDirect * (an implicit cap of 0) is handled by the dedicated 'direct' path; this covers * explicit caps >= 1, where the user tolerates a connection but not a 3-leg odyssey. */ maxStops?: number; /** User explicitly wants — or is happy with — a LONG layover (a stopover to break * the journey or see a city). Inverts the default short-layover preference in * scoreLayover so longer connections score higher, and boosts the layover weight. * Default (undefined/false) leaves the normal short-layover scoring intact. */ preferLongLayover?: boolean; /** Hard lower bound on departure time, in minutes from midnight. * Flights departing before this time receive a heavy score penalty (effectively filtered out). */ departAfterMins?: number; /** Hard upper bound on departure time, in minutes from midnight. */ departBeforeMins?: number; } interface ScoreBreakdown { price: number; stops: number; duration: number; depTime: number; arrivalTime: number; baggage: number; savings: number; comfortHours: number; layover: number; } interface RankedOffer { offer: T; score: number; rank: number; breakdown: ScoreBreakdown; heroFacts: string[]; tradeoffs: string[]; /** Hero only: names of user-stated criteria gates that had to be relaxed to * find a hero (no offer satisfied them all). Surfaced so the UI can banner * the mismatch — e.g. "No direct flights on this route" or "No refundable * fare available". Empty/undefined means the hero satisfied every stated * criterion. Relax order: refund -> bag -> time -> direct. */ relaxedGates?: string[]; } /** Remove near-duplicate offers: when multiple connectors return the same physical * flight (e.g. Ryanair FR1234 from both the direct connector and Kiwi/Skyscanner), * keep only the cheapest. Two offers are considered identical only when they share * the same calendar date, route, airline, outbound timing buckets, inbound timing * buckets (for round-trips), stop counts, and core fare conditions. Including the * inbound leg prevents collapsing distinct return options for the same outbound. */ declare function deduplicateOffers(offers: T[]): T[]; /** * Rank an array of flight offers by personalized score. * Returns a new array sorted best-first. The original array is not mutated. * * @param offers Array of flight offers (any type extending RankOffer) * @param ctx User intent context from the NL query parser */ declare function rankOffers(offers: T[], ctx: RankingContext, options?: { skipDedup?: boolean; }): RankedOffer[]; /** * From an already-ranked list, picks the top N offers that are genuinely * different from each other — so runner-ups are real propositions, not just * the same flight at +$3 from a different booking source. * * Two offers are considered "the same" for this purpose if both their * departure time slot (3-hour window) AND their stop count are identical. * Diversity requires differing in at least one of those dimensions. * * Falls back to next-best-ranked when the pool lacks enough diverse options. */ declare function selectDiverseTop(ranked: RankedOffer[], n: number): RankedOffer[]; /** * Returns a short human-readable label describing the ranking profile * that was applied (e.g. "City break", "Family holiday"). Returns null * if the default generic profile is used. */ declare function getProfileLabel(ctx: RankingContext): string | null; type OfferConditionState = 'allowed' | 'not_allowed' | 'allowed_with_fee' | 'unknown'; type OfferAmenityState = 'included' | 'available' | 'unavailable' | 'unknown'; type OfferAmenityConfidence = 'verified' | 'inferred' | 'unknown'; type OfferAmenitySource = 'ancillary' | 'condition' | 'unknown'; interface OfferDetailAncillary { included?: boolean; price?: number; currency?: string; description?: string; } interface OfferDetailConditions { refund_before_departure?: OfferConditionState; change_before_departure?: OfferConditionState; [key: string]: string | undefined; } interface OfferDetailSegmentLike { airline?: string; flight_number?: string; aircraft?: string; origin?: string; destination?: string; /** Per-leg Starlink verdict; see StarlinkSegmentVerdict in index.ts. */ starlink?: 'confirmed' | 'likely'; } interface OfferDetailLegLike { segments?: OfferDetailSegmentLike[]; } interface OfferDetailLike { airline?: string; flight_number?: string; segments?: OfferDetailSegmentLike[]; inbound?: OfferDetailLegLike; ancillaries?: { cabin_bag?: OfferDetailAncillary; checked_bag?: OfferDetailAncillary; seat_selection?: OfferDetailAncillary; }; conditions?: OfferDetailConditions; } type OfferServiceSignal = 'included' | 'available' | null; interface OfferAmenityAssessment { state: OfferAmenityState; confidence: OfferAmenityConfidence; source: OfferAmenitySource; evidence?: string; } interface OfferDetailSignals { refundability: OfferConditionState | null; changeability: OfferConditionState | null; meals: OfferServiceSignal; refreshments: OfferServiceSignal; insurance: OfferServiceSignal; lounge: OfferServiceSignal; wifi: OfferServiceSignal; power: OfferServiceSignal; entertainment: OfferServiceSignal; amenities: { meals: OfferAmenityAssessment; refreshments: OfferAmenityAssessment; insurance: OfferAmenityAssessment; lounge: OfferAmenityAssessment; wifi: OfferAmenityAssessment; power: OfferAmenityAssessment; entertainment: OfferAmenityAssessment; }; } interface OfferDetailBadge { key: string; label: string; tone: 'positive' | 'neutral' | 'negative'; } declare function extractOfferDetailSignals(offer: OfferDetailLike): OfferDetailSignals; declare function getOfferDetailBadges(offer: OfferDetailLike): OfferDetailBadge[]; declare function getOfferDetailPromptNotes(offer: OfferDetailLike): string[]; /** * LetsFG — Agent-native flight search & booking SDK for Node.js/TypeScript. * * Server-side engine covers hundreds of airlines. Free search via PFS Bearer token * or prepaid Developer API. Zero external JS dependencies. Uses native fetch (Node 18+). * * @example * ```ts * import { LetsFG } from 'letsfg'; * * // PFS (free, Bearer token from `letsfg auth`) * const bt = new LetsFG({ bearerToken: process.env.LETSFG_BEARER_TOKEN }); * const flights = await bt.search('GDN', 'BER', '2026-03-03'); * * // Developer API (look-to-book search: 200 free after every booking) * const bt2 = new LetsFG({ apiKey: process.env.LETSFG_API_KEY }); * const flights2 = await bt2.search('LHR', 'JFK', '2026-04-15'); * ``` */ interface FlightSegment { airline: string; airline_name: string; flight_no: string; origin: string; destination: string; origin_city: string; destination_city: string; departure: string; arrival: string; duration_seconds: number; cabin_class: string; aircraft: string; /** Starlink Wi-Fi on this leg. 'confirmed' = the carrier has fitted every * aircraft of this type. 'likely' = installation on this type is underway * but incomplete, so this airframe may not have it. Undefined means no * information — NOT an absence of Wi-Fi. */ starlink?: StarlinkSegmentVerdict; } type StarlinkSegmentVerdict = 'confirmed' | 'likely'; /** Itinerary-level Starlink verdict. '*_some' means at least one leg has none; * 'likely_*' means the rollout on that subfleet is underway but incomplete. * Only 'confirmed_*' should be shown to an end user as a fact. */ type StarlinkOfferVerdict = 'confirmed_all' | 'confirmed_some' | 'likely_all' | 'likely_some'; interface FlightRoute { segments: FlightSegment[]; total_duration_seconds: number; stopovers: number; } interface FlightOffer { id: string; price: number; currency: string; price_formatted: string; outbound: FlightRoute; inbound: FlightRoute | null; airlines: string[]; owner_airline: string; bags_price: Record; /** Starlink Wi-Fi across the whole itinerary; per-leg detail is on each * segment's `starlink`. Undefined when no leg has any information. */ starlink?: StarlinkOfferVerdict; availability_seats: number | null; conditions: Record; is_locked: boolean; fetched_at: string; booking_url: string; } interface FlightSearchResult { search_id: string; offer_request_id: string; passenger_ids: string[]; origin: string; destination: string; currency: string; offers: FlightOffer[]; total_results: number; search_params: Record; pricing_note: string; } interface UnlockResult { offer_id: string; unlock_status: string; payment_charged: boolean; payment_amount_cents: number; payment_currency: string; payment_intent_id: string; confirmed_price: number | null; confirmed_currency: string; offer_expires_at: string; message: string; } interface Passenger { id: string; given_name: string; family_name: string; born_on: string; gender?: string; title?: string; email?: string; phone_number?: string; } interface BookingResult { booking_id: string; status: string; booking_type: string; offer_id: string; flight_price: number; service_fee: number; service_fee_percentage: number; total_charged: number; currency: string; order_id: string; booking_reference: string; unlock_payment_id: string; fee_payment_id: string; created_at: string; details: Record; } interface SearchOptions { returnDate?: string; adults?: number; children?: number; infants?: number; cabinClass?: 'M' | 'W' | 'C' | 'F'; maxStopovers?: number; currency?: string; limit?: number; sort?: 'price' | 'duration'; departureTimeFrom?: string; departureTimeTo?: string; } interface LetsFGConfig { /** PFS Bearer token from `letsfg auth`. Enables free search via POST /api/search polling. */ bearerToken?: string; /** Developer API key. Look-to-book search; no booking fee, no transaction fee. */ apiKey?: string; baseUrl?: string; timeout?: number; } declare const ErrorCode: { readonly SUPPLIER_TIMEOUT: "SUPPLIER_TIMEOUT"; readonly RATE_LIMITED: "RATE_LIMITED"; readonly SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE"; readonly NETWORK_ERROR: "NETWORK_ERROR"; readonly INVALID_IATA: "INVALID_IATA"; readonly INVALID_DATE: "INVALID_DATE"; readonly INVALID_PASSENGERS: "INVALID_PASSENGERS"; readonly UNSUPPORTED_ROUTE: "UNSUPPORTED_ROUTE"; readonly MISSING_PARAMETER: "MISSING_PARAMETER"; readonly INVALID_PARAMETER: "INVALID_PARAMETER"; readonly AUTH_INVALID: "AUTH_INVALID"; readonly PAYMENT_REQUIRED: "PAYMENT_REQUIRED"; readonly PAYMENT_DECLINED: "PAYMENT_DECLINED"; readonly OFFER_EXPIRED: "OFFER_EXPIRED"; readonly OFFER_NOT_UNLOCKED: "OFFER_NOT_UNLOCKED"; readonly FARE_CHANGED: "FARE_CHANGED"; readonly ALREADY_BOOKED: "ALREADY_BOOKED"; readonly BOOKING_FAILED: "BOOKING_FAILED"; }; type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode]; declare const ErrorCategory: { readonly TRANSIENT: "transient"; readonly VALIDATION: "validation"; readonly BUSINESS: "business"; }; type ErrorCategoryType = (typeof ErrorCategory)[keyof typeof ErrorCategory]; declare class LetsFGError extends Error { statusCode: number; response: Record; errorCode: string; errorCategory: ErrorCategoryType; isRetryable: boolean; constructor(message: string, statusCode?: number, response?: Record, errorCode?: string); } declare class AuthenticationError extends LetsFGError { constructor(message: string, response?: Record); } declare class PaymentRequiredError extends LetsFGError { constructor(message: string, response?: Record); } declare class OfferExpiredError extends LetsFGError { constructor(message: string, response?: Record); } declare class ValidationError extends LetsFGError { constructor(message: string, statusCode?: number, response?: Record, errorCode?: string); } /** One-line offer summary */ declare function offerSummary(offer: FlightOffer): string; /** Get cheapest offer from search results */ declare function cheapestOffer(result: FlightSearchResult): FlightOffer | null; /** * Every hotel booking job status after which polling is pointless. `attention` is * final for the caller too: a person settles it, and booking again would book twice. */ declare const HOTEL_BOOKING_FINAL_STATUSES: readonly ["succeeded", "failed", "attention"]; type HotelBookingStatus = 'in_progress' | (typeof HOTEL_BOOKING_FINAL_STATUSES)[number]; declare class LetsFG { private bearerToken; private apiKey; private baseUrl; private timeout; constructor(config?: LetsFGConfig); private requireAuth; private requireApiKey; /** True when using PFS Bearer token (free search path) */ private get usingPFS(); /** * Search for flights — FREE. * * Uses PFS (Bearer token) or Developer API (X-API-Key) depending on config. * PFS: async polling (POST /api/search -> poll /api/results/ every 10s). * Developer API: synchronous call. * * @param origin - IATA code (e.g., "GDN", "LON") * @param destination - IATA code (e.g., "BER", "BCN") * @param dateFrom - Departure date "YYYY-MM-DD" * @param options - Optional search parameters */ search(origin: string, destination: string, dateFrom: string, options?: SearchOptions): Promise; /** PFS path: POST /api/search -> poll /api/results/ */ private searchPFS; /** * Resolve a city/airport name to IATA codes. * * Developer API key only. There is no location endpoint on the PFS Bearer * lane — the same dead end `unlock()` documents below. This used to send * PFS callers to `/api/locations?q=`, a route that has never existed on * letsfg.co (verified 2026-08-16: 404, text/html), so they got a JSON parse * error off the 404 page instead of an answer. Pass an IATA code directly * on the PFS lane; a city code expands to every airport in that city. */ resolveLocation(query: string): Promise>>; /** * RETIRED 2026-09-08. Throws instead of calling the server. * * There is no unlock step on any lane. Unlock existed to confirm a live price * before charging; booking now HOLDS the fare on the connected payment method * and captures only once a real airline PNR exists, so a fare that moved * cannot become a charge for a ticket you did not get. If it moves at * checkout you get a `price_change` question to accept or decline instead. * * Kept as a method, and throwing locally rather than making the request, so an * older caller gets one clear sentence at the line that is actually wrong — * not a 410 body to decode, and not a TypeError somewhere else. */ unlock(_offerId: string): Promise; /** * Book a flight. * * PFS (Bearer token): POST /api/agent-book. Free — nothing charged beyond * the ticket price. Pass searchId (from search()'s result). Returns either * { ok, booked: true, order_id } or, when the booking genuinely could not * complete, { ok, booked: false, booking_url } — hand the link to the user, * nothing was charged. * * Developer API (X-API-Key): POST /flights/book. NO unlock step. searchId is * REQUIRED — an offer is bookable only inside the search that produced it. * The connected Revolut method is HELD, not charged; a LetsFG booking agent * buys the ticket and the hold is captured only against a real airline PNR. * Returns the 202 { ok, booking_id, state, held, charged: 0, poll_url } — * poll getBooking(bookingId) until `terminal`, or use bookAndWait(). * Always provide idempotencyKey: a retry with the same key returns the * existing booking instead of opening a second hold on the card. */ book(offerId: string, passengers: Passenger[], contactEmail: string, contactPhone?: string, idempotencyKey?: string, searchId?: string): Promise>; /** * Poll a Developer API flight booking. * * Poll every few seconds until `terminal` is true. The poll is ALSO how LetsFG * knows you are still there, which is what keeps a booking paused on a * question alive — so do not back off to minutes. * * States: authorised, card_issued, booking_in_progress, awaiting_settlement, * then completed (with `pnr` and `charged_amount`), failed (hold released, * nothing charged) or needs_attention (a human at LetsFG is on it — do not * book again). */ getBooking(bookingId: string): Promise>; /** * Answer the open `question` on a booking. * * Echo the question's `round`. A stale round is refused with 409 rather than * guessed at, so an answer to an old question can never be applied to a new * one. Seat: { seats: [...] } or { skip: true }. Price change or paid extra: * { confirm: true } or { skip: true } — declining an extra still completes * the booking, without it. */ answerBooking(bookingId: string, round: number, answer?: { seats?: Array>; confirm?: boolean; skip?: boolean; }): Promise>; /** * Book and poll to a terminal state. Mirrors bookHotelAndWait(). * * Blocks for as long as the booking takes (4–11 minutes typically), so use * book() + getBooking() instead if your caller has a request timeout. * * `onQuestion` returns the answer for answerBooking(). Without it, a fare * increase is ACCEPTED and a paid extra is DECLINED — the conservative * reading of "the traveller asked for this flight". */ bookAndWait(offerId: string, passengers: Passenger[], contactEmail: string, searchId: string, opts?: { contactPhone?: string; idempotencyKey?: string; pollMs?: number; timeoutMs?: number; onQuestion?: (q: Record) => { seats?: Array>; confirm?: boolean; skip?: boolean; }; }): Promise>; /** * Resolve a place name to the city id that searchHotels() needs. * * Use `Id` from the first result as `cityId` and `Name` as `cityName`. */ hotelDestinations(text: string): Promise>>; /** * Search real, bookable hotel inventory. * * Slow by nature — the supplier streams a whole city and every rate is priced * — so this gets its own generous timeout rather than the client default. * * The response carries `session_id`, `currency`, `supplier_currency`, * `markup_rate`, `fx_rate`, `fx_as_of`, `count`, `hotels`, `terms` and * `caveats`. Each offer carries `price` (what the guest pays, in `currency`), * `currency`, `fx_rate`, `expected_cost` (the supplier's cost, in PLN), * `refundable`, `free_cancellation_until` (refundable rates only), * `cancellation_policy` and its own `session_id`. * * `price` is the supplier's cost plus `markup_rate` (6.4% for Revolut Pay or an * EEA-issued card, 8.3% for a card issued outside the EEA); nothing is added at * booking. Keep the chosen offer whole: bookHotel() needs its `session_id`, * `combination_id_v2`, `price`, `expected_cost`, `currency` and `fx_rate`. */ searchHotels(params: { cityId: number; cityName: string; checkIn: string; checkOut: string; adults?: number; children?: number; childAges?: number[]; /** Two-letter code. Rates and taxes genuinely differ by nationality. */ nationality?: string; limit?: number; withImages?: boolean; /** ISO code every `price` is quoted in. Default USD; PLN is the supplier's own. */ currency?: string; }): Promise>; /** * Start a booking. Returns a job immediately — it does NOT book inline. * * What happens, in order: the offer's full `price` is HELD on the Revolut * payment method connected to this account (authorised, not taken); LetsFG * books the room with the supplier and pays the supplier itself; the hold is * captured only once the supplier has confirmed. If the booking fails for any * reason, the hold is released and nothing is charged. There is no * reservation fee, no deposit and no pay link. * * A booking takes minutes and no proxy holds a connection that long, so this * returns at once and you poll hotelBooking() for the outcome. Use * bookHotelAndWait() if you would rather block. * * Send `expectedPrice` (the offer's `price`), `expectedCost`, `currency` and * `fxRate` exactly as search returned them. The booking is refused if the * supplier's live cost is above `expectedCost`, and a USD offer sent without * its `currency` is refused with 400 price_mismatch (the API assumes PLN). * Guest names, phone and e-mail are checked before anything is held; a problem * returns 400 invalid_details naming the fields. * * Do NOT call this again for a booking whose job is still running: poll it. * A retry of the same booking returns the job already under way * (`duplicate: true`) rather than holding the money twice. */ bookHotel(params: { sessionId: string; hotelCode: number; combinationIdV2: string; /** The offer's `price`, verbatim. */ expectedPrice: number; /** The offer's `expected_cost` (the supplier's cost, PLN), verbatim. */ expectedCost: number; /** The offer's `currency`. Copy it — omitted means PLN. */ currency?: string; /** The offer's `fx_rate` (null for a PLN offer). */ fxRate?: number | null; cityId: number; cityName: string; checkIn: string; checkOut: string; /** * ONE entry per guest in the room, children included: adults first, then children in the * `childAges` order used to search (the party travels with the offer's session). The hotel * requires a name for every guest; fewer names than guests is refused before anything is * submitted, and the hold is released. */ guests: Array<{ title: string; first_name: string; last_name: string; }>; /** The guest's e-mail: the confirmation, or a note that it did not go through, goes here. */ email: string; phone: string; /** Adults in the room, as searched. */ adults?: number; combinationId?: number; hotelName?: string; phoneCountryCode?: string; specialRequests?: string[]; /** Optional. A retry with the same key returns the booking already under way. */ idempotencyKey?: string; }): Promise>; /** * Collect the result of a booking started with bookHotel(). * * `status` is 'in_progress', 'succeeded', 'failed' or 'attention'; the last * three are final (HOTEL_BOOKING_FINAL_STATUSES). * * - succeeded: `confirmation`, `booking_id`, `hotel`, `room`, `total_price` + * `currency` (what the guest is charged), `supplier_paid` + * `supplier_currency` (what the supplier was paid), `payment_status`, * `refundable`, `free_cancellation_until`, `cancellation_ladder`, `terms`. * - failed: `error`, written for the guest. The hold has been released and * nothing was charged. * - attention: `error` (and `confirmation` when known). The outcome could not * be settled automatically; the hold is kept — nothing is charged — while a * person checks with the supplier. Do not book again. * * The guest is e-mailed in every case. */ hotelBooking(bookingJobId: string): Promise>; /** * bookHotel(), then poll until the booking settles. Convenience only. * * Stops at 'succeeded', 'failed' or 'attention' and never re-books. Giving up * after `maxWaitMs` (default 30 minutes: a booking usually takes 5-10, and * hotel bookings run one at a time) does NOT cancel anything — the booking may * still complete. The returned object carries `booking_job_id` so you can keep * polling, and the guest is e-mailed the outcome regardless. */ bookHotelAndWait(params: Parameters[0] & { pollIntervalMs?: number; maxWaitMs?: number; }): Promise>; /** * Release a reservation at the supplier and refund the guest. * * Only this account's own bookings can be cancelled (anything else is 404). A * zero-charge cancellation — a refundable rate before its * `free_cancellation_until` — refunds the charge in full, or releases a hold * not yet captured. A cancellation that would cost money is refused with 409; * the hotel's own ladder is in the booking's `terms`. * * This drives a browser at the supplier and takes over a minute. If it times * out, do not assume it failed — re-check before retrying. */ cancelHotel(confirmation: string): Promise>; /** * [Developer API] Mint a one-time link for connecting a Revolut payment method. * * This replaced setupPayment() on 2026-09-08. Nothing is charged to connect, and * card details never touch LetsFG: the returned `connect_url` opens a hosted page * where the developer saves a card, Revolut Pay or Google Pay. A PERSON must open * it in a browser — there is no endpoint that takes card details, so do not ask a * user for a card number and do not try to automate this step. * * Most agents should NOT need a Developer API account at all. To authenticate for * search and booking, run `letsfg auth`, which creates no billing account. */ connectPayment(): Promise>; /** * RETIRED 2026-09-08 with Stripe. Throws instead of calling the server. * * `/agents/setup-payment` answers 410 Gone. Payment enrolment moved onto the same * Revolut rail as the rest of the product: call connectPayment() and open the * `connect_url` it returns. * * Kept as a method, and throwing locally rather than making the request, for the * same reason as unlock() — an older caller gets one clear sentence at the line that * is actually wrong, not a 410 body to decode and not a TypeError somewhere else. */ setupPayment(_token?: string): Promise>; /** * Get current agent profile and usage stats. */ me(): Promise>; /** * [Developer API only] Create a PAID Developer API account with its own billing. * * Most agents should NOT call this — it creates a billing account you probably * do not want. To search and book flights, run `letsfg auth` instead. */ static register(agentName: string, email: string, baseUrl?: string, ownerName?: string, description?: string): Promise>; private postWithBearer; private getNoAuth; private getWithAuth; private postWithAuth; private post; private get; private requestWithHeaders; } declare const BoostedTravel: typeof LetsFG; declare const BoostedTravelError: typeof LetsFGError; type BoostedTravelConfig = LetsFGConfig; export { AuthenticationError, type BookingResult, BoostedTravel, type BoostedTravelConfig, BoostedTravelError, ErrorCategory, type ErrorCategoryType, ErrorCode, type ErrorCodeType, type FlightOffer, type FlightRoute, type FlightSearchResult, type FlightSegment, HOTEL_BOOKING_FINAL_STATUSES, type HotelBookingStatus, LetsFG, type LetsFGConfig, LetsFGError, type OfferDetailSignals, OfferExpiredError, type Passenger, PaymentRequiredError, type RankOffer, type RankedOffer, type RankingContext, type ScoreBreakdown, type SearchOptions, type StarlinkOfferVerdict, type StarlinkSegmentVerdict, TRIP_PURPOSES, type TripPurpose, type TripPurposeOptions, type UnlockResult, ValidationError, cheapestOffer, deduplicateOffers, LetsFG as default, extractOfferDetailSignals, getOfferDetailBadges, getOfferDetailPromptNotes, getPrimaryTripPurpose, getProfileLabel, normalizeTripPurposes, offerSummary, rankOffers, selectDiverseTop };