{"openapi":"3.1.0","info":{"title":"PegaRex API","description":"Public read API for PegaRex — a Brazilian vehicle meta-search engine aggregating 1.8M+ car & motorcycle listings from 96 platforms. No auth, CORS-open. LLM agents can also use the MCP server at /api/mcp (sign-in with a free PegaRex account; see /llms.txt). Free-text `query` matches the listing title only; for exact filtering resolve brand/model to ids via /api/brands and /api/models.","version":"2.0.0"},"paths":{"/api/auth/signup":{"post":{"tags":["Authentication"],"summary":"Signup","description":"Register a new user. Account is created in unverified state and a\n6-digit code is emailed. The session is NOT issued until verification.","operationId":"signup_api_auth_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignUpRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/verify-email":{"post":{"tags":["Authentication"],"summary":"Verify Email","description":"Confirm signup with the 6-digit code, then issue tokens.","operationId":"verify_email_api_auth_verify_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/resend-code":{"post":{"tags":["Authentication"],"summary":"Resend Code","description":"Resend the email-verification code. Rate-limited per email+IP.","operationId":"resend_code_api_auth_resend_code_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendCodeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/login":{"post":{"tags":["Authentication"],"summary":"Login","description":"Email+password login. Unverified accounts get a fresh code re-sent\ninstead of a session.","operationId":"login_api_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/google":{"post":{"tags":["Authentication"],"summary":"Google Auth","description":"Sign in (or sign up) with a Google ID token from the frontend GIS button.\n\nNo email-verification code needed — Google already verifies email.","operationId":"google_auth_api_auth_google_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GoogleAuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/forgot-password":{"post":{"tags":["Authentication"],"summary":"Forgot Password","description":"Email a reset code. Always returns the same message to avoid leaking\nwhether an email exists.","operationId":"forgot_password_api_auth_forgot_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgotPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/reset-password":{"post":{"tags":["Authentication"],"summary":"Reset Password","description":"Validate the code and set a new password. Revokes all sessions.","operationId":"reset_password_api_auth_reset_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPasswordRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/logout":{"post":{"tags":["Authentication"],"summary":"Logout","description":"Revoke the session and clear the refresh cookie. Reads the refresh token\nfrom the cookie (not the access token), so logout still works even if the\naccess token has already expired.","operationId":"logout_api_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/refresh":{"post":{"tags":["Authentication"],"summary":"Refresh Token","description":"Rotate the session: validate the refresh token from the cookie, revoke it,\nissue a new access+refresh pair, and set the new refresh cookie. Returns only\nthe access token in the body.","operationId":"refresh_token_api_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/me":{"get":{"tags":["Authentication"],"summary":"Get Me","operationId":"get_me_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/auth/password-update":{"post":{"tags":["Authentication"],"summary":"Update Password","description":"Update password for authenticated user. Requires current password.\n\nFor Google-only users without a password, this acts as 'set a password'\nafter a verified session — still safe because the user is authenticated.","operationId":"update_password_api_auth_password_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordUpdateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/auth/oauth/providers":{"get":{"tags":["Authentication"],"summary":"List Providers","description":"Which social logins are actually usable right now.\n\nThe frontend renders a button only for what this returns, so a half-\nconfigured provider is invisible instead of being a button that 500s.","operationId":"list_providers_api_auth_oauth_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/oauth/{provider_name}/start":{"get":{"tags":["Authentication"],"summary":"Oauth Start","description":"Kick off the login: stash state in a cookie, bounce to the provider.\n\n`rerequest` is set only by our own callback, when the provider came back\nmissing a permission we require. It re-opens the dialog asking for the\ndeclined permission again, and marks the state so we can tell a retry from\na first attempt and never bounce more than once.","operationId":"oauth_start_api_auth_oauth__provider_name__start_get","parameters":[{"name":"provider_name","in":"path","required":true,"schema":{"type":"string","title":"Provider Name"}},{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}},{"name":"rerequest","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Rerequest"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/oauth/{provider_name}/callback":{"get":{"tags":["Authentication"],"summary":"Oauth Callback","description":"Provider hands back a code; trade it for tokens, resolve the user, open a\nsession and bounce back to the site.\n\nFailures redirect to the frontend with ?auth_error=... rather than rendering\na raw API error page at the end of a login the user started in the UI.","operationId":"oauth_callback_api_auth_oauth__provider_name__callback_get","parameters":[{"name":"provider_name","in":"path","required":true,"schema":{"type":"string","title":"Provider Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/favorites":{"get":{"tags":["Favorites"],"summary":"Get Favorites","description":"List the user's favorites, merging live car_listings data when the car is still active.\n\nInactive cars (LEFT JOIN miss) fall back to the snapshot stored at favorite-time and\nreturn is_active=false so the UI can show \"no longer available\".","operationId":"get_favorites_api_favorites_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/favorites/ids":{"get":{"tags":["Favorites"],"summary":"Get Favorite Ids","description":"External IDs of the user's favorites, used by the frontend for star-state lookups.\n\nReturns one entry per favorite. For still-active favorites, uses the live car_listings.id;\nfor deactivated ones, falls back to the snapshot car_id so the star stays consistent on\nthe favorites page.","operationId":"get_favorite_ids_api_favorites_ids_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/favorites/toggle":{"post":{"tags":["Favorites"],"summary":"Toggle Favorite","description":"Toggle favorite by external_id (which is the car_listings.id at favorite-time).\n\nOn add: snapshot the current car_listings row into the favorites row.\nOn remove: simple delete.","operationId":"toggle_favorite_api_favorites_toggle_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteCarRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/favorites/{car_id}":{"post":{"tags":["Favorites"],"summary":"Add Favorite","description":"Add a car to favorites by DB ID, snapshotting the current car_listings row.","operationId":"add_favorite_api_favorites__car_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FavoriteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Favorites"],"summary":"Remove Favorite","description":"Remove a car from favorites by DB ID","operationId":"remove_favorite_api_favorites__car_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/favorites/by-external/{source}/{external_id}":{"delete":{"tags":["Favorites"],"summary":"Remove Favorite By External","description":"Remove a favorite by external_id (which is the snapshot car_id) and source.","operationId":"remove_favorite_by_external_api_favorites_by_external__source___external_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"source","in":"path","required":true,"schema":{"type":"string","title":"Source"}},{"name":"external_id","in":"path","required":true,"schema":{"type":"string","title":"External Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/favorites/{car_id}/check":{"get":{"tags":["Favorites"],"summary":"Check Favorite","description":"Check if a car is in favorites","operationId":"check_favorite_api_favorites__car_id__check_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/favorites/searches":{"get":{"tags":["Favorites"],"summary":"Get Saved Searches","description":"Get all saved searches for the current user","operationId":"get_saved_searches_api_favorites_searches_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Favorites"],"summary":"Save Search","description":"Save a search","operationId":"save_search_api_favorites_searches_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SaveSearchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/favorites/searches/{search_id}":{"delete":{"tags":["Favorites"],"summary":"Delete Saved Search","description":"Delete a saved search","operationId":"delete_saved_search_api_favorites_searches__search_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"search_id","in":"path","required":true,"schema":{"type":"string","title":"Search Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/favorites/history":{"get":{"tags":["Favorites"],"summary":"Get Search History","description":"Get search history for the current user","operationId":"get_search_history_api_favorites_history_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","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/favorites/alerts":{"get":{"tags":["Favorites"],"summary":"Get Price Alerts","description":"List active price alerts. Same LEFT JOIN pattern as favorites — if the car has been\ndeactivated, fall back to the snapshot fields and surface is_active=false.","operationId":"get_price_alerts_api_favorites_alerts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Favorites"],"summary":"Create Price Alert","description":"Create a price alert, snapshotting the car for offline rendering after deactivation.","operationId":"create_price_alert_api_favorites_alerts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceAlertRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/favorites/alerts/{alert_id}":{"delete":{"tags":["Favorites"],"summary":"Delete Price Alert","description":"Delete a price alert","operationId":"delete_price_alert_api_favorites_alerts__alert_id__delete","security":[{"HTTPBearer":[]}],"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/calculator/brands":{"get":{"tags":["calculator"],"summary":"Get Brands","description":"Return all FIPE car brands, canonicalized and de-duplicated.\n\n`year` lets the cascade run in any order: pick a year first and the brand\nlist narrows to the brands that actually have entries for it, instead of\noffering 222 brands and only failing later.","operationId":"get_brands_api_calculator_brands_get","parameters":[{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Only brands with entries for this year","title":"Year"},"description":"Only brands with entries for this year"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/models":{"get":{"tags":["calculator"],"summary":"Get Models","description":"Return all models for a given brand, canonicalized and de-duplicated.","operationId":"get_models_api_calculator_models_get","parameters":[{"name":"brand_code","in":"query","required":true,"schema":{"type":"string","description":"Brand display name (case-insensitive)","title":"Brand Code"},"description":"Brand display name (case-insensitive)"},{"name":"year","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Only models with entries for this year","title":"Year"},"description":"Only models with entries for this year"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/years":{"get":{"tags":["calculator"],"summary":"Get Years","description":"Available years for the current selection.\n\nWith a brand AND model, returns year/fuel COMBINATIONS — the fuel variant is\npart of what identifies a FIPE row, so /evaluate needs it. That is the code\nformat \"2020-Gasolina\".\n\nWith neither, returns PLAIN years (\"2020\"), so the control can be used as a\nfilter before anything else is picked. The frontend tells the two apart by\nwhether the code contains a \"-\", and only enables the lookup once it holds a\nfull combination.","operationId":"get_years_api_calculator_years_get","parameters":[{"name":"brand_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand Code"}},{"name":"model_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/evaluate":{"get":{"tags":["calculator"],"summary":"Evaluate","description":"FIPE valuation for one vehicle configuration.\n\nAnonymous: current-month price only.\nLogged-in: current-month price + up to 12 months of history.","operationId":"evaluate_api_calculator_evaluate_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"brand_code","in":"query","required":true,"schema":{"type":"string","title":"Brand Code"}},{"name":"model_code","in":"query","required":true,"schema":{"type":"string","title":"Model Code"}},{"name":"year_code","in":"query","required":true,"schema":{"type":"string","title":"Year Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/evaluate/fipe/{fipe_code}":{"get":{"tags":["calculator"],"summary":"Evaluate By Code","description":"Instant valuation lookup by FIPE code.\n\nAnonymous: current-month prices for each year/fuel variant.\nLogged-in: same + 12-month history for the primary (newest-year) variant.","operationId":"evaluate_by_code_api_calculator_evaluate_fipe__fipe_code__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fipe_code","in":"path","required":true,"schema":{"type":"string","title":"Fipe Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/search":{"get":{"tags":["calculator"],"summary":"Search Vehicles","description":"Type-ahead over FIPE's current table (fipe_search.py): versions ranked by match and by\nhow many ads we have of them, plus the brands the first word could mean. An empty query\nreturns the popular brands and versions instead.","operationId":"search_vehicles_api_calculator_search_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":80,"description":"Marca, modelo, versão, ano ou código FIPE","default":"","title":"Q"},"description":"Marca, modelo, versão, ano ou código FIPE"},{"name":"tipo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"carros | motos | caminhoes","title":"Tipo"},"description":"carros | motos | caminhoes"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":30,"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/calculator/explore":{"get":{"tags":["calculator"],"summary":"Explore","description":"Popular brands and versions — what the page shows before anything is typed.","operationId":"explore_api_calculator_explore_get","parameters":[{"name":"tipo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tipo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calculator/vehicle/{fipe_code}":{"get":{"tags":["calculator"],"summary":"Vehicle","description":"Everything the result page shows, in one call, for anyone (no login):\n- the version's variants (year x fuel) from ONE FIPE table (see _LATEST_MONTH_OF);\n- the chosen variant's price and its monthly history back to Dez/2007 (fipe_series.py),\n  with the 1- and 12-month change;\n- what the market asks for it: live ads with this FIPE code and model year (median and\n  quartiles; index-only on idx_car_listings_fipe_cover), and a link to those ads.","operationId":"vehicle_api_calculator_vehicle__fipe_code__get","parameters":[{"name":"fipe_code","in":"path","required":true,"schema":{"type":"string","title":"Fipe Code"}},{"name":"ano","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"2020, 0km …","title":"Ano"},"description":"2020, 0km …"},{"name":"combustivel","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Combustivel"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/alerts":{"get":{"tags":["Vehicle Alerts"],"summary":"Get Alerts","description":"List all alerts for the current user with match counts","operationId":"get_alerts_api_alerts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AlertResponse"},"type":"array","title":"Response Get Alerts Api Alerts Get"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Vehicle Alerts"],"summary":"Create Alert","description":"Create a new vehicle alert — at most MAX_ALERTS_PER_USER per account.\n\nCounts every alert the user has, active or not: that is the list the alerts\npage shows, so the limit matches what the user can see and delete.","operationId":"create_alert_api_alerts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleAlertRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/alerts/preview":{"post":{"tags":["Vehicle Alerts"],"summary":"Preview Alert","description":"How many active listings an unsaved alert draft matches right now.\n\nLets the form answer \"is this alert too narrow?\" before it is saved, instead\nof leaving someone to find out days later from an inbox that stayed empty.\nDeliberately NOT routed through /api/search: that endpoint has its own\nlocation and sort semantics and burns the daily anti-scraping quota, and a\npreview that disagrees with the alert is worse than no preview.\n\nA count needs a model — brand-only spans ~200k rows and turns a\nkeystroke-debounced preview into a multi-second query.","operationId":"preview_alert_api_alerts_preview_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleAlertRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/alerts/email-preference":{"get":{"tags":["Vehicle Alerts"],"summary":"Get Email Preference","description":"Whether the nightly alert email goes out to this user.","operationId":"get_email_preference_api_alerts_email_preference_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"put":{"tags":["Vehicle Alerts"],"summary":"Set Email Preference","description":"Turn the alert emails on or off from the alerts page. Emails only: the\nalerts and their matches on the site are untouched either way.","operationId":"set_email_preference_api_alerts_email_preference_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailPreference"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/alerts/{alert_id}":{"put":{"tags":["Vehicle Alerts"],"summary":"Update Alert","description":"Update an existing vehicle alert","operationId":"update_alert_api_alerts__alert_id__put","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VehicleAlertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Vehicle Alerts"],"summary":"Delete Alert","description":"Delete a vehicle alert","operationId":"delete_alert_api_alerts__alert_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","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/{alert_id}/cars":{"get":{"tags":["Vehicle Alerts"],"summary":"Get Alert Matches","description":"The NEW arrivals recorded against this alert — not everything on the site\nthat happens to fit it — one page at a time, unseen ones first.\n\nThis deliberately reads alert_matches rather than re-querying car_listings.\nAn alert answers \"what showed up since I asked?\", so a listing only appears\nhere once a sync has brought it in and a matcher has recorded it against this\nalert; the full standing inventory is one click away behind the search page's\nown filters (\"Ver na busca\" in the UI), newest listings first.\n\nA freshly-created alert is therefore empty until the next sync runs, which is\nintended — the empty state says so.\n\nLIFETIME: a match stays until the user has seen it, at most 7 days from\nmatched_at (expires_at, set at insert). The nightly digest deletes expired\nrows and seen+emailed rows once their car is past the matcher's 48h window\n(deleting sooner would let the matcher re-insert them); the expires_at\nfilter here keeps the list honest in between, and expires_at rides along so\nthe UI can count down.\n\nORDER: cars not seen before this visit first, then cars seen on a previous\nvisit (the UI shows those darker, marked \"visto\"). \"Before this visit\" is\ndecided by `visit`, a server timestamp: the first page (no cursor) takes\nNOW() and returns it; the client sends it back on later pages, and /read\nstamps read_at on the same clock — so a car marked seen DURING the visit\nnever jumps to the end mid-session and pages don't shift as they are read.\nWithin each group: newest sync batch first, then the digest email's order\n(first_seen_at DESC, car_id DESC), so the cars an email lists are the first\nones here. Every part of the key is load-bearing: one sync stamps its whole\nbatch with a single matched_at (9,874 cars in one production batch), and\nthose shared only 1,184 distinct first_seen_at values, so car_id is what\nmakes the order total.\n\nPAGINATION is keyset over that 4-part key, not OFFSET: a sync can insert or\nexpire matches while someone is paging, and an offset would then repeat or\nskip cars. `total` and `unread_total` come with the first page only.","operationId":"get_alert_matches_api_alerts__alert_id__cars_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Cursor"}},{"name":"visit","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visit"}}],"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}/read":{"post":{"tags":["Vehicle Alerts"],"summary":"Mark Alert As Read","description":"Mark matches as seen — the ids in the body (the page just rendered), or\nevery match of the alert when there is no body.\n\nPer page, deliberately: with 165 pages, \"seen\" has to mean the cars that\nwere actually on screen, or one open would retire an alert the reader has\nbarely looked at. Only rows still unread are touched — this used to rewrite\nevery match on every open (39,326 rows, 3.3 s, on the largest alert).\nread_at is stamped on the server clock, the same clock /cars uses for\n`visit`, which is what keeps a car marked during a visit from being sorted\nto the end before the visit is over.","operationId":"mark_alert_as_read_api_alerts__alert_id__read_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Alert Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ReadRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/track":{"post":{"tags":["Analytics"],"summary":"Track Events","description":"Accept a batch of behavioral events. Returns 204 (no body) on success.","operationId":"track_events_api_track_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrackBatch"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/cars/{car_id}/similar":{"get":{"tags":["Recommendations"],"summary":"Similar Cars","description":"Return the top-N similar cars for a given car_id (cached in-process).","operationId":"similar_cars_api_cars__car_id__similar_get","parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":24,"minimum":1,"default":12,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/me":{"get":{"tags":["Admin"],"summary":"Admin Me","description":"Probe endpoint — frontend uses this to check 'am I an admin?' before rendering /painel.\n`is_owner` decides whether the page shows access management.","operationId":"admin_me_api_admin_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/overview":{"get":{"tags":["Admin"],"summary":"Overview","description":"Top-line numbers for the dashboard header.","operationId":"overview_api_admin_overview_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/events-by-day":{"get":{"tags":["Admin"],"summary":"Events By Day","description":"Daily counts of each event type — drives the stacked chart.","operationId":"events_by_day_api_admin_events_by_day_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":14,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/top-viewed-cars":{"get":{"tags":["Admin"],"summary":"Top Viewed Cars","description":"Most-viewed cars in the last N days. Drives the 'popular cars' list\nAND eventually feeds the precompute prioritization.","operationId":"top_viewed_cars_api_admin_top_viewed_cars_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","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/admin/top-searches":{"get":{"tags":["Admin"],"summary":"Top Searches","description":"Most-used search filters. Tells you what users actually look for.\n\nThe search UI sends brand_id / model_id (catalog ids), not names — only\nthe free-text API path ever sends `brand` as a string. This used to group\non the string, so every dropdown search fell into \"(any) / (any)\" and the\ntable said nothing (on the office mirror: 0 of 60 events had a brand\nstring, 32 had a brand_id). Resolve the ids through the catalog and keep\nthe string as a fallback for the API path.\n\nThe ids arrive as JSON text; the regex guard keeps a malformed value from\nfailing the whole query on the cast.","operationId":"top_searches_api_admin_top_searches_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/funnel":{"get":{"tags":["Admin"],"summary":"Funnel","description":"Visitor → searcher → viewer → outbound. Each step is a distinct anon_id\nthat produced that event. Gives a top-of-funnel conversion shape.","operationId":"funnel_api_admin_funnel_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/sources":{"get":{"tags":["Admin"],"summary":"Traffic Sources","description":"Where sessions come from — classified from the referrer of each\nsession's FIRST page_view.\n\nPer session, not per page_view: in a single-page app document.referrer\nkeeps the original external value across in-app navigation, so counting\npage views would weight a source by how many pages its visitors clicked\nthrough rather than by how many of them arrived.\n\nAn internal (pegarex.com.br) referrer on a session's first page view is\nworth its own bucket: the SEO landing pages under /precos and the brand\nhubs are served by landing_routes.py rather than the SPA, so they emit no\npage_view of their own — a session that arrives from one shows up here\nwith the site itself as the referrer. Hard reloads land there too.\n\nSame consent caveat as everything on this page: only visitors who accepted\nthe cookie banner are tracked at all.","operationId":"traffic_sources_api_admin_sources_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/pages":{"get":{"tags":["Admin"],"summary":"Top Pages","description":"Which pages get seen. Car detail pages are collapsed into one bucket —\nthere are millions of them and top-viewed-cars already ranks them\nindividually. Path comes from props.path (set by PageViewTracker); the url\ncolumn is the fallback for any older row without it.","operationId":"top_pages_api_admin_pages_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":15,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/recent-signups":{"get":{"tags":["Admin"],"summary":"Recent Signups","description":"Last N signups. Pure ops info — useful for sanity-checking that auth still works.","operationId":"recent_signups_api_admin_recent_signups_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","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/admin/search-usage":{"get":{"tags":["Admin"],"summary":"Search Usage","description":"Per-logged-in-user search-page volume, to spot likely scrapers. Flags\naccounts whose peak day exceeded the per-user daily limit. Backed by the\nsame anon_search_usage counters (scope='user').","operationId":"search_usage_api_admin_search_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/mcp-usage":{"get":{"tags":["Admin"],"summary":"Mcp Usage","description":"Use of the public MCP server (/api/mcp), from mcp_calls (migration 049):\ntotals, calls per day, per tool, per client and user-agent, and the latest\ncalls. Tool errors are read from the reply, because MCP answers a failed tool\nwith HTTP 200; rate-limited requests are the 429s. What went wrong is split\nthree ways (see _MCP_FAIL and below), because one \"errors\" number counted\nevery crawler that used the protocol wrong as a failure of ours.","operationId":"mcp_usage_api_admin_mcp_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","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/admin/feedback/summary":{"get":{"tags":["Admin"],"summary":"Feedback Summary","description":"Counts for the panel badge, plus today's volume so a flood is visible\nat a glance rather than only in the digest.","operationId":"feedback_summary_api_admin_feedback_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/feedback/suggestions":{"get":{"tags":["Admin"],"summary":"Feedback Suggestions","description":"Suggested sites, GROUPED BY HOST — this is the scraper backlog ranked by\ndemand, so five people asking for one site must read as one strong row and\nnot five weak ones. distinct_users matters more than the raw count: five\nsuggestions from one account is one person, not five.","operationId":"feedback_suggestions_api_admin_feedback_suggestions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/feedback/reports":{"get":{"tags":["Admin"],"summary":"Feedback Reports","description":"Reported problems, each with the snapshot AND the listing as it stands\nnow, so the panel can show \"reportado R$ 89.900 -> agora R$ 84.900\" without\na second query. LEFT JOIN, never inner: a listing that vanished is exactly\nthe case worth seeing, and the nightly swap makes that common.","operationId":"feedback_reports_api_admin_feedback_reports_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/feedback/reports/{report_id}":{"patch":{"tags":["Admin"],"summary":"Patch Report","operationId":"patch_report_api_admin_feedback_reports__report_id__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"integer","title":"Report Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackStatusIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/feedback/suggestions/{host}":{"patch":{"tags":["Admin"],"summary":"Patch Suggestion","description":"Status is set per HOST, not per row — the panel groups by host, so a\ndecision is about the site, not about whichever person mentioned it.","operationId":"patch_suggestion_api_admin_feedback_suggestions__host__patch","security":[{"HTTPBearer":[]}],"parameters":[{"name":"host","in":"path","required":true,"schema":{"type":"string","title":"Host"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackStatusIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/feedback/block":{"post":{"tags":["Admin"],"summary":"Block User","description":"Silence an account. Its submissions keep returning 201 and are discarded\n— telling it would only prompt a new account.","operationId":"block_user_api_admin_feedback_block_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/feedback/block/{user_id}":{"delete":{"tags":["Admin"],"summary":"Unblock User","operationId":"unblock_user_api_admin_feedback_block__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","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/admin/fipe/coverage":{"get":{"tags":["Admin"],"summary":"Fipe Coverage","description":"Per-month FIPE coverage, which month anonymous users see, and the gaps in\nthe rolling 12-month window. The at-a-glance answer to 'do we have the data?'.","operationId":"fipe_coverage_api_admin_fipe_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/fipe/upload":{"post":{"tags":["Admin"],"summary":"Fipe Upload","description":"Upload one month's FIPE CSV. Validates a single month, guards against\npartial files, normalizes brands/models, and upserts into fipe_prices.","operationId":"fipe_upload_api_admin_fipe_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_fipe_upload_api_admin_fipe_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/fipe/normalize":{"post":{"tags":["Admin"],"summary":"Fipe Normalize Data","description":"Clean up existing FIPE data: consistent brand casing + fuel labels + tidy\nmodel whitespace. Run once; uploads keep new data clean automatically.","operationId":"fipe_normalize_data_api_admin_fipe_normalize_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/access":{"get":{"tags":["Admin"],"summary":"List Panel Access","description":"Who can open /painel: owners (ADMIN_EMAILS — listed, never removable here) and\nevery account granted from the page.","operationId":"list_panel_access_api_admin_access_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Admin"],"summary":"Grant Panel Access","description":"Let an existing, e-mail-verified account open /painel.","operationId":"grant_panel_access_api_admin_access_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessGrantIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/access/users":{"get":{"tags":["Admin"],"summary":"Search Users For Access","description":"Find an existing account to grant access to. Needs 3+ characters and returns at\nmost 10 rows: a lookup for someone the owner already has in mind, not a listing of\nthe user base.","operationId":"search_users_for_access_api_admin_access_users_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","default":"","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/access/{user_id}":{"delete":{"tags":["Admin"],"summary":"Revoke Panel Access","description":"Take /painel away from a granted account. Takes effect on that account's next\nrequest — require_admin reads panel_access every time instead of caching it.","operationId":"revoke_panel_access_api_admin_access__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","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/me/recommendations":{"get":{"tags":["Recommendations"],"summary":"My Recommendations","description":"Personalized rail. Returns up to `limit` cars.\n\nFor logged-in users we look up personalized recs first; for anonymous\nusers (and cold-start logged-in users) we serve popular cars.","operationId":"my_recommendations_api_me_recommendations_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":24,"minimum":1,"default":12,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chat/car/{car_id}":{"post":{"tags":["chat"],"summary":"Chat Car Stream","description":"Stream the assistant's answer as Server-Sent Events.","operationId":"chat_car_stream_api_chat_car__car_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chat/car/{car_id}/sync":{"post":{"tags":["chat"],"summary":"Chat Car Sync","description":"Non-streaming variant: returns the full answer in one JSON response.","operationId":"chat_car_sync_api_chat_car__car_id__sync_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/seo/car/{brand_slug}/{model_slug}/{car_id}":{"get":{"summary":"Seo Car Descriptive","description":"/car/{marca}/{modelo}/{id} — the canonical listing URL.","operationId":"seo_car_descriptive_seo_car__brand_slug___model_slug___car_id__get","parameters":[{"name":"brand_slug","in":"path","required":true,"schema":{"type":"string","title":"Brand Slug"}},{"name":"model_slug","in":"path","required":true,"schema":{"type":"string","title":"Model Slug"}},{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/seo/car/{car_id}":{"get":{"summary":"Seo Car","description":"Bare /car/{id}. Kept working forever (1.8M of these exist in the wild),\nbut 301s to the descriptive form so crawlers settle on one URL per listing.","operationId":"seo_car_seo_car__car_id__get","parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/fipe/{brand}/{model}":{"get":{"summary":"Fipe Page","description":"/fipe/{marca}/{modelo} — FIPE reference table vs real market prices.","operationId":"fipe_page_fipe__brand___model__get","parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"model","in":"path","required":true,"schema":{"type":"string","title":"Model"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/precos/{brand}/{model}":{"get":{"summary":"Precos Page","operationId":"precos_page_precos__brand___model__get","parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"model","in":"path","required":true,"schema":{"type":"string","title":"Model"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/precos/populares":{"get":{"summary":"Popular Price Pages","description":"The most-listed models that have a price page, for the home page's block\nof links into /precos. The home page is the site's most-crawled page, and\nuntil this it linked to no model page at all.","operationId":"popular_price_pages_api_precos_populares_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/precos":{"get":{"summary":"Precos Index","description":"Crawl hub: every brand → brand hub → model pages (the internal link graph).","operationId":"precos_index_precos_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/precos/estados":{"get":{"summary":"Precos Estados","description":"State hub: the crawl entry point for the state-scoped model pages.","operationId":"precos_estados_precos_estados_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/precos/{brand}":{"get":{"summary":"Precos Brand Page","description":"Brand hub: brand-wide price stats + the brand's models (each links to its model page).","operationId":"precos_brand_page_precos__brand__get","parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string","title":"Brand"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/precos/{brand}/{model}/{scope}":{"get":{"summary":"Precos Scope Page","description":"One path, two page types — the third segment is either a model year\n(/precos/honda/civic/2020) or a state UF (/precos/honda/civic/sp). Declaring\nit as `str` and dispatching here keeps the URL shape the boss's guide asks\nfor; a typed `{year:int}` route would 422 on the UF form instead.","operationId":"precos_scope_page_precos__brand___model___scope__get","parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"model","in":"path","required":true,"schema":{"type":"string","title":"Model"}},{"name":"scope","in":"path","required":true,"schema":{"type":"string","title":"Scope"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/precos/{brand}/{model}/{year}/{uf}":{"get":{"summary":"Precos Year State Page","description":"Model + year + state, e.g. /precos/jeep/renegade/2020/sc.","operationId":"precos_year_state_page_precos__brand___model___year___uf__get","parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string","title":"Brand"}},{"name":"model","in":"path","required":true,"schema":{"type":"string","title":"Model"}},{"name":"year","in":"path","required":true,"schema":{"type":"integer","title":"Year"}},{"name":"uf","in":"path","required":true,"schema":{"type":"string","title":"Uf"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/mcp":{"get":{"summary":"Mcp Page","operationId":"mcp_page_mcp_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/sobre/metodologia":{"get":{"summary":"Methodology Page","operationId":"methodology_page_sobre_metodologia_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/sitemap.xml":{"get":{"summary":"Sitemap Index","operationId":"sitemap_index_sitemap_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-static.xml":{"get":{"summary":"Sitemap Static","operationId":"sitemap_static_sitemap_static_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-brands.xml":{"get":{"summary":"Sitemap Brands","operationId":"sitemap_brands_sitemap_brands_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-models.xml":{"get":{"summary":"Sitemap Models","operationId":"sitemap_models_sitemap_models_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-model-years.xml":{"get":{"summary":"Sitemap Model Years","operationId":"sitemap_model_years_sitemap_model_years_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-model-states.xml":{"get":{"summary":"Sitemap Model States","operationId":"sitemap_model_states_sitemap_model_states_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-model-year-states.xml":{"get":{"summary":"Sitemap Model Year States","operationId":"sitemap_model_year_states_sitemap_model_year_states_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-model-trims.xml":{"get":{"summary":"Sitemap Model Trims","description":"Trim (acabamento) pages — the grain users actually search\n(\"mobi like\", \"onix lt\"). Membership and the URL slug both come straight\nfrom mv_model_trim_stats, which already applies the >=25 floor.","operationId":"sitemap_model_trims_sitemap_model_trims_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/sitemap-fipe.xml":{"get":{"summary":"Sitemap Fipe","description":"FIPE table pages. \"tabela fipe\" is our single largest search intent\n(871 of 2,601 impressions), so these get a high priority.","operationId":"sitemap_fipe_sitemap_fipe_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/review/me":{"get":{"tags":["Review"],"summary":"Review Me","description":"Probe the frontend calls before rendering /review.","operationId":"review_me_api_review_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/queue":{"get":{"tags":["Review"],"summary":"Queue","operationId":"queue_api_review_queue_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"tab","in":"query","required":false,"schema":{"type":"string","default":"review","title":"Tab"}},{"name":"brand","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"}},{"name":"flag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flag"}},{"name":"min_listings","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Listings"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"resolved_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolved By"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"hide_stale","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Hide Stale"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/spec/{spec_key}":{"get":{"tags":["Review"],"summary":"Spec Detail","operationId":"spec_detail_api_review_spec__spec_key__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"spec_key","in":"path","required":true,"schema":{"type":"string","title":"Spec Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/fipe/search":{"get":{"tags":["Review"],"summary":"Fipe Search","description":"Typeahead for the corrected code on a reject. Returns each code's priced years so the\nreviewer can see before submitting whether the code is even priced for the spec's year.","operationId":"fipe_search_api_review_fipe_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"brand","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/fipe/table":{"get":{"tags":["Review"],"summary":"Fipe Table","description":"FIPE prices by year for one code, beside the real market median.\n\nExists so a reviewer never has to leave the page for fipe.org.br: we already hold\nevery month FIPE publishes, and the decision they are making is precisely \"does this\ncode price like this car\".","operationId":"fipe_table_api_review_fipe_table_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"fipe_code","in":"query","required":true,"schema":{"type":"string","minLength":6,"maxLength":12,"title":"Fipe Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/stats":{"get":{"tags":["Review"],"summary":"Stats","operationId":"stats_api_review_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/history":{"get":{"tags":["Review"],"summary":"History","operationId":"history_api_review_history_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"reviewer_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reviewer Email"}},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action"}},{"name":"brand","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/family":{"get":{"tags":["Review"],"summary":"Family","description":"Sibling specs carrying the same wrong code, so one judgement can fix the whole family.\n\n`target_code` is the code the reviewer is about to apply (the correction on a reject, or\nthe existing code on an accept); eligibility is computed against it, so the list updates\nas they pick a different code.","operationId":"family_api_review_family_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"spec_key","in":"query","required":true,"schema":{"type":"string","title":"Spec Key"}},{"name":"target_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Code"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":60,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/brands":{"get":{"tags":["Review"],"summary":"Brands","description":"Brand filter options, ordered by how much of the queue they represent.","operationId":"brands_api_review_brands_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/decide":{"post":{"tags":["Review"],"summary":"Decide","operationId":"decide_api_review_decide_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecideIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/undo":{"post":{"tags":["Review"],"summary":"Undo","operationId":"undo_api_review_undo_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/review_routes__UndoIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/export":{"get":{"tags":["Review"],"summary":"Export Decisions","description":"Every decision the office has not consumed yet. Undone ones come back too (the undo\nhandler nulls pulled_at), which is how a reversal reaches the office at all.","operationId":"export_decisions_api_review_export_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":1,"default":5000,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/mark-pulled":{"post":{"tags":["Review"],"summary":"Mark Pulled","description":"Confirm the office has applied these. Deliberately does not touch undone_at — an\nundone decision that has been consumed is finished, not resurrected.","operationId":"mark_pulled_api_review_mark_pulled_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/review_routes__MarkPulledIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/users":{"get":{"tags":["Review"],"summary":"List Users","operationId":"list_users_api_review_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["Review"],"summary":"Add User","operationId":"add_user_api_review_users_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/users/{email}":{"delete":{"tags":["Review"],"summary":"Remove User","operationId":"remove_user_api_review_users__email__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"path","required":true,"schema":{"type":"string","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/car/{car_id}/not-same":{"post":{"tags":["Vehicle"],"summary":"Report Not Same","description":"A visitor says two ads the site grouped are different cars.","operationId":"report_not_same_api_car__car_id__not_same_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotSameIn"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/identity/stats":{"get":{"tags":["Review"],"summary":"Stats","operationId":"stats_api_review_identity_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/identity/prices":{"get":{"tags":["Review"],"summary":"Price Queue","operationId":"price_queue_api_review_identity_prices_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"seller_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seller Key"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"listing_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Listing Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/identity/prices/decide":{"post":{"tags":["Review"],"summary":"Price Decide","operationId":"price_decide_api_review_identity_prices_decide_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceDecideIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/identity/pairs":{"get":{"tags":["Review"],"summary":"Pair Queue","operationId":"pair_queue_api_review_identity_pairs_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"rule","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rule"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"listing_a","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Listing A"}},{"name":"listing_b","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Listing B"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/identity/pairs/decide":{"post":{"tags":["Review"],"summary":"Pair Decide","operationId":"pair_decide_api_review_identity_pairs_decide_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PairDecideIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/identity/recent":{"get":{"tags":["Review"],"summary":"Recent","operationId":"recent_api_review_identity_recent_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"what","in":"query","required":false,"schema":{"type":"string","pattern":"^(price|pair)$","default":"price","title":"What"}},{"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"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/identity/undo":{"post":{"tags":["Review"],"summary":"Undo","operationId":"undo_api_review_identity_undo_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/identity_review_routes__UndoIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/review/identity/export":{"get":{"tags":["Review"],"summary":"Export Decisions","operationId":"export_decisions_api_review_identity_export_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/review/identity/mark-pulled":{"post":{"tags":["Review"],"summary":"Mark Pulled","operationId":"mark_pulled_api_review_identity_mark_pulled_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"type":"string","title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/identity_review_routes__MarkPulledIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/feedback/site-suggestion":{"post":{"tags":["Feedback"],"summary":"Suggest Site","description":"Record a site a user thinks we should index.","operationId":"suggest_site_api_feedback_site_suggestion_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SiteSuggestionIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/feedback/listing-report":{"post":{"tags":["Feedback"],"summary":"Report Listing","description":"Record a problem a user found on a listing.","operationId":"report_listing_api_feedback_listing_report_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListingReportIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/fipe/search":{"get":{"tags":["FIPE API"],"summary":"Search By Name","description":"Find codes by brand/model text. The table is ~7,400 rows, so this scans — at that size\na scan beats maintaining a trigram index, and the cost is honest.","operationId":"search_by_name_api_v1_fipe_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":80,"title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/fipe/{fipe_code}":{"get":{"tags":["FIPE API"],"summary":"Get By Code","operationId":"get_by_code_api_v1_fipe__fipe_code__get","parameters":[{"name":"fipe_code","in":"path","required":true,"schema":{"type":"string","title":"Fipe Code"}},{"name":"X-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/fipe-keys":{"get":{"tags":["FIPE API keys"],"summary":"List Keys","operationId":"list_keys_api_admin_fipe_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["FIPE API keys"],"summary":"Create Key","description":"Mint a key. The plaintext is in this response and nowhere else, ever.","operationId":"create_key_api_admin_fipe_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KeyIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/admin/fipe-keys/{key_id}/revoke":{"post":{"tags":["FIPE API keys"],"summary":"Revoke Key","operationId":"revoke_key_api_admin_fipe_keys__key_id__revoke_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"integer","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth-consent/requests/{request_id}":{"get":{"tags":["MCP login"],"summary":"Get Request","operationId":"get_request_api_oauth_consent_requests__request_id__get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth-consent/requests/{request_id}/approve":{"post":{"tags":["MCP login"],"summary":"Approve Request","operationId":"approve_request_api_oauth_consent_requests__request_id__approve_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth-consent/requests/{request_id}/deny":{"post":{"tags":["MCP login"],"summary":"Deny Request","operationId":"deny_request_api_oauth_consent_requests__request_id__deny_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/sync-status":{"get":{"summary":"Get Sync Status","description":"{\"publishing\": bool}: whether an office->VPS publish is loading (see sync_status.py).\n503 when the database cannot be reached, so a caller can tell \"no\" from \"don't know\".","operationId":"get_sync_status_api_sync_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/stats":{"get":{"summary":"Get Stats","description":"Returns database statistics with caching.","operationId":"get_stats_api_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/body_styles":{"get":{"summary":"Get Body Styles","description":"Returns distinct body styles for the given category, grouping odd values.\n\nReads from mv_car_metadata (a pre-aggregated rollup) instead of scanning\ncar_listings on every cache miss.","operationId":"get_body_styles_api_body_styles_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/brands":{"get":{"summary":"Get Brands","description":"Returns distinct brands from the taxonomy table filtered by category and optionally body_style.","operationId":"get_brands_api_brands_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}},{"name":"body_style","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/transmissions":{"get":{"summary":"Get Transmissions","description":"Returns canonical transmission options.","operationId":"get_transmissions_api_transmissions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/fuel_types":{"get":{"summary":"Get Fuel Types","description":"Returns canonical fuel type options.","operationId":"get_fuel_types_api_fuel_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/sources":{"get":{"summary":"Get Sources","description":"Returns distinct sources with counts, with caching.","operationId":"get_sources_api_sources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/models":{"get":{"summary":"Get Models","description":"Returns distinct models for a brand filtered by category and optionally body_style.","operationId":"get_models_api_models_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","title":"Brand"}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"integer","title":"Brand Id"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}},{"name":"body_style","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/versions":{"get":{"summary":"Get Versions","description":"Returns distinct versions for a model filtered by category and optionally\nbody_style and a year range.\n\nWhen a year filter is supplied, we read from `mv_car_filters` (10-dim rollup\nthat has the `year` column) so the version list reflects exactly the years\nthe user has selected. Without a year filter, we keep the original\n`mv_car_metadata` path (smaller, faster) for parity with the old behavior.","operationId":"get_versions_api_versions_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","title":"Brand"}},{"name":"model","in":"query","required":false,"schema":{"type":"string","title":"Model"}},{"name":"model_id","in":"query","required":false,"schema":{"type":"integer","title":"Model Id"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}},{"name":"body_style","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"}},{"name":"year_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year Min"}},{"name":"year_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year Max"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/years":{"get":{"summary":"Get Years","description":"Returns distinct years available for the given brand/model/version slice.\n\nReads from `mv_car_filters`, the 10-dim pre-aggregated rollup that carries\nthe `year` dimension. Used by the frontend to populate the Year dropdown\nonly with years that actually have listings for the current selection.","operationId":"get_years_api_years_get","parameters":[{"name":"brand","in":"query","required":false,"schema":{"type":"string","title":"Brand"}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"integer","title":"Brand Id"}},{"name":"model","in":"query","required":false,"schema":{"type":"string","title":"Model"}},{"name":"model_id","in":"query","required":false,"schema":{"type":"integer","title":"Model Id"}},{"name":"version_id","in":"query","required":false,"schema":{"type":"integer","title":"Version Id"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}},{"name":"body_style","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search-suggest":{"get":{"summary":"Search Suggest","description":"Autocomplete suggestions for the smart search input.\n\nParses ``q`` into year tokens (4-digit numbers in 1950–2030) and word tokens,\nthen matches word tokens against brands → models → versions, returning the\ntop-N most-listed combinations. Each suggestion carries the IDs the frontend\nneeds to set filters directly.","operationId":"search_suggest_api_search_suggest_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Q"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}},{"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/search-resolve":{"get":{"summary":"Search Resolve","description":"Resolve a free-text query to the longest catalog brand + model, returning\nthe leftover text as a title keyword.\n\nUsed by the smart-search box when the user presses Enter WITHOUT picking a\nsuggestion: the frontend applies brand_id + model_id as filters and pushes\nthe residual (\"the version part\") into the free-text ``query`` keyword search.\nVersions are intentionally NOT resolved — their strings are too inconsistent\nacross the 93 sources to match exactly, so the trailing trim text is matched\nagainst car_listings.title instead.","operationId":"search_resolve_api_search_resolve_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"title":"Q"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","default":"Cars","title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search/keywords":{"post":{"summary":"Search Keywords","description":"Top-N most-frequent words across the title/version/info of the current\nfilter set.\n\nComputed exactly (no sampling) on the result set. Gating: requires at\nleast a brand or model filter so we never scan the full 1M-row table.\nCached for 5 min per filter signature.","operationId":"search_keywords_api_search_keywords_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchFilters"}}},"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":{"post":{"summary":"Search Cars","description":"Search car listings (public; anonymous users are rate-limited per day).","operationId":"search_cars_api_search_post","security":[{"HTTPBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchFilters"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Search Cars Get","description":"GET endpoint for searching cars.","operationId":"search_cars_get_api_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"brand","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"}},{"name":"model","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"}},{"name":"brand_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand Id"}},{"name":"model_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Model Id"}},{"name":"version_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version Id"}},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"}},{"name":"price_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Min"}},{"name":"price_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Price Max"}},{"name":"year_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year Min"}},{"name":"year_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year Max"}},{"name":"fuel_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fuel Type"}},{"name":"transmission","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transmission"}},{"name":"mileage_min","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Min"}},{"name":"mileage_max","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Max"}},{"name":"location","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"Cars","title":"Category"}},{"name":"sort_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"default":"newest","title":"Sort By"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Per Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/car/{car_id}":{"get":{"summary":"Get Car By Id","description":"Fetch a single car listing by ID.","operationId":"get_car_by_id_api_car__car_id__get","parameters":[{"name":"car_id","in":"path","required":true,"schema":{"type":"integer","title":"Car Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CarListingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/fipe/history":{"get":{"summary":"Get Fipe History","description":"Monthly FIPE price of (fipe_code, year): FIPE's own tables plus the price history back\nto Dez/2007 (migration 053) — ONE fuel's line, the listing's `fuel` when FIPE prices it\n(fipe_series.py explains the rule; mixing fuels drew a zigzag).\n\nA model year FIPE does not price falls back to the 0 km rows the old loader stamped with\nthat year (`zero_km: true`) — exactly what this showed before the switch; fipe_years.py.\n\nBeside it (migration 058, fipe_outlook.py): `market`, the monthly median asking price of our\nads for this code and year, and `forecast`, the next 6 months of FIPE with an 80% band —\nneither for a 0 km stand-in. A plain `def`: its database work blocks, and as a coroutine it\nfroze the worker for every visitor while it ran.","operationId":"get_fipe_history_api_fipe_history_get","parameters":[{"name":"fipe_code","in":"query","required":true,"schema":{"type":"string","title":"Fipe Code"}},{"name":"year","in":"query","required":true,"schema":{"type":"integer","title":"Year"}},{"name":"fuel","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fuel"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AccessGrantIn":{"properties":{"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["user_id"],"title":"AccessGrantIn"},"AlertResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"brand_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand Id"},"model_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Model Id"},"version_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version Id"},"min_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Year"},"max_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Year"},"min_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Price"},"max_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Price"},"fuel_type":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fuel Type"},"transmission":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transmission"},"mileage_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Min"},"mileage_max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Max"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"is_active":{"type":"boolean","title":"Is Active"},"match_count":{"type":"integer","title":"Match Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","is_active","match_count","created_at"],"title":"AlertResponse"},"BlockIn":{"properties":{"user_id":{"type":"string","title":"User Id"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","required":["user_id"],"title":"BlockIn"},"Body_fipe_upload_api_admin_fipe_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_fipe_upload_api_admin_fipe_upload_post"},"CarData":{"properties":{"external_id":{"type":"string","title":"External Id"},"source":{"type":"string","title":"Source"},"title":{"type":"string","title":"Title"},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"year":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"title":"Year"},"km":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"title":"Km"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"},"link":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Link"},"fuel":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fuel"},"transmission":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transmission"}},"type":"object","required":["external_id","source","title"],"title":"CarData"},"CarListingResponse":{"properties":{"id":{"type":"integer","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"body_style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"},"year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Year"},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price"},"price_display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Display"},"mileage":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage"},"mileage_display":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mileage Display"},"image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Image Url"},"url":{"type":"string","title":"Url"},"source":{"type":"string","title":"Source"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"city":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"City"},"state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"},"transmission":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transmission"},"fuel_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fuel Type"},"info":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Info"},"vendor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vendor"},"fipe_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Fipe Price"},"fipe_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fipe Code"},"first_seen_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"First Seen At"},"deal_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Deal Score"},"liquidity":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Liquidity"},"vehicle_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vehicle Id"},"vehicle_first_seen_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vehicle First Seen At"},"vehicle_sites":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vehicle Sites"},"price_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Kind"},"price_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Ratio"},"vehicle":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Vehicle"}},"type":"object","required":["id","title","brand","model","year","price","price_display","mileage","mileage_display","image_url","url","source","location","city","state","transmission","fuel_type","info","vendor"],"title":"CarListingResponse"},"ChatRequest":{"properties":{"message":{"type":"string","maxLength":1000,"minLength":1,"title":"Message"},"history":{"items":{"$ref":"#/components/schemas/ChatTurn"},"type":"array","maxItems":24,"title":"History"}},"type":"object","required":["message"],"title":"ChatRequest"},"ChatTurn":{"properties":{"role":{"type":"string","pattern":"^(user|model|assistant)$","title":"Role"},"text":{"type":"string","maxLength":2000,"minLength":1,"title":"Text"}},"type":"object","required":["role","text"],"title":"ChatTurn"},"DecideIn":{"properties":{"spec_key":{"type":"string","title":"Spec Key"},"action":{"type":"string","pattern":"^(accept|reject)$","title":"Action"},"corrected_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Corrected Code"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"also_spec_keys":{"items":{"type":"string"},"type":"array","title":"Also Spec Keys"}},"type":"object","required":["spec_key","action"],"title":"DecideIn"},"EmailPreference":{"properties":{"alert_emails":{"type":"boolean","title":"Alert Emails"}},"type":"object","required":["alert_emails"],"title":"EmailPreference"},"FavoriteCarRequest":{"properties":{"car":{"$ref":"#/components/schemas/CarData"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["car"],"title":"FavoriteCarRequest"},"FavoriteRequest":{"properties":{"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"FavoriteRequest"},"FeedbackStatusIn":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"admin_note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Admin Note"}},"type":"object","title":"FeedbackStatusIn"},"ForgotPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"ForgotPasswordRequest"},"GoogleAuthRequest":{"properties":{"credential":{"type":"string","title":"Credential"}},"type":"object","required":["credential"],"title":"GoogleAuthRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KeyIn":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"daily_limit":{"type":"integer","maximum":1000000.0,"minimum":1.0,"title":"Daily Limit","default":10000}},"type":"object","required":["name"],"title":"KeyIn"},"ListingReportIn":{"properties":{"car_id":{"type":"integer","exclusiveMinimum":0.0,"title":"Car Id"},"category":{"type":"string","title":"Category"},"fields":{"items":{"type":"string"},"type":"array","maxItems":8,"title":"Fields"},"reason":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Reason"},"suggested_fipe":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Suggested Fipe"},"message":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Message"},"notify":{"type":"boolean","title":"Notify","default":true},"website":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Website"}},"type":"object","required":["car_id","category"],"title":"ListingReportIn"},"LoginRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"NotSameIn":{"properties":{"other_id":{"type":"integer","title":"Other Id"}},"type":"object","required":["other_id"],"title":"NotSameIn"},"PairDecideIn":{"properties":{"listing_a":{"type":"integer","title":"Listing A"},"listing_b":{"type":"integer","title":"Listing B"},"rule":{"type":"string","pattern":"^(R1|R1b|R2|R3|R4|R4b|R4s|R5|reported)$","title":"Rule"},"verdict":{"type":"string","pattern":"^(same|different|unsure)$","title":"Verdict"}},"type":"object","required":["listing_a","listing_b","rule","verdict"],"title":"PairDecideIn"},"PasswordUpdateRequest":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_password":{"type":"string","maxLength":128,"minLength":8,"title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"PasswordUpdateRequest"},"PriceAlertRequest":{"properties":{"car_id":{"type":"integer","title":"Car Id"},"target_price":{"type":"number","title":"Target Price"}},"type":"object","required":["car_id","target_price"],"title":"PriceAlertRequest"},"PriceDecideIn":{"properties":{"listing_id":{"type":"integer","title":"Listing Id"},"verdict":{"type":"string","pattern":"^(installment|equity|problem|sale|not_car|unsure)$","title":"Verdict"},"apply_to_seller":{"type":"boolean","title":"Apply To Seller","default":false}},"type":"object","required":["listing_id","verdict"],"title":"PriceDecideIn"},"ReadRequest":{"properties":{"car_ids":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Car Ids"}},"type":"object","title":"ReadRequest"},"ResendCodeRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"ResendCodeRequest"},"ResetPasswordRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"code":{"type":"string","maxLength":6,"minLength":6,"title":"Code"},"new_password":{"type":"string","maxLength":128,"minLength":8,"title":"New Password"}},"type":"object","required":["email","code","new_password"],"title":"ResetPasswordRequest"},"SaveSearchRequest":{"properties":{"name":{"type":"string","title":"Name"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"filters":{"additionalProperties":true,"type":"object","title":"Filters"},"notify_on_new":{"type":"boolean","title":"Notify On New","default":false}},"type":"object","required":["name","filters"],"title":"SaveSearchRequest"},"SearchFilters":{"properties":{"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"body_style":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Style"},"brand_id":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Brand Id"},"model_id":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Model Id"},"version_id":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Version Id"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"},"price_min":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Price Min"},"price_max":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Price Max"},"year_min":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Year Min"},"year_max":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Year Max"},"fuel_type":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fuel Type"},"transmission":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transmission"},"mileage_min":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Mileage Min"},"mileage_max":{"anyOf":[{"type":"integer","maximum":9.223372036854776e+18,"minimum":-9.223372036854776e+18},{"type":"null"}],"title":"Mileage Max"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"sort_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sort By","default":"newest"},"page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page","default":1},"per_page":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Per Page"}},"type":"object","title":"SearchFilters"},"SearchResponse":{"properties":{"listings":{"items":{"$ref":"#/components/schemas/CarListingResponse"},"type":"array","title":"Listings"},"total_count":{"type":"integer","title":"Total Count"},"current_page":{"type":"integer","title":"Current Page"},"total_pages":{"type":"integer","title":"Total Pages"},"max_page":{"type":"integer","title":"Max Page"},"per_page":{"type":"integer","title":"Per Page"}},"type":"object","required":["listings","total_count","current_page","total_pages","max_page","per_page"],"title":"SearchResponse"},"SessionResponse":{"properties":{"user":{"$ref":"#/components/schemas/UserResponse"},"access_token":{"type":"string","title":"Access Token"}},"type":"object","required":["user","access_token"],"title":"SessionResponse"},"SignUpRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","maxLength":128,"minLength":8,"title":"Password"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"}},"type":"object","required":["email","password"],"title":"SignUpRequest"},"SiteSuggestionIn":{"properties":{"url":{"type":"string","maxLength":500,"minLength":3,"title":"Url"},"name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Name"},"note":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Note"},"notify":{"type":"boolean","title":"Notify","default":true},"website":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Website"}},"type":"object","required":["url"],"title":"SiteSuggestionIn"},"TrackBatch":{"properties":{"events":{"items":{"$ref":"#/components/schemas/TrackEvent"},"type":"array","maxItems":50,"minItems":1,"title":"Events"}},"type":"object","required":["events"],"title":"TrackBatch"},"TrackEvent":{"properties":{"event_type":{"type":"string","maxLength":64,"minLength":1,"title":"Event Type"},"anon_id":{"type":"string","maxLength":64,"minLength":8,"title":"Anon Id"},"session_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Session Id"},"car_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Car Id"},"url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Url"},"referrer":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Referrer"},"occurred_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Occurred At"},"props":{"additionalProperties":true,"type":"object","title":"Props"}},"type":"object","required":["event_type","anon_id"],"title":"TrackEvent"},"UserIn":{"properties":{"email":{"type":"string","title":"Email"},"role":{"type":"string","pattern":"^(owner|reviewer)$","title":"Role","default":"reviewer"}},"type":"object","required":["email"],"title":"UserIn"},"UserResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"auth_provider":{"type":"string","title":"Auth Provider"},"email_verified":{"type":"boolean","title":"Email Verified"}},"type":"object","required":["id","email","auth_provider","email_verified"],"title":"UserResponse"},"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"},"VehicleAlertRequest":{"properties":{"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"brand_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Brand Id"},"model_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Model Id"},"version_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version Id"},"min_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Min Year"},"max_year":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Year"},"min_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min Price"},"max_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max Price"},"fuel_type":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Fuel Type"},"transmission":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Transmission"},"mileage_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Min"},"mileage_max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mileage Max"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category","default":"Cars"}},"type":"object","title":"VehicleAlertRequest"},"VerifyEmailRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"code":{"type":"string","maxLength":6,"minLength":6,"title":"Code"}},"type":"object","required":["email","code"],"title":"VerifyEmailRequest"},"identity_review_routes__MarkPulledIn":{"properties":{"price_ids":{"items":{"type":"integer"},"type":"array","title":"Price Ids","default":[]},"pair_ids":{"items":{"type":"integer"},"type":"array","title":"Pair Ids","default":[]}},"type":"object","title":"MarkPulledIn"},"identity_review_routes__UndoIn":{"properties":{"what":{"type":"string","pattern":"^(price|pair)$","title":"What"},"decision_id":{"type":"integer","title":"Decision Id"}},"type":"object","required":["what","decision_id"],"title":"UndoIn"},"review_routes__MarkPulledIn":{"properties":{"ids":{"items":{"type":"integer"},"type":"array","title":"Ids"}},"type":"object","required":["ids"],"title":"MarkPulledIn"},"review_routes__UndoIn":{"properties":{"decision_id":{"type":"integer","title":"Decision Id"}},"type":"object","required":["decision_id"],"title":"UndoIn"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}}