{"openapi":"3.1.0","info":{"title":"Pointify Deal Engine","description":"Enterprise travel search API powering the Pointify Travel Platform.\n\nAggregates flights from Duffel, SerpAPI, and Seats.aero with advanced fare intelligence including self-connect routing, fare prediction, and geo-pricing.\n\n**Authentication**: Pass your API key via `Authorization: Bearer pt_live_...` header.\nRequests without an API key are allowed for first-party web-app access.","contact":{"name":"Pointify Travel Technologies","url":"https://pointifytravels.com/","email":"api@pointifytravels.com"},"license":{"name":"Proprietary"},"version":"1.0.0"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Health","description":"Liveness probe — always HTTP 200, but honest about dependencies.\n\nDeliberately still 200 even when degraded: Docker's healthcheck calls\nthis endpoint, so returning non-200 when Postgres is down would mark\nthe container unhealthy and restart-loop a service that is other-wise\nperfectly able to serve live search traffic.\n\nWhat changed and why: this used to return a flat ``{\"status\": \"ok\"}``\nwith no dependency checks at all. On 2026-08-05 postgres and redis were\nstopped and never restarted, and because search still streams live\nresults straight from the providers, this endpoint reported \"ok\" for\nthree days while every search went unpersisted and the booking path's\nRedis offer cache was gone. A liveness probe that cannot go yellow is\nindistinguishable from one that is not running.\n\nThe verdict is cached for 30s so this stays cheap.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}},"/health/auth-config":{"get":{"tags":["health"],"summary":"Health Auth Config","description":"Diagnostic probe for the auth pipeline.\n\nReturns:\n- whether SUPABASE_JWT_SECRET / SUPABASE_URL / SUPABASE_ANON_KEY /\n  INTERNAL_API_KEY are populated (just the presence bit, never the\n  value) so we can tell from outside whether the Hetzner env file\n  has the keys our auth middleware depends on;\n- what JWTAuthMiddleware decided about the current request —\n  user_id (if populated, the first 8 chars as a sanity check) and\n  auth_error (the failure reason string if any).\n\nNo secrets in the response. Exempt from ``InternalAuthMiddleware``\nvia the ``/health`` prefix allowlist so mobile/curl can reach it\nwithout an internal key.","operationId":"health_auth_config_health_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Auth Config Health Auth Config Get"}}}}}}},"/health/providers":{"get":{"tags":["health"],"summary":"Health Providers","description":"Per-provider search health — why a provider returned nothing.\n\nWHY THIS EXISTS: Duffel contributed zero offers to every cash search in\nproduction and there was no way to tell why from outside the box (#4838).\nThree very different causes are indistinguishable in the search stream:\n\n  1. credential missing — ``duffel_provider.search()`` returns ``[]`` when\n     ``duffel_api_token`` is blank, logs a warning, and (since #3780) an\n     empty API result is deliberately NOT a circuit-breaker failure. So a\n     blank or misnamed env var yields zero deals, no error, no breaker\n     trip, and no external signal at all.\n  2. circuit breaker open — the provider is skipped before it is called.\n  3. genuinely no inventory on the route — a correct, healthy answer.\n\nAll three surfaced as the same thing because the aggregator labels any\nzero-result provider ``status: \"error\"``. This endpoint separates them.\n\nFollows the ``/health/auth-config`` convention: credentials are reported as\na PRESENCE BIT only, never the value. Inherits the ``/health`` internal-auth\nexemption so it is curl-able without a key — it exposes no secrets, no PII\nand no booking state, only whether our own plumbing is connected.","operationId":"health_providers_health_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Providers Health Providers Get"}}}}}}},"/health/deep":{"get":{"tags":["health"],"summary":"Health Deep","description":"Deep health check — database, Redis, browsers, and downstream services.","operationId":"health_deep_health_deep_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Deep Health Deep Get"}}}}}}},"/version":{"get":{"tags":["version"],"summary":"Version","description":"Return the running deploy's SHA, branch, and timestamps.\n\nAll values are read from environment variables populated by\n``scripts/hetzner-autopull.sh`` before ``docker compose up``. No secrets\nare ever returned — just infrastructure state.","operationId":"version_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Version Version Get"}}}}}}},"/api/search":{"post":{"tags":["search"],"summary":"Search Flights","operationId":"search_flights_api_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flights/upgrade-options":{"post":{"tags":["upgrades"],"summary":"Upgrade Options","operationId":"upgrade_options_api_flights_upgrade_options_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeOptionsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpgradeOptionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/seat-hold":{"post":{"tags":["booking","seats"],"summary":"Hold Seat","description":"Hold a seat for this offer + passenger slot.\n\nUPSERT on (offer_id, passenger_slot, segment_id) — changing which\nseat a passenger sits in for a given leg replaces the prior hold\natomically. `status` always resets to 'held' because a user who\njust picked a seat hasn't ticketed it yet.","operationId":"hold_seat_api_booking_seat_hold_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatHoldInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/seat-selections":{"get":{"tags":["booking","seats"],"summary":"List Selections","description":"List all seat selections for an offer.\n\nDrives realtime UI sync — when one device holds a seat, the other\ndevice's next poll (or realtime subscription) sees the new row.","operationId":"list_selections_api_booking_seat_selections_get","parameters":[{"name":"offer_id","in":"query","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/seat-confirm":{"post":{"tags":["booking","seats"],"summary":"Confirm Seats","description":"Mark all held seats for an offer as confirmed + linked to booking.\n\nCalled from `booking.create` after the Duffel order succeeds.\nFires after the order is in hand so a failed booking never leaves\norphan 'confirmed' rows.","operationId":"confirm_seats_api_booking_seat_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatConfirmInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/seat-release":{"post":{"tags":["booking","seats"],"summary":"Release Seats","description":"Release all held seats for an offer — user abandoned the flow\nor picked a different flight. We keep the row (for analytics) but\nflip status to 'released' so /seat-hold UPSERTs cleanly on a\nfuture retry.","operationId":"release_seats_api_booking_seat_release_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatReleaseInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/seat-selections/by-booking/{booking_id}":{"get":{"tags":["booking","seats"],"summary":"List By Booking","description":"Render seats on the trip detail page — all confirmed selections\nfor a given booking.","operationId":"list_by_booking_api_booking_seat_selections_by_booking__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/create-offer":{"post":{"tags":["booking"],"summary":"Create Offer Request","description":"Dual-mode:\n\n* **Checkout** — when ``payment_intent_id`` + ``offer_id`` are set, verify\n  the Stripe PaymentIntent is ``succeeded`` and then create the Duffel\n  order via the existing booking service. Returns\n  ``{booking_ref, status, confirmation_url}``.\n* **Search** — otherwise, creates a Duffel offer request and returns\n  bookable offers (preserves legacy web behavior).","operationId":"create_offer_request_api_booking_create_offer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOfferInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/offers/{offer_id}":{"get":{"tags":["booking"],"summary":"Get Offer","description":"Fetch a single Duffel offer by id — backs the booking staleness probe.\n\nThe web ``/api/booking/pending`` route probes this before creating a\nbooking so it can show an honest \"offer expired, search again\" message.\nContract the web relies on:\n\n  * **200** — Duffel still has the offer (live, bookable).\n  * **410 Gone** — Duffel no longer recognises it (offers expire ~30 min\n    after search). The probe maps ONLY 410 to OFFER_EXPIRED.\n  * **502** — upstream/transport error; the probe falls through and lets\n    the booking proceed (confirm is the real backstop).\n\nDuffel offer ids may carry a ``duffel_`` prefix in our DB — strip it the\nsame way the rest of booking_service does before calling Duffel.","operationId":"get_offer_api_offers__offer_id__get","parameters":[{"name":"offer_id","in":"path","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/payment-intent":{"post":{"tags":["booking"],"summary":"Create Payment Intent","description":"Create a Stripe PaymentIntent for a Duffel offer.\n\nResolves the offer price from Duffel (or uses ``amount`` override) and\nreturns the ``client_secret`` the mobile PaymentSheet needs.","operationId":"create_payment_intent_api_booking_payment_intent_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routes__booking__PaymentIntentInput__1"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/book":{"post":{"tags":["booking"],"summary":"Create Booking","description":"Create a booking via Duffel.\n\nOpen to guests — cash Duffel tickets are sellable without a\nPointify account because Duffel holds the passenger record and\nStripe collects payment separately. Authenticated callers are\nstill identified via `optional_auth` so downstream analytics +\nloyalty-card auto-sync still work; guests pass per-passenger\nloyalty numbers inline on the booking payload instead.\n\nThe agent-assisted `/book/agent` variant below keeps\n`require_auth` + a tier gate because that path charges against a\npaid subscription and must know the caller.","operationId":"create_booking_api_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/book/agent":{"post":{"tags":["booking"],"summary":"Create Agent Booking","description":"Create a booking via the AI agent (Pro+ tiers only).","operationId":"create_agent_booking_api_book_agent_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/baggage":{"get":{"tags":["booking"],"summary":"Get Baggage Options","description":"Return real baggage pricing for a Duffel offer.\n\nMirrors the seat-map pattern: guests can see real add-ons before\nthey sign up, and carriers that don't publish baggage via Duffel\nreturn an empty list (the UI should show an \"available at the\nairport\" hint in that case rather than a fake flat fee).","operationId":"get_baggage_options_api_booking_baggage_get","parameters":[{"name":"offer_id","in":"query","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/fare-conditions":{"get":{"tags":["booking"],"summary":"Get Fare Conditions","description":"Return refund + change policies for a Duffel offer.\n\nOpen to guests — policies must be visible on the checkout summary\nbefore the user creates an account.","operationId":"get_fare_conditions_api_booking_fare_conditions_get","parameters":[{"name":"offer_id","in":"query","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/fare-brands":{"get":{"tags":["booking"],"summary":"Get Fare Brands","description":"Return fare-brand options for the same flight as the given offer.\n\nMirrors Google Flights' \"Choose your fare\" comparison panel:\n  - Looks up the offer's `offer_request_id`\n  - Fetches all offers from that request\n  - Groups by the same itinerary (same segment flight numbers)\n  - Returns one card per fare brand with refund/change/baggage flags\n    and the price delta vs the base offer\n\nThe frontend renders Basic Economy / Main Cabin / Premium / Business\nside-by-side so travellers see exactly what extra $50/$100/$200\nactually buys them.","operationId":"get_fare_brands_api_booking_fare_brands_get","parameters":[{"name":"offer_id","in":"query","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}":{"get":{"tags":["booking"],"summary":"Get Booking","description":"Get order details from Duffel.","operationId":"get_booking_api_booking__order_id__get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/change-request":{"post":{"tags":["booking"],"summary":"Create Order Change Request","description":"Kick off a Duffel change request — propose new slices.\n\nReturns the Duffel `order_change_request` object with any\n`order_change_offers` that have populated so far. Callers should\npoll `GET /booking/change-requests/{id}` until offers fill in\n(usually <5s, occasionally longer for multi-carrier itineraries).","operationId":"create_order_change_request_api_booking__order_id__change_request_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateChangeRequestInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/change-requests/{change_request_id}":{"get":{"tags":["booking"],"summary":"Get Order Change Request","description":"Poll a change request for populated offers.","operationId":"get_order_change_request_api_booking_change_requests__change_request_id__get","parameters":[{"name":"change_request_id","in":"path","required":true,"schema":{"type":"string","title":"Change Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/change-confirm":{"post":{"tags":["booking"],"summary":"Confirm Order Change","description":"Accept one of the change offers — Duffel bills the delta and\nissues the new ticket. Re-fetch the order after to get the\nupdated slices + e-tickets.","operationId":"confirm_order_change_api_booking__order_id__change_confirm_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmChangeInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/cancel":{"post":{"tags":["booking"],"summary":"Cancel Booking","description":"Cancel a Duffel order. Backend-only — apps/web checks ownership.\n\nThis used to depend on ``require_auth``, which was wrong in both\ndirections at once.\n\nTOO WEAK: ``require_auth`` proves someone is signed in, not that the\nbooking is theirs, and ``order_id`` goes straight to Duffel. Since\n``InternalAuthMiddleware`` admits any verified Supabase JWT, any account\nholder with an order id could cancel that booking. Ownership cannot be\nchecked here — ``bookings`` lives in the WEB database, which this service\nreaches only when ``WEB_DATABASE_URL`` is provisioned, so a check here\nwould fail closed on every cancellation whenever it isn't.\n\nTOO STRONG: the only real caller is apps/web, which proxies with\n``X-Internal-Key`` and no ``Authorization`` header, so ``require_auth``\nrejected the one caller meant to get through.\n\nThe ownership gate therefore lives at the web layer, which has the user's\nsession and an RLS-scoped view of ``bookings``; this route only has to\nestablish that the request came from our own backend.","operationId":"cancel_booking_api_booking__order_id__cancel_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/documents":{"get":{"tags":["booking"],"summary":"Get Booking Documents","description":"Return e-tickets and boarding passes for a confirmed Duffel order.\n\nDuffel exposes PDF download URLs on the order's `documents` array\nafter the airline issues tickets (usually within seconds of order\ncreation for instant bookings). Returns an empty list while the\nairline is still processing.","operationId":"get_booking_documents_api_booking__order_id__documents_get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/revenue":{"get":{"tags":["booking"],"summary":"Get Booking Revenue","description":"Get the revenue breakdown for a booking (admin only).","operationId":"get_booking_revenue_api_booking__order_id__revenue_get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/duffel/health":{"get":{"tags":["booking"],"summary":"Duffel Health","description":"Admin healthcheck: Duffel API reachability + balance + token age.\n\nSurfaces three things ops needs to spot a broken booking pipeline\nBEFORE customers hit it:\n\n1. **API reachability** — can we hit `/identity/me` with our token?\n2. **Balance state** — how much float is in our Duffel balance? An\n   empty balance is the #1 silent cause of `insufficient_balance`\n   booking 422s that map to a confusing user-facing error.\n3. **Token validity** — Duffel sandbox vs production matters; we\n   echo the environment back so the operator can sanity-check\n   `DUFFEL_API_TOKEN` was set to the right key.\n\nNo PII / customer-data ever flows through this endpoint. Read-only\nagainst Duffel.","operationId":"duffel_health_api_admin_duffel_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user/travelers":{"get":{"tags":["booking"],"summary":"List Travelers","description":"Return the caller's saved traveler profiles.\n\nFail-open to ``[]`` so the mobile checkout pre-fill never blocks on\na cold DB or a missing table.","operationId":"list_travelers_api_user_travelers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["booking"],"summary":"Upsert Traveler","description":"Upsert a traveler profile for the caller.\n\nUses the ``(user_id, first_name, last_name, dob)`` unique key as the\nconflict target so re-submitting the same passenger rewrites their\ncontact info without creating a duplicate row.","operationId":"upsert_traveler_api_user_travelers_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TravelerInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user/travelers/{traveler_id}":{"delete":{"tags":["booking"],"summary":"Delete Traveler","description":"Delete one of the caller's saved traveler profiles.\n\nScoped to ``user_id`` so a caller can only remove their own rows.\nCompares ``id::text`` so a UUID or text primary key both work, and\nfails open to ``{\"ok\": True}`` on a cold table (nothing to delete).","operationId":"delete_traveler_api_user_travelers__traveler_id__delete","parameters":[{"name":"traveler_id","in":"path","required":true,"schema":{"type":"string","title":"Traveler Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/hold":{"post":{"tags":["booking"],"summary":"Create Hold Order","description":"Create a hold order (type='hold'). No payment taken yet — call\n/booking/pay to settle later.","operationId":"create_hold_order_api_booking_hold_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldOrderInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/pay":{"post":{"tags":["booking"],"summary":"Pay For Hold Order","operationId":"pay_for_hold_order_api_booking_pay_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldOrderPayInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/payment-intents":{"post":{"tags":["booking"],"summary":"Create Duffel Payment Intent","description":"Create a Duffel PaymentIntent — returns client_token for the\nDuffel card component on the frontend.","operationId":"create_duffel_payment_intent_api_booking_payment_intents_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routes__booking__PaymentIntentInput__2"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/payment-intents/{payment_intent_id}/confirm":{"post":{"tags":["booking"],"summary":"Confirm Duffel Payment Intent","operationId":"confirm_duffel_payment_intent_api_booking_payment_intents__payment_intent_id__confirm_post","parameters":[{"name":"payment_intent_id","in":"path","required":true,"schema":{"type":"string","title":"Payment Intent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/3ds-session":{"post":{"tags":["booking"],"summary":"Create 3Ds Session","operationId":"create_3ds_session_api_booking_3ds_session_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreeDSInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/available-services":{"get":{"tags":["booking"],"summary":"List Post Booking Services","operationId":"list_post_booking_services_api_booking__order_id__available_services_get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/services":{"post":{"tags":["booking"],"summary":"Purchase Post Booking Service","description":"Buy an extra bag / paid seat / other ancillary on an existing order.","operationId":"purchase_post_booking_service_api_booking__order_id__services_post","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostBookingServiceInput"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/{order_id}/check-in-links":{"get":{"tags":["booking"],"summary":"Get Check In Links","operationId":"get_check_in_links_api_booking__order_id__check_in_links_get","parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","title":"Order Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/places/suggest":{"get":{"tags":["booking"],"summary":"Suggest Places","description":"Typeahead for city / airport / location — replaces our hardcoded\n16-city table for Stays destinations.","operationId":"suggest_places_api_places_suggest_get","parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","title":"Query"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/bookings/{booking_id}/cancel":{"post":{"tags":["booking"],"summary":"Cancel Stays Booking Route","operationId":"cancel_stays_booking_route_api_hotels_bookings__booking_id__cancel_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/booking/change-requests/{change_request_id}/offers":{"get":{"tags":["booking"],"summary":"List Change Offers","operationId":"list_change_offers_api_booking_change_requests__change_request_id__offers_get","parameters":[{"name":"change_request_id","in":"path","required":true,"schema":{"type":"string","title":"Change Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts":{"post":{"tags":["alerts"],"summary":"Create Alert","description":"Create a smart fare alert for a route.","operationId":"create_alert_api_alerts_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAlertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["alerts"],"summary":"List Alerts","description":"List all alerts for a user.\n\nSee the matching comment on ``list_trips`` — ``user_id`` query\nparam is optional and falls back to the JWT ``sub`` so mobile\nclients don't have to echo their own UUID on every call.\n\nA brand-new user that's never set a fare alert should see the\nempty-state on mobile, not a 500. We therefore fail-open on any\nDB / cache hiccup below: log it and return ``{\"alerts\": []}``.\nThe Trips screen already does the same, and mobile's\n``getAlerts`` has no retry path so a 500 here = the whole Deals\ntab reads as broken.","operationId":"list_alerts_api_alerts_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts/{alert_id}":{"delete":{"tags":["alerts"],"summary":"Delete Alert","description":"Delete a fare alert.","operationId":"delete_alert_api_alerts__alert_id__delete","parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","title":"Alert Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts/deals":{"get":{"tags":["alerts"],"summary":"Get Recent Deals","description":"Get recently detected mistake fare deals (last 24h).\n\nPublic feed — the result is identical for everyone (not user-scoped),\nso it uses optional_auth instead of require_auth. Requiring auth here\nwas a bug: the homepage/alerts deal feed 401'd for signed-out users\n(and the web proxy doesn't forward a user token), so it always failed.","operationId":"get_recent_deals_api_alerts_deals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/hotels/search":{"post":{"tags":["hotels"],"summary":"Search Hotels","operationId":"search_hotels_api_hotels_search_post","parameters":[{"name":"include_rentals","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":false,"title":"Include Rentals"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/awards/search":{"post":{"tags":["hotels"],"summary":"Search Hotel Awards","description":"Search hotel award availability across all 5 major loyalty programs.\n\nReturns award night results sorted by points per night ascending.\nEach result includes: program, points_per_night, taxes_usd, hotel_name,\ncategory, location, check_in, check_out, room_type, available.\n\nSupply ``?program=Marriott+Bonvoy`` to restrict to a single chain.","operationId":"search_hotel_awards_api_hotels_awards_search_post","parameters":[{"name":"program","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to a single loyalty program: 'Marriott Bonvoy', 'Hilton Honors', 'World of Hyatt', 'IHG Rewards', or 'Wyndham Rewards'.","title":"Program"},"description":"Filter to a single loyalty program: 'Marriott Bonvoy', 'Hilton Honors', 'World of Hyatt', 'IHG Rewards', or 'Wyndham Rewards'."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelSearchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/{hotel_id}":{"get":{"tags":["hotels"],"summary":"Get Hotel","description":"Fetch detailed hotel information by hotel_id.\n\nDeprecated: the per-hotel detail lookup used SerpAPI's\n``engine=google_hotels&hotel_id=...`` variant, which has no equivalent\nin the Google Travel scraper (the scraper only returns search lists, not\ndetail pages). Clients should navigate to the booking URL returned in\nthe search result instead. Kept as a 410 Gone so the contract stays\nexplicit until the Next.js layer drops its own /api/hotels/{id}\nproxy route.","operationId":"get_hotel_api_hotels__hotel_id__get","parameters":[{"name":"hotel_id","in":"path","required":true,"schema":{"type":"string","title":"Hotel Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HotelDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/rates":{"post":{"tags":["hotels"],"summary":"Fetch Stay Rates","description":"Return every room + rate variant for a Duffel Stays search result.\n\nStep 2 of Duffel's 4-step Stays flow — search returns only the\ncheapest rate per accommodation; this surfaces every option (King\nvs Twin, breakfast vs not, refundable vs not) so the user can\npick before we lock the price via /hotels/quote.","operationId":"fetch_stay_rates_api_hotels_rates_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StayRatesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/quote":{"post":{"tags":["hotels"],"summary":"Create Stay Quote","description":"Lock the price on a Duffel Stays room rate.\n\nCalled right before the user submits payment. The returned quote has\na TTL (typically 30 min) during which the booking endpoint will\naccept it at the quoted price.","operationId":"create_stay_quote_api_hotels_quote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StayQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/book":{"post":{"tags":["hotels"],"summary":"Create Stay Booking","description":"Finalize a Duffel Stays booking from a quote_id + guest list.\n\nCaller is the Next.js webhook handler after Stripe captures payment.\nDo not call directly from the browser — payment must settle first.","operationId":"create_stay_booking_api_hotels_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StayBookingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/hotels/booking/{booking_id}":{"get":{"tags":["hotels"],"summary":"Get Stay Booking","description":"Fetch a Duffel Stays booking by its Duffel id.\n\nCalled by the web /api/hotels/confirm route after Stripe redirects\nto /hotels/book/success — Duffel is the authoritative source for\nthe confirmation-page fields their go-live review requires.","operationId":"get_stay_booking_api_hotels_booking__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/payment-intent":{"post":{"tags":["cars"],"summary":"Create Car Payment Intent","description":"Create a Stripe PaymentIntent for the Pointify car service fee.\n\nOnly the fee charged by Pointify (margin + selected add-ons) goes through\nStripe. The rental itself is paid at the counter.","operationId":"create_car_payment_intent_api_cars_payment_intent_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarPaymentIntentRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/search":{"post":{"tags":["cars"],"summary":"Search Cars","operationId":"search_cars_api_cars_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/book":{"post":{"tags":["cars"],"summary":"Book Car","operationId":"book_car_api_cars_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarBookingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/quote":{"post":{"tags":["cars"],"summary":"Create Car Quote","description":"Lock the price on a Duffel Cars rate (the offer_token from a\nduffel_cars CarDeal). Returns a quote_id valid for a short TTL.","operationId":"create_car_quote_api_cars_quote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/duffel/book":{"post":{"tags":["cars"],"summary":"Book Car Duffel","description":"Finalize a Duffel Cars booking from a quote.\n\nSibling endpoint to /cars/book — that one handles the Stripe-fee +\nlegacy flow; this one is the merchant-of-record direct-booking flow\nwhen the user picks a duffel_cars rate.","operationId":"book_car_duffel_api_cars_duffel_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarDuffelBookingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/booking/{booking_id}":{"get":{"tags":["cars"],"summary":"Get Booking","operationId":"get_booking_api_cars_booking__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cars/booking/{booking_id}/cancel":{"post":{"tags":["cars"],"summary":"Cancel Booking","operationId":"cancel_booking_api_cars_booking__booking_id__cancel_post","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cruises/search":{"post":{"tags":["cruises"],"summary":"Search Cruises","operationId":"search_cruises_api_cruises_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CruiseSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/optimize":{"post":{"tags":["points"],"summary":"Optimize Redemption","operationId":"optimize_redemption_api_points_optimize_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptimizeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/programs":{"get":{"tags":["points"],"summary":"Get Programs","operationId":"get_programs_api_points_programs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/points/partners/{bank_program}":{"get":{"tags":["points"],"summary":"Get Transfer Partners","operationId":"get_transfer_partners_api_points_partners__bank_program__get","parameters":[{"name":"bank_program","in":"path","required":true,"schema":{"type":"string","title":"Bank Program"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/paths/{target_program}":{"get":{"tags":["points"],"summary":"Get Transfer Paths","operationId":"get_transfer_paths_api_points_paths__target_program__get","parameters":[{"name":"target_program","in":"path","required":true,"schema":{"type":"string","title":"Target Program"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/buy-calculator":{"post":{"tags":["points"],"summary":"Buy Points Calculator","operationId":"buy_points_calculator_api_points_buy_calculator_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuyCalcRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/transfer-bonuses":{"get":{"tags":["points"],"summary":"Get Transfer Bonuses","description":"Return all currently active transfer bonus promotions.","operationId":"get_transfer_bonuses_api_points_transfer_bonuses_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/points/transfer-bonuses/check":{"post":{"tags":["points"],"summary":"Check Transfer Bonus Eligibility","description":"Return active bonuses that match the user's linked bank programs.","operationId":"check_transfer_bonus_eligibility_api_points_transfer_bonuses_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EligibilityRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points/sweetspots":{"get":{"tags":["points"],"summary":"Get Sweetspots","description":"Top curated award sweet spots sorted by cents-per-point descending.\n\nThe Explore mobile shelf + web Suggested Places surface key off this\nendpoint — it never returns a 5xx on an empty or malformed dataset\n(SweetSpotFinder fails open to ``[]`` if the JSON is missing).","operationId":"get_sweetspots_api_points_sweetspots_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points-router/paths":{"post":{"tags":["points-router","points-router"],"summary":"Get Paths","description":"Return ranked redemption paths for a flight.","operationId":"get_paths_api_points_router_paths_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PathsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Paths Api Points Router Paths Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/points-router/health":{"get":{"tags":["points-router","points-router"],"summary":"Health","operationId":"health_api_points_router_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Api Points Router Health Get"}}}}}}},"/api/user/playbook":{"get":{"tags":["playbook","playbook"],"summary":"Get Playbook","description":"Return the user's playbook, or a conservative default.","operationId":"get_playbook_api_user_playbook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Playbook Api User Playbook Get"}}}}}},"put":{"tags":["playbook","playbook"],"summary":"Upsert Playbook","description":"Create or update the caller's playbook row.","operationId":"upsert_playbook_api_user_playbook_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Playbook"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Upsert Playbook Api User Playbook Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips":{"post":{"tags":["trips"],"summary":"Create Trip","description":"Create a new group trip.","operationId":"create_trip_api_trips_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routes__trips__CreateTripRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["trips"],"summary":"List Trips","description":"List all trips for a user.\n\n``user_id`` query param is optional — when omitted we fall back to\nthe authenticated caller (JWT ``sub``). Mobile clients carry a\nBearer token already and shouldn't have to echo their own UUID in\nthe query string. Server-to-server callers that need to scope to\na different user still pass ``?user_id=<uuid>`` explicitly.","operationId":"list_trips_api_trips_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}":{"get":{"tags":["trips"],"summary":"Get Trip","description":"Get a specific trip by ID.","operationId":"get_trip_api_trips__trip_id__get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/activities":{"post":{"tags":["trips"],"summary":"Add Activity","description":"Add an activity to a trip.","operationId":"add_activity_api_trips__trip_id__activities_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddActivityRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/expenses":{"post":{"tags":["trips"],"summary":"Add Expense","description":"Add an expense to a trip.","operationId":"add_expense_api_trips__trip_id__expenses_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddExpenseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/travelers":{"post":{"tags":["trips"],"summary":"Add Traveler","description":"Add a traveler to a trip.","operationId":"add_traveler_api_trips__trip_id__travelers_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddTravelerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/status":{"patch":{"tags":["trips"],"summary":"Update Status","description":"Update the status of a trip.","operationId":"update_status_api_trips__trip_id__status_patch","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStatusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/expenses/summary":{"get":{"tags":["trips"],"summary":"Expense Summary","description":"Get expense summary with balances for a trip.","operationId":"expense_summary_api_trips__trip_id__expenses_summary_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/budget":{"put":{"tags":["trips"],"summary":"Set Budget","description":"Set or update the budget for a trip.","operationId":"set_budget_api_trips__trip_id__budget_put","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetBudgetRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["trips"],"summary":"Get Budget","description":"Get budget tracking info for a trip.","operationId":"get_budget_api_trips__trip_id__budget_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/share":{"post":{"tags":["trips"],"summary":"Generate Share Link","description":"Generate a persistent share link for a trip.","operationId":"generate_share_link_api_trips__trip_id__share_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateShareLinkRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/shared/{token}":{"get":{"tags":["trips"],"summary":"Get Shared Trip","description":"Public endpoint: get a shared trip by token (no auth required).","operationId":"get_shared_trip_api_trips_shared__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/votes":{"get":{"tags":["trips"],"summary":"List Votes","description":"List all votes/polls for a trip.","operationId":"list_votes_api_trips__trip_id__votes_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["trips"],"summary":"Create Vote","description":"Create a new vote/poll in a trip.","operationId":"create_vote_api_trips__trip_id__votes_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/votes/{vote_id}/cast":{"post":{"tags":["trips"],"summary":"Cast Vote","description":"Cast a vote on a poll option.","operationId":"cast_vote_api_trips__trip_id__votes__vote_id__cast_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}},{"name":"vote_id","in":"path","required":true,"schema":{"type":"string","title":"Vote Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CastVoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/votes/{vote_id}/close":{"post":{"tags":["trips"],"summary":"Close Vote","description":"Close a vote/poll.","operationId":"close_vote_api_trips__trip_id__votes__vote_id__close_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}},{"name":"vote_id","in":"path","required":true,"schema":{"type":"string","title":"Vote Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/messages":{"get":{"tags":["trips"],"summary":"List Messages","description":"List messages in a trip chat.","operationId":"list_messages_api_trips__trip_id__messages_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Before"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["trips"],"summary":"Send Message","description":"Send a message in trip chat.","operationId":"send_message_api_trips__trip_id__messages_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/invite":{"post":{"tags":["trips"],"summary":"Generate Invite Code","description":"Generate an invite code for a trip.","operationId":"generate_invite_code_api_trips__trip_id__invite_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateInviteCodeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/join/{code}":{"post":{"tags":["trips"],"summary":"Join Trip By Code","description":"Join a trip using an invite code.","operationId":"join_trip_by_code_api_trips_join__code__post","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinTripRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/itinerary":{"get":{"tags":["trips"],"summary":"List Itinerary","description":"List itinerary items for a trip, grouped by day.","operationId":"list_itinerary_api_trips__trip_id__itinerary_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/{trip_id}/itinerary/reorder":{"put":{"tags":["trips"],"summary":"Reorder Itinerary","description":"Reorder itinerary items (drag-and-drop).","operationId":"reorder_itinerary_api_trips__trip_id__itinerary_reorder_put","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReorderItineraryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips":{"post":{"tags":["group-trips"],"summary":"Create Group Trip","description":"Host creates a new group trip in ``planning`` state.","operationId":"create_group_trip_api_group_trips_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routes__group_trips__CreateTripRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/invite":{"post":{"tags":["group-trips"],"summary":"Invite Member","description":"Host invites a traveler by email. Sends a magic-link email.\n\nThe invite record is created up-front (so the host can see pending\ninvites) and the magic-link URL is returned in the response for the\nhost UI; the same URL is also dispatched via Resend.","operationId":"invite_member_api_group_trips__trip_id__invite_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/join-code":{"post":{"tags":["group-trips"],"summary":"Create Join Code","description":"Host generates (or rotates) the trip-level public join code.\n\nIdempotent if a code already exists on the trip — returns the existing\ncode with ``public_join_enabled=true``. Pass ``?rotate=1`` to mint a\nfresh one (invalidates any QR already in the wild).","operationId":"create_join_code_api_group_trips__trip_id__join_code_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/join/{code}":{"get":{"tags":["group-trips"],"summary":"Preview Join Code","description":"Public preview: returns trip destination + date window so the scanner\ncan render a confirm sheet before the user taps Join.","operationId":"preview_join_code_api_group_trips_join__code__get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["group-trips"],"summary":"Join By Code","description":"Public join: creates a pending member row and returns an\n``invite_token`` so the scanner can route straight to ``/confirm``.\nNo auth required — the token is the only capability the invitee\nneeds until they confirm their passenger info.","operationId":"join_by_code_api_group_trips_join__code__post","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/members/{member_id}/confirm":{"post":{"tags":["group-trips"],"summary":"Confirm Member","description":"Member confirms their origin + dates + passenger info.\n\nGated by the single-use invite token so unauthenticated members can\nconfirm without signing in yet. The ``user_id`` column is later\npopulated when the member signs in via the magic link.","operationId":"confirm_member_api_group_trips__trip_id__members__member_id__confirm_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}},{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}},{"name":"token","in":"query","required":true,"schema":{"type":"string","description":"Invite token from the magic-link email.","title":"Token"},"description":"Invite token from the magic-link email."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmMemberRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}":{"get":{"tags":["group-trips"],"summary":"Get Group Trip","description":"Full trip view — trip row plus per-member statuses.\n\n``trip_id`` is typed as ``uuid.UUID`` so FastAPI rejects non-UUID path\nvalues (e.g. ``/api/group-trips/list``) with a clean 422 before the\nasyncpg layer crashes trying to encode a non-UUID literal as a UUID\nbind parameter. The internal helpers accept the stringified form.","operationId":"get_group_trip_api_group_trips__trip_id__get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/search":{"post":{"tags":["group-trips"],"summary":"Trigger Search","description":"Host kicks off the optimiser. Returns a task_id + stream URL.","operationId":"trigger_search_api_group_trips__trip_id__search_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/search/{task_id}/stream":{"get":{"tags":["group-trips"],"summary":"Search Stream","description":"Replay the optimiser events as an SSE stream.","operationId":"search_stream_api_group_trips__trip_id__search__task_id__stream_get","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}},{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/group-trips/{trip_id}/book":{"post":{"tags":["group-trips"],"summary":"Book Group Trip","description":"Accept the agent's recommended combo and issue one Duffel order\nper member.\n\nOn failure we cancel every order that already succeeded so the\ngroup either books atomically or not at all.","operationId":"book_group_trip_api_group_trips__trip_id__book_post","parameters":[{"name":"trip_id","in":"path","required":true,"schema":{"type":"string","title":"Trip Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingSelection"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/plan":{"post":{"tags":["trip-planner"],"summary":"Plan Trip","description":"AI-powered trip planning from natural language.","operationId":"plan_trip_api_trips_plan_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripPlanRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trips/plan/refine":{"post":{"tags":["trip-planner"],"summary":"Refine Trip","description":"Refine an existing trip plan based on follow-up natural language instructions.","operationId":"refine_trip_api_trips_plan_refine_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TripRefineRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/packages":{"post":{"tags":["packages"],"summary":"Create Package","description":"Create a new travel package.","operationId":"create_package_api_packages_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePackageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["packages"],"summary":"List Packages","description":"List packages. If agent_id is provided, list agent's packages.","operationId":"list_packages_api_packages_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","default":"published","title":"Status"}},{"name":"agent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/packages/{package_id}":{"get":{"tags":["packages"],"summary":"Get Package","description":"Get a specific package by ID.","operationId":"get_package_api_packages__package_id__get","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/packages/{package_id}/components":{"post":{"tags":["packages"],"summary":"Add Component","description":"Add a component to a package.","operationId":"add_component_api_packages__package_id__components_post","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddComponentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/packages/{package_id}/status":{"patch":{"tags":["packages"],"summary":"Update Status","description":"Update the status of a package.","operationId":"update_status_api_packages__package_id__status_patch","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStatusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/packages/{package_id}/pricing":{"get":{"tags":["packages"],"summary":"Package Pricing","description":"Get cost breakdown for a package.","operationId":"package_pricing_api_packages__package_id__pricing_get","parameters":[{"name":"package_id","in":"path","required":true,"schema":{"type":"string","title":"Package Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/clients":{"post":{"tags":["admin"],"summary":"Create Client","description":"Create a new client.","operationId":"create_client_api_admin_clients_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClientRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Clients","description":"List clients for an agent, optionally filtered by search query.","operationId":"list_clients_api_admin_clients_get","parameters":[{"name":"agent_id","in":"query","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/clients/{client_id}":{"get":{"tags":["admin"],"summary":"Get Client","description":"Get a specific client by ID.","operationId":"get_client_api_admin_clients__client_id__get","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin"],"summary":"Update Client","description":"Update a client.","operationId":"update_client_api_admin_clients__client_id__patch","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateClientRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/clients/{client_id}/notes":{"post":{"tags":["admin"],"summary":"Add Note","description":"Add a note to a client.","operationId":"add_note_api_admin_clients__client_id__notes_post","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Notes","description":"List all notes for a client.","operationId":"list_notes_api_admin_clients__client_id__notes_get","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/pipeline":{"post":{"tags":["admin"],"summary":"Create Pipeline Item","description":"Create a new booking pipeline item.","operationId":"create_pipeline_item_api_admin_pipeline_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePipelineRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Pipeline","description":"List pipeline items for an agent, optionally filtered by stage.","operationId":"list_pipeline_api_admin_pipeline_get","parameters":[{"name":"agent_id","in":"query","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"stage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stage"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/pipeline/{item_id}/stage":{"patch":{"tags":["admin"],"summary":"Update Pipeline Stage","description":"Update the stage of a pipeline item.","operationId":"update_pipeline_stage_api_admin_pipeline__item_id__stage_patch","parameters":[{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateStageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/stats":{"get":{"tags":["admin"],"summary":"Get Agent Stats","description":"Get aggregated stats for an agent.","operationId":"get_agent_stats_api_admin_stats_get","parameters":[{"name":"agent_id","in":"query","required":true,"schema":{"type":"string","title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/templates":{"post":{"tags":["admin"],"summary":"Create Template","description":"Create a new email template.","operationId":"create_template_api_admin_templates_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTemplateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Templates","description":"List email templates for an agent.","operationId":"list_templates_api_admin_templates_get","parameters":[{"name":"agent_id","in":"query","required":true,"schema":{"type":"string","title":"Agent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/templates/{template_id}":{"get":{"tags":["admin"],"summary":"Get Template","description":"Get a specific email template by ID.","operationId":"get_template_api_admin_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/quotes":{"post":{"tags":["quotes"],"summary":"Create Quote","description":"Create a new quote.","operationId":"create_quote_api_admin_quotes_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["quotes"],"summary":"List Quotes","description":"List quotes for an agent, optionally filtered by status.","operationId":"list_quotes_api_admin_quotes_get","parameters":[{"name":"agent_id","in":"query","required":true,"schema":{"type":"string","title":"Agent Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/quotes/{quote_id}":{"get":{"tags":["quotes"],"summary":"Get Quote","description":"Get a specific quote by ID.","operationId":"get_quote_api_admin_quotes__quote_id__get","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["quotes"],"summary":"Update Quote","description":"Update a quote.","operationId":"update_quote_api_admin_quotes__quote_id__patch","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateQuoteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["quotes"],"summary":"Delete Quote","description":"Delete a quote.","operationId":"delete_quote_api_admin_quotes__quote_id__delete","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/quotes/{quote_id}/status":{"patch":{"tags":["quotes"],"summary":"Update Quote Status","description":"Update quote status.","operationId":"update_quote_status_api_admin_quotes__quote_id__status_patch","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateQuoteStatusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/quotes/{quote_id}/items":{"post":{"tags":["quotes"],"summary":"Add Quote Item","description":"Add an item to a quote.","operationId":"add_quote_item_api_admin_quotes__quote_id__items_post","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateQuoteItemRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["quotes"],"summary":"List Quote Items","description":"List items for a quote.","operationId":"list_quote_items_api_admin_quotes__quote_id__items_get","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/quotes/{quote_id}/items/{item_id}":{"delete":{"tags":["quotes"],"summary":"Delete Quote Item","description":"Delete a quote item.","operationId":"delete_quote_item_api_admin_quotes__quote_id__items__item_id__delete","parameters":[{"name":"quote_id","in":"path","required":true,"schema":{"type":"string","title":"Quote Id"}},{"name":"item_id","in":"path","required":true,"schema":{"type":"string","title":"Item Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/interactions":{"post":{"tags":["quotes"],"summary":"Create Interaction","description":"Create a customer interaction record.","operationId":"create_interaction_api_admin_interactions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInteractionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/interactions/{customer_id}":{"get":{"tags":["quotes"],"summary":"List Interactions","description":"List interactions for a customer.","operationId":"list_interactions_api_admin_interactions__customer_id__get","parameters":[{"name":"customer_id","in":"path","required":true,"schema":{"type":"string","title":"Customer Id"}},{"name":"interaction_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Interaction Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calendar/search":{"post":{"tags":["calendar"],"summary":"Calendar Search","operationId":"calendar_search_api_calendar_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarSearchParams"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calendar/fares":{"get":{"tags":["calendar"],"summary":"Calendar Fares Get","description":"Fast read-only calendar view of fares for a route + date window.\n\nReturns whatever is already in the cache and kicks off a background\nwarm for misses. The POST /calendar/search SSE endpoint is still\nthe right path when a client wants live scraper results streamed\nas they arrive; this GET exists for the marketing calendar page\nwhich needs a snappy non-streaming response and is happy to show\ndashes for uncached days.\n\nResponse shape matches what the Next.js route at\napps/web/src/app/api/calendar/fares/route.ts already expects, so\nthe web side is a pure pass-through.","operationId":"calendar_fares_get_api_calendar_fares_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Origin"}},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Destination"}},{"name":"start_date","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Start Date"}},{"name":"end_date","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"End Date"}},{"name":"cabin","in":"query","required":false,"schema":{"type":"string","default":"economy","title":"Cabin"}},{"name":"search_type","in":"query","required":false,"schema":{"type":"string","default":"both","title":"Search Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/advanced/self-connect":{"post":{"tags":["advanced"],"summary":"Self Connect Search","operationId":"self_connect_search_api_advanced_self_connect_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfConnectRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/advanced/prediction":{"post":{"tags":["advanced"],"summary":"Fare Prediction","operationId":"fare_prediction_api_advanced_prediction_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/advanced/geo-price":{"post":{"tags":["advanced"],"summary":"Geo Pricing","operationId":"geo_pricing_api_advanced_geo_price_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeoPricingRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/api-keys":{"post":{"tags":["api-keys"],"summary":"Create Api Key","description":"Generate a new API key for the user.\n\nThe raw key is returned **once** in the response and is never stored.\nOnly a SHA-256 hash is persisted.","operationId":"create_api_key_api_api_keys_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["api-keys"],"summary":"List Api Keys","description":"List API keys for a user (prefix only, not full key).\n\nReturns all active (non-revoked) keys belonging to the user.","operationId":"list_api_keys_api_api_keys_get","parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/api-keys/{key_id}":{"delete":{"tags":["api-keys"],"summary":"Revoke Api Key","description":"Revoke an API key.\n\nSets ``revoked_at`` and ``is_active = false``. The key can no longer\nbe used for authentication.","operationId":"revoke_api_key_api_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/marketing/featured-deals":{"get":{"tags":["marketing"],"summary":"List Featured Deals","description":"Return featured deals from published, non-expired catalogs.\n\nPublic endpoint — no authentication required.","operationId":"list_featured_deals_api_marketing_featured_deals_get","parameters":[{"name":"deal_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by deal type (flight, hotel, package, all_inclusive, cruise)","title":"Deal Type"},"description":"Filter by deal type (flight, hotel, package, all_inclusive, cruise)"},{"name":"origin","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by origin IATA code","title":"Origin"},"description":"Filter by origin IATA code"},{"name":"destination","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by destination IATA code or city","title":"Destination"},"description":"Filter by destination IATA code or city"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page (max 100)","default":20,"title":"Page Size"},"description":"Results per page (max 100)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeaturedDealsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/marketing/featured-deals/{deal_id}":{"get":{"tags":["marketing"],"summary":"Get Featured Deal","description":"Return a single featured deal and increment its click count.\n\nPublic endpoint — no authentication required.\nReturns 404 if the deal does not exist or its catalog is not published.","operationId":"get_featured_deal_api_marketing_featured_deals__deal_id__get","parameters":[{"name":"deal_id","in":"path","required":true,"schema":{"type":"string","title":"Deal Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeaturedDealResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/marketing/catalogs/{catalog_id}/deals":{"get":{"tags":["marketing"],"summary":"List Catalog Deals","description":"Return all deals in a specific published catalog, ordered by sort_order.\n\nPublic endpoint — no authentication required.\nReturns 404 if the catalog does not exist or is not published.","operationId":"list_catalog_deals_api_marketing_catalogs__catalog_id__deals_get","parameters":[{"name":"catalog_id","in":"path","required":true,"schema":{"type":"string","title":"Catalog Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogDealsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flight-status/booking/{booking_id}":{"get":{"tags":["flight-status"],"summary":"Flight Status By Booking","description":"Get flight statuses for all flights in a booking.\n\nReturns an array of flight status objects — one per segment in the\nitinerary (outbound, return, connections).  Results are cached for\n5 minutes.","operationId":"flight_status_by_booking_api_flight_status_booking__booking_id__get","parameters":[{"name":"booking_id","in":"path","required":true,"schema":{"type":"string","title":"Booking Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flight-status/{flight_number}":{"get":{"tags":["flight-status"],"summary":"Flight Status","description":"Get real-time status for a flight number (e.g., DL123).\n\nOptionally pass `date` (YYYY-MM-DD) to target a specific flight date.\nFalls back to mock data when no AviationStack API key is configured.\n\nReturns: flight number, airline, origin/destination airports,\nscheduled/actual times, gate, terminal, delay minutes, delay reason,\nand overall status (scheduled | active | landed | cancelled | diverted).","operationId":"flight_status_api_flight_status__flight_number__get","parameters":[{"name":"flight_number","in":"path","required":true,"schema":{"type":"string","title":"Flight Number"}},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flight-status/{flight_number}/history":{"get":{"tags":["flight-status"],"summary":"Flight Status History","description":"Get recent status change history for a flight (from cache).\n\nReturns timestamped snapshots showing how the flight status evolved,\nuseful for building a delay timeline.","operationId":"flight_status_history_api_flight_status__flight_number__history_get","parameters":[{"name":"flight_number","in":"path","required":true,"schema":{"type":"string","title":"Flight Number"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/live-activity/dispatch-changes":{"post":{"tags":["live-activity"],"summary":"Dispatch Changes","description":"Sweep every active Live Activity, push when status changed.\n\nReturns counts: {checked, dispatched, skipped_unchanged, skipped_unconfigured, errors}.","operationId":"dispatch_changes_api_live_activity_dispatch_changes_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Dispatch Changes Api Live Activity Dispatch Changes Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/mistake-fares":{"get":{"tags":["mistake-fares"],"summary":"List Mistake Fares","description":"Return currently detected mistake fares (>60% below 30-day average).\n\nQueries the ``fare_history`` Postgres table populated by the fare\nscheduler.  Returns routes where the latest observed price is more than\n60% below the rolling 30-day average, sorted by discount percentage.","operationId":"list_mistake_fares_api_mistake_fares_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/mistake-fares/check":{"get":{"tags":["mistake-fares"],"summary":"Check Mistake Fare","description":"Check whether a specific fare is anomalously cheap for its route.\n\nCompares ``price`` against the 30-day average from the ``fare_history``\nPostgres table.  Returns an anomaly dict when the price is >40% below\nthe average (minimum 5 data points required), or ``{\"is_anomaly\": false}``\notherwise.","operationId":"check_mistake_fare_api_mistake_fares_check_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Origin"}},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Destination"}},{"name":"price","in":"query","required":true,"schema":{"type":"number","exclusiveMinimum":0,"title":"Price"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mistake-fares":{"get":{"tags":["mistake-fares"],"summary":"List Redis Mistake Fares","description":"Return top real-time mistake fares (and optionally deals) from Redis fare history.\n\nScans the Redis sorted sets built by live search results\n(``fare_history:{origin}:{destination}:{cabin_class}``).  For each\ntracked route the latest observed price is scored against the median\nof the cached history.  Routes where the latest fare is >50% below\nthe median are classified as ``\"mistake_fare\"`` and always returned.\n\nWhen ``include_deals=true``, routes labeled ``\"deal\"`` (>30% below\nmedian) are also surfaced. This is what the marketing site's\n``/api/deals/mistake-fares`` proxy requests so users see something\nwhenever any route is meaningfully discounted, not only when there\nis a dramatic 50%+ outlier.\n\nUnlike the Postgres-backed ``/mistake-fares`` endpoint, this endpoint\nreflects fares seen in the current search session and updates in\nnear-real-time as users run searches.\n\nReturns at most ``limit`` results (default 10, max 50), sorted by\n``savings_pct`` descending across both labels.","operationId":"list_redis_mistake_fares_api_v1_mistake_fares_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"include_deals","in":"query","required":false,"schema":{"type":"boolean","description":"When true, also include routes labeled 'deal' (>30% below median) alongside 'mistake_fare' (>50% below). Default false preserves the prior contract for existing callers.","default":false,"title":"Include Deals"},"description":"When true, also include routes labeled 'deal' (>30% below median) alongside 'mistake_fare' (>50% below). Default false preserves the prior contract for existing callers."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flights/multi-city-offer":{"post":{"tags":["multi-city"],"summary":"Multi City Combined Offer","description":"Build ONE bookable Duffel offer covering all legs (single-PNR multi-city).\n\nReturns {available, offer_id, total_amount, currency}. On any failure\n(invalid legs, no combined offer, Duffel error) returns available:false so\nthe web layer cleanly falls back to per-leg one-way booking.","operationId":"multi_city_combined_offer_api_flights_multi_city_offer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CombinedOfferRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/multi-city":{"post":{"tags":["multi-city"],"summary":"Search Multi City","description":"Search for multi-city flights across all legs concurrently.\n\nReturns an SSE stream with per-leg results and positioning suggestions.","operationId":"search_multi_city_api_search_multi_city_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultiCitySearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/visa/check":{"get":{"tags":["visa"],"summary":"Visa Check","description":"Check visa requirements for a specific passport-country / destination pair.\n\nReturns the requirement type (visa_free, e_visa, eta, visa_required),\nmaximum stay in days (if applicable), and any notes.","operationId":"visa_check_api_visa_check_get","parameters":[{"name":"passport","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"description":"Passport country ISO code (e.g. US)","title":"Passport"},"description":"Passport country ISO code (e.g. US)"},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"description":"Destination country ISO code (e.g. JP)","title":"Destination"},"description":"Destination country ISO code (e.g. JP)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/visa/requirements":{"get":{"tags":["visa"],"summary":"Visa Requirements","description":"Return all destination visa requirements for a given passport country.","operationId":"visa_requirements_api_visa_requirements_get","parameters":[{"name":"passport","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":2,"description":"Passport country ISO code","title":"Passport"},"description":"Passport country ISO code"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/visa/countries":{"get":{"tags":["visa"],"summary":"Visa Countries","description":"Return all supported countries in the visa database.\n\nUseful for populating dropdown selectors in the UI.","operationId":"visa_countries_api_visa_countries_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/carbon/estimate":{"get":{"tags":["carbon"],"summary":"Carbon Estimate","description":"Estimate carbon emissions for a flight route.\n\nReturns CO2 in kg, offset price in USD, and tree-planting equivalent.\nUses Haversine great-circle distance and ICAO emissions factors.","operationId":"carbon_estimate_api_carbon_estimate_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":4,"description":"Origin IATA airport code (e.g. JFK)","title":"Origin"},"description":"Origin IATA airport code (e.g. JFK)"},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":4,"description":"Destination IATA airport code (e.g. LHR)","title":"Destination"},"description":"Destination IATA airport code (e.g. LHR)"},{"name":"cabin","in":"query","required":false,"schema":{"type":"string","description":"Cabin class: economy, premium_economy, business, first","default":"economy","title":"Cabin"},"description":"Cabin class: economy, premium_economy, business, first"},{"name":"passengers","in":"query","required":false,"schema":{"type":"integer","maximum":9,"minimum":1,"description":"Number of passengers","default":1,"title":"Passengers"},"description":"Number of passengers"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/carbon/airports":{"get":{"tags":["carbon"],"summary":"Carbon Airports","description":"Return all supported airports for carbon calculations.\n\nUseful for validating airport codes in the UI before making an estimate.","operationId":"carbon_airports_api_carbon_airports_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/search/awards":{"post":{"tags":["award-search"],"summary":"Search Awards","description":"Search all airline award pages for availability.\n\nScrapes United, Delta, and American Airlines award search pages\nin parallel.  Returns combined, deduplicated results sorted by\nmiles cost (ascending).","operationId":"search_awards_api_search_awards_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AwardSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AwardSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/awards/deals":{"post":{"tags":["award-search"],"summary":"Search Awards As Deals","description":"Search award availability and return results as ``FlightDeal`` objects.\n\nThis endpoint returns the same shape as the main flight search,\nmaking it easy to merge award and cash results on the frontend.","operationId":"search_awards_as_deals_api_search_awards_deals_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FlightDeal"},"type":"array","title":"Response Search Awards As Deals Api Search Awards Deals Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health/scrapers/dashboard":{"get":{"tags":["scraper-health"],"summary":"Get Scraper Dashboard","description":"Return comprehensive dashboard data for ALL scrapers.\n\nMerges the static registry (89 loyalty programs) with live Redis\nhealth metrics.  Used by the admin Scrapers dashboard page.","operationId":"get_scraper_dashboard_api_health_scrapers_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health/scrapers/registry":{"get":{"tags":["scraper-health"],"summary":"Get Scraper Registry","description":"Return the raw scraper registry with all program metadata.","operationId":"get_scraper_registry_api_health_scrapers_registry_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health/scrapers":{"get":{"tags":["scraper-health"],"summary":"Get All Scraper Health","description":"Return health stats for all scrapers that have recorded data.","operationId":"get_all_scraper_health_api_health_scrapers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health/scrapers/{provider}":{"get":{"tags":["scraper-health"],"summary":"Get Scraper Health","description":"Return health stats for a specific scraper provider.","operationId":"get_scraper_health_api_health_scrapers__provider__get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health/scrapers/{provider}/reset":{"delete":{"tags":["scraper-health"],"summary":"Reset Scraper Health","description":"Clear the health history for a provider.\n\nUsed when the circuit breaker has tripped based on stale failures\n(e.g., after fixing a misconfigured secret). Removes the Redis list\n`scraper_health:{provider}` so the next attempt starts fresh and the\ncircuit breaker gives the provider another chance.","operationId":"reset_scraper_health_api_health_scrapers__provider__reset_delete","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health/scrapers/{provider}/test":{"post":{"tags":["scraper-health"],"summary":"Test Scraper","description":"Trigger a quick test scrape for the given provider.\n\nThis is a lightweight health probe -- it records the result in the\nhealth metrics so the dashboard updates in real time.","operationId":"test_scraper_api_health_scrapers__provider__test_post","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/health/proxy":{"get":{"tags":["scraper-health"],"summary":"Get Proxy Health","description":"Return health and usage statistics for all configured proxies.\n\nShows per-proxy success rate, cooldown status, request counts, and\nlast success/failure timestamps.  Returns an empty list when no\nproxies are configured (PROXY_ENABLED=false).","operationId":"get_proxy_health_api_health_proxy_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/checkout/navigate":{"post":{"tags":["checkout"],"summary":"Navigate To Checkout","description":"Navigate to the checkout page for a specific deal.\n\nRequires Pro+ tier (agent booking method).  Acquires a browser from\nthe pool, navigates through the carrier's booking flow, and returns\nthe checkout URL with a screenshot.","operationId":"navigate_to_checkout_api_checkout_navigate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutNavigateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutNavigateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/checkout/fill-passenger":{"post":{"tags":["checkout"],"summary":"Fill Passenger","description":"Fill passenger information on the checkout page.\n\nRequires an active browser session (from a prior /navigate call).\nPro+ tier required.","operationId":"fill_passenger_api_checkout_fill_passenger_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FillPassengerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FillPassengerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/checkout/get-url":{"post":{"tags":["checkout"],"summary":"Get Checkout Url","description":"Get the checkout redirect URL without navigating.\n\nAvailable to all tiers (free users get affiliate redirect).\nExtracts the booking URL from the deal data or constructs one\nfrom the provider + deal information.","operationId":"get_checkout_url_api_checkout_get_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetUrlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetUrlResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/selectors/{provider}":{"get":{"tags":["selectors"],"summary":"Get Provider Selectors","description":"Return the current selector config for *provider*.","operationId":"get_provider_selectors_api_admin_selectors__provider__get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["selectors"],"summary":"Update Provider Selectors","description":"Replace the selector config for *provider*.\n\nThe new config takes effect immediately (cache is invalidated).","operationId":"update_provider_selectors_api_admin_selectors__provider__put","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelectorUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/selectors/{provider}/test":{"post":{"tags":["selectors"],"summary":"Test Provider Selectors","description":"Test the current selectors for *provider* by running a quick scrape.\n\nReturns which selectors matched and which did not.","operationId":"test_provider_selectors_api_admin_selectors__provider__test_post","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/selectors/cache/invalidate":{"post":{"tags":["selectors"],"summary":"Invalidate Selector Cache","description":"Force-invalidate the in-memory selector cache for all providers.","operationId":"invalidate_selector_cache_api_admin_selectors_cache_invalidate_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/memberships/link":{"post":{"tags":["memberships","memberships"],"summary":"Link Membership","description":"Encrypt and store portal credentials, then verify login works.","operationId":"link_membership_api_memberships_link_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/memberships/unlink":{"post":{"tags":["memberships","memberships"],"summary":"Unlink Membership","description":"Delete a stored membership.","operationId":"unlink_membership_api_memberships_unlink_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlinkRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/memberships/list":{"get":{"tags":["memberships","memberships"],"summary":"List Memberships","description":"List user's linked memberships (no credentials returned).","operationId":"list_memberships_api_memberships_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/memberships/verify":{"post":{"tags":["memberships","memberships"],"summary":"Verify Membership","description":"Test that stored login credentials still work.","operationId":"verify_membership_api_memberships_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cards/featured":{"get":{"tags":["cards"],"summary":"Get Featured Cards","description":"Return the editorial shelf of top travel credit cards.\n\nCapped at 20 to keep the payload light even if the curated list\ngrows — the mobile shelf renders 6-8 tiles, web Suggested Places\nuses the top 1.","operationId":"get_featured_cards_api_cards_featured_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":8,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cards/recommend":{"post":{"tags":["cards"],"summary":"Recommend Cards","description":"Recommend optimal credit cards based on spending patterns and travel goals.\n\nReturns top 10 cards ranked by estimated first-year value.","operationId":"recommend_cards_api_cards_recommend_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecommendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cards/all":{"get":{"tags":["cards"],"summary":"Get All Cards","description":"Return all credit cards in the database.","operationId":"get_all_cards_api_cards_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllCardsResponse"}}}}}}},"/api/cards/{card_id}":{"get":{"tags":["cards"],"summary":"Get Card","description":"Return a single credit card by ID.","operationId":"get_card_api_cards__card_id__get","parameters":[{"name":"card_id","in":"path","required":true,"schema":{"type":"string","title":"Card Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seats/{offer_id}":{"get":{"tags":["seats"],"summary":"Get Seat Map","description":"Return the seat map for a flight offer.\n\nPrimary path hits Duffel's `/air/seat_maps` so the UI renders the\nactual aircraft layout with real per-seat pricing. If Duffel\nreturns no map (common for LCCs + some US carriers), we fall back\nto our deterministic mock so the picker still renders.\n\nOpen to guests — the seat map isn't user-specific and a shopper\nneeds to see it before signing up.","operationId":"get_seat_map_api_seats__offer_id__get","parameters":[{"name":"offer_id","in":"path","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seats/select":{"post":{"tags":["seats"],"summary":"Select Seats","description":"Select seats for one or more passengers in a booking.\n\nEach selection must include a passenger_id, row, and column. The\nendpoint validates seat availability against the mock seat map and\nreturns confirmed assignments with total fees.","operationId":"select_seats_api_seats_select_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatSelectionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/baggage/{offer_id}":{"get":{"tags":["baggage"],"summary":"Get Baggage Options","description":"Return available baggage options and pricing for a flight offer.\n\nOptions include personal item (free), carry-on ($0-35), first checked\nbag ($30-60), and second checked bag ($40-75). Pricing varies by cabin\nclass — business and first class include more bags at no extra cost.","operationId":"get_baggage_options_api_baggage__offer_id__get","parameters":[{"name":"offer_id","in":"path","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/baggage/select":{"post":{"tags":["baggage"],"summary":"Select Baggage","description":"Add baggage selections to a booking for one or more passengers.\n\nValidates the selected baggage types against available options and\nreturns confirmed items with total fees.","operationId":"select_baggage_api_baggage_select_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BaggageSelectionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/redemption-charts":{"get":{"tags":["redemption-charts"],"summary":"List Redemption Charts","description":"Return redemption chart data with optional filters and CPP estimates.","operationId":"list_redemption_charts_api_redemption_charts_get","parameters":[{"name":"airline","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by airline name (case-insensitive partial match)","title":"Airline"},"description":"Filter by airline name (case-insensitive partial match)"},{"name":"origin_region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by origin region","title":"Origin Region"},"description":"Filter by origin region"},{"name":"destination_region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by destination region","title":"Destination Region"},"description":"Filter by destination region"},{"name":"cabin_class","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by cabin class: economy, premium-economy, business, first","title":"Cabin Class"},"description":"Filter by cabin class: economy, premium-economy, business, first"},{"name":"is_saver","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter saver (true) vs standard/dynamic (false) awards","title":"Is Saver"},"description":"Filter saver (true) vs standard/dynamic (false) awards"},{"name":"program","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by program name","title":"Program"},"description":"Filter by program name"},{"name":"sweet_spots_only","in":"query","required":false,"schema":{"type":"boolean","description":"Only return sweet-spot redemptions","default":false,"title":"Sweet Spots Only"},"description":"Only return sweet-spot redemptions"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max rows to return","default":200,"title":"Limit"},"description":"Max rows to return"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Pagination offset","default":0,"title":"Offset"},"description":"Pagination offset"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/redemption-charts/{airline}":{"get":{"tags":["redemption-charts"],"summary":"Get Airline Charts","description":"Return all charts for a given airline, with sweet-spot recommendations.","operationId":"get_airline_charts_api_redemption_charts__airline__get","parameters":[{"name":"airline","in":"path","required":true,"schema":{"type":"string","title":"Airline"}},{"name":"cabin_class","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cabin Class"}},{"name":"origin_region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin Region"}},{"name":"destination_region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination Region"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/sms-reply":{"post":{"tags":["customer-service"],"summary":"Cs Sms Reply","description":"One-shot chat for SMS / email adapters.\n\nInternal endpoint — guarded by `X-Internal-Key` (the Vercel proxy\nsets this; external callers can't reach it). Body is a plain JSON\ndict with `message`, optional `from_phone`, `from_email`,\n`conversation_id`, `customer_id`. Returns the text response so the\ncaller (e.g. the Twilio webhook handler) can drop it straight into\na TwiML reply or a Resend send.","operationId":"cs_sms_reply_api_cs_sms_reply_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Request"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/chat":{"post":{"tags":["customer-service"],"summary":"Cs Chat","description":"Send a message to the AI customer service agent.\n\nReturns a Server-Sent Events stream with the agent's response,\nincluding any tool calls (flight searches, booking lookups, etc).","operationId":"cs_chat_api_cs_chat_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/tickets":{"get":{"tags":["customer-service"],"summary":"Get Tickets","description":"List the authenticated user's support tickets.","operationId":"get_tickets_api_cs_tickets_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["customer-service"],"summary":"Create Ticket Endpoint","description":"Create a support ticket directly (not through AI chat).","operationId":"create_ticket_endpoint_api_cs_tickets_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTicketRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/tickets/all":{"get":{"tags":["customer-service"],"summary":"Get All Tickets","description":"List all tickets (admin/agent only).","operationId":"get_all_tickets_api_cs_tickets_all_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/tickets/escalated":{"get":{"tags":["customer-service"],"summary":"Get Escalated","description":"Get all escalated tickets requiring human attention.","operationId":"get_escalated_api_cs_tickets_escalated_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cs/escalate":{"post":{"tags":["customer-service"],"summary":"Escalate Endpoint","description":"Escalate a conversation to a human agent.","operationId":"escalate_endpoint_api_cs_escalate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EscalateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/conversations":{"get":{"tags":["customer-service"],"summary":"Get Conversations","description":"List the authenticated user's conversations.","operationId":"get_conversations_api_cs_conversations_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/conversations/all":{"get":{"tags":["customer-service"],"summary":"Get All Conversations","description":"List all conversations (admin/agent only).","operationId":"get_all_conversations_api_cs_conversations_all_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/conversations/{conversation_id}/messages":{"get":{"tags":["customer-service"],"summary":"Get Messages","description":"Get all messages in a conversation.","operationId":"get_messages_api_cs_conversations__conversation_id__messages_get","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/conversations/{conversation_id}/resolve":{"post":{"tags":["customer-service"],"summary":"Resolve Endpoint","description":"Resolve a conversation and optionally submit a satisfaction rating.","operationId":"resolve_endpoint_api_cs_conversations__conversation_id__resolve_post","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/conversations/{conversation_id}/agent-message":{"post":{"tags":["customer-service"],"summary":"Agent Message Endpoint","description":"Human agent sends a message in a conversation (admin/agent only).","operationId":"agent_message_endpoint_api_cs_conversations__conversation_id__agent_message_post","parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentMessageRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/canned-responses":{"get":{"tags":["customer-service"],"summary":"Canned Responses Endpoint","description":"Get canned response library.","operationId":"canned_responses_endpoint_api_cs_canned_responses_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/stats":{"get":{"tags":["customer-service"],"summary":"Cs Stats","description":"Get customer service stats (admin/agent only).","operationId":"cs_stats_api_cs_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/cs/transcribe":{"post":{"tags":["customer-service"],"summary":"Transcribe Audio","description":"Transcribe an audio file to text using Claude's vision/audio capabilities.\n\nAccepts audio/ogg, audio/mp4, audio/mpeg, audio/wav, audio/webm.\nReturns {\"transcript\": \"<text>\", \"language\": \"<detected_lang>\", \"confidence\": <0-1>}.\n\nThis endpoint is called by the WhatsApp webhook to transcribe voice messages\nbefore routing them to the CS agent.","operationId":"transcribe_audio_api_cs_transcribe_post","parameters":[{"name":"language","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO language code hint (e.g. 'en', 'es')","title":"Language"},"description":"ISO language code hint (e.g. 'en', 'es')"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_transcribe_audio_api_cs_transcribe_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/knowledge-base":{"get":{"tags":["customer-service"],"summary":"List Knowledge Base","description":"List knowledge base entries, optionally filtered by category.","operationId":"list_knowledge_base_api_cs_knowledge_base_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cs/knowledge-base/search":{"post":{"tags":["customer-service"],"summary":"Search Knowledge Base","description":"Search KB entries by keyword overlap.","operationId":"search_knowledge_base_api_cs_knowledge_base_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KBSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/concierge/quote":{"post":{"tags":["concierge"],"summary":"Quote Concierge Booking","description":"Preview the price — no side effects. Rejects sources the agent\ndoesn't know how to complete so the web never renders a broken CTA.","operationId":"quote_concierge_booking_api_concierge_quote_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConciergeQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConciergeQuoteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/concierge/book":{"post":{"tags":["concierge"],"summary":"Book Concierge","description":"Create the booking record. Assumes Stripe already collected payment —\nwe verify the PaymentIntent is 'succeeded' before writing anything.","operationId":"book_concierge_api_concierge_book_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConciergeBookRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConciergeBookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/concierge/agent-callback":{"post":{"tags":["concierge"],"summary":"Concierge Agent Callback","description":"Receive terminal-state callback from services/agent-service.\n\nCloses #2567. The agent-service POSTs here once a Playwright booking\nrun hits completed / failed / cancelled / awaiting_approval. We\nverify the HMAC signature, then:\n\n  - Update the concierge_bookings row's status + provider fields\n  - On `completed`: persist provider_confirmation_code +\n    provider_booking_url + agent_completed_at\n  - On `failed`: capture error_message for support triage\n  - In all cases: stamp updated_at so dashboards show fresh state\n\nUser-facing notifications (confirmation email + SMS) are NOT fired\nfrom this route — they live in the existing notification cron so\nwe don't double-send when the cron retries a callback that\nalready landed.","operationId":"concierge_agent_callback_api_concierge_agent_callback_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/fares/history":{"get":{"tags":["fares"],"summary":"Get Fare History","description":"Cheapest observed fare per day for a route over the last ``days``.\n\nAggregates in SQL rather than in Python: ``fare_history`` holds one row per\n(route, departure_date, cabin_class, sample) so a single day can carry many\nrows, and the chart wants one point per day.","operationId":"get_fare_history_api_fares_history_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Origin"}},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Destination"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":180,"minimum":7,"default":90,"title":"Days"}},{"name":"currency","in":"query","required":false,"schema":{"type":"string","minLength":3,"maxLength":3,"default":"USD","title":"Currency"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FareHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trending":{"get":{"tags":["trending"],"summary":"Get Trending Routes","description":"Return the most-searched routes, optionally filtered to a recent time window.","operationId":"get_trending_routes_api_trending_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"hours","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":720,"minimum":1},{"type":"null"}],"title":"Hours"}},{"name":"origin","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":3,"maxLength":3},{"type":"null"}],"description":"3-letter IATA code. When set, returns only routes departing from this airport — used by the mobile Popular Routes shelf to personalise to the user's typed FROM airport.","title":"Origin"},"description":"3-letter IATA code. When set, returns only routes departing from this airport — used by the mobile Popular Routes shelf to personalise to the user's typed FROM airport."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrendingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/deals/feed":{"get":{"tags":["deals"],"summary":"Get Deal Feed","description":"Return the aggregated deal feed with filtering, sorting, and pagination.\n\nPublic endpoint — no authentication required. Results are cached for 15 minutes.","operationId":"get_deal_feed_api_deals_feed_get","parameters":[{"name":"origin","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by origin IATA code","title":"Origin"},"description":"Filter by origin IATA code"},{"name":"destination","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by destination IATA code","title":"Destination"},"description":"Filter by destination IATA code"},{"name":"program","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by points program name","title":"Program"},"description":"Filter by points program name"},{"name":"deal_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by deal type: flight, hotel, card, points, package","title":"Deal Type"},"description":"Filter by deal type: flight, hotel, card, points, package"},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","description":"Sort field: savings_pct, price, expiration","default":"savings_pct","title":"Sort By"},"description":"Sort field: savings_pct, price, expiration"},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"description":"Page number","default":1,"title":"Page"},"description":"Page number"},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Results per page (max 100)","default":50,"title":"Page Size"},"description":"Results per page (max 100)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealFeedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/deals/feed/hot":{"get":{"tags":["deals"],"summary":"Get Hot Deals","description":"Return only hot deals (>30% below average fare).\n\nPublic endpoint. These are the deals worthy of push notifications.","operationId":"get_hot_deals_api_deals_feed_hot_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":20,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DealFeedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/deals/content-ideas":{"get":{"tags":["deals"],"summary":"Get Content Ideas","description":"Return trending content ideas for the blog content calendar.\n\nDerived from TPG and other travel content sites.","operationId":"get_content_ideas_api_deals_content_ideas_get","parameters":[{"name":"topic","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by topic: destination, program, strategy, news","title":"Topic"},"description":"Filter by topic: destination, program, strategy, news"},{"name":"status","in":"query","required":false,"schema":{"type":"string","description":"Filter by status: new, planned, published, rejected","default":"new","title":"Status"},"description":"Filter by status: new, planned, published, rejected"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentIdeasResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/deals/feed/refresh":{"post":{"tags":["deals"],"summary":"Refresh Deal Feed","description":"Force-refresh the deal feed and content ideas.\n\nAdmin endpoint — triggers a fresh scrape of all sources.","operationId":"refresh_deal_feed_api_deals_feed_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshResponse"}}}}}}},"/api/deals/military":{"get":{"tags":["deals"],"summary":"Get Military Deals","description":"Return a curated list of military travel deals.\n\nAuth-gated so we can track verified-military accounts separately\nonce the ID.me/GovX flow lands — the current payload is the same\nstatic list everyone sees, but wiring JWT here now means we won't\nneed a breaking change to start personalising the feed later.\n\nCache: 10 min at the edge. The list only updates when we ship new\ndeal copy, so a long TTL keeps this endpoint cheap.","operationId":"get_military_deals_api_deals_military_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilitaryDealsResponse"}}}}}}},"/api/availability":{"get":{"tags":["availability"],"summary":"Get Availability","description":"Get cached award availability for a route and date.\n\nReturns instant results from the scanner cache (sub-100ms).\nIf no cached data exists, triggers a background scan and returns\nan empty result set with ``scan_triggered=true``.","operationId":"get_availability_api_availability_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"description":"IATA origin code","title":"Origin"},"description":"IATA origin code"},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"description":"IATA destination code","title":"Destination"},"description":"IATA destination code"},{"name":"date","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Departure date YYYY-MM-DD","title":"Date"},"description":"Departure date YYYY-MM-DD"},{"name":"cabin","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"economy, business, first, premium_economy","title":"Cabin"},"description":"economy, business, first, premium_economy"},{"name":"program","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by program name","title":"Program"},"description":"Filter by program name"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/availability/calendar":{"get":{"tags":["availability"],"summary":"Get Availability Calendar","description":"Get award availability calendar for a month.\n\nReturns the best (lowest miles) option per day, useful for\nrendering a color-coded calendar view.","operationId":"get_availability_calendar_api_availability_calendar_get","parameters":[{"name":"origin","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Origin"}},{"name":"destination","in":"query","required":true,"schema":{"type":"string","minLength":3,"maxLength":3,"title":"Destination"}},{"name":"month","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$","description":"Month YYYY-MM","title":"Month"},"description":"Month YYYY-MM"},{"name":"cabin","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cabin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/availability/routes":{"get":{"tags":["availability"],"summary":"Get Routes For Program","description":"Get all routes with availability for a specific program.","operationId":"get_routes_for_program_api_availability_routes_get","parameters":[{"name":"program","in":"query","required":true,"schema":{"type":"string","description":"Program name (e.g. 'United MileagePlus')","title":"Program"},"description":"Program name (e.g. 'United MileagePlus')"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoutesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/availability/scan":{"post":{"tags":["availability"],"summary":"Trigger Scan","description":"Admin endpoint: trigger a manual availability scan.\n\nCan scan a specific route or popular routes by tier.","operationId":"trigger_scan_api_availability_scan_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/availability/stats":{"get":{"tags":["availability"],"summary":"Get Scanner Stats","description":"Get scanner health statistics.","operationId":"get_scanner_stats_api_availability_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScannerStats"}}}}}}},"/api/flights/{offer_id}":{"get":{"tags":["flights"],"summary":"Get Flight Offer","description":"Return a cached flight offer by its ``offer_id``.\n\nLookup order:\n  1. Redis ``flight_offer:<offer_id>`` — populated by the search\n     pipeline with a short TTL.\n  2. Redis ``flight_offer:stale:<offer_id>`` — shadow copy with a\n     longer TTL so a recently-expired offer can still display\n     (the client re-checks the real price before any booking\n     action, so a slightly stale cache entry is not dangerous).\n  3. 404 with ``retry_search=true`` — the mobile client knows\n     how to interpret this and falls back to the search flow.\n\nKept open (no auth guard) so share-links work without forcing a\nlogin first; the offer payload itself contains no PII.","operationId":"get_flight_offer_api_flights__offer_id__get","parameters":[{"name":"offer_id","in":"path","required":true,"schema":{"type":"string","title":"Offer Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Flight Offer Api Flights  Offer Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/ops/seed-fare-history":{"post":{"tags":["admin-ops"],"summary":"Seed Fare History Endpoint","description":"Populate ``fare_history`` with baseline rows so the mistake-fare\ndetector has enough history (>=5 observations per route) to fire.\n\nIdempotent — re-running is safe; rows tagged ``source='seed_baseline_v1'``\nare skipped via a ``NOT EXISTS`` guard.","operationId":"seed_fare_history_endpoint_api_admin_ops_seed_fare_history_post","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":30,"title":"Days"}},{"name":"route","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[A-Z]{3}-[A-Z]{3}$"},{"type":"null"}],"title":"Route"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/ops/seed-featured-deals":{"post":{"tags":["admin-ops"],"summary":"Seed Featured Deals Endpoint","description":"One-shot populate ``featured_deals`` from live Duffel offers.\n\nRuns the full fare-sampler cycle including the seed pass, regardless\nof the ``ENABLE_FEATURED_DEALS_SEED`` env flag on the worker. Useful\nfor warming prod on first boot or after the ``featured_deals`` table\ngets cleared.","operationId":"seed_featured_deals_endpoint_api_admin_ops_seed_featured_deals_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user/push-token":{"get":{"tags":["push"],"summary":"Get Push Token","description":"Return the most recently registered token for the caller, 404 if none.","operationId":"get_push_token_api_user_push_token_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushTokenResponse"}}}}}},"post":{"tags":["push"],"summary":"Register Push Token","description":"Upsert an Expo push token for the signed-in user.","operationId":"register_push_token_api_user_push_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterTokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user/push-test":{"post":{"tags":["push"],"summary":"Test Push","description":"Send a test notification to every token on file for the caller.","operationId":"test_push_api_user_push_test_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestPushResponse"}}}}}}},"/api/user/notification-preferences":{"get":{"tags":["push"],"summary":"Get Notification Prefs","description":"Read persisted toggles — defaults returned when no row exists yet.","operationId":"get_notification_prefs_api_user_notification_preferences_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}}}}},"post":{"tags":["push"],"summary":"Set Notification Prefs","description":"Full replace — echoes back the persisted row.","operationId":"set_notification_prefs_api_user_notification_preferences_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["push"],"summary":"Patch Notification Prefs","description":"Partial update — merges the patch onto the existing row (or defaults).","operationId":"patch_notification_prefs_api_user_notification_preferences_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefsPatch"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationPrefs"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watches/scan":{"post":{"tags":["push"],"summary":"Trigger Watches Scan","description":"On-demand price-drop scan for the signed-in user.\n\nWraps ``watch_monitor.scan_and_notify_user`` so the mobile app can\nforce a re-check from the Watchlist screen. Fails open — any\ntransport / DB error returns a zeroed summary rather than surfacing\na 5xx to the user.","operationId":"trigger_watches_scan_api_watches_scan_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WatchesScanResponse"}}}}}}},"/api/blog/posts":{"get":{"tags":["blog"],"summary":"List Posts","description":"Paginated list of blog posts (newest-first).\n\nThe snapshot is already sorted by ``published_at`` descending, so\nslicing is enough. Category filter is an equality match on the\ncategory slug (``points``, ``deals``, ``guides``, etc).","operationId":"list_posts_api_blog_posts_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category slug","title":"Category"},"description":"Filter by category slug"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Posts Api Blog Posts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/blog/posts/{slug}":{"get":{"tags":["blog"],"summary":"Get Post","description":"Full post — returns everything including the HTML ``content`` body.","operationId":"get_post_api_blog_posts__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Post Api Blog Posts  Slug  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/sms/send":{"post":{"tags":["auth-2fa"],"summary":"Send Sms Code","description":"Issue a 6-digit code and SMS it via Twilio.","operationId":"send_sms_code_api_auth_2fa_sms_send_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmsSendRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/sms/verify":{"post":{"tags":["auth-2fa"],"summary":"Verify Sms Code","description":"Check a 6-digit code against Redis. Marks profiles.phone_verified on success.","operationId":"verify_sms_code_api_auth_2fa_sms_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmsVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/2fa/email/send":{"post":{"tags":["auth-2fa"],"summary":"Send Email Code","description":"Send a 6-digit code to the user's email (Resend).","operationId":"send_email_code_api_auth_2fa_email_send_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendResponse"}}}}}}},"/api/auth/2fa/email/verify":{"post":{"tags":["auth-2fa"],"summary":"Verify Email Code","description":"Check an emailed code. Sets profiles.email_verified_2fa on success.","operationId":"verify_email_code_api_auth_2fa_email_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user/verify-military":{"post":{"tags":["user"],"summary":"Verify Military","description":"Mark the caller as military or kick off the external verify flow.\n\n* ``self-attest`` — immediately sets ``is_military = true``. We do\n  this even without third-party proof because some travel providers\n  (airlines, hotels) accept self-certification at checkout; any\n  discount that actually requires documentation is enforced at\n  booking time, not here.\n* ``govx`` / ``id.me`` — flips ``pending_verification = true`` and\n  returns the redirect URL for the client to open. A subsequent\n  webhook (handled elsewhere) flips ``is_military = true`` once\n  verification succeeds.","operationId":"verify_military_api_user_verify_military_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyMilitaryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyMilitaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/email-hook":{"post":{"tags":["auth-hooks"],"summary":"Supabase Email Hook","description":"Receive a Supabase Auth email event and deliver it via Resend.\n\nRaw-body validation is mandatory — Pydantic parses from ``request.json()``\nbut HMAC needs the exact bytes Supabase signed, so we read ``.body()``\nonce and parse downstream.","operationId":"supabase_email_hook_api_auth_email_hook_post","parameters":[{"name":"Webhook-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook-Id"}},{"name":"Webhook-Signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailHookResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/phone/start":{"post":{"tags":["auth-phone"],"summary":"Start Phone Otp","description":"Issue a Twilio Verify SMS challenge for ``phone``.","operationId":"start_phone_otp_api_auth_phone_start_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneStartRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/phone/verify":{"post":{"tags":["auth-phone"],"summary":"Verify Phone Otp","description":"Verify the Twilio code, then sign the user in / up via Supabase.","operationId":"verify_phone_otp_api_auth_phone_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneVerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/health":{"get":{"tags":["credential-health"],"summary":"Get Credential Health","description":"Return comprehensive health summary for all credential pools.\n\nShows: active/locked/cooldown credentials per program, success rates,\nlast use timestamps, and cooldown expiry times.","operationId":"get_credential_health_api_admin_credentials_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/credentials/health/{program}":{"get":{"tags":["credential-health"],"summary":"Get Program Credential Health","description":"Return health status for a specific program's credential pool.","operationId":"get_program_credential_health_api_admin_credentials_health__program__get","parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string","title":"Program"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/sessions":{"get":{"tags":["credential-health"],"summary":"Get Active Sessions","description":"Return all active authenticated sessions.\n\nShows session age, expiry, health check status, cookie counts.\nDoes NOT expose cookie values or session tokens.","operationId":"get_active_sessions_api_admin_credentials_sessions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/credentials/rotate":{"post":{"tags":["credential-health"],"summary":"Force Rotate Credentials","description":"Force rotation for a program's credential pool.\n\nResets cooldowns on all credentials and advances the round-robin\npointer.  Use when a credential is stuck in cooldown and you want\nto manually unstick the pool.","operationId":"force_rotate_credentials_api_admin_credentials_rotate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceRotateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/add":{"post":{"tags":["credential-health"],"summary":"Add Credential","description":"Add a credential to the rotation pool.\n\nThe username and password are encrypted at rest using AES-256.\nMultiple credentials can be added per program for rotation.","operationId":"add_credential_api_admin_credentials_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddCredentialRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/{program}/{credential_id}":{"delete":{"tags":["credential-health"],"summary":"Remove Credential","description":"Remove a credential from the rotation pool.","operationId":"remove_credential_api_admin_credentials__program___credential_id__delete","parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string","title":"Program"}},{"name":"credential_id","in":"path","required":true,"schema":{"type":"string","title":"Credential Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/rate-limits":{"get":{"tags":["credential-health"],"summary":"Get Rate Limit Status","description":"Return rate limit status for all programs and credentials.","operationId":"get_rate_limit_status_api_admin_credentials_rate_limits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/credentials/rate-limits/{program}":{"get":{"tags":["credential-health"],"summary":"Get Program Rate Limit Status","description":"Return rate limit status for a specific program.","operationId":"get_program_rate_limit_status_api_admin_credentials_rate_limits__program__get","parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string","title":"Program"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/rate-limits/reset":{"post":{"tags":["credential-health"],"summary":"Reset Rate Limits","description":"Reset rate limit counters for a program or specific credential.","operationId":"reset_rate_limits_api_admin_credentials_rate_limits_reset_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetRateLimitRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/sessions/{program}":{"delete":{"tags":["credential-health"],"summary":"Invalidate Program Sessions","description":"Invalidate all cached sessions for a program.\n\nForces fresh logins on the next scrape for this program.","operationId":"invalidate_program_sessions_api_admin_credentials_sessions__program__delete","parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string","title":"Program"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/credentials/sessions/{program}/{credential_id}":{"delete":{"tags":["credential-health"],"summary":"Invalidate Credential Session","description":"Invalidate the cached session for a specific credential.","operationId":"invalidate_credential_session_api_admin_credentials_sessions__program___credential_id__delete","parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string","title":"Program"}},{"name":"credential_id","in":"path","required":true,"schema":{"type":"string","title":"Credential Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/rankings":{"get":{"tags":["seo-tracking"],"summary":"Get Rankings","description":"Get current keyword rankings with position change from previous check.","operationId":"get_rankings_api_seo_rankings_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by keyword category","title":"Category"},"description":"Filter by keyword category"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/rankings/history":{"get":{"tags":["seo-tracking"],"summary":"Get Rankings History","description":"Get historical ranking data for sparklines and trend charts.","operationId":"get_rankings_history_api_seo_rankings_history_get","parameters":[{"name":"keyword_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by keyword ID","title":"Keyword Id"},"description":"Filter by keyword ID"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Number of days of history","default":30,"title":"Days"},"description":"Number of days of history"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/traffic":{"get":{"tags":["seo-tracking"],"summary":"Get Traffic Overview","description":"Organic traffic overview aggregated from ranking data.","operationId":"get_traffic_overview_api_seo_traffic_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/competitors":{"get":{"tags":["seo-tracking"],"summary":"Get Competitors","description":"Competitor ranking comparison for tracked keywords.","operationId":"get_competitors_api_seo_competitors_get","parameters":[{"name":"keyword_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Keyword Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/health":{"get":{"tags":["seo-tracking"],"summary":"Get Health Checks","description":"Get latest technical SEO health check results.","operationId":"get_health_checks_api_seo_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/seo/keywords":{"post":{"tags":["seo-tracking"],"summary":"Manage Keywords","description":"Add a new keyword to track.","operationId":"manage_keywords_api_seo_keywords_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeywordCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["seo-tracking"],"summary":"List Keywords","description":"List all tracked keywords.","operationId":"list_keywords_api_seo_keywords_get","parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/seo/keywords/{keyword_id}":{"delete":{"tags":["seo-tracking"],"summary":"Delete Keyword","description":"Deactivate a tracked keyword (soft delete).","operationId":"delete_keyword_api_seo_keywords__keyword_id__delete","parameters":[{"name":"keyword_id","in":"path","required":true,"schema":{"type":"string","title":"Keyword Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/voice/actions/propose":{"post":{"tags":["voice-agent"],"summary":"Voice Propose Action","description":"Verify the caller and stage an action WITHOUT performing it.","operationId":"voice_propose_action_api_voice_actions_propose_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposeActionInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/voice/actions/execute":{"post":{"tags":["voice-agent"],"summary":"Voice Execute Action","description":"Perform an action the caller has just confirmed out loud.","operationId":"voice_execute_action_api_voice_actions_execute_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteActionInput"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AddActivityRequest":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"day":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Day"},"estimated_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Estimated Cost"},"created_by":{"type":"string","title":"Created By","default":""}},"type":"object","required":["name"],"title":"AddActivityRequest"},"AddComponentRequest":{"properties":{"type":{"type":"string","title":"Type"},"supplier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supplier"},"cost_to_agency":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cost To Agency"},"retail_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Retail Price"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["type"],"title":"AddComponentRequest"},"AddCredentialRequest":{"properties":{"credential_id":{"type":"string","title":"Credential Id"},"program":{"type":"string","title":"Program"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["credential_id","program","username","password"],"title":"AddCredentialRequest"},"AddExpenseRequest":{"properties":{"paid_by":{"type":"string","title":"Paid By"},"amount":{"type":"number","title":"Amount"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"split_type":{"type":"string","title":"Split Type","default":"equal"},"currency":{"type":"string","title":"Currency","default":"USD"},"split_details":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Split Details"}},"type":"object","required":["paid_by","amount"],"title":"AddExpenseRequest"},"AddTravelerRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","title":"Role","default":"traveler"}},"type":"object","required":["user_id"],"title":"AddTravelerRequest"},"AgentMessageRequest":{"properties":{"content":{"type":"string","title":"Content"}},"type":"object","required":["content"],"title":"AgentMessageRequest"},"AlertResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"max_price":{"type":"number","title":"Max Price"},"cabin_class":{"type":"string","title":"Cabin Class"},"notification_email":{"type":"string","title":"Notification Email"}},"type":"object","required":["id","user_id","origin","destination","max_price","cabin_class","notification_email"],"title":"AlertResponse"},"AllCardsResponse":{"properties":{"cards":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Cards"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["cards","count"],"title":"AllCardsResponse"},"ApiKeyResponse":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"prefix":{"type":"string","title":"Prefix"},"name":{"type":"string","title":"Name"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"rate_limit":{"type":"integer","title":"Rate Limit"}},"type":"object","required":["id","key","prefix","name","scopes","rate_limit"],"title":"ApiKeyResponse","description":"Returned when a new key is created -- ``key`` is shown only once."},"AvailabilityResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/AvailabilityResult"},"type":"array","title":"Results"},"total":{"type":"integer","title":"Total"},"cached":{"type":"boolean","title":"Cached","default":true},"scan_triggered":{"type":"boolean","title":"Scan Triggered","default":false}},"type":"object","required":["results","total"],"title":"AvailabilityResponse","description":"Response for the availability endpoint."},"AvailabilityResult":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"date":{"type":"string","title":"Date"},"program":{"type":"string","title":"Program"},"cabin_class":{"type":"string","title":"Cabin Class"},"available":{"type":"boolean","title":"Available"},"miles_cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Miles Cost"},"taxes_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Taxes Usd"},"seats_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats Remaining"},"source":{"type":"string","title":"Source","default":""},"flight_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flight Number"},"departure_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Departure Time"},"arrival_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Arrival Time"},"duration_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Minutes"},"award_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Award Type"},"transfer_partners":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transfer Partners"},"scanned_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scanned At"}},"type":"object","required":["origin","destination","date","program","cabin_class","available"],"title":"AvailabilityResult","description":"Single award availability record."},"AwardOption":{"properties":{"programName":{"type":"string","title":"Programname"},"pointsRequired":{"type":"integer","title":"Pointsrequired"},"cppValue":{"type":"number","title":"Cppvalue"},"transferFrom":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transferfrom"},"transferRatio":{"type":"number","title":"Transferratio","default":1.0},"transferPartners":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transferpartners"},"isTransferPartner":{"type":"boolean","title":"Istransferpartner","default":false},"cabinClass":{"type":"string","title":"Cabinclass","default":"economy"},"estimated":{"type":"boolean","title":"Estimated","default":false}},"type":"object","required":["programName","pointsRequired","cppValue"],"title":"AwardOption","description":"Single loyalty-program redemption option. Serialized with camelCase keys."},"AwardResult":{"properties":{"program":{"type":"string","title":"Program"},"miles":{"type":"integer","title":"Miles"},"taxes_usd":{"type":"number","title":"Taxes Usd"},"cabin":{"type":"string","title":"Cabin"},"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"date":{"type":"string","title":"Date"},"source":{"type":"string","title":"Source"},"award_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Award Type"},"departure_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Departure Time"},"flight_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flight Number"},"seats_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats Remaining"},"transfer_partners":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transfer Partners"}},"type":"object","required":["program","miles","taxes_usd","cabin","origin","destination","date","source"],"title":"AwardResult","description":"Single award availability result."},"AwardSearchRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin","description":"IATA airport code"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination","description":"IATA airport code"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"cabin_class":{"type":"string","title":"Cabin Class","description":"economy | premium_economy | business | first","default":"economy"}},"type":"object","required":["origin","destination","departure_date"],"title":"AwardSearchRequest","description":"Input for the award search endpoint."},"AwardSearchResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/AwardResult"},"type":"array","title":"Results"},"total":{"type":"integer","title":"Total"},"sources_searched":{"items":{"type":"string"},"type":"array","title":"Sources Searched"}},"type":"object","required":["results","total","sources_searched"],"title":"AwardSearchResponse","description":"Combined results from all award scrapers."},"BaggageSelectionRequest":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Id"},"selections":{"items":{"$ref":"#/components/schemas/PassengerBaggageSelection"},"type":"array","title":"Selections","description":"Per-passenger list of selected baggage types"}},"type":"object","required":["offer_id","selections"],"title":"BaggageSelectionRequest"},"BaggageType":{"type":"string","enum":["personal_item","carry_on","checked_1","checked_2"],"title":"BaggageType"},"Body_transcribe_audio_api_cs_transcribe_post":{"properties":{"audio":{"type":"string","contentMediaType":"application/octet-stream","title":"Audio"}},"type":"object","required":["audio"],"title":"Body_transcribe_audio_api_cs_transcribe_post"},"BookingRequest":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"passengers":{"items":{"$ref":"#/components/schemas/PassengerInfo"},"type":"array","title":"Passengers"},"payment_amount":{"type":"string","title":"Payment Amount"},"payment_currency":{"type":"string","title":"Payment Currency","default":"USD"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"seat_service_ids":{"items":{"type":"string"},"type":"array","title":"Seat Service Ids"}},"type":"object","required":["offer_id","passengers","payment_amount"],"title":"BookingRequest"},"BookingSelection":{"properties":{"legs":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Legs"},"total_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total Price"},"arrival_window_minutes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Arrival Window Minutes"}},"type":"object","required":["legs"],"title":"BookingSelection","description":"Host's chosen combo from the agent recommendations."},"BuyCalcRequest":{"properties":{"buy_rate_cents":{"type":"number","title":"Buy Rate Cents"},"redemption_cpp":{"type":"number","title":"Redemption Cpp"}},"type":"object","required":["buy_rate_cents","redemption_cpp"],"title":"BuyCalcRequest"},"CabinClass":{"type":"string","enum":["economy","premium_economy","business","first"],"title":"CabinClass"},"CalendarDay":{"properties":{"date":{"type":"string","title":"Date"},"program":{"type":"string","title":"Program"},"cabin_class":{"type":"string","title":"Cabin Class"},"miles_cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Miles Cost"},"taxes_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Taxes Usd"},"seats_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats Remaining"},"available":{"type":"boolean","title":"Available","default":false},"award_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Award Type"}},"type":"object","required":["date","program","cabin_class"],"title":"CalendarDay","description":"Single day in the calendar view."},"CalendarResponse":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"month":{"type":"string","title":"Month"},"days":{"items":{"$ref":"#/components/schemas/CalendarDay"},"type":"array","title":"Days"},"total_available_days":{"type":"integer","title":"Total Available Days"}},"type":"object","required":["origin","destination","month","days","total_available_days"],"title":"CalendarResponse","description":"Month calendar view response."},"CalendarSearchParams":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"target_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Target Date"},"days_before":{"type":"integer","maximum":30.0,"minimum":0.0,"title":"Days Before","default":30},"days_after":{"type":"integer","maximum":30.0,"minimum":0.0,"title":"Days After","default":30},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"},"search_type":{"$ref":"#/components/schemas/SearchType","default":"both"}},"type":"object","required":["origin","destination","target_date"],"title":"CalendarSearchParams"},"CarBookingRequest":{"properties":{"offer_token":{"type":"string","title":"Offer Token"},"driver_first_name":{"type":"string","title":"Driver First Name"},"driver_last_name":{"type":"string","title":"Driver Last Name"},"driver_email":{"type":"string","title":"Driver Email"},"driver_phone":{"type":"string","title":"Driver Phone"},"driver_dob":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Driver Dob"},"driver_license":{"type":"string","title":"Driver License"},"license_country":{"type":"string","title":"License Country"},"stripe_payment_intent_id":{"type":"string","title":"Stripe Payment Intent Id"},"add_insurance":{"type":"boolean","title":"Add Insurance","default":false},"add_gps":{"type":"boolean","title":"Add Gps","default":false},"add_price_protection":{"type":"boolean","title":"Add Price Protection","default":false},"provider":{"type":"string","title":"Provider"},"vehicle_name":{"type":"string","title":"Vehicle Name"},"pickup_location":{"type":"string","title":"Pickup Location"},"dropoff_location":{"type":"string","title":"Dropoff Location"},"pickup_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Pickup Date"},"dropoff_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Dropoff Date"},"total_price":{"type":"number","title":"Total Price"},"currency":{"type":"string","title":"Currency","default":"USD"},"booking_url":{"type":"string","title":"Booking Url"},"vehicle_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vehicle Type"},"nationality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nationality"}},"type":"object","required":["offer_token","driver_first_name","driver_last_name","driver_email","driver_phone","driver_dob","driver_license","license_country","stripe_payment_intent_id","provider","vehicle_name","pickup_location","dropoff_location","pickup_date","dropoff_date","total_price","booking_url"],"title":"CarBookingRequest"},"CarDriver":{"properties":{"given_name":{"type":"string","title":"Given Name"},"family_name":{"type":"string","title":"Family Name"},"born_on":{"type":"string","title":"Born On"},"license_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Number"},"license_issuing_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Issuing Country"},"license_expires_on":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"License Expires On"}},"type":"object","required":["given_name","family_name","born_on"],"title":"CarDriver"},"CarDuffelBookingRequest":{"properties":{"quote_id":{"type":"string","title":"Quote Id"},"email":{"type":"string","title":"Email"},"phone_number":{"type":"string","title":"Phone Number"},"driver":{"$ref":"#/components/schemas/CarDriver"},"loyalty":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Loyalty"}},"type":"object","required":["quote_id","email","phone_number","driver"],"title":"CarDuffelBookingRequest"},"CarPaymentIntentRequest":{"properties":{"pickup_date":{"type":"string","title":"Pickup Date"},"dropoff_date":{"type":"string","title":"Dropoff Date"},"add_insurance":{"type":"boolean","title":"Add Insurance","default":false},"add_gps":{"type":"boolean","title":"Add Gps","default":false},"add_price_protection":{"type":"boolean","title":"Add Price Protection","default":false},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"}},"type":"object","required":["pickup_date","dropoff_date"],"title":"CarPaymentIntentRequest"},"CarQuoteRequest":{"properties":{"rate_id":{"type":"string","title":"Rate Id"}},"type":"object","required":["rate_id"],"title":"CarQuoteRequest"},"CarSearchRequest":{"properties":{"pickup_location":{"type":"string","title":"Pickup Location"},"dropoff_location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dropoff Location"},"pickup_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Pickup Date"},"dropoff_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Dropoff Date"},"vehicle_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vehicle Type"}},"type":"object","required":["pickup_location","pickup_date","dropoff_date"],"title":"CarSearchRequest"},"CardRecommendation":{"properties":{"card_id":{"type":"string","title":"Card Id"},"card_name":{"type":"string","title":"Card Name"},"issuer":{"type":"string","title":"Issuer"},"program":{"type":"string","title":"Program"},"annual_fee":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Annual Fee"},"annual_points":{"type":"integer","title":"Annual Points"},"point_value_cpp":{"type":"number","title":"Point Value Cpp"},"annual_value":{"type":"number","title":"Annual Value"},"signup_bonus_value":{"type":"number","title":"Signup Bonus Value"},"perks_value":{"type":"number","title":"Perks Value"},"total_first_year_value":{"type":"number","title":"Total First Year Value"},"total_ongoing_value":{"type":"number","title":"Total Ongoing Value"},"top_categories":{"items":{"$ref":"#/components/schemas/CategoryInfo"},"type":"array","title":"Top Categories"},"transfer_partners":{"items":{"$ref":"#/components/schemas/TransferPartnerInfo"},"type":"array","title":"Transfer Partners"},"recommendation_reason":{"type":"string","title":"Recommendation Reason"}},"type":"object","required":["card_id","card_name","issuer","program","annual_fee","annual_points","point_value_cpp","annual_value","signup_bonus_value","perks_value","total_first_year_value","total_ongoing_value","top_categories","transfer_partners","recommendation_reason"],"title":"CardRecommendation"},"CastVoteRequest":{"properties":{"option_id":{"type":"string","title":"Option Id"},"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["option_id","user_id"],"title":"CastVoteRequest"},"CatalogDealsResponse":{"properties":{"catalog_name":{"type":"string","title":"Catalog Name"},"catalog_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Catalog Description"},"deals":{"items":{"$ref":"#/components/schemas/FeaturedDealResponse"},"type":"array","title":"Deals"}},"type":"object","required":["catalog_name","deals"],"title":"CatalogDealsResponse"},"CategoryInfo":{"properties":{"category":{"type":"string","title":"Category"},"rate":{"type":"number","title":"Rate"},"annual_points":{"type":"integer","title":"Annual Points"}},"type":"object","required":["category","rate","annual_points"],"title":"CategoryInfo"},"ChangeRequestSlice":{"properties":{"slice_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slice Id"},"remove":{"type":"boolean","title":"Remove","default":false},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"departure_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Departure Date"},"cabin_class":{"type":"string","title":"Cabin Class","default":"economy"}},"type":"object","title":"ChangeRequestSlice","description":"One slice in a change request — either remove an existing leg\n(`remove: True`) or add a new one. Duffel requires both sides:\nevery original slice must be kept or removed, and every new leg\nmust be explicitly added."},"ChatRequest":{"properties":{"message":{"type":"string","title":"Message"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id"}},"type":"object","required":["message"],"title":"ChatRequest"},"CheckoutNavigateRequest":{"properties":{"provider":{"type":"string","title":"Provider","description":"Provider ID e.g. 'google_flights', 'united_awards'"},"deal_data":{"additionalProperties":true,"type":"object","title":"Deal Data","description":"Deal data including deal_index, booking_url, etc."},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id","description":"User ID to load passenger data (optional)"}},"type":"object","required":["provider","deal_data"],"title":"CheckoutNavigateRequest","description":"Request to navigate to an airline checkout page."},"CheckoutNavigateResponse":{"properties":{"url":{"type":"string","title":"Url"},"status":{"type":"string","title":"Status"},"screenshot":{"type":"string","title":"Screenshot","default":""},"can_book":{"type":"boolean","title":"Can Book","default":false},"page_title":{"type":"string","title":"Page Title","default":""},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["url","status"],"title":"CheckoutNavigateResponse","description":"Response from checkout navigation."},"CombinedOfferLeg":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"}},"type":"object","required":["origin","destination","departure_date"],"title":"CombinedOfferLeg"},"CombinedOfferRequest":{"properties":{"legs":{"items":{"$ref":"#/components/schemas/CombinedOfferLeg"},"type":"array","title":"Legs"},"passengers":{"type":"integer","maximum":9.0,"minimum":1.0,"title":"Passengers","default":1},"cabin_class":{"type":"string","title":"Cabin Class","default":"economy"}},"type":"object","required":["legs"],"title":"CombinedOfferRequest"},"ConciergeBookRequest":{"properties":{"source":{"type":"string","title":"Source"},"provider_price":{"type":"number","minimum":0.0,"title":"Provider Price"},"deal_snapshot":{"additionalProperties":true,"type":"object","title":"Deal Snapshot","description":"Full serialized deal (origin, destination, dates, URL, etc.)"},"traveler_first_name":{"type":"string","title":"Traveler First Name"},"traveler_last_name":{"type":"string","title":"Traveler Last Name"},"traveler_email":{"type":"string","title":"Traveler Email"},"traveler_phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Traveler Phone"},"stripe_payment_intent_id":{"type":"string","title":"Stripe Payment Intent Id"}},"type":"object","required":["source","provider_price","deal_snapshot","traveler_first_name","traveler_last_name","traveler_email","stripe_payment_intent_id"],"title":"ConciergeBookRequest"},"ConciergeBookResponse":{"properties":{"booking_id":{"type":"string","title":"Booking Id"},"confirmation_code":{"type":"string","title":"Confirmation Code"},"status":{"type":"string","title":"Status"},"total_charged":{"type":"number","title":"Total Charged"},"pointify_revenue":{"type":"number","title":"Pointify Revenue"},"agent_session_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Session Id"}},"type":"object","required":["booking_id","confirmation_code","status","total_charged","pointify_revenue"],"title":"ConciergeBookResponse"},"ConciergeQuoteRequest":{"properties":{"source":{"type":"string","title":"Source","description":"e.g. 'google_flights_scraper'"},"provider_price":{"type":"number","minimum":0.0,"title":"Provider Price"}},"type":"object","required":["source","provider_price"],"title":"ConciergeQuoteRequest","description":"Minimal fields needed to compute a price — no PII, safely cacheable."},"ConciergeQuoteResponse":{"properties":{"provider_price":{"type":"number","title":"Provider Price"},"markup_amount":{"type":"number","title":"Markup Amount"},"flat_fee":{"type":"number","title":"Flat Fee"},"total_charged":{"type":"number","title":"Total Charged"},"pointify_revenue":{"type":"number","title":"Pointify Revenue"}},"type":"object","required":["provider_price","markup_amount","flat_fee","total_charged","pointify_revenue"],"title":"ConciergeQuoteResponse"},"ConfirmChangeInput":{"properties":{"selected_order_change_offer_id":{"type":"string","title":"Selected Order Change Offer Id"}},"type":"object","required":["selected_order_change_offer_id"],"title":"ConfirmChangeInput"},"ConfirmMemberRequest":{"properties":{"origin_iata":{"type":"string","maxLength":3,"minLength":3,"title":"Origin Iata"},"preferred_depart":{"type":"string","title":"Preferred Depart"},"preferred_return":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preferred Return"},"preferred_cabin":{"type":"string","title":"Preferred Cabin","default":"economy"},"passenger_info":{"additionalProperties":true,"type":"object","title":"Passenger Info"},"loyalty_programs":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Loyalty Programs"}},"type":"object","required":["origin_iata","preferred_depart"],"title":"ConfirmMemberRequest"},"ContentIdeaItem":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"topic":{"type":"string","title":"Topic"},"destination":{"type":"string","title":"Destination","default":""},"program":{"type":"string","title":"Program","default":""},"trending_score":{"type":"number","title":"Trending Score","default":0},"source_url":{"type":"string","title":"Source Url","default":""},"source":{"type":"string","title":"Source","default":""},"status":{"type":"string","title":"Status","default":"new"},"discovered_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Discovered At"}},"type":"object","required":["id","title","topic"],"title":"ContentIdeaItem"},"ContentIdeasResponse":{"properties":{"ideas":{"items":{"$ref":"#/components/schemas/ContentIdeaItem"},"type":"array","title":"Ideas"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["ideas","total"],"title":"ContentIdeasResponse"},"CreateAlertRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"max_price":{"type":"number","exclusiveMinimum":0.0,"title":"Max Price"},"cabin_class":{"type":"string","title":"Cabin Class","default":"economy"},"notification_email":{"type":"string","title":"Notification Email"}},"type":"object","required":["user_id","origin","destination","max_price","notification_email"],"title":"CreateAlertRequest"},"CreateApiKeyRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"rate_limit":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Rate Limit","default":100}},"type":"object","required":["user_id","name"],"title":"CreateApiKeyRequest","description":"Payload for creating a new API key."},"CreateChangeRequestInput":{"properties":{"order_id":{"type":"string","title":"Order Id"},"slices":{"items":{"$ref":"#/components/schemas/ChangeRequestSlice"},"type":"array","title":"Slices"}},"type":"object","required":["order_id","slices"],"title":"CreateChangeRequestInput"},"CreateClientRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"status":{"type":"string","title":"Status","default":"lead"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["agent_id","first_name","last_name"],"title":"CreateClientRequest"},"CreateInteractionRequest":{"properties":{"customer_id":{"type":"string","title":"Customer Id"},"agent_id":{"type":"string","title":"Agent Id"},"type":{"type":"string","title":"Type","default":"note"},"content":{"type":"string","title":"Content"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["customer_id","agent_id","content"],"title":"CreateInteractionRequest"},"CreateNoteRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"type":{"type":"string","title":"Type","default":"general"},"content":{"type":"string","title":"Content"}},"type":"object","required":["agent_id","content"],"title":"CreateNoteRequest"},"CreateOfferInput":{"properties":{"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"departure_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Departure Date"},"return_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return Date"},"passengers":{"anyOf":[{},{"type":"null"}],"title":"Passengers"},"cabin_class":{"type":"string","title":"Cabin Class","default":"economy"},"offer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Offer Id"},"payment_intent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Payment Intent Id"},"seat_service_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Seat Service Ids"}},"type":"object","title":"CreateOfferInput"},"CreatePackageRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"destinations":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Destinations"},"price_per_person":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Per Person"},"min_travelers":{"type":"integer","title":"Min Travelers","default":1},"max_travelers":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Travelers"},"markup_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Markup Pct"}},"type":"object","required":["agent_id","name"],"title":"CreatePackageRequest"},"CreatePipelineRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"trip_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Trip Summary"},"stage":{"type":"string","title":"Stage","default":"inquiry"},"estimated_value":{"type":"number","title":"Estimated Value","default":0},"due_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Due Date"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["agent_id","client_name"],"title":"CreatePipelineRequest"},"CreateQuoteItemRequest":{"properties":{"flight_data":{"additionalProperties":true,"type":"object","title":"Flight Data"},"markup_amount":{"type":"number","title":"Markup Amount","default":0},"base_price":{"type":"number","title":"Base Price","default":0},"total_price":{"type":"number","title":"Total Price","default":0},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["flight_data"],"title":"CreateQuoteItemRequest"},"CreateQuoteRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Id"},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Name"},"customer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Email"},"flights":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Flights","default":[]},"markup":{"type":"number","title":"Markup","default":0},"subtotal":{"type":"number","title":"Subtotal","default":0},"total":{"type":"number","title":"Total","default":0},"currency":{"type":"string","title":"Currency","default":"USD"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"terms":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Terms"},"expiry_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiry Date"},"status":{"type":"string","title":"Status","default":"draft"}},"type":"object","required":["agent_id"],"title":"CreateQuoteRequest"},"CreateTemplateRequest":{"properties":{"agent_id":{"type":"string","title":"Agent Id"},"name":{"type":"string","title":"Name"},"subject":{"type":"string","title":"Subject"},"body":{"type":"string","title":"Body"},"category":{"type":"string","title":"Category","default":"custom"}},"type":"object","required":["agent_id","name","subject","body"],"title":"CreateTemplateRequest"},"CreateTicketRequest":{"properties":{"subject":{"type":"string","title":"Subject"},"description":{"type":"string","title":"Description"},"priority":{"type":"string","title":"Priority","default":"medium"},"category":{"type":"string","title":"Category","default":"other"},"conversation_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conversation Id"}},"type":"object","required":["subject","description"],"title":"CreateTicketRequest"},"CreateVoteRequest":{"properties":{"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"vote_type":{"type":"string","title":"Vote Type","default":"single"},"options":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Options"},"created_by":{"type":"string","title":"Created By"},"closes_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closes At"}},"type":"object","required":["title","options","created_by"],"title":"CreateVoteRequest"},"CruiseSearchRequest":{"properties":{"departure_port":{"type":"string","title":"Departure Port"},"destination":{"type":"string","title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"return_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Return Date"},"cruise_line":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cruise Line"},"travelers":{"type":"integer","maximum":8.0,"minimum":1.0,"title":"Travelers","default":2}},"type":"object","required":["departure_port","destination","departure_date"],"title":"CruiseSearchRequest"},"DealFeedItem":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"origin":{"type":"string","title":"Origin","default":""},"destination":{"type":"string","title":"Destination","default":""},"price_cash":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Cash"},"price_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Points"},"points_program":{"type":"string","title":"Points Program","default":""},"airline":{"type":"string","title":"Airline","default":""},"hotel_chain":{"type":"string","title":"Hotel Chain","default":""},"deal_type":{"type":"string","title":"Deal Type","default":"unknown"},"deal_url":{"type":"string","title":"Deal Url","default":""},"image_url":{"type":"string","title":"Image Url","default":""},"description":{"type":"string","title":"Description","default":""},"savings_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Savings Pct"},"is_hot":{"type":"boolean","title":"Is Hot","default":false},"source":{"type":"string","title":"Source","default":""},"expiration_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiration Date"},"scraped_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scraped At"}},"type":"object","required":["id","title"],"title":"DealFeedItem"},"DealFeedResponse":{"properties":{"deals":{"items":{"$ref":"#/components/schemas/DealFeedItem"},"type":"array","title":"Deals"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"has_next":{"type":"boolean","title":"Has Next"}},"type":"object","required":["deals","total","page","page_size","has_next"],"title":"DealFeedResponse"},"DealSource":{"type":"string","enum":["duffel","seats_aero","google_flights","amex_travel","costco_travel","aaa_travel","sams_club_travel","bjs_travel","chase_ur_portal","amex_mr_portal","arkalis"],"title":"DealSource"},"EligibilityRequest":{"properties":{"programs":{"items":{"type":"string"},"type":"array","title":"Programs"}},"type":"object","required":["programs"],"title":"EligibilityRequest"},"EmailHookResponse":{"properties":{"delivered":{"type":"boolean","title":"Delivered"},"provider":{"type":"string","title":"Provider"}},"type":"object","required":["delivered","provider"],"title":"EmailHookResponse"},"EmailVerifyRequest":{"properties":{"code":{"type":"string","maxLength":10,"minLength":4,"title":"Code"}},"type":"object","required":["code"],"title":"EmailVerifyRequest"},"EscalateRequest":{"properties":{"conversation_id":{"type":"string","title":"Conversation Id"},"reason":{"type":"string","title":"Reason"},"priority":{"type":"string","title":"Priority","default":"medium"}},"type":"object","required":["conversation_id","reason"],"title":"EscalateRequest"},"ExecuteActionInput":{"properties":{"confirmation_token":{"type":"string","title":"Confirmation Token"},"call_id":{"type":"string","title":"Call Id"}},"type":"object","required":["confirmation_token","call_id"],"title":"ExecuteActionInput"},"FareBreakdown":{"properties":{"baseFare":{"type":"number","title":"Basefare"},"taxes":{"type":"number","title":"Taxes"},"surcharges":{"type":"number","title":"Surcharges","default":0},"totalCash":{"type":"number","title":"Totalcash"}},"type":"object","required":["baseFare","taxes","totalCash"],"title":"FareBreakdown","description":"Cash fare decomposition. Serialized with camelCase keys for the frontend."},"FareHistoryPoint":{"properties":{"date":{"type":"string","title":"Date"},"price":{"type":"number","title":"Price"}},"type":"object","required":["date","price"],"title":"FareHistoryPoint"},"FareHistoryResponse":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"days":{"type":"integer","title":"Days"},"currency":{"type":"string","title":"Currency"},"history":{"items":{"$ref":"#/components/schemas/FareHistoryPoint"},"type":"array","title":"History"},"stats":{"anyOf":[{"$ref":"#/components/schemas/FareHistoryStats"},{"type":"null"}]},"source":{"type":"string","title":"Source"}},"type":"object","required":["origin","destination","days","currency","history","stats","source"],"title":"FareHistoryResponse"},"FareHistoryStats":{"properties":{"current":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current"},"avg30d":{"type":"integer","title":"Avg30D"},"avg90d":{"type":"integer","title":"Avg90D"},"min":{"type":"number","title":"Min"},"max":{"type":"number","title":"Max"},"trend":{"type":"string","title":"Trend"},"signal":{"type":"string","title":"Signal"},"pctFromAvg":{"type":"integer","title":"Pctfromavg"}},"type":"object","required":["current","avg30d","avg90d","min","max","trend","signal","pctFromAvg"],"title":"FareHistoryStats"},"FeaturedDealResponse":{"properties":{"id":{"type":"string","title":"Id"},"catalog_id":{"type":"string","title":"Catalog Id"},"deal_type":{"type":"string","title":"Deal Type"},"title":{"type":"string","title":"Title"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"destination":{"type":"string","title":"Destination"},"departure_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Departure Date"},"return_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return Date"},"original_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Original Price"},"deal_price":{"type":"number","title":"Deal Price"},"currency":{"type":"string","title":"Currency","default":"USD"},"savings_percent":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Savings Percent"},"cabin_class":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cabin Class"},"hotel_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hotel Name"},"hotel_stars":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hotel Stars"},"nights":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nights"},"includes":{"items":{"type":"string"},"type":"array","title":"Includes","default":[]},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"},"booking_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Url"},"sort_order":{"type":"integer","title":"Sort Order","default":0},"is_featured":{"type":"boolean","title":"Is Featured","default":false},"click_count":{"type":"integer","title":"Click Count","default":0},"conversion_count":{"type":"integer","title":"Conversion Count","default":0},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","catalog_id","deal_type","title","destination","deal_price","created_at"],"title":"FeaturedDealResponse"},"FeaturedDealsListResponse":{"properties":{"deals":{"items":{"$ref":"#/components/schemas/FeaturedDealResponse"},"type":"array","title":"Deals"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"}},"type":"object","required":["deals","total","page","page_size"],"title":"FeaturedDealsListResponse"},"FillPassengerRequest":{"properties":{"provider":{"type":"string","title":"Provider"},"session_id":{"type":"string","title":"Session Id","description":"Browser session ID to reuse the page"},"passenger_data":{"additionalProperties":true,"type":"object","title":"Passenger Data","description":"Passenger info: first_name, last_name, email, etc."},"airline":{"type":"string","title":"Airline","description":"Airline identifier for selector lookup"}},"type":"object","required":["provider","session_id","passenger_data","airline"],"title":"FillPassengerRequest","description":"Request to fill passenger info on a checkout page."},"FillPassengerResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"fields_filled":{"type":"integer","title":"Fields Filled"},"fields_failed":{"items":{"type":"string"},"type":"array","title":"Fields Failed","default":[]}},"type":"object","required":["success","fields_filled"],"title":"FillPassengerResponse","description":"Response from passenger form fill."},"FlightDeal":{"properties":{"id":{"type":"string","title":"Id"},"segments":{"items":{"$ref":"#/components/schemas/FlightSegment"},"type":"array","title":"Segments"},"price_cash":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Cash"},"price_currency":{"type":"string","title":"Price Currency","default":"USD"},"price_points":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Points"},"points_program_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Points Program Name"},"cabin_class":{"$ref":"#/components/schemas/CabinClass"},"stops":{"type":"integer","title":"Stops"},"total_duration_minutes":{"type":"integer","title":"Total Duration Minutes"},"source":{"$ref":"#/components/schemas/DealSource"},"deep_link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deep Link"},"seats_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats Remaining"},"price_delta_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Delta Pct"},"fare_breakdown":{"anyOf":[{"$ref":"#/components/schemas/FareBreakdown"},{"type":"null"}]},"award_options":{"anyOf":[{"items":{"$ref":"#/components/schemas/AwardOption"},"type":"array"},{"type":"null"}],"title":"Award Options"},"duffel_offer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Duffel Offer Id"},"bookable":{"type":"boolean","title":"Bookable","default":false},"fare_brand_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fare Brand Name"},"cabin_class_marketing_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cabin Class Marketing Name"},"refundable":{"type":"boolean","title":"Refundable","default":false},"refund_penalty":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refund Penalty"},"changeable":{"type":"boolean","title":"Changeable","default":false},"change_penalty":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Penalty"},"carry_on_included":{"type":"boolean","title":"Carry On Included","default":false},"checked_bags_included":{"type":"integer","title":"Checked Bags Included","default":0},"seat_selection_included":{"type":"boolean","title":"Seat Selection Included","default":false},"alliance":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Alliance"},"earns_redeemable_miles":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Earns Redeemable Miles"},"earns_status_credit":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Earns Status Credit"},"cpp":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cpp"}},"type":"object","required":["id","segments","cabin_class","stops","total_duration_minutes","source"],"title":"FlightDeal"},"FlightSearchRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"return_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Return Date"},"trip_type":{"$ref":"#/components/schemas/TripType","default":"round_trip"},"passengers":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Passengers","default":1},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"},"search_type":{"$ref":"#/components/schemas/SearchType","default":"cash"}},"type":"object","required":["origin","destination","departure_date"],"title":"FlightSearchRequest"},"FlightSegment":{"properties":{"departure_airport":{"type":"string","title":"Departure Airport"},"arrival_airport":{"type":"string","title":"Arrival Airport"},"departure_time":{"type":"string","title":"Departure Time"},"arrival_time":{"type":"string","title":"Arrival Time"},"carrier":{"type":"string","title":"Carrier"},"carrier_name":{"type":"string","title":"Carrier Name"},"flight_number":{"type":"string","title":"Flight Number"},"duration_minutes":{"type":"integer","title":"Duration Minutes"}},"type":"object","required":["departure_airport","arrival_airport","departure_time","arrival_time","carrier","carrier_name","flight_number","duration_minutes"],"title":"FlightSegment"},"ForceRotateRequest":{"properties":{"program":{"type":"string","title":"Program"}},"type":"object","required":["program"],"title":"ForceRotateRequest"},"GenerateInviteCodeRequest":{"properties":{"role":{"type":"string","title":"Role","default":"traveler"},"max_uses":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Uses"},"created_by":{"type":"string","title":"Created By"}},"type":"object","required":["created_by"],"title":"GenerateInviteCodeRequest"},"GenerateShareLinkRequest":{"properties":{"role":{"type":"string","title":"Role","default":"viewer"}},"type":"object","title":"GenerateShareLinkRequest"},"GeoPricingRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"}},"type":"object","required":["origin","destination","departure_date"],"title":"GeoPricingRequest"},"GetUrlRequest":{"properties":{"provider":{"type":"string","title":"Provider"},"deal_data":{"additionalProperties":true,"type":"object","title":"Deal Data"}},"type":"object","required":["provider","deal_data"],"title":"GetUrlRequest","description":"Request to get the checkout redirect URL without navigating."},"GetUrlResponse":{"properties":{"url":{"type":"string","title":"Url"},"provider":{"type":"string","title":"Provider"},"status":{"type":"string","title":"Status","default":"redirect"}},"type":"object","required":["url","provider"],"title":"GetUrlResponse","description":"Response with the checkout redirect URL."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HoldOrderInput":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"passengers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Passengers"},"services":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Services"}},"type":"object","required":["offer_id","passengers"],"title":"HoldOrderInput"},"HoldOrderPayInput":{"properties":{"order_id":{"type":"string","title":"Order Id"},"amount":{"type":"string","title":"Amount"},"currency":{"type":"string","title":"Currency"},"payment_type":{"type":"string","title":"Payment Type","default":"balance"}},"type":"object","required":["order_id","amount","currency"],"title":"HoldOrderPayInput"},"HotelDetailResponse":{"properties":{"hotel_id":{"type":"string","title":"Hotel Id"},"name":{"type":"string","title":"Name"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"rating":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rating"},"stars":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stars"},"amenities":{"items":{"type":"string"},"type":"array","title":"Amenities","default":[]},"images":{"items":{"type":"string"},"type":"array","title":"Images","default":[]},"price_range":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Range"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"source":{"type":"string","title":"Source","default":"google_hotels_scraper"}},"type":"object","required":["hotel_id","name"],"title":"HotelDetailResponse"},"HotelSearchRequest":{"properties":{"destination":{"type":"string","title":"Destination"},"check_in":{"type":"string","title":"Check In"},"check_out":{"type":"string","title":"Check Out"},"guests":{"type":"integer","title":"Guests","default":1},"rooms":{"type":"integer","title":"Rooms","default":1},"search_type":{"type":"string","title":"Search Type","default":"both"}},"type":"object","required":["destination","check_in","check_out"],"title":"HotelSearchRequest"},"InviteRequest":{"properties":{"email":{"type":"string","title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"member_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Name"}},"type":"object","required":["email"],"title":"InviteRequest"},"JoinTripRequest":{"properties":{"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["user_id"],"title":"JoinTripRequest"},"KBSearchRequest":{"properties":{"query":{"type":"string","title":"Query"},"threshold":{"type":"integer","title":"Threshold","default":2},"max_results":{"type":"integer","title":"Max Results","default":3}},"type":"object","required":["query"],"title":"KBSearchRequest"},"KeywordCreate":{"properties":{"keyword":{"type":"string","title":"Keyword"},"target_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Url"},"search_volume":{"type":"integer","title":"Search Volume","default":0},"category":{"type":"string","title":"Category","default":"general"}},"type":"object","required":["keyword"],"title":"KeywordCreate"},"LinkRequest":{"properties":{"membership_type":{"type":"string","pattern":"^(amex_travel|costco_travel|aaa_travel)$","title":"Membership Type"},"username":{"type":"string","minLength":1,"title":"Username"},"password":{"type":"string","minLength":1,"title":"Password"}},"type":"object","required":["membership_type","username","password"],"title":"LinkRequest"},"LoyaltyProgrammeAccount":{"properties":{"airline_iata_code":{"type":"string","title":"Airline Iata Code"},"account_number":{"type":"string","title":"Account Number"}},"type":"object","required":["airline_iata_code","account_number"],"title":"LoyaltyProgrammeAccount","description":"One loyalty-programme account attached to a passenger.\n\nMaps 1:1 to Duffel's passenger `loyalty_programme_accounts[]`\nshape. When present on create_order, Duffel forwards the member\nnumber to the airline so miles credit posts automatically."},"MilitaryDeal":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"provider":{"type":"string","title":"Provider"},"url":{"type":"string","title":"Url"},"savings_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Savings Pct"},"description":{"type":"string","title":"Description","default":""},"subtitle":{"type":"string","title":"Subtitle","default":""}},"type":"object","required":["id","title","provider","url"],"title":"MilitaryDeal"},"MilitaryDealsResponse":{"properties":{"deals":{"items":{"$ref":"#/components/schemas/MilitaryDeal"},"type":"array","title":"Deals"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["deals","total"],"title":"MilitaryDealsResponse"},"MultiCityLeg":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Date"}},"type":"object","required":["origin","destination","date"],"title":"MultiCityLeg"},"MultiCitySearchRequest":{"properties":{"legs":{"items":{"$ref":"#/components/schemas/MultiCityLeg"},"type":"array","maxItems":6,"minItems":2,"title":"Legs"},"passengers":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Passengers","default":1},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"},"search_type":{"$ref":"#/components/schemas/SearchType","default":"both"}},"type":"object","required":["legs"],"title":"MultiCitySearchRequest"},"NotificationPrefs":{"properties":{"price_drops":{"type":"boolean","title":"Price Drops","default":true},"deal_alerts":{"type":"boolean","title":"Deal Alerts","default":true},"trip_updates":{"type":"boolean","title":"Trip Updates","default":true},"account_activity":{"type":"boolean","title":"Account Activity","default":true},"weekly_digest":{"type":"boolean","title":"Weekly Digest","default":false},"marketing_emails":{"type":"boolean","title":"Marketing Emails","default":false}},"type":"object","title":"NotificationPrefs"},"NotificationPrefsPatch":{"properties":{"price_drops":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Price Drops"},"deal_alerts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Deal Alerts"},"trip_updates":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Trip Updates"},"account_activity":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Account Activity"},"weekly_digest":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Weekly Digest"},"marketing_emails":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Marketing Emails"}},"type":"object","title":"NotificationPrefsPatch","description":"Partial update — every field optional so the mobile toggle\ncan PATCH a single key without echoing the whole map back."},"OptimizeRequest":{"properties":{"cash_price":{"type":"number","title":"Cash Price"},"points_options":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Points Options"},"user_balances":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"User Balances","default":[]}},"type":"object","required":["cash_price","points_options"],"title":"OptimizeRequest"},"PassengerBaggageSelection":{"properties":{"passenger_id":{"type":"string","title":"Passenger Id"},"items":{"items":{"$ref":"#/components/schemas/BaggageType"},"type":"array","title":"Items"}},"type":"object","required":["passenger_id","items"],"title":"PassengerBaggageSelection"},"PassengerInfo":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"middle_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Middle Name"},"date_of_birth":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date Of Birth"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"passport_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Passport Number"},"passport_expiry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Passport Expiry"},"passport_country":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Passport Country"},"nationality":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nationality"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"infant_passenger_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Infant Passenger Id"},"known_traveler_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Known Traveler Number"},"redress_number":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redress Number"},"loyalty_programme_accounts":{"items":{"$ref":"#/components/schemas/LoyaltyProgrammeAccount"},"type":"array","title":"Loyalty Programme Accounts"}},"type":"object","required":["first_name","last_name"],"title":"PassengerInfo"},"PathsRequest":{"properties":{"origin":{"type":"string","maxLength":5,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":5,"minLength":3,"title":"Destination"},"depart_date":{"type":"string","title":"Depart Date"},"cabin":{"type":"string","title":"Cabin","default":"economy"},"passengers":{"type":"integer","maximum":9.0,"minimum":1.0,"title":"Passengers","default":1},"carrier_iata":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Carrier Iata"},"cash_price_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cash Price Cents"},"user_balances":{"additionalProperties":{"type":"integer"},"type":"object","title":"User Balances"},"allow_all":{"type":"boolean","title":"Allow All","default":false}},"type":"object","required":["origin","destination","depart_date"],"title":"PathsRequest"},"PhoneStartRequest":{"properties":{"phone":{"type":"string","maxLength":20,"minLength":6,"title":"Phone"}},"type":"object","required":["phone"],"title":"PhoneStartRequest"},"PhoneStartResponse":{"properties":{"sent":{"type":"boolean","title":"Sent"},"challenge_id":{"type":"string","title":"Challenge Id"}},"type":"object","required":["sent","challenge_id"],"title":"PhoneStartResponse"},"PhoneVerifyRequest":{"properties":{"phone":{"type":"string","maxLength":20,"minLength":6,"title":"Phone"},"code":{"type":"string","maxLength":10,"minLength":4,"title":"Code"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["phone","code"],"title":"PhoneVerifyRequest"},"PhoneVerifyResponse":{"properties":{"verified":{"type":"boolean","title":"Verified"},"needs_email":{"type":"boolean","title":"Needs Email","default":false},"is_new_user":{"type":"boolean","title":"Is New User","default":false},"token_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Hash"},"type":{"anyOf":[{"type":"string","enum":["magiclink","signup"]},{"type":"null"}],"title":"Type"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["verified"],"title":"PhoneVerifyResponse"},"Playbook":{"properties":{"preferred_programs":{"items":{"type":"string"},"type":"array","title":"Preferred Programs"},"max_transfer_per_booking":{"type":"integer","title":"Max Transfer Per Booking","default":50000},"require_biometric_over_cents":{"type":"integer","title":"Require Biometric Over Cents","default":10000},"auto_transfer_enabled":{"type":"boolean","title":"Auto Transfer Enabled","default":false},"auto_book_enabled":{"type":"boolean","title":"Auto Book Enabled","default":false},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"Playbook"},"PostBookingServiceInput":{"properties":{"service_id":{"type":"string","title":"Service Id"},"quantity":{"type":"integer","title":"Quantity","default":1},"payment":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payment"}},"type":"object","required":["service_id"],"title":"PostBookingServiceInput"},"PredictionRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"},"current_price":{"type":"number","title":"Current Price"},"carrier":{"anyOf":[{"type":"string","maxLength":3},{"type":"null"}],"title":"Carrier"},"flight_number":{"anyOf":[{"type":"string","maxLength":8},{"type":"null"}],"title":"Flight Number"}},"type":"object","required":["origin","destination","departure_date","current_price"],"title":"PredictionRequest"},"ProposeActionInput":{"properties":{"kind":{"type":"string","title":"Kind"},"booking_reference":{"type":"string","title":"Booking Reference"},"verification_last_name":{"type":"string","title":"Verification Last Name"},"call_id":{"type":"string","title":"Call Id"}},"type":"object","required":["kind","booking_reference","verification_last_name","call_id"],"title":"ProposeActionInput"},"PushTokenResponse":{"properties":{"token":{"type":"string","title":"Token"},"platform":{"type":"string","enum":["ios","android"],"title":"Platform"}},"type":"object","required":["token","platform"],"title":"PushTokenResponse"},"RecommendRequest":{"properties":{"spending":{"additionalProperties":{"type":"number"},"type":"object","title":"Spending","description":"Monthly spend by category (e.g. dining, grocery, flights, hotels, gas, online_shopping, everything_else)","examples":[{"dining":500,"everything_else":300,"flights":200,"gas":150,"grocery":800,"hotels":100}]},"travel_goals":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Travel Goals","description":"Optional travel goals like 'flights_to_japan', 'business_class', 'hotel_in_paris'"},"existing_cards":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Existing Cards","description":"Card IDs the user already has (these will be excluded from recommendations)"}},"type":"object","required":["spending"],"title":"RecommendRequest"},"RecommendResponse":{"properties":{"recommendations":{"items":{"$ref":"#/components/schemas/CardRecommendation"},"type":"array","title":"Recommendations"},"spending_categories":{"items":{"type":"string"},"type":"array","title":"Spending Categories","default":["dining","grocery","flights","hotels","gas","online_shopping","everything_else"]}},"type":"object","required":["recommendations"],"title":"RecommendResponse"},"RefreshResponse":{"properties":{"deals_count":{"type":"integer","title":"Deals Count"},"content_ideas_count":{"type":"integer","title":"Content Ideas Count"},"message":{"type":"string","title":"Message"}},"type":"object","required":["deals_count","content_ideas_count","message"],"title":"RefreshResponse"},"RegisterTokenRequest":{"properties":{"token":{"type":"string","maxLength":512,"minLength":1,"title":"Token"},"platform":{"type":"string","enum":["ios","android"],"title":"Platform"}},"type":"object","required":["token","platform"],"title":"RegisterTokenRequest"},"RegisterTokenResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true}},"type":"object","title":"RegisterTokenResponse"},"ReorderItineraryRequest":{"properties":{"items":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"ReorderItineraryRequest"},"ResetRateLimitRequest":{"properties":{"program":{"type":"string","title":"Program"},"credential_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credential Id"}},"type":"object","required":["program"],"title":"ResetRateLimitRequest"},"ResolveRequest":{"properties":{"rating":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rating"},"feedback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feedback"}},"type":"object","title":"ResolveRequest"},"RevokeResponse":{"properties":{"status":{"type":"string","title":"Status"},"id":{"type":"string","title":"Id"}},"type":"object","required":["status","id"],"title":"RevokeResponse"},"RouteAvailability":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"date":{"type":"string","title":"Date"},"cabin_class":{"type":"string","title":"Cabin Class"},"miles_cost":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Miles Cost"},"seats_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seats Remaining"},"award_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Award Type"}},"type":"object","required":["origin","destination","date","cabin_class"],"title":"RouteAvailability","description":"Route with availability for a program."},"RoutesResponse":{"properties":{"program":{"type":"string","title":"Program"},"routes":{"items":{"$ref":"#/components/schemas/RouteAvailability"},"type":"array","title":"Routes"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["program","routes","total"],"title":"RoutesResponse","description":"All routes with availability for a program."},"ScanRequest":{"properties":{"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tier","description":"hot, warm, standard, or null for all"},"max_routes":{"type":"integer","maximum":500.0,"minimum":1.0,"title":"Max Routes","default":50},"origin":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Origin"},"destination":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Destination"}},"type":"object","title":"ScanRequest","description":"Admin request to trigger a manual scan."},"ScanResponse":{"properties":{"status":{"type":"string","title":"Status"},"routes_scanned":{"type":"integer","title":"Routes Scanned","default":0},"results_found":{"type":"integer","title":"Results Found","default":0},"errors":{"type":"integer","title":"Errors","default":0},"duration_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Duration Seconds"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"route":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Route"}},"type":"object","required":["status"],"title":"ScanResponse","description":"Response from a manual scan trigger."},"ScannerStats":{"properties":{"routes_scanned":{"type":"integer","title":"Routes Scanned","default":0},"results_found":{"type":"integer","title":"Results Found","default":0},"errors":{"type":"integer","title":"Errors","default":0},"last_scan_started":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Last Scan Started"},"last_scan_completed":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Last Scan Completed"}},"type":"object","title":"ScannerStats","description":"Scanner health statistics."},"SearchType":{"type":"string","enum":["cash","points","both"],"title":"SearchType"},"SeatConfirmInput":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"booking_id":{"type":"string","title":"Booking Id"},"passenger_duffel_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Passenger Duffel Ids"}},"type":"object","required":["offer_id","booking_id"],"title":"SeatConfirmInput"},"SeatHoldInput":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"passenger_slot":{"type":"integer","maximum":8.0,"minimum":0.0,"title":"Passenger Slot"},"row":{"type":"integer","title":"Row"},"column":{"type":"string","maxLength":2,"minLength":1,"title":"Column"},"service_id":{"type":"string","title":"Service Id"},"segment_id":{"type":"string","title":"Segment Id"},"price_cents":{"type":"integer","minimum":0.0,"title":"Price Cents","default":0},"currency":{"type":"string","title":"Currency","default":"USD"}},"type":"object","required":["offer_id","passenger_slot","row","column","service_id","segment_id"],"title":"SeatHoldInput"},"SeatReleaseInput":{"properties":{"offer_id":{"type":"string","title":"Offer Id"}},"type":"object","required":["offer_id"],"title":"SeatReleaseInput"},"SeatSelectionRequest":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"booking_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Booking Id"},"selections":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Selections","description":"List of {passenger_id, row, column} selections"}},"type":"object","required":["offer_id","selections"],"title":"SeatSelectionRequest"},"SeatUpgrade":{"properties":{"label":{"type":"string","title":"Label"},"price":{"type":"number","title":"Price"},"currency":{"type":"string","title":"Currency","default":"USD"},"serviceId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serviceid"}},"type":"object","required":["label","price"],"title":"SeatUpgrade","description":"A paid within-cabin seat upgrade (extra-legroom / preferred)."},"SelectorUpdateRequest":{"properties":{"version":{"type":"integer","title":"Version","default":1},"updated_at":{"type":"string","title":"Updated At","default":""},"selectors":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object","title":"Selectors"}},"type":"object","required":["selectors"],"title":"SelectorUpdateRequest","description":"Body for PUT /api/admin/selectors/{provider}."},"SelfConnectRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"cabin_class":{"$ref":"#/components/schemas/CabinClass","default":"economy"}},"type":"object","required":["origin","destination","departure_date"],"title":"SelfConnectRequest"},"SendMessageRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"content":{"type":"string","title":"Content"},"message_type":{"type":"string","title":"Message Type","default":"text"}},"type":"object","required":["user_id","content"],"title":"SendMessageRequest"},"SendResponse":{"properties":{"sent":{"type":"boolean","title":"Sent"},"ttl_seconds":{"type":"integer","title":"Ttl Seconds"}},"type":"object","required":["sent","ttl_seconds"],"title":"SendResponse"},"SetBudgetRequest":{"properties":{"budget":{"type":"number","title":"Budget"},"currency":{"type":"string","title":"Currency","default":"USD"}},"type":"object","required":["budget"],"title":"SetBudgetRequest"},"SmsSendRequest":{"properties":{"phone":{"type":"string","maxLength":20,"minLength":6,"title":"Phone"}},"type":"object","required":["phone"],"title":"SmsSendRequest"},"SmsVerifyRequest":{"properties":{"phone":{"type":"string","maxLength":20,"minLength":6,"title":"Phone"},"code":{"type":"string","maxLength":10,"minLength":4,"title":"Code"}},"type":"object","required":["phone","code"],"title":"SmsVerifyRequest"},"StayBookingGuest":{"properties":{"given_name":{"type":"string","title":"Given Name"},"family_name":{"type":"string","title":"Family Name"},"born_on":{"type":"string","title":"Born On"}},"type":"object","required":["given_name","family_name","born_on"],"title":"StayBookingGuest"},"StayBookingRequest":{"properties":{"quote_id":{"type":"string","title":"Quote Id"},"email":{"type":"string","title":"Email"},"phone_number":{"type":"string","title":"Phone Number"},"guests":{"items":{"$ref":"#/components/schemas/StayBookingGuest"},"type":"array","title":"Guests"},"accommodation_special_requests":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accommodation Special Requests"}},"type":"object","required":["quote_id","email","phone_number","guests"],"title":"StayBookingRequest"},"StayQuoteRequest":{"properties":{"rate_id":{"type":"string","title":"Rate Id"}},"type":"object","required":["rate_id"],"title":"StayQuoteRequest"},"StayRatesRequest":{"properties":{"search_result_id":{"type":"string","title":"Search Result Id"}},"type":"object","required":["search_result_id"],"title":"StayRatesRequest"},"TestPushResponse":{"properties":{"sent":{"type":"integer","title":"Sent"},"tickets":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Tickets","default":[]}},"type":"object","required":["sent"],"title":"TestPushResponse"},"ThreeDSInput":{"properties":{"card_id":{"type":"string","title":"Card Id"},"resource_id":{"type":"string","title":"Resource Id"},"services":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Services"}},"type":"object","required":["card_id","resource_id"],"title":"ThreeDSInput"},"TransferPartnerInfo":{"properties":{"name":{"type":"string","title":"Name"},"ratio":{"type":"number","title":"Ratio"},"type":{"type":"string","title":"Type"},"transfer_time":{"type":"string","title":"Transfer Time"}},"type":"object","required":["name","ratio","type","transfer_time"],"title":"TransferPartnerInfo"},"TravelerInput":{"properties":{"first_name":{"type":"string","title":"First Name"},"last_name":{"type":"string","title":"Last Name"},"dob":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dob"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"}},"type":"object","required":["first_name","last_name"],"title":"TravelerInput"},"TrendingResponse":{"properties":{"routes":{"items":{"$ref":"#/components/schemas/TrendingRoute"},"type":"array","title":"Routes"},"generated_at":{"type":"string","title":"Generated At"},"window_hours":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Window Hours"}},"type":"object","required":["routes","generated_at","window_hours"],"title":"TrendingResponse"},"TrendingRoute":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"search_count":{"type":"integer","title":"Search Count"},"tier":{"type":"string","title":"Tier"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["origin","destination","search_count","tier","updated_at"],"title":"TrendingRoute"},"TripPlanRequest":{"properties":{"prompt":{"type":"string","title":"Prompt"},"home_city":{"type":"string","title":"Home City","default":"JFK"},"user_preferences":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"User Preferences"}},"type":"object","required":["prompt"],"title":"TripPlanRequest"},"TripRefineRequest":{"properties":{"prompt":{"type":"string","title":"Prompt"},"current_plan":{"additionalProperties":true,"type":"object","title":"Current Plan"}},"type":"object","required":["prompt","current_plan"],"title":"TripRefineRequest"},"TripType":{"type":"string","enum":["one_way","round_trip","multi_city"],"title":"TripType"},"UnlinkRequest":{"properties":{"membership_type":{"type":"string","pattern":"^(amex_travel|costco_travel|aaa_travel)$","title":"Membership Type"}},"type":"object","required":["membership_type"],"title":"UnlinkRequest"},"UpdateClientRequest":{"properties":{"first_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Name"},"last_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"company":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"UpdateClientRequest"},"UpdateQuoteRequest":{"properties":{"customer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Id"},"customer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Name"},"customer_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Customer Email"},"flights":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Flights"},"markup":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Markup"},"subtotal":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Subtotal"},"total":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Total"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"terms":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Terms"},"expiry_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expiry Date"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},"type":"object","title":"UpdateQuoteRequest"},"UpdateQuoteStatusRequest":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"UpdateQuoteStatusRequest"},"UpdateStageRequest":{"properties":{"stage":{"type":"string","title":"Stage"}},"type":"object","required":["stage"],"title":"UpdateStageRequest"},"UpdateStatusRequest":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"UpdateStatusRequest"},"UpgradeLikelihood":{"properties":{"band":{"type":"string","title":"Band"},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score"},"basis":{"items":{"type":"string"},"type":"array","title":"Basis","default":[]},"estimate":{"type":"boolean","title":"Estimate","default":true}},"type":"object","required":["band"],"title":"UpgradeLikelihood","description":"Estimated openness of a target cabin, from real signals only."},"UpgradeOption":{"properties":{"cabin":{"$ref":"#/components/schemas/CabinClass"},"cabinMarketingName":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cabinmarketingname"},"priceCash":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pricecash"},"upgradeCostCash":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Upgradecostcash"},"pricePoints":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pricepoints"},"pointsProgramName":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pointsprogramname"},"cppValue":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cppvalue"},"currency":{"type":"string","title":"Currency","default":"USD"},"seatsRemaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seatsremaining"},"bookable":{"type":"boolean","title":"Bookable","default":false},"bookingOfferId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookingofferid"},"likelihood":{"anyOf":[{"$ref":"#/components/schemas/UpgradeLikelihood"},{"type":"null"}]}},"type":"object","required":["cabin"],"title":"UpgradeOption","description":"One higher cabin the user could move up to for this itinerary."},"UpgradeOptionsRequest":{"properties":{"origin":{"type":"string","maxLength":3,"minLength":3,"title":"Origin"},"destination":{"type":"string","maxLength":3,"minLength":3,"title":"Destination"},"departure_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","title":"Departure Date"},"return_date":{"anyOf":[{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},{"type":"null"}],"title":"Return Date"},"passengers":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Passengers","default":1},"base_cabin":{"$ref":"#/components/schemas/CabinClass","default":"economy"},"base_price_cash":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Price Cash"},"base_offer_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Offer Id"}},"type":"object","required":["origin","destination","departure_date"],"title":"UpgradeOptionsRequest"},"UpgradeOptionsResponse":{"properties":{"origin":{"type":"string","title":"Origin"},"destination":{"type":"string","title":"Destination"},"baseCabin":{"$ref":"#/components/schemas/CabinClass"},"options":{"items":{"$ref":"#/components/schemas/UpgradeOption"},"type":"array","title":"Options","default":[]},"seatUpgrades":{"items":{"$ref":"#/components/schemas/SeatUpgrade"},"type":"array","title":"Seatupgrades","default":[]},"source":{"type":"string","title":"Source","default":"ok"}},"type":"object","required":["origin","destination","baseCabin"],"title":"UpgradeOptionsResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"VerifyMilitaryRequest":{"properties":{"method":{"type":"string","enum":["self-attest","govx","id.me"],"title":"Method"}},"type":"object","required":["method"],"title":"VerifyMilitaryRequest"},"VerifyMilitaryResponse":{"properties":{"status":{"type":"string","title":"Status"},"redirect_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Url"},"is_military":{"type":"boolean","title":"Is Military","default":false},"pending_verification":{"type":"boolean","title":"Pending Verification","default":false}},"type":"object","required":["status"],"title":"VerifyMilitaryResponse"},"VerifyRequest":{"properties":{"membership_type":{"type":"string","pattern":"^(amex_travel|costco_travel|aaa_travel)$","title":"Membership Type"}},"type":"object","required":["membership_type"],"title":"VerifyRequest"},"VerifyResponse":{"properties":{"verified":{"type":"boolean","title":"Verified"}},"type":"object","required":["verified"],"title":"VerifyResponse"},"WatchesScanResponse":{"properties":{"scanned":{"type":"integer","title":"Scanned"},"fired":{"type":"integer","title":"Fired"},"skipped":{"type":"integer","title":"Skipped"},"details":{"additionalProperties":true,"type":"object","title":"Details","default":{}}},"type":"object","required":["scanned","fired","skipped"],"title":"WatchesScanResponse","description":"Summary returned by ``POST /api/watches/scan``.\n\nMirrors ``watch_monitor.scan_and_notify_user``'s dict shape so the\nmobile client can surface \"fired 2, skipped 1\" in its toast."},"app__routes__booking__PaymentIntentInput__1":{"properties":{"offer_id":{"type":"string","title":"Offer Id"},"amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount"},"currency":{"type":"string","title":"Currency","default":"USD"},"seat_service_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Seat Service Ids"}},"type":"object","required":["offer_id"],"title":"PaymentIntentInput"},"app__routes__booking__PaymentIntentInput__2":{"properties":{"amount":{"type":"string","title":"Amount"},"currency":{"type":"string","title":"Currency"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["amount","currency"],"title":"PaymentIntentInput"},"app__routes__group_trips__CreateTripRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"destination_iata":{"type":"string","maxLength":3,"minLength":3,"title":"Destination Iata"},"earliest_depart":{"type":"string","title":"Earliest Depart"},"latest_depart":{"type":"string","title":"Latest Depart"},"min_duration_nights":{"type":"integer","title":"Min Duration Nights","default":3},"max_duration_nights":{"type":"integer","title":"Max Duration Nights","default":14},"budget_per_person":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Budget Per Person"}},"type":"object","required":["destination_iata","earliest_depart","latest_depart"],"title":"CreateTripRequest"},"app__routes__trips__CreateTripRequest":{"properties":{"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"start_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"},"end_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"}},"type":"object","required":["user_id","name"],"title":"CreateTripRequest"}}},"tags":[{"name":"health","description":"Service health checks"},{"name":"search","description":"Flight search across multiple providers (Duffel, SerpAPI, Seats.aero)"},{"name":"calendar","description":"Flexible-date calendar search for lowest fares"},{"name":"advanced","description":"Advanced fare intelligence — self-connect, geo-pricing, predictions"},{"name":"booking","description":"Flight and hotel booking management"},{"name":"hotels","description":"Hotel search and comparison"},{"name":"cars","description":"Car rental search"},{"name":"cruises","description":"Cruise search"},{"name":"points","description":"Points optimization and transfer partner analysis"},{"name":"alerts","description":"Fare alert subscriptions and notifications"},{"name":"trips","description":"Group trip planning and collaboration"},{"name":"trip-planner","description":"AI-powered trip planning from natural language prompts"},{"name":"packages","description":"Curated travel packages for agents"},{"name":"admin","description":"Administrative endpoints for platform management"},{"name":"api-keys","description":"API key creation, listing, and revocation for enterprise access"},{"name":"marketing","description":"Public marketing endpoints — featured deals and catalogs"},{"name":"flight-status","description":"Real-time flight status tracking and delay alerts"},{"name":"mistake-fares","description":"Mistake fare detection using historical price analysis"},{"name":"multi-city","description":"Multi-city flight search with positioning optimization"},{"name":"visa","description":"Visa and travel restriction checker for passport/destination pairs"},{"name":"carbon","description":"Carbon emissions estimation and offset pricing for flights"},{"name":"award-search","description":"Direct airline award search — scrapes United, Delta, and AA award pages"},{"name":"scraper-health","description":"Scraper health monitoring and self-healing diagnostics"},{"name":"selectors","description":"Admin management of externalized CSS selector configs"},{"name":"checkout","description":"Checkout navigation — navigate to airline checkout pages and fill passenger forms"},{"name":"memberships","description":"Travel portal membership management (Amex, Costco, AAA)"},{"name":"cards","description":"Credit card recommendation engine — spending-based card suggestions"},{"name":"seats","description":"Seat map display and seat selection for flight bookings"},{"name":"baggage","description":"Baggage options and selection for flight bookings"},{"name":"redemption-charts","description":"Airline redemption / award charts with CPP analysis and sweet spots"},{"name":"customer-service","description":"Agentic AI customer service — chat, tickets, escalations, CSAT"}]}