{"openapi":"3.1.0","info":{"title":"Propaired Home Buyer Assistant API","description":"AI-powered real estate tools for home buyers and clients. Search properties with natural language, compare listings, find realtors, get property appraisals, save favorites, and manage your home search journey.","version":"1.0.0","contact":{"name":"Propaired","url":"https://propaired.ai"}},"servers":[{"url":"https://www.propaired.ai","description":"Production"}],"security":[{"bearerAuth":[]}],"paths":{"/api/tools/searchProperties":{"post":{"operationId":"searchProperties","summary":"Search for properties using natural language queries and structured filters like city, price, beds, baths, amenities, and lifestyle keywords. Returns up to 6 listing cards.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"minPrice":{"type":"number","description":"Minimum price"},"maxPrice":{"type":"number","description":"Maximum price"},"minBeds":{"type":"number","description":"Minimum bedrooms"},"maxBeds":{"type":"number","description":"Maximum bedrooms"},"minBaths":{"type":"number","description":"Minimum bathrooms"},"propertyType":{"type":"string","description":"MLS property_type values used in the database: 'Residential' (covers all single-family, condo, townhouse, multi-family homes), 'Land' (lots / vacant land), 'Farm', 'Commercial Sale', 'Commercial Lease', 'Residential Lease'. The MLS does NOT use 'Single Family' / 'Condo' at this level — those are subtypes. For phrases like 'homes' / 'houses' / 'a place to live', use 'Residential'. Only switch to Land / Farm / Commercial when the user explicitly asks for those."},"propertySubType":{"type":"string","description":"MLS property_subtype value when the user asks for a specific subtype such as Single Family Residence, Condominium, Townhouse, Duplex, Triplex, or Manufactured Home."},"zipCode":{"type":"string","description":"ZIP code filter when the user names a ZIP code. Use this as a structured geographic filter, not keywords."},"county":{"type":"string","description":"County filter when the user names a county. Use the county name without the word County when possible."},"school":{"type":"string","description":"MLS school name filter across elementary, middle, and high school fields. Use when the user names a specific school."},"schoolLevel":{"type":"string","enum":["Elementary","Middle","High","K-8"],"description":"School level to target when school is set."},"schoolDistrict":{"type":"string","description":"School district name filter; use with a named district or to tighten a school-name search."},"schoolRating":{"type":"string","description":"Graph-backed nearby school quality tier from the Supabase livability graph: Very High, High, Typical, or Low."},"schoolSpecialty":{"type":"string","description":"Graph-backed school specialty tag such as stem_excellence or strong_college_readiness."},"sqftMin":{"type":"number","description":"Minimum square footage"},"sqftMax":{"type":"number","description":"Maximum square footage"},"yearBuiltMin":{"type":"number","description":"Minimum year built (e.g. 2000 for homes built after 2000)"},"yearBuiltMax":{"type":"number","description":"Maximum year built (e.g. 1980 for \"built before 1980\" / historic queries). Pair with yearBuiltMin for a range."},"lotSizeMin":{"type":"number","description":"Minimum lot size in acres"},"hasPool":{"type":"boolean","description":"Must have a private swimming pool"},"hasGarage":{"type":"boolean","description":"Must have a garage"},"hasBasement":{"type":"boolean","description":"Must have a basement"},"waterfront":{"type":"boolean","description":"Must be waterfront property"},"hasFireplace":{"type":"boolean","description":"Must have a fireplace"},"horseProperty":{"type":"boolean","description":"Must be a horse/equestrian property"},"hasView":{"type":"boolean","description":"Must have a meaningful MLS View value. Use for generic phrases like \"with a view\"."},"viewTerms":{"type":"array","items":{"type":"string"},"description":"Normalized MLS View categories to require. Use values like mountain, valley, city, lake, water, scenic for phrases like \"mountain view\"."},"maxHOA":{"type":"number","description":"Maximum monthly HOA fees in dollars. Use for ranges like \"HOA under $200\". For \"no HOA\" / \"HOA-free\" intents, prefer noHoa: true (covers NULL hoa_fees too — most no-HOA homes have NULL not 0)."},"noHoa":{"type":"boolean","description":"Set true when the user wants homes with NO HOA at all (\"no HOA\", \"HOA-free\", \"no homeowners association\"). Matches listings where hoa_fees IS NULL or = 0. Use this instead of maxHOA: 0."},"strPotential":{"type":"boolean","description":"Set true when the user mentions short-term rental / Airbnb / vacation rental potential. Maps to dealClassifications: [\"Strong STR Cash Flow\"] in the underlying intelligence pipeline."},"daysOnMarketMax":{"type":"number","description":"Maximum days on market (for new listings)"},"keywords":{"type":"string","description":"Semantic search keywords for features NOT covered by structured filters (e.g. \"ski access\", \"mountain views\", \"RV parking\", \"trampoline\"). Triggers vector similarity search against listing descriptions."},"lifestyleTags":{"type":"array","items":{"type":"string"},"description":"Lifestyle tag filters (e.g. [\"is_ski_property\", \"is_waterfront\", \"is_horse_property\", \"is_golf_property\", \"is_mountain_view\", \"is_luxury_estate\"]). Filters on pre-computed boolean tags from listing intelligence."},"dealClassifications":{"type":"array","items":{"type":"string"},"description":"Deal classification filter (e.g. [\"Exceptional Deal\", \"Great Deal\"])"},"minDealScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum composite deal score (0-100)"},"minInvestorScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum investor special score (0-100)"},"minRecreationScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum recreation proximity score (0-100)"},"minRentCoverage":{"type":"number","minimum":0,"maximum":300,"description":"Minimum long-term rent coverage percentage; 100 means estimated rent covers monthly ownership cost."},"strPositive":{"type":"boolean","description":"Require positive short-term-rental monthly cash flow."},"minDscr":{"type":"number","description":"Minimum long-term DSCR (Debt Service Coverage Ratio). 1.0 = rent exactly covers the mortgage payment; 2.0 = rent is twice the mortgage. Use for queries like \"DSCR over 2\" or \"debt service coverage above 1.5\"."},"minStrDscr":{"type":"number","description":"Minimum short-term rental DSCR. Use when the user asks about Airbnb or vacation rental DSCR (e.g. \"STR DSCR above 1.5\")."},"primaryNiche":{"type":"string","description":"Primary niche filter (e.g. \"ski\", \"waterfront\", \"luxury\")"},"nearSkiResort":{"type":"boolean","description":"Property is near a ski resort"},"nearLake":{"type":"boolean","description":"Property is near a lake"},"nearNationalPark":{"type":"boolean","description":"Property is near a national park"},"nearTrailhead":{"type":"boolean","description":"Property is near a trailhead"},"fiberOnly":{"type":"boolean","description":"Only show homes with fiber internet available"},"minBroadbandScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum broadband connectivity score (0-100)"},"nearCategories":{"type":"array","items":{"type":"string"},"description":"Filter homes near specific amenity or recreation types. Use tag format: near_golf, near_ski, near_park, near_grocery, near_hospital, near_school, near_trailhead, near_national_park, near_campground, near_museum, near_cafe, near_ev_charging"},"sortBy":{"type":"string","enum":["newest","price-low","price-high","featured","sqft","bedrooms","best-deals","best-rec","best-investor","oldest-listing"],"description":"Sort order. \"newest\" = listing_date desc. \"price-low\"/\"price-high\". \"featured\" / \"best-deals\" = composite_deal_score desc (deal+lifestyle). \"best-rec\" = recreation_score desc (skiing/lake/parks). \"best-investor\" = investor_special_score desc. \"oldest-listing\" = listing_date asc (sleeper deals). \"sqft\"/\"bedrooms\" sort by size/beds."},"luxurySearch":{"type":"boolean","description":"Set true for luxury queries — the tool resolves the city-appropriate IQR price threshold and applies it as minPrice automatically. Use together with sortBy: \"price-high\" and lifestyleTags: [\"is_luxury_estate\"]."},"query":{"type":"string","description":"Natural language search query"},"address":{"type":"string","description":"Specific street address or address fragment when the user names a property (e.g. \"872 West 3800 South Bountiful\", \"1234 Main St\", or just \"872 3800\"). Use this — NOT keywords or query — whenever the user mentions a street number or street name. Tokens are AND-matched against a normalized search column (directional words like \"West\"/\"South\" are stripped on both sides), so \"872 West 3800 South\" matches stored \"872 W 3800 S\". Pass in city/zip/listing key as part of the same string when the user provides them; do not also set `city`. When set, no other filters are applied."},"city":{"type":"string","description":"City name — REQUIRED whenever the user has named a SINGLE city (current or prior turn). For \"Sandy OR Draper\" multi-city queries use `cities` instead of issuing parallel calls. Do NOT put city names only in `keywords` (keywords does vector similarity, not geographic filtering — it will return homes from anywhere in Utah). Only omit when user explicitly wants a statewide search."},"cities":{"type":"array","items":{"type":"string"},"description":"Multiple city names for OR-style multi-city searches: \"Sandy or Draper\" → [\"Sandy\",\"Draper\"]. Single call returns the union. When set, `city` is ignored. Use canonical MLS-cased names (e.g. \"Salt Lake City\", \"Park City\", \"Heber City\", \"St. George\")."},"subdivision":{"type":"string","description":"Subdivision / community / development name (e.g. \"Timber Lakes\", \"Promontory\", \"Glenwild\", \"Daybreak\", \"Suncrest\", \"Tuhaye\", \"Red Ledges\", \"Victory Ranch\"). Set this — NOT `city` — when the user names a community that lives inside a parent city. The filter does substring ILIKE on `subdivision_name`, so partial names like \"Timber\" still hit. Combine with city only when both are explicitly mentioned."},"excludePropertyTypes":{"type":"array","items":{"type":"string","enum":["Residential","Land","Farm","Commercial Sale","Commercial Lease","Residential Lease"]},"description":"Property types to EXCLUDE from results. Use when the user wants to omit a category they don't care about (\"no Land\", \"no Commercial\", \"homes only\"). Common: [\"Land\", \"Commercial Sale\", \"Commercial Lease\"] to surface residential-only when the user is browsing a mixed area like Park City."},"centerLat":{"type":"number","description":"Latitude of a proximity anchor (from resolvePlace). Set with centerLng + radiusMiles to find homes near a point."},"centerLng":{"type":"number","description":"Longitude of a proximity anchor (from resolvePlace)."},"radiusMiles":{"type":"number","minimum":1,"maximum":50,"description":"Radius in miles for proximity search. Used with centerLat/centerLng. The tool auto-expands 4→10→25 if 0 results."},"bbox":{"type":"array","items":{"type":"number"},"description":"Bounding box for map-viewport searches: [west, south, east, north] in decimal degrees (4 numbers). Use when the user is asking about homes inside a specific rectangular area (often from a prior map handoff). Cannot be combined with centerLat/Lng/radius."},"h3Cells":{"type":"array","items":{"type":"string"},"description":"Pre-computed H3 res-8 cells for multi-place intersections. Only set when resolvePlace returned them."},"softMode":{"type":"boolean","description":"When true (default for free-text queries), demote soft signals (subdivision, near_*, lifestyleTags, dealClassifications, primaryNiche, viewTerms, score floors) from hard filters to relevance boosts so ES ranks rather than excludes. When false, every filter is a hard intersection (strict mode) — use ONLY when the user said something like \"MUST have a pool\" or \"ONLY 4-bed\". For most natural-language queries leave this unset."},"relaxableFilters":{"type":"array","items":{"type":"string"},"description":"Filter keys the LLM thinks could be relaxed if the initial search returns ≤2 results. The tool's adaptive relaxation pipeline already handles this generically; only set this when the user signaled a specific willingness to compromise (\"I'd consider a 3-bed instead of 4-bed\" → [\"bedsMin\"]). Common values: [\"yearBuiltMin\", \"sqftMin\", \"lotSizeMin\", \"subdivision\", \"lifestyleTags\", \"viewTerms\"]."},"limit":{"type":"number","minimum":1,"maximum":50,"default":6,"description":"Number of results to return (1-50). Default 6 for chat cards; bump higher for richer browsing intents (\"show me everything in Park City under 800k\") or non-chat surfaces that show all matches in a scroll."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/recommendProperties":{"post":{"operationId":"recommendProperties","summary":"Recommend properties based on the user's saved preferences, search history, and lifestyle priorities. Returns up to 6 listings with personalized match reasons.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"What kind of recommendation the user wants"},"limit":{"type":"number","minimum":1,"maximum":6,"default":6,"description":"Number of results"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/compareListings":{"post":{"operationId":"compareListings","summary":"Compare 2-4 property listings side-by-side. Supports direct comparison by listing key or finding similar homes to a reference listing.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["direct","find_similar"],"description":"direct: compare specific listings. find_similar: find homes similar to a reference listing."},"listingKeys":{"type":"array","items":{"type":"string"},"maxItems":4,"description":"For direct mode: 2-4 listing keys to compare. Resolve from conversation search results."},"referenceListingKey":{"type":"string","description":"For find_similar mode: listing_key of the reference listing."},"similarCount":{"type":"number","minimum":1,"maximum":3,"default":3,"description":"For find_similar: how many similar listings to find (1-3). Default 3."},"focusAreas":{"type":"array","items":{"type":"string"},"description":"Aspects to emphasize (e.g. \"schools\", \"price per sqft\", \"lot size\")."},"searchCriteria":{"type":"object","properties":{"hasPool":{"type":"boolean"},"hasGarage":{"type":"boolean"},"hasBasement":{"type":"boolean"},"hasFireplace":{"type":"boolean"},"waterfront":{"type":"boolean"},"horseProperty":{"type":"boolean"},"sqftMin":{"type":"number"},"sqftMax":{"type":"number"},"yearBuiltMin":{"type":"number"},"maxHOA":{"type":"number"},"keywords":{"type":"string"}},"additionalProperties":false,"description":"For find_similar: carry forward filters from the original search (e.g. hasPool, hasGarage). Copy boolean and numeric filters from the most recent searchProperties call in conversation history."}},"required":["mode"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/findSimilarListings":{"post":{"operationId":"findSimilarListings","summary":"Find homes similar to a specific listing using pre-computed similarity scores based on price, features, location, and description.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listingKey":{"type":"string","description":"The listing_key of the reference listing to find similar homes for"},"limit":{"type":"number","minimum":1,"maximum":6,"default":5,"description":"Number of similar homes to return (1-6)"}},"required":["listingKey"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/findDeals":{"post":{"operationId":"findDeals","summary":"Find investment deals and undervalued properties using deal scores, rental yield, STR potential, and listing intelligence data.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":"string","description":"City name to filter deals"},"minPrice":{"type":"number","description":"Minimum listing price"},"maxPrice":{"type":"number","description":"Maximum listing price"},"minBeds":{"type":"number","description":"Minimum bedrooms"},"propertyType":{"type":"string","description":"Property type filter (e.g. \"Condo\", \"Townhouse\", \"Single Family\", \"Multi-Family\", \"Residential\"). Use when the user specifies a property type — investors often target condos/townhomes for rentals."},"minDealScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum composite deal score (0-100)"},"minInvestorScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum investor special score (0-100)"},"dealClassifications":{"type":"array","items":{"type":"string","enum":["Exceptional Deal","Great Deal (vs Similar)","Great Deal (vs Market)","Strong Cash Flow","Strong STR Cash Flow","Growth Market Deal","Motivated Seller"]},"description":"Deal classification filter (multi-tag array overlap). Values: \"Exceptional Deal\" (priced below comps AND below market), \"Great Deal (vs Similar)\" / \"Great Deal (vs Market)\" (below one benchmark), \"Strong Cash Flow\" (long-term rent covers all ownership costs), \"Strong STR Cash Flow\" (short-term rental revenue covers all costs in tourism markets), \"Growth Market Deal\", \"Motivated Seller\". IMPORTANT: \"Investor Special\" is NOT a valid value here — when the user says \"investor special\", \"investment property\", \"cash flow deal\", or \"rental property\", use [\"Strong Cash Flow\"] or set minInvestorScore instead. For \"Airbnb\" / \"short-term rental\" / \"STR\" / \"vacation rental\" use [\"Strong STR Cash Flow\"] or strPotential: true. \"Manufactured homes\" are filtered via the propertyType field, not this one."},"minRentalYield":{"type":"number","description":"Minimum long-term rent coverage percentage (rent / total monthly ownership cost × 100). 100 = breakeven, 150 = strong. Replaces the old yield-on-price which was meaningless for luxury homes."},"strPotential":{"type":"boolean","description":"Filter for short-term rental opportunity — returns only listings where STR net monthly cash flow > 0 after operating costs and full monthly ownership cost."},"minDscr":{"type":"number","description":"Minimum long-term DSCR (Debt Service Coverage Ratio). 1.0 = rent exactly covers the mortgage; 2.0 = rent is twice the mortgage. Use for queries like \"DSCR over 2\"."},"minStrDscr":{"type":"number","description":"Minimum short-term rental DSCR. Use for Airbnb/vacation rental DSCR queries."},"lifestyleTags":{"type":"array","items":{"type":"string"},"description":"Lifestyle tag filters (e.g. [\"is_ski_property\", \"is_waterfront\"])"},"limit":{"type":"number","minimum":1,"maximum":6,"default":6,"description":"Number of results to return"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/askAboutListing":{"post":{"operationId":"askAboutListing","summary":"Answer a question about a specific property listing. Provides comprehensive details including price, features, location, schools, interior/exterior, photos, HOA, lot size, and more.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listingKey":{"type":"string","description":"Listing key to ask about"},"question":{"type":"string","description":"Question about the listing"}},"required":["listingKey","question"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/searchNearPlace":{"post":{"operationId":"searchNearPlace","summary":"Search for homes near a specific place, business, or landmark using Google Maps. Supports multi-place proximity search.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"minPrice":{"type":"number","description":"Minimum price"},"maxPrice":{"type":"number","description":"Maximum price"},"minBeds":{"type":"number","description":"Minimum bedrooms"},"maxBeds":{"type":"number","description":"Maximum bedrooms"},"minBaths":{"type":"number","description":"Minimum bathrooms"},"propertyType":{"type":"string","description":"MLS property_type values used in the database: 'Residential' (covers all single-family, condo, townhouse, multi-family homes), 'Land' (lots / vacant land), 'Farm', 'Commercial Sale', 'Commercial Lease', 'Residential Lease'. The MLS does NOT use 'Single Family' / 'Condo' at this level — those are subtypes. For phrases like 'homes' / 'houses' / 'a place to live', use 'Residential'. Only switch to Land / Farm / Commercial when the user explicitly asks for those."},"propertySubType":{"type":"string","description":"MLS property_subtype value when the user asks for a specific subtype such as Single Family Residence, Condominium, Townhouse, Duplex, Triplex, or Manufactured Home."},"zipCode":{"type":"string","description":"ZIP code filter when the user names a ZIP code. Use this as a structured geographic filter, not keywords."},"county":{"type":"string","description":"County filter when the user names a county. Use the county name without the word County when possible."},"school":{"type":"string","description":"MLS school name filter across elementary, middle, and high school fields. Use when the user names a specific school."},"schoolLevel":{"type":"string","enum":["Elementary","Middle","High","K-8"],"description":"School level to target when school is set."},"schoolDistrict":{"type":"string","description":"School district name filter; use with a named district or to tighten a school-name search."},"schoolRating":{"type":"string","description":"Graph-backed nearby school quality tier from the Supabase livability graph: Very High, High, Typical, or Low."},"schoolSpecialty":{"type":"string","description":"Graph-backed school specialty tag such as stem_excellence or strong_college_readiness."},"sqftMin":{"type":"number","description":"Minimum square footage"},"sqftMax":{"type":"number","description":"Maximum square footage"},"yearBuiltMin":{"type":"number","description":"Minimum year built (e.g. 2000 for homes built after 2000)"},"yearBuiltMax":{"type":"number","description":"Maximum year built (e.g. 1980 for \"built before 1980\" / historic queries). Pair with yearBuiltMin for a range."},"lotSizeMin":{"type":"number","description":"Minimum lot size in acres"},"hasPool":{"type":"boolean","description":"Must have a private swimming pool"},"hasGarage":{"type":"boolean","description":"Must have a garage"},"hasBasement":{"type":"boolean","description":"Must have a basement"},"waterfront":{"type":"boolean","description":"Must be waterfront property"},"hasFireplace":{"type":"boolean","description":"Must have a fireplace"},"horseProperty":{"type":"boolean","description":"Must be a horse/equestrian property"},"hasView":{"type":"boolean","description":"Must have a meaningful MLS View value. Use for generic phrases like \"with a view\"."},"viewTerms":{"type":"array","items":{"type":"string"},"description":"Normalized MLS View categories to require. Use values like mountain, valley, city, lake, water, scenic for phrases like \"mountain view\"."},"maxHOA":{"type":"number","description":"Maximum monthly HOA fees in dollars. Use for ranges like \"HOA under $200\". For \"no HOA\" / \"HOA-free\" intents, prefer noHoa: true (covers NULL hoa_fees too — most no-HOA homes have NULL not 0)."},"noHoa":{"type":"boolean","description":"Set true when the user wants homes with NO HOA at all (\"no HOA\", \"HOA-free\", \"no homeowners association\"). Matches listings where hoa_fees IS NULL or = 0. Use this instead of maxHOA: 0."},"strPotential":{"type":"boolean","description":"Set true when the user mentions short-term rental / Airbnb / vacation rental potential. Maps to dealClassifications: [\"Strong STR Cash Flow\"] in the underlying intelligence pipeline."},"daysOnMarketMax":{"type":"number","description":"Maximum days on market (for new listings)"},"keywords":{"type":"string","description":"Semantic search keywords for features NOT covered by structured filters (e.g. \"ski access\", \"mountain views\", \"RV parking\", \"trampoline\"). Triggers vector similarity search against listing descriptions."},"lifestyleTags":{"type":"array","items":{"type":"string"},"description":"Lifestyle tag filters (e.g. [\"is_ski_property\", \"is_waterfront\", \"is_horse_property\", \"is_golf_property\", \"is_mountain_view\", \"is_luxury_estate\"]). Filters on pre-computed boolean tags from listing intelligence."},"dealClassifications":{"type":"array","items":{"type":"string"},"description":"Deal classification filter (e.g. [\"Exceptional Deal\", \"Great Deal\"])"},"minDealScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum composite deal score (0-100)"},"minInvestorScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum investor special score (0-100)"},"minRecreationScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum recreation proximity score (0-100)"},"minRentCoverage":{"type":"number","minimum":0,"maximum":300,"description":"Minimum long-term rent coverage percentage; 100 means estimated rent covers monthly ownership cost."},"strPositive":{"type":"boolean","description":"Require positive short-term-rental monthly cash flow."},"minDscr":{"type":"number","description":"Minimum long-term DSCR (Debt Service Coverage Ratio). 1.0 = rent exactly covers the mortgage payment; 2.0 = rent is twice the mortgage. Use for queries like \"DSCR over 2\" or \"debt service coverage above 1.5\"."},"minStrDscr":{"type":"number","description":"Minimum short-term rental DSCR. Use when the user asks about Airbnb or vacation rental DSCR (e.g. \"STR DSCR above 1.5\")."},"primaryNiche":{"type":"string","description":"Primary niche filter (e.g. \"ski\", \"waterfront\", \"luxury\")"},"nearSkiResort":{"type":"boolean","description":"Property is near a ski resort"},"nearLake":{"type":"boolean","description":"Property is near a lake"},"nearNationalPark":{"type":"boolean","description":"Property is near a national park"},"nearTrailhead":{"type":"boolean","description":"Property is near a trailhead"},"fiberOnly":{"type":"boolean","description":"Only show homes with fiber internet available"},"minBroadbandScore":{"type":"number","minimum":0,"maximum":100,"description":"Minimum broadband connectivity score (0-100)"},"nearCategories":{"type":"array","items":{"type":"string"},"description":"Filter homes near specific amenity or recreation types. Use tag format: near_golf, near_ski, near_park, near_grocery, near_hospital, near_school, near_trailhead, near_national_park, near_campground, near_museum, near_cafe, near_ev_charging"},"sortBy":{"type":"string","enum":["newest","price-low","price-high","featured","sqft","bedrooms","best-deals","best-rec","best-investor","oldest-listing"],"description":"Sort order. \"newest\" = listing_date desc. \"price-low\"/\"price-high\". \"featured\" / \"best-deals\" = composite_deal_score desc (deal+lifestyle). \"best-rec\" = recreation_score desc (skiing/lake/parks). \"best-investor\" = investor_special_score desc. \"oldest-listing\" = listing_date asc (sleeper deals). \"sqft\"/\"bedrooms\" sort by size/beds."},"luxurySearch":{"type":"boolean","description":"Set true for luxury queries — the tool resolves the city-appropriate IQR price threshold and applies it as minPrice automatically. Use together with sortBy: \"price-high\" and lifestyleTags: [\"is_luxury_estate\"]."},"place":{"type":"string","description":"Named place, business, or landmark to search near. Examples: \"Harmons\", \"Baker Hot Springs\", \"Liberty Park\", \"Costco\""},"additionalPlaces":{"type":"array","items":{"type":"string"},"description":"Additional named places for multi-place proximity search. The tool finds listings reachable from ALL places. Example: user says \"near Harmons and Point of the Mountain\" → place: \"Harmons\", additionalPlaces: [\"Point of the Mountain\"]"},"placeType":{"type":"string","enum":["grocery_or_supermarket","school","hospital","gym","restaurant","park","library","pharmacy","shopping_mall","transit_station","church","bank","gas_station"],"description":"Google Places type for GENERIC searches only (e.g., user says \"near a grocery store\"). Do NOT set for named businesses — use place field instead."},"city":{"type":"string","description":"City to scope the place search within. Populate from conversation context, prior search, or user profile."},"radiusMiles":{"type":"number","minimum":1,"maximum":50,"description":"Search radius in miles. Default 4. Set when user specifies distance like \"within 2 miles\"."},"limit":{"type":"number","minimum":1,"maximum":6,"description":"Maximum listings to return, default 6"}},"required":["place"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/resolvePlace":{"post":{"operationId":"resolvePlace","summary":"Resolve a named place, business, landmark, or generic type to its geographic coordinates using Google Maps. Use this as the FIRST STEP whenever the user mentions proximity to a named place.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"place":{"type":"string","description":"Named place, business, or landmark to resolve. Examples: \"Harmons\", \"Baker Hot Springs\", \"Nice Sky Adventures\"."},"additionalPlaces":{"type":"array","items":{"type":"string"},"description":"Additional named places for multi-place proximity. Resolved via H3 intersection — returned h3Cells covers the area reachable from ALL places."},"placeType":{"type":"string","enum":["grocery_or_supermarket","school","hospital","gym","restaurant","park","library","pharmacy","shopping_mall","transit_station","church","bank","gas_station"],"description":"Google Places type for GENERIC searches only (e.g., \"near a grocery store\"). Do NOT set for named businesses."},"city":{"type":"string","description":"City to scope the place search within. Populate from conversation context, prior search, or user profile."},"radiusMiles":{"type":"number","minimum":1,"maximum":50,"description":"Desired search radius in miles. Default 4. The tool returns this (or the default) as suggestedRadiusMiles for the follow-up searchProperties call."}},"required":["place"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/queryNearestAmenity":{"post":{"operationId":"queryNearestAmenity","summary":"Find the nearest amenity or recreation place (grocery store, hospital, school, park, ski resort, etc.) for a specific listing or city. Use when the user asks \"what is the nearest X?\" or \"how far is the closest X?\"","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","description":"Single amenity / recreation category. Examples: grocery, hospital, school, park, restaurant, pharmacy, gas_station, library, gym, dentist, doctors, cafe, bank, ev_charging, ski_resort, trailhead, golf_course, campground. Use `categories` for multi-category lookups."},"categories":{"type":"array","items":{"type":"string"},"description":"Multiple categories to look up in one call. Use when the user asks about several amenity types at once (e.g. \"how far to the nearest grocery, pharmacy, and coffee shop?\"). Returns one entry per category. Mutually exclusive with `category`."},"minRating":{"type":"number","minimum":0,"maximum":5,"description":"When set, filter city-level results to places with a Google rating ≥ minRating. Use 4.0 for \"well-rated\" / \"good\", 4.5 for \"top-rated\". Only applies to the city strategy (not listing-level proximity, which already uses pre-computed nearest)."},"qualityFilter":{"type":"string","enum":["good","top_rated"],"description":"Convenience quality filter. `good` maps to minRating 4.0 and `top_rated` maps to minRating 4.5 for city-level searches."},"city":{"type":"string","description":"City to search in"},"listingKey":{"type":"string","description":"Specific listing to find nearest amenity for"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/lookupPlaceKnowledge":{"post":{"operationId":"lookupPlaceKnowledge","summary":"Look up factual context about a named place in Utah (national parks, ski resorts, state parks, cities, monuments, hot springs, etc.). Returns a Wikipedia-sourced description and summary.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"placeName":{"type":"string","minLength":2,"description":"Name of the place to look up — e.g. \"Zion National Park\", \"Alta Ski Area\", \"Moab\", \"Capitol Reef\". Use when the user asks about a specific location, attraction, or geographic feature in Utah."}},"required":["placeName"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/getShowingChecklist":{"post":{"operationId":"getShowingChecklist","summary":"Get a link to the viewing checklist for a specific showing. Includes property details and checklist progress.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"showingId":{"type":"string","format":"uuid","description":"Showing ID to get checklist for"}},"required":["showingId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/summarizeJourney":{"post":{"operationId":"summarizeJourney","summary":"Summarize the user's home search journey — what they have viewed, saved, searched for, and where they are in the buying process. Use this when the user asks to summarize their search, review their journey, see what they've been looking at, or asks 'where am I in my search?'","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"timeFilter":{"type":"string","enum":["all","7d","30d","90d"],"default":"all","description":"Time window for the summary. \"all\" = full history, \"7d\" = last 7 days, \"30d\" = last 30 days, \"90d\" = last 90 days. Default: \"all\""}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/queryMarketData":{"post":{"operationId":"queryMarketData","summary":"Query real estate market data and analytics using natural language. Returns charts, tables, and insights about Utah market trends.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":2000,"description":"The market data question to ask. Examples: \"Average home price in 84121?\", \"Top selling agents in Sandy?\", \"Price trends in Utah County\", \"Median price per sqft in Sandy\""}},"required":["question"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/askClarifyingQuestion":{"post":{"operationId":"askClarifyingQuestion","summary":"Ask the user a short clarifying question when their request is ambiguous AND cannot be resolved by a reasonable default. Prefer to make a defensible default call and narrate what you assumed; only call this tool when there is no single reasonable default (e.g.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":300,"description":"The clarifying question to ask the user. Keep it short and specific."},"options":{"type":"array","items":{"type":"string","minLength":1,"maxLength":80},"minItems":2,"maxItems":4,"description":"2-4 short quick-reply options shown as clickable chips below the question."}},"required":["question"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/webLookup":{"post":{"operationId":"webLookup","summary":"Last-resort web-search enrichment for non-MLS factual questions about properties, neighborhoods, developments, school-district changes, or local news. PROPAIRED.AI CONTENT IS USED FIRST; web search only fills gaps.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","minLength":3,"maxLength":400,"description":"The specific factual question to answer using propaired.ai content first, web fallback second."},"placeHint":{"type":"string","description":"City, neighborhood, or place name if the query is geographic — enables propaired place_knowledge pre-fetch."},"listingContext":{"type":"string","description":"Address or listing_key to scope the answer to a specific property."}},"required":["query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/findHomesByLifestyle":{"post":{"operationId":"findHomesByLifestyle","summary":"Find homes by multi-criteria lifestyle match: schools, walkability, recreation, safety, deal quality, commute.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","minLength":3,"maxLength":500,"description":"Natural-language query describing the desired home + lifestyle criteria. Example: \"3-bed in Sandy under $700k walkable to a top-rated elementary\"."},"limit":{"type":"integer","minimum":1,"maximum":50,"default":8,"description":"Max listings to return (default 8, max 50 for show-all-matches surfaces)."},"cities":{"type":"array","items":{"type":"string"},"description":"Override cities the supervisor extracted. Use when the user has named explicit cities in this turn or the immediate prior turn that the supervisor might miss. Canonical MLS-cased names. When set, replaces the supervisor's extracted city list."},"centerLat":{"type":"number","description":"Override center latitude from a prior resolvePlace call. Pair with centerLng + radiusMiles to anchor the search to a known point — bypasses the supervisor's place-name resolution which can mis-classify (e.g. \"Heber\" → Herriman)."},"centerLng":{"type":"number","description":"Override center longitude (see centerLat)."},"radiusMiles":{"type":"number","minimum":1,"maximum":50,"description":"Override radius in miles for the proximity anchor (see centerLat)."},"sortBy":{"type":"string","enum":["composite","best-deals","best-rec","best-investor","newest","price-low","price-high"],"description":"Override the default ranking. \"composite\" (default) = CompositeScorer over livability sub-scores + soft criteria from supervisor. \"best-deals\" = composite_deal_score. \"best-rec\" = recreation_score. \"best-investor\" = investor_special_score. \"newest\"/\"price-low\"/\"price-high\" route through ES sort."},"softMode":{"type":"boolean","description":"Force strict or soft filter intersection. Default behavior (unset): soft mode on — soft signals (near_*, lifestyleTags, subdivision) become relevance boosts rather than hard ANDs, which prevents the \"Bear Lake waterfront → 0 results\" failure mode. Set true to keep soft mode; set false ONLY when the user used MUST/REQUIRED-style language."}},"required":["query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/queryNeighborhoodMarket":{"post":{"operationId":"queryNeighborhoodMarket","summary":"Query neighborhood-level market intelligence: median price, price-per-sqft, momentum, days-on-market, agent count, monthly payment, tax, active inventory. Accepts a place name OR an H3 cell ID.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"placeName":{"type":"string","minLength":2,"maxLength":200,"description":"City, neighborhood, or area name. Either this OR cellId must be set."},"cellId":{"type":"string","description":"H3 cell ID (res 6, 7, or 8). Either this OR placeName must be set."},"metric":{"type":"string","enum":["price","ppsf","momentum","dom","agents","transactions","monthly_payment","tax","active_listings"],"default":"price","description":"Which market metric to surface."},"propertySubType":{"type":"string","description":"Optional: filter by property subtype (SingleFamilyResidence, Condominium, etc.)"},"resolution":{"type":"integer","minimum":4,"maximum":8,"default":7,"description":"H3 resolution to query (default 7)."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/queryRentalIntelligence":{"post":{"operationId":"queryRentalIntelligence","summary":"Query rental investment intelligence: long-term rent yields, short-term ADR / occupancy / revenue, LTR-vs-STR comparison, viability (DSCR, cash flow, deal score). Accepts a place name OR H3 cell ID and a mode (ltr | str | comparison | viability).","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"placeName":{"type":"string","minLength":2,"maxLength":200,"description":"City, neighborhood, or area name. Either this OR cellId must be set."},"cellId":{"type":"string","description":"H3 cell ID. Either this OR placeName must be set."},"mode":{"type":"string","enum":["ltr","str","comparison","viability"],"default":"viability","description":"ltr: long-term rent metrics. str: short-term rate/revenue/occupancy. comparison: LTR vs STR premium. viability: DSCR + cash-flow + deal score."},"resolution":{"type":"integer","minimum":4,"maximum":8,"default":7}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/findGoodSchoolsNearby":{"post":{"operationId":"findGoodSchoolsNearby","summary":"Find quality + specialty-aware schools near a place, listing, or coordinate. Reads schools.school_search_indicators (already federated from Databricks v_school_search_indicators).","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"placeName":{"type":"string","description":"City, neighborhood, district, or address."},"lat":{"type":"number"},"lng":{"type":"number"},"listingKey":{"type":"string","description":"Find schools near a specific listing (uses the listing's coords)."},"level":{"type":"string","enum":["elementary","middle","high","any"],"default":"any"},"specialtyTags":{"type":"array","items":{"type":"string","enum":["stem","magnet","dual_language","arts","ib_or_ap","charter"]},"description":"Filter to schools with these specialty programs."},"minQualityTier":{"type":"string","enum":["average","strong","excellent"],"default":"strong","description":"Minimum quality tier — default \"strong\"."},"radiusMiles":{"type":"number","minimum":0.5,"maximum":20,"default":5},"limit":{"type":"integer","minimum":1,"maximum":20,"default":8}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/proposeNeighborhoods":{"post":{"operationId":"proposeNeighborhoods","summary":"Propose 3-5 Utah neighborhoods (H3 cells) that best match a lifestyle-rich query. Use when the user describes WHAT they want but not WHERE — e.g. \"I want a quiet retiree area with mountain views and walkable medical\", \"best neighborhoods for an investor focused on STR yield\".","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"query":{"type":"string","minLength":3,"maxLength":500,"description":"Natural-language description of desired lifestyle + buyer profile, when no specific city is mentioned. Example: \"quiet retiree area in Utah with low crime, walkable to medical, mountain views\"."},"limit":{"type":"integer","minimum":1,"maximum":15,"default":5}},"required":["query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/explainMatch":{"post":{"operationId":"explainMatch","summary":"Produce a 2-3 sentence rationale for why a listing matches a buyer's natural-language query, citing specific signals (school name + distance, walkability minutes, deal score, etc.). Use when the user clicks \"why did you suggest this?\" or asks to compare match reasons across listings.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listingKey":{"type":"string","description":"Listing identifier to explain."},"query":{"type":"string","minLength":3,"maxLength":500,"description":"The original buyer query. The explainer re-runs the supervisor to know what to cite."}},"required":["listingKey","query"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/getMySavedSearches":{"post":{"operationId":"getMySavedSearches","summary":"Return the user's saved searches as selectable cards. Use this tool when the user asks \"show me my saved searches\", \"what's new in my saved searches?\", \"list my searches\", or similar.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{},"additionalProperties":true}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/getMySavedHomes":{"post":{"operationId":"getMySavedHomes","summary":"Fetch the user's most recently saved listings (newest first). Use this whenever the user asks to \"show my saved homes\", \"list my saves\", \"what did I save\", or asks to \"compare my saved homes\" / \"compare my recent saves\" — in the compare case, call this tool FIRST to get the listing_keys, then imm...","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":1,"maximum":6,"default":4,"description":"Number of most-recently-saved listings to fetch (1-6). Default 4 — matches the compareListings max so the result feeds straight into a comparison without further trimming."},"includeFullCards":{"type":"boolean","default":true,"description":"When true (default), resolve each saved listing to a full card payload (address, price, beds, photo) for direct rendering. Set false when the caller only needs the listing_keys (e.g. immediately chaining into compareListings)."}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/saveHome":{"post":{"operationId":"saveHome","summary":"Save or unsave a property to the user's saved homes list. Use \"save\" to add and \"unsave\" to remove.","tags":["Shared Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listingKey":{"type":"string","description":"Listing key to save/unsave"},"action":{"type":"string","enum":["save","unsave"],"default":"save","description":"Save or unsave"}},"required":["listingKey"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/findRealtor":{"post":{"operationId":"findRealtor","summary":"Find real estate agents/realtors with sales activity in a city, ranked by actual MLS transactions. Includes both Propaired-verified profiles AND MLS-listed stub agents (so the user sees the full local market).","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"specialty":{"type":"string","description":"Specialty area (e.g. luxury, first-time buyers)"},"city":{"type":"string","description":"City/area the realtor should cover"},"language":{"type":"string","description":"Preferred language"},"limit":{"type":"number","minimum":1,"maximum":4,"default":4,"description":"Number of results"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/getShowings":{"post":{"operationId":"getShowings","summary":"Get a filtered list of individual property viewings by status (upcoming/past/all). DO NOT use this for day planner, schedule overview, or \"what do I have today\" — use getMyDay for those.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["upcoming","past","all"],"default":"all","description":"Filter by showing status"},"limit":{"type":"number","minimum":1,"maximum":10,"default":10,"description":"Number of results"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/extractUpdateUserInfo":{"post":{"operationId":"extractUpdateUserInfo","summary":"Save the user's long-term housing preferences to their profile for personalized recommendations across sessions.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"object","properties":{"budget_min":{"type":"number","description":"Minimum budget in dollars"},"budget_max":{"type":"number","description":"Maximum budget in dollars"},"preferred_cities":{"type":"array","items":{"type":"string"},"description":"List of preferred cities"},"preferred_property_types":{"type":"array","items":{"type":"string"},"description":"E.g. Single Family, Condo, Townhouse"},"min_beds":{"type":"number","description":"Minimum bedrooms wanted"},"min_baths":{"type":"number","description":"Minimum bathrooms wanted"},"deal_breakers":{"type":"array","items":{"type":"string"},"description":"Things the user absolutely does not want"},"lifestyle_priorities":{"type":"array","items":{"type":"string"},"description":"E.g. good schools, walkability, quiet neighborhood"},"move_timeline":{"type":"string","description":"E.g. within 3 months, ASAP, no rush"},"client_tags":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","maxLength":80,"description":"Canonical tag name from CLIENT_TAGS taxonomy (lowercase-hyphen)"},"reason":{"type":"string","maxLength":300,"description":"Short paraphrase or verbatim quote of the triggering user statement"}},"required":["tag"],"additionalProperties":false},"description":"Buyer-archetype tags detected in this exchange. Only emit when CLEARLY signaled. See lib/constants/client-tags.ts for the canonical taxonomy (~55 tags across 8 categories)."}},"additionalProperties":false,"description":"Long-term user preferences to persist. Only include fields the user explicitly stated."}},"required":["preferences"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/connectWithRealtor":{"post":{"operationId":"connectWithRealtor","summary":"Connect the user with a realtor by creating a pairing request. IMPORTANT: Always confirm with the user before calling this tool. The user must explicitly agree to connect.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"realtorProfileId":{"type":"string","format":"uuid","description":"Realtor profile ID to connect with"},"message":{"type":"string","description":"Optional message to the realtor"}},"required":["realtorProfileId"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/saveSearch":{"post":{"operationId":"saveSearch","summary":"Save a set of search filters so the user can quickly re-run the search later. Automatically generates a name if not provided.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Search name (auto-generated if not provided)"},"filters":{"type":"object","additionalProperties":{},"description":"Search filter parameters to save"}},"required":["filters"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/tools/sendOfferRequest":{"post":{"operationId":"sendOfferRequest","summary":"Send a request to the user's connected realtor to start an offer on a property. IMPORTANT: Always confirm with the user before calling this tool.","tags":["Client Tools"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listingKey":{"type":"string","description":"Listing key to request offer for"},"notes":{"type":"string","description":"Additional notes for the realtor"}},"required":["listingKey"],"additionalProperties":false}}}},"responses":{"200":{"description":"Tool result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the tool executed successfully"},"data":{"type":"object","description":"Tool-specific result data"},"error":{"type":"string","description":"Error message if success is false"}}}}}},"401":{"description":"Missing or invalid API key"},"403":{"description":"Insufficient permissions or role mismatch"},"404":{"description":"Unknown tool"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/public/leads":{"get":{"operationId":"listPublicLeads","summary":"List leads owned by the API key's account, paginated.","tags":["Public API"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"source","in":"query","schema":{"type":"string"}},{"name":"search","in":"query","schema":{"type":"string"}},{"name":"min_score","in":"query","schema":{"type":"number"}},{"name":"max_score","in":"query","schema":{"type":"number"}},{"name":"limit","in":"query","schema":{"type":"integer","default":25}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated leads"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}},"post":{"operationId":"createPublicLead","summary":"Create a lead. Runs the full pipeline: smart-tag, route, emit lead.created.","tags":["Public API"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","maxLength":100},"email":{"anyOf":[{"anyOf":[{"not":{}},{"type":"string","format":"email"}]},{"type":"string","const":""}]},"phone":{"type":"string","maxLength":20},"source":{"type":"string","enum":["website","referral","social_media","zillow","realtor_com","cold_call","open_house","advertising","cold_csv","api","listing_inquiry","profile_contact","other"]},"status":{"type":"string","enum":["new","contacted","qualified","nurturing","converted","lost"]},"score":{"type":"number","minimum":0,"maximum":100},"notes":{"type":"string","maxLength":2000},"budget_min":{"type":"number","minimum":0},"budget_max":{"type":"number","minimum":0},"property_type":{"type":"string","maxLength":100},"timeline":{"type":"string","maxLength":200},"idempotency_key":{"type":"string","maxLength":200}},"required":["first_name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Lead created"},"400":{"description":"Invalid request body"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/public/leads/{id}":{"get":{"operationId":"getPublicLead","summary":"Fetch a single lead owned by the API key's account.","tags":["Public API"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Lead"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"404":{"description":"Lead not found, or not owned by this account"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}},"patch":{"operationId":"updatePublicLead","summary":"Update a lead owned by the API key's account.","tags":["Public API"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","maxLength":100},"email":{"anyOf":[{"anyOf":[{"not":{}},{"type":"string","format":"email"}]},{"type":"string","const":""}]},"phone":{"type":"string","maxLength":20},"source":{"type":"string","enum":["website","referral","social_media","zillow","realtor_com","cold_call","open_house","advertising","cold_csv","api","listing_inquiry","profile_contact","other"]},"status":{"type":"string","enum":["new","contacted","qualified","nurturing","converted","lost"]},"score":{"type":"number","minimum":0,"maximum":100},"notes":{"type":"string","maxLength":2000},"budget_min":{"type":"number","minimum":0},"budget_max":{"type":"number","minimum":0},"property_type":{"type":"string","maxLength":100},"timeline":{"type":"string","maxLength":200}},"additionalProperties":false}}}},"responses":{"200":{"description":"Updated lead"},"400":{"description":"Invalid request body"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"404":{"description":"Lead not found, or not owned by this account"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}},"/api/public/contacts":{"get":{"operationId":"listPublicContacts","summary":"List contacts (non-lead clients) owned by the API key's account, paginated.","tags":["Public API"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"search","in":"query","schema":{"type":"string"}},{"name":"lifecycle_stage","in":"query","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":25}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated contacts"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}},"post":{"operationId":"createPublicContact","summary":"Create a contact. Emits client.created — does not run the lead pipeline.","tags":["Public API"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","minLength":1,"maxLength":100},"last_name":{"type":"string","maxLength":100},"email":{"anyOf":[{"anyOf":[{"not":{}},{"type":"string","format":"email"}]},{"type":"string","const":""}]},"phone":{"type":"string","maxLength":20},"lifecycle_stage":{"type":"string","enum":["prospect","active","active_buyer","active_seller","investor","past_buyer","past_seller","homeowner"]},"notes":{"type":"string","maxLength":2000},"idempotency_key":{"type":"string","maxLength":200}},"required":["first_name"],"additionalProperties":false}}}},"responses":{"201":{"description":"Contact created"},"400":{"description":"Invalid request body"},"401":{"description":"Missing, invalid, or expired API key"},"403":{"description":"Valid key but missing the required scope"},"429":{"description":"Rate limited"},"500":{"description":"Internal error"}}}}},"components":{"schemas":{},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key (pk_...) from your Propaired Settings → AI Integrations page"}}},"tags":[{"name":"Shared Tools","description":"Available to both clients and realtors"},{"name":"Client Tools","description":"Client-only tools (home search, realtor matching)"},{"name":"Public API","description":"External developer REST API for leads and contacts (pk_ API key auth)"}]}