{"openapi":"3.1.0","info":{"title":"Sonlight Window Cleaning — Booking API","version":"2026-05-06","summary":"Public API for AI agents (ChatGPT, Claude, Perplexity, Gemini, Copilot, Meta AI, Mistral, Grok, DeepSeek, Apple Intelligence, etc.) to quote and book residential window cleaning in Colorado without a human in the loop.","description":"Sonlight Window Cleaning is a family-owned company serving the Colorado Front Range since 1978. This API lets autonomous agents complete a booking end-to-end: get a real price from a real address, check available dates with discounts, and reserve a real calendar slot. The customer receives an SMS + email confirmation immediately. No phone call, no in-person visit, no sales callback.\n\nIf an agent identifies itself via a recognized User-Agent (GPTBot, ClaudeBot, PerplexityBot, etc.), bookings are tagged `lead_source=llm_<agent>` and admin notifications use the prefix \"New LLM Sale\" — useful for tracking agent-driven revenue.\n\nHuman fallback: 303-990-2878.","contact":{"name":"Sonlight Window Cleaning","email":"info@sonlightwindow.com","url":"https://sonlightwindow.com"},"license":{"name":"Public — agent use permitted","url":"https://sonlightwindow.com/llms.txt"},"x-logo":{"url":"https://sonlightwindow.com/logo.png"}},"servers":[{"url":"https://sonlightwindow.com","description":"Production"}],"tags":[{"name":"quote","description":"Get a price for an address"},{"name":"availability","description":"Check open calendar dates and discounts"},{"name":"book","description":"Reserve a real calendar slot"}],"paths":{"/api/quote":{"post":{"tags":["quote"],"summary":"Get a firm price for an address","description":"Looks up the property in public records, returns a tamper-proof quote keyed to the address. Pricing reflects square footage, story count, and access. Result is the same price the customer would see on the public booking page.","operationId":"getQuote","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"},"examples":{"denver":{"summary":"Denver address","value":{"address":"1600 Glenarm Pl, Denver, CO 80202"}},"boulder":{"summary":"Boulder address","value":{"address":"1600 Pearl St, Boulder, CO 80302"}}}}}},"responses":{"200":{"description":"Quote returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"},"example":{"fullService":325,"exteriorOnly":250,"gutterCleaning":100,"softWash":525,"propertyDetails":{"sqft":2400,"stories":2,"bathrooms":3,"estimatedValue":650000,"propertyType":"single_family"},"address":"1600 Glenarm Pl, Denver, CO 80202"}}}},"400":{"description":"Address invalid or out of service area","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/quote/availability":{"get":{"tags":["availability"],"summary":"List open dates with any active discounts","description":"Returns the next ~30 days of calendar slots for an address. Each slot includes any auto-applied discount (in dollars) when our calendar has open capacity. Pass the discount value back to /api/book as `discountDayFixed` to lock it in.","operationId":"checkAvailability","parameters":[{"name":"address","in":"query","required":true,"schema":{"type":"string"},"description":"Full street address including city, state, zip.","example":"1600 Glenarm Pl, Denver, CO 80202"}],"responses":{"200":{"description":"Available slots","content":{"application/json":{"schema":{"type":"object","properties":{"slots":{"type":"array","items":{"$ref":"#/components/schemas/AvailabilitySlot"}}}}}}},"400":{"description":"Address invalid or out of service area","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/book":{"post":{"tags":["book"],"summary":"Reserve a real calendar slot — completes the booking","description":"Creates the customer record, books the technician on our real dispatch calendar, sends an SMS + email confirmation, and returns a job id. Idempotent against duplicate phone+address+date submissions. If the customer is a returning customer (matched by phone), the existing record is reused.\n\nSet `utmSource` to `llm_<agent>` (e.g. `llm_chatgpt`, `llm_claude`, `llm_perplexity`) so admin notifications fire as \"New LLM Sale\" instead of the standard \"New Booking\". Recognized User-Agents auto-tag this if `utmSource` is omitted.","operationId":"bookWindowCleaning","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookRequest"},"example":{"name":"Jane Doe","phone":"303-555-1212","email":"jane@example.com","address":"1600 Glenarm Pl, Denver, CO 80202","selectedServices":["interior_exterior"],"preferredDate":"2026-05-15","preferredTime":"10am","discountDayFixed":50,"utmSource":"llm_chatgpt"}}}},"responses":{"200":{"description":"Booking confirmed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookResponse"},"example":{"success":true,"jobId":"8f3c1e2a-..."}}}},"400":{"description":"Missing required fields","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Booking system temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"QuoteRequest":{"type":"object","required":["address"],"properties":{"address":{"type":"string","description":"Full street address. City, state, and zip optional but improve match accuracy.","minLength":5,"examples":["1600 Glenarm Pl, Denver, CO 80202"]},"city":{"type":"string","description":"Optional — pre-split city."},"state":{"type":"string","description":"Optional — pre-split state, default CO."},"zip":{"type":"string","description":"Optional — pre-split zip."}}},"QuoteResponse":{"type":"object","required":["fullService","exteriorOnly","gutterCleaning","softWash","address"],"properties":{"fullService":{"type":"number","description":"Interior + exterior window cleaning, USD."},"exteriorOnly":{"type":"number","description":"Exterior-only window cleaning, USD."},"gutterCleaning":{"type":"number","description":"Standalone gutter cleaning, USD."},"softWash":{"type":"number","description":"Soft wash / pressure washing, USD."},"propertyDetails":{"type":"object","properties":{"sqft":{"type":["integer","null"]},"stories":{"type":["integer","null"]},"bathrooms":{"type":["number","null"]},"estimatedValue":{"type":["number","null"]},"propertyType":{"type":["string","null"]}}},"address":{"type":"string","description":"Canonicalized address used for the quote."}}},"AvailabilitySlot":{"type":"object","required":["date","slot"],"properties":{"date":{"type":"string","format":"date","example":"2026-05-15"},"slot":{"type":"string","enum":["8am","10am","12pm","2pm","morning","afternoon","allday"]},"discount_dollars":{"type":"integer","description":"Active discount for this slot, USD. 0 means no discount."}}},"BookRequest":{"type":"object","required":["name","phone","address","selectedServices","preferredDate","preferredTime"],"properties":{"name":{"type":"string","description":"Customer's full name."},"phone":{"type":"string","description":"US phone number, any common format. Required for confirmation SMS.","example":"303-555-1212"},"email":{"type":"string","format":"email","description":"Optional but recommended for the email confirmation."},"address":{"type":"string","description":"Full service address — same form returned by /api/quote."},"selectedServices":{"type":"array","minItems":1,"items":{"type":"string","enum":["interior_exterior","exterior","gutter","soft_wash"]},"description":"One or more services to bundle on this visit."},"preferredDate":{"type":"string","format":"date","description":"YYYY-MM-DD. Must be a future date; same-day cuts off at 6pm Mountain Time the prior evening."},"preferredTime":{"type":"string","enum":["8am","10am","12pm","2pm","morning","afternoon","allday"],"description":"Time slot for arrival."},"discountDayFixed":{"type":"integer","minimum":0,"maximum":75,"description":"Optional. Pass back the discount returned by /api/quote/availability to lock it in (server-revalidates)."},"utmSource":{"type":"string","description":"Set to `llm_<agent>` (e.g. `llm_chatgpt`) so admin notifications fire as 'New LLM Sale'. Auto-detected from User-Agent if omitted.","examples":["llm_chatgpt","llm_claude","llm_perplexity","llm_gemini"]},"notes":{"type":"string","description":"Free-form notes from the customer."}}},"BookResponse":{"type":"object","properties":{"success":{"type":"boolean"},"jobId":{"type":"string","format":"uuid","description":"Internal job id — pass back if customer needs to reference the booking."},"duplicate":{"type":"boolean","description":"True if a matching booking already existed within the past 24 hours and we returned the existing one."}}},"Error":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable error message safe to show end users."}}}}},"x-mcp-server":{"url":"https://sonlightwindow.com/mcp","protocol":"Model Context Protocol (HTTP transport)","tools":["get_quote","check_availability","book_window_cleaning"]}}