{"openapi":"3.1.0","info":{"title":"Latchbell Front Desk","description":"A front desk for cleaning businesses: firm quotes, open times, reply drafts, follow-ups, bookings and bad-lead credit requests. This API is for cleaning businesses and needs the pro's Latchbell key. Booking a cleaner for a customer needs no key: read https://slotted.agenthippo.ai/llms.txt. Latchbell Front Desk and Latchbell Local Pros are one system: bookings customers make through Local Pros (or any AI assistant) appear here in schedule and updates. Assistants writing their own client should prefer the MCP endpoint https://slotted.agenthippo.ai/mcp (JSON-RPC: initialize, tools/list, tools/call; plain JSON, no session), which exposes the same operations and lists new ones automatically.","version":"1.0.0"},"servers":[{"url":"https://slotted.agenthippo.ai"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Send the key in the Authorization header: \"Authorization: Bearer <key>\". paste your Latchbell key (shown at signup; make a new one from your dashboard). Optionally send \"X-Client-Name: <assistant name>\" (e.g. Muse) so bookings show which assistant made them."}}},"security":[{"apiKey":[]}],"paths":{"/api/v1/my_business":{"get":{"operationId":"my_business","summary":"My business","description":"Show the business profile, prices, add-ons, service ZIPs, booking page and this month's pipeline.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[]}},"/api/v1/log_lead":{"post":{"operationId":"log_lead","summary":"Log a new lead","description":"Call this when a new lead arrives (e.g. a Thumbtack, Angi, Bark or Yelp lead email in Gmail, or a Facebook/Instagram message). Returns a firm quote, open times, and a reply draft. For platform leads (Thumbtack, Angi, Bark, Yelp, Google), the customer name and contact are not stored and the reply must be sent inside that platform; never send platform customers links to book elsewhere. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","enum":["thumbtack","angi","bark","yelp","google","facebook","instagram","website","referral","other"]},"lead_ref":{"description":"The platform's lead or request ID","type":"string","maxLength":100},"customer_first_name":{"type":"string","maxLength":50},"contact":{"description":"Only for direct leads","type":"string","maxLength":200},"zip":{"type":"string","pattern":"^\\d{5}$"},"lead_cost":{"description":"What the platform charged, USD","type":"number","minimum":0,"maximum":1000},"bedrooms":{"type":"integer","minimum":0,"maximum":10},"bathrooms":{"type":"number","minimum":1,"maximum":10},"clean_type":{"default":"standard","description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","type":"string","enum":["standard","deep","move_out","turnover"]},"sqft":{"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","type":"integer","minimum":200,"maximum":15000},"frequency":{"default":"once","description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","type":"string","enum":["once","weekly","biweekly","monthly"]},"add_ons":{"default":[],"type":"array","items":{"type":"string"}},"pets":{"default":false,"type":"boolean"}},"required":["source"]}}}}}},"/api/v1/quote_job":{"get":{"operationId":"quote_job","summary":"Quote a job","description":"Price a cleaning from the pro's own price list, with the line items and estimated hours.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[{"name":"bedrooms","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":10}},{"name":"bathrooms","in":"query","required":true,"description":"Can be a half, e.g. 2.5","schema":{"type":"number","minimum":1,"maximum":10,"description":"Can be a half, e.g. 2.5"}},{"name":"clean_type","in":"query","required":false,"description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","schema":{"default":"standard","description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","type":"string","enum":["standard","deep","move_out","turnover"]}},{"name":"sqft","in":"query","required":false,"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","schema":{"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","type":"integer","minimum":200,"maximum":15000}},{"name":"frequency","in":"query","required":false,"description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","schema":{"default":"once","description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","type":"string","enum":["once","weekly","biweekly","monthly"]}},{"name":"add_ons","in":"query","required":false,"description":"e.g. fridge, oven, windows, cabinets, laundry Comma-separated list.","schema":{"type":"string"}},{"name":"pets","in":"query","required":false,"schema":{"default":false,"type":"boolean"}}]}},"/api/v1/open_times":{"get":{"operationId":"open_times","summary":"Open times","description":"List open start times for a job of this size, given working hours and existing bookings.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[{"name":"bedrooms","in":"query","required":true,"schema":{"type":"integer","minimum":0,"maximum":10}},{"name":"bathrooms","in":"query","required":true,"description":"Can be a half, e.g. 2.5","schema":{"type":"number","minimum":1,"maximum":10,"description":"Can be a half, e.g. 2.5"}},{"name":"clean_type","in":"query","required":false,"description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","schema":{"default":"standard","description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","type":"string","enum":["standard","deep","move_out","turnover"]}},{"name":"sqft","in":"query","required":false,"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","schema":{"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","type":"integer","minimum":200,"maximum":15000}},{"name":"frequency","in":"query","required":false,"description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","schema":{"default":"once","description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","type":"string","enum":["once","weekly","biweekly","monthly"]}},{"name":"add_ons","in":"query","required":false,"description":"e.g. fridge, oven, windows, cabinets, laundry Comma-separated list.","schema":{"type":"string"}},{"name":"pets","in":"query","required":false,"schema":{"default":false,"type":"boolean"}},{"name":"start_after","in":"query","required":false,"description":"Earliest start, local HH:MM, e.g. the guest checkout time for a turnover","schema":{"description":"Earliest start, local HH:MM, e.g. the guest checkout time for a turnover","type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}},{"name":"finish_by","in":"query","required":false,"description":"The job must be finished by this local HH:MM, e.g. the next guest check-in time","schema":{"description":"The job must be finished by this local HH:MM, e.g. the next guest check-in time","type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$"}},{"name":"date","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"days","in":"query","required":false,"schema":{"default":7,"type":"integer","minimum":1,"maximum":21}}]}},"/api/v1/book_job":{"post":{"operationId":"book_job","summary":"Book a job","description":"Book a job on this pro's own schedule, usually for a lead who picked a time. It cannot search or book other cleaners; a customer looking for a cleaner uses Latchbell Local Pros instead. Requires the customer's name, the address, and customer_phone or customer_email. Front desk bookings are free. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"string"},"bedrooms":{"type":"integer","minimum":0,"maximum":10},"bathrooms":{"type":"number","minimum":1,"maximum":10,"description":"Can be a half, e.g. 2.5"},"clean_type":{"default":"standard","description":"move_out also covers move-in. turnover = short-term rental (Airbnb/Vrbo) clean between guests","type":"string","enum":["standard","deep","move_out","turnover"]},"sqft":{"description":"Home size in square feet, only if the customer said it. Homes larger than usual for their bedroom count cost more; never guess","type":"integer","minimum":200,"maximum":15000},"frequency":{"default":"once","description":"Only what the customer asked for; default one-time. Never switch to a recurring frequency just to get a lower price","type":"string","enum":["once","weekly","biweekly","monthly"]},"add_ons":{"default":[],"description":"e.g. fridge, oven, windows, cabinets, laundry","type":"array","items":{"type":"string"}},"pets":{"default":false,"type":"boolean"},"start":{"type":"string"},"customer_name":{"type":"string","minLength":1,"maxLength":100},"customer_phone":{"type":"string","maxLength":30},"customer_email":{"type":"string","maxLength":200},"address":{"type":"string","minLength":3,"maxLength":300},"zip":{"type":"string","pattern":"^\\d{5}$"},"notes":{"type":"string","maxLength":500}},"required":["bedrooms","bathrooms","start","customer_name","address","zip"]}}}}}},"/api/v1/follow_ups_due":{"get":{"operationId":"follow_ups_due","summary":"Follow-ups due","description":"List quoted leads that haven't replied and are due a follow-up (after 1 day, then 2 more days), each with a draft. After the pro sends one, call update_lead with contacted=true.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[]}},"/api/v1/update_lead":{"post":{"operationId":"update_lead","summary":"Update a lead","description":"Update a lead: mark a follow-up as sent (contacted=true), or set status to lost/booked, or add notes. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"string"},"status":{"type":"string","enum":["quoted","booked","lost"]},"contacted":{"type":"boolean"},"notes":{"type":"string","maxLength":500}},"required":["lead_id"]}}}}}},"/api/v1/flag_bad_lead":{"post":{"operationId":"flag_bad_lead","summary":"Flag a bad lead","description":"Mark a paid lead as bad and prepare a lead-credit request with evidence for the pro to submit to the platform themselves. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"lead_id":{"type":"string"},"reason":{"type":"string","enum":["out_of_area","duplicate","wrong_contact","no_response","not_requested","service_not_offered"]},"evidence":{"default":[],"description":"What happened, with dates","maxItems":10,"type":"array","items":{"type":"string","maxLength":300}}},"required":["lead_id","reason"]}}}}}},"/api/v1/cancel_job":{"post":{"operationId":"cancel_job","summary":"Cancel a job","description":"Cancel an upcoming job on this pro's schedule (booking id from schedule or book_job). No Latchbell fee is charged for cancelled jobs. Latchbell does not message the customer yet, so tell the pro to let the customer know. Confirm with the pro first. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"}},"required":["booking_id"]}}}}}},"/api/v1/updates":{"get":{"operationId":"updates","summary":"Updates since last check","description":"Return only what changed since the last check: new bookings (including from AI assistants), cancellations, follow-ups due, jobs today. Pass since = the cursor from the previous call. For a scheduled watcher, check about every next_check_in_minutes, and only message the pro when events is not empty.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[{"name":"since","in":"query","required":false,"description":"cursor from the previous updates call","schema":{"description":"cursor from the previous updates call","type":"integer","minimum":0,"maximum":9007199254740991}}]}},"/api/v1/report_no_show":{"post":{"operationId":"report_no_show","summary":"Report a no-show","description":"Report that a customer booked through Latchbell did not show up or cancel. The $5 Latchbell fee for that booking is waived. This changes data; confirm with the user first.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"}},"required":["booking_id"]}}}}}},"/api/v1/pipeline":{"get":{"operationId":"pipeline","summary":"Lead pipeline report","description":"Monthly report: leads and spend by platform, jobs booked, true cost per booked job, possible lead credits, and customers Latchbell brought directly.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[{"name":"month","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}$"}}]}},"/api/v1/schedule":{"get":{"operationId":"schedule","summary":"Upcoming schedule","description":"Show upcoming jobs (with customer contact and address) and unread notifications such as new bookings from AI agents.","responses":{"200":{"description":"Success. JSON result.","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Invalid input or the action was refused; `error` explains why.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Missing or invalid API key."}},"parameters":[]}}}}