{"openapi":"3.1.0","info":{"title":"Open Hood public API","version":"1.1.0","summary":"Consumer advocate — VIN, quotes, shops, recalls, integrations. Native fetch only.","description":"Public routes that exist on this origin. POST /api/agent runs bay tools then writes a Beginner or Expert brief. GET /api/integrations/status is the honesty matrix: public probes, weekend keys configured:false when missing (probed:skip when on), Stripe test packet Checkout, live packet only with an existing Price ID, escrow refused. GET /agent/api renders this spec. Not a shop booking API. Not TecDoc. Not a Carfax feed."},"servers":[{"url":"/","description":"Same origin as the bay"}],"tags":[{"name":"Advocate","description":"Advocate desk + bay tools"},{"name":"Integrations","description":"Patch bay, probes, honest no-op adapters"},{"name":"Vehicle","description":"VIN, fluids, catalog menus"},{"name":"Shops","description":"Directory / maps. Overpass is a separate desk."},{"name":"Ticket","description":"Quote, estimate, history, trust, contact"},{"name":"Spec","description":"This OpenAPI document"}],"paths":{"/api/agent":{"get":{"tags":["Advocate"],"operationId":"getAgentStatus","summary":"Desk status and tool list","description":"Whether the optional photo connection is on, callable tool names, reading levels beginner | expert.","responses":{"200":{"description":"Status"}}},"post":{"tags":["Advocate"],"operationId":"postAgent","summary":"Ask the advocate","description":"Messages, optional vehicle, beginner|expert, optional image. Native fetch. JSON or SSE.","x-openhood-fetch":"const r = await fetch('/api/agent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: [{ role: 'user', content: 'P0420 on a 2003 Honda Accord' }], readingLevel: 'beginner' }) });\nconst brief = await r.json();","responses":{"200":{"description":"JSON brief or SSE"},"400":{"description":"Missing messages"}}}},"/api/openapi":{"get":{"tags":["Spec"],"operationId":"getOpenApi","summary":"OpenAPI 3.1 document","description":"yaml (default) or ?format=json. Same contract as /openapi.yaml.","parameters":[{"name":"format","in":"query","schema":{"type":"string","enum":["yaml","json"]}}],"responses":{"200":{"description":"OpenAPI 3.1"}}}},"/api/integrations/status":{"get":{"tags":["Integrations"],"operationId":"getIntegrationsStatus","summary":"Honesty matrix + public probes","description":"Every jack. Live probes for NHTSA vPIC, Nominatim, FuelEconomy.gov. recallsByVin probe is 403. Paid keys: configured:false unless the adapter is wired and the env is on (then probed:skip — we do not hammer paid APIs). Chrome / MOTOR / TecDoc stay false. Stripe: test → configured true, product none; live → liveKeyDetected, product none, escrow refused. ?probe=0 skips upstream probes.","x-openhood-fetch":"const r = await fetch('/api/integrations/status');\nconst j = await r.json();\n// weekend keys: configured === false unless the env is actually on (then probed:skip)\n// stripe: test packet Checkout; live needs price_; escrow false","responses":{"200":{"description":"Matrix, keys, probes, stripe, licensed empty catalog"}}}},"/api/integrations/nhtsa":{"get":{"tags":["Integrations"],"operationId":"getIntegrationsNhtsa","summary":"vPIC / SaferCar / NCAP / complaints","description":"kind=vin|vin-campaign|recallsByVin|recalls|complaints|ratings. recallsByVin reports the 403. No HTML scrape.","x-openhood-fetch":"const r = await fetch('/api/integrations/nhtsa?kind=vin&vin=1HGCM82633A004352');\nconst { specs } = await r.json();","responses":{"200":{"description":"Decode or campaigns"},"400":{"description":"Need VIN or Y/M/M"}}}},"/api/integrations/mpg":{"get":{"tags":["Integrations"],"summary":"EPA FuelEconomy.gov MPG","description":"Official city / hwy / combined. No key.","x-openhood-fetch":"const r = await fetch('/api/integrations/mpg?year=2003&make=Honda&model=Accord');\nconst { rows } = await r.json();","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/geocode":{"get":{"tags":["Integrations"],"summary":"Nominatim pin","description":"ZIP or city → lat/lon. User-Agent required. 1 req/s.","x-openhood-fetch":"const r = await fetch('/api/integrations/geocode?q=97214');\nconst pin = await r.json();","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/youtube":{"get":{"tags":["Integrations"],"summary":"YouTube how-to","description":"No scrape. Key off → videos:[], searchUrl still works.","x-openhood-fetch":"const r = await fetch('/api/integrations/youtube?q=cabin+filter+how+to');\nconst { youtube, searchUrl } = await r.json();\n// no key → youtube.videos === [] ; use searchUrl","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/links":{"get":{"tags":["Integrations"],"summary":"Computed outbound hrefs","description":"Every jack’s Open / Connect / Request access URL for the ticket context.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/places":{"get":{"tags":["Integrations"],"summary":"Google Places adapter","description":"Missing key → configured:false, results:[]. No invented shops.","x-openhood-fetch":"const r = await fetch('/api/integrations/places?q=97214');\nconst { configured, results } = await r.json();\n// missing key → configured:false, results:[]","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/yelp":{"get":{"tags":["Integrations"],"summary":"Yelp Fusion adapter","description":"Missing key → configured:false, results:[]. No invented stars.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/plate":{"get":{"tags":["Integrations"],"summary":"Plate-to-VIN status","description":"CarsXE / MarketCheck. Off → configured:false. Plate stays a note.","responses":{"200":{"description":"Status"}}},"post":{"tags":["Integrations"],"summary":"Plate-to-VIN (no-op without keys)","description":"JSON {plate, state}. No key → vin:null. We do not invent a VIN, shop, or price.","x-openhood-fetch":"const r = await fetch('/api/integrations/plate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ plate: 'ABC123', state: 'OR' }) });\nconst j = await r.json();\n// no key → vin:null — we do not invent a VIN","responses":{"200":{"description":"Decode or honest dark body"}}}},"/api/integrations/openai":{"get":{"tags":["Integrations"],"summary":"Photo reading status","description":"GET only. Does not call OpenAI. configured:false without OPENAI_API_KEY.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/chrome":{"get":{"tags":["Integrations"],"summary":"Chrome Data stub","description":"Always connected:false. No fake window sticker. ROADMAP after contract.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/motor":{"get":{"tags":["Integrations"],"summary":"MOTOR / Identifix stub","description":"Always configured:false. No Motor client. No invented hours. ROADMAP.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/stripe":{"get":{"tags":["Integrations"],"summary":"Stripe test vs live vs none","description":"sk_test_ → configured true, product:packet, Checkout. sk_live_ → packet fee only with STRIPE_PACKET_PRICE_ID. Escrow, shop cuts, marketplace refused.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/catalog":{"get":{"tags":["Integrations"],"summary":"Licensed catalog empty bay","description":"skus:[], hours:[], tecdoc/motor/partstech/chrome false. Contract names + ROADMAP. No fake SKUs. OSM stays the shop map.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/partstech":{"get":{"tags":["Integrations"],"summary":"PartsTech empty adapter","description":"Always connected:false, skus:[]. PartsTech shop parts book. ROADMAP.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/integrations/vinaudit":{"get":{"tags":["Integrations"],"summary":"VinAudit weekend key","description":"configured:true only with VINAUDIT_API_KEY. Title snapshot on /history. Never a dummy Carfax.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/stripe/packet":{"get":{"tags":["Integrations"],"summary":"Packet fee status","description":"Optional print packet / hold this bay. Test vs live vs none. Escrow refused.","responses":{"200":{"description":"Status or session"}}},"post":{"tags":["Integrations"],"summary":"Open packet Checkout","description":"sk_test_ or sk_live_ + existing price_. Founder can complete a test payment. Not escrow.","responses":{"200":{"description":"Checkout URL"},"403":{"description":"Live without product"},"404":{"description":"No key"}}}},"/api/vin":{"post":{"tags":["Vehicle"],"summary":"Decode a VIN","description":"NHTSA vPIC + nameplate recalls + fluids card. Native fetch.","x-openhood-fetch":"const r = await fetch('/api/vin', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ vin: '1HGCM82633A004352' }) });\nconst { specs } = await r.json();","responses":{"200":{"description":"specs, fluids, recalls"}}}},"/api/identify":{"post":{"tags":["Vehicle"],"summary":"Identify from VIN or year/make/model","description":"Plate rides as a note. Plate-to-VIN is a different jack.","responses":{"200":{"description":"Identity packet"}}}},"/api/fluids":{"get":{"tags":["Vehicle"],"summary":"Fluids / PSI card","description":"Door jamb still wins a catalog miss.","x-openhood-fetch":"const r = await fetch('/api/fluids?year=2003&make=Honda&model=Accord');\nconst { fluids } = await r.json();","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/catalog":{"get":{"tags":["Vehicle"],"summary":"Year / make / model menus","description":"NHTSA vPIC menus. Not TecDoc.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/quote":{"post":{"tags":["Ticket"],"summary":"Mark up a repair order","description":"Local price book. Photo vision only when OPENAI_API_KEY is on.","responses":{"200":{"description":"Marked ticket"}}}},"/api/estimate":{"get":{"tags":["Ticket"],"summary":"ZIP labor band","description":"Heuristic hours. Not Motor. Not Mitchell.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/directory/search":{"get":{"tags":["Shops"],"summary":"Overpass rooftops","description":"Owned by the directory desk. This spec only documents the public GET.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/directory/geocode":{"get":{"tags":["Shops"],"summary":"Directory Nominatim","description":"Same public geocoder as /api/integrations/geocode.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/directory/places":{"get":{"tags":["Shops"],"summary":"Places / Yelp merge","description":"Missing keys → connected:false, results:[]. OSM is a separate sweep.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/directory/catalog":{"get":{"tags":["Shops"],"summary":"Directory provider catalog","description":"Machine list + env flags. Licensed jacks stay honest.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/history":{"get":{"tags":["Ticket"],"summary":"History dossier","description":"NHTSA identity + campaigns + consumer report link-outs. No Carfax XML.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/history/title":{"get":{"tags":["Ticket"],"summary":"Paid title snapshot status","description":"VinAudit or CarsXE history only if that env key is on. Off → empty bay. Not a Carfax file. Not Consumer Reports.","responses":{"200":{"description":"Status or snapshot"}}},"post":{"tags":["Ticket"],"summary":"Paid title snapshot","description":"JSON { vin }. No key → empty. Key on → named vendor. We do not invent wrecks.","responses":{"200":{"description":"Snapshot or empty bay"}}}},"/api/history/plate":{"get":{"tags":["Ticket"],"summary":"History plate status","responses":{"200":{"description":"Dark or live"}}},"post":{"tags":["Ticket"],"summary":"History plate decode","description":"Same honesty as /api/integrations/plate.","responses":{"200":{"description":"Decode or note"}}}},"/api/contact":{"get":{"tags":["Ticket"],"summary":"Contact mailer status","description":"RESEND_API_KEY + CONTACT_TO_EMAIL or local JSONL.","responses":{"200":{"description":"delivery email|local"}}},"post":{"tags":["Ticket"],"summary":"Submit a contact ticket","responses":{"200":{"description":"Filed"}}}},"/api/trust":{"get":{"tags":["Ticket"],"summary":"Trust desk status","description":"No users in this repo. Hold is test-or-demo.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/trust/hold":{"get":{"tags":["Ticket"],"summary":"Hold status","description":"sk_live_ refused. Not escrow.","responses":{"200":{"description":"Hold mode"}}}},"/api/ocr":{"post":{"tags":["Vehicle"],"summary":"Photo OCR","description":"OPENAI vision is the primary RO reader when OPENAI_API_KEY is set (usedCloud:true). Off → Tesseract lottery on the device. Typical-hour book, not a licensed hour guide.","responses":{"200":{"description":"Read text"}}}},"/api/symptoms":{"get":{"tags":["Advocate"],"summary":"Symptom map","description":"Noise + when → shop questions.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/guides":{"get":{"tags":["Advocate"],"summary":"Guide glossary","description":"Owner how-to jobs.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/finder":{"get":{"tags":["Advocate"],"summary":"Parts finder","description":"Job → storefront search URLs. Not live stock.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/expert/playbooks":{"get":{"tags":["Advocate"],"summary":"Owner playbooks","description":"Not stolen TSB PDFs.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/auctions/search":{"get":{"tags":["Advocate"],"summary":"Auction link-outs","description":"No lot scrape.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/book":{"get":{"tags":["Ticket"],"summary":"Visit packet","description":"We do not write the dealer RO.","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/api/jobs/dtc":{"get":{"tags":["Advocate"],"summary":"DTC book","description":"?code=P0420","responses":{"200":{"description":"JSON"},"400":{"description":"Bad input"},"502":{"description":"Upstream"}}}},"/agent":{"get":{"tags":["Advocate"],"summary":"Advocate desk","description":"HTML. Not an API.","responses":{"200":{"description":"HTML"}}}},"/agent/api":{"get":{"tags":["Spec"],"summary":"Readable OpenAPI view","responses":{"200":{"description":"HTML docs"}}}},"/integrations":{"get":{"tags":["Integrations"],"summary":"Patch bay","description":"HTML honesty board.","responses":{"200":{"description":"HTML"}}}}},"x-openhood-tools":{"decode_vin":"NHTSA vPIC. 17-character VIN.","lookup_dtc":"Local DTC book plus generic SAE layout.","analyze_quote_text":"Local quote book. Flags flushes and padded lines.","diagnose_symptoms":"Noise plus when maps to shop questions.","get_fluids_for_vehicle":"Catalog or heuristic fluids card.","search_guides":"Owner guide glossary.","search_directory":"ZIP plus type to OSM rooftops. No booking.","get_recalls":"NHTSA year/make/model campaigns. Not VIN open/closed.","get_epa_mpg":"FuelEconomy.gov official MPG.","search_playbooks":"Named owner playbooks. Not a stolen TSB.","lookup_ro_term":"Invoice slang. Not legal advice.","search_parts":"Outbound catalog search URLs. Not TecDoc. Not live stock."}}