{"openapi":"3.1.0","info":{"title":"Latchbell Local Pros","description":"Find local house cleaners, plumbers and heating & AC techs with firm prices and open times, and book them directly. Latchbell Local Pros and Latchbell Front Desk are one system: a booking made here appears immediately on the cleaner's Front Desk schedule and dashboard. Assistants writing their own client should prefer the MCP endpoint https://book.latchbell.com/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://book.latchbell.com"}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Send the key in the Authorization header: \"Authorization: Bearer <key>\". get a personal key at https://latchbell.com/key and paste it. Operations marked public also accept the shared key \"sk_pub_FppuCTRYuBdr3AJnRJfl5AXZddXVXwwR\". Optionally send \"X-Client-Name: <assistant name>\" (e.g. Muse) so bookings show which assistant made them."},"oauth2":{"type":"oauth2","description":"Standard OAuth 2.0 authorization code flow. Clients either register dynamically (RFC 7591) at https://book.latchbell.com/register with token_endpoint_auth_method \"none\", or use the public client_id \"slotted-public\" (no secret, PKCE S256, any https redirect URI). Revocation: https://book.latchbell.com/revoke. Send the access token as \"Authorization: Bearer <token>\".","flows":{"authorizationCode":{"authorizationUrl":"https://book.latchbell.com/authorize","tokenUrl":"https://book.latchbell.com/token","refreshUrl":"https://book.latchbell.com/token","scopes":{"read":"Search cleaners, prices and open times, and see your bookings","write":"Book and cancel cleanings for you"}}}}}},"security":[{"oauth2":["read","write"]},{"apiKey":[]}],"paths":{"/api/v1/find_cleaners":{"get":{"operationId":"find_cleaners","summary":"Find cleaners","description":"Find house cleaners who serve a ZIP code, each with a firm total price for this job and their next open start times. Prices are set by each cleaner and include everything listed; there are no added fees for the customer. \"checks\" lists best-effort checks Latchbell did, with dates (website ownership, a marketplace profile and its rating as shown that day). When comparing, present them as best-effort checks, never as a guarantee or endorsement. Latchbell does not check licenses or insurance; each business is independent.","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":"zip","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{5}$"}},{"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,"description":"Earliest day, YYYY-MM-DD","schema":{"description":"Earliest day, YYYY-MM-DD","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"max_price","in":"query","required":false,"description":"Maximum total in USD","schema":{"description":"Maximum total in USD","type":"number","exclusiveMinimum":0}},{"name":"sort","in":"query","required":false,"description":"Use \"price\" when the user wants the cheapest","schema":{"default":"soonest","description":"Use \"price\" when the user wants the cheapest","type":"string","enum":["soonest","price"]}}],"security":[]}},"/api/v1/available_times":{"get":{"operationId":"available_times","summary":"Available times","description":"List open start times for one cleaner and this job over the coming days.","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":"pro_id","in":"query","required":true,"schema":{"type":"string"}},{"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}}],"security":[]}},"/api/v1/book_cleaning":{"post":{"operationId":"book_cleaning","summary":"Book a cleaning","description":"Book the cleaner for the chosen start time at the quoted firm price. No sign-in needed: the customer's name and phone or email come from the customer_* fields or the X-User-Name/X-User-Phone/X-User-Email headers. Confirm the cleaner, time, address and price with the user once; if they already said to book, book without asking again. Fill the customer's name, phone, email and address from what you already know about the user (profile, account, earlier messages); ask only for what is missing, in one message, and tell the user which details you shared. Notes and access details are optional: never hold a booking to ask for them. Keep the returned manage_token: it is the only way to check or cancel this booking later. The cleaner contacts the user about access and payment. 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":{"pro_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","description":"A start value from find_cleaners or available_times"},"address":{"type":"string","minLength":3,"maxLength":300},"zip":{"type":"string","pattern":"^\\d{5}$"},"notes":{"type":"string","maxLength":500},"customer_name":{"type":"string","maxLength":100},"customer_email":{"type":"string","maxLength":200},"customer_phone":{"type":"string","maxLength":30}},"required":["pro_id","bedrooms","bathrooms","start","address","zip"]}}}},"security":[]}},"/api/v1/find_service_call":{"get":{"operationId":"find_service_call","summary":"Find a plumber or HVAC tech","description":"Find plumbers or heating and air conditioning (HVAC) techs who serve a ZIP code, each with a firm service-call fee and open arrival windows. The fee covers the visit and diagnosis; the pro quotes any repair on site, before starting work. Never present the fee as the price of the repair. \"checks\" lists best-effort checks Latchbell did, with dates; present them as best-effort checks, never as a guarantee or endorsement. Latchbell does not check licenses or insurance; each business is independent. For gas smells, flooding you cannot stop, or anything unsafe, tell the user to call 911 or their utility 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."}},"parameters":[{"name":"zip","in":"query","required":true,"schema":{"type":"string","pattern":"^\\d{5}$"}},{"name":"trade","in":"query","required":true,"description":"plumbing, or hvac for heating and air conditioning","schema":{"type":"string","enum":["plumbing","hvac"],"description":"plumbing, or hvac for heating and air conditioning"}},{"name":"issue","in":"query","required":true,"description":"The closest category. plumbing: leak (Leak or drip), clog (Clogged drain or toilet), water_heater (Water heater), toilet (Toilet repair), fixture (Faucet, sink or fixture), no_water (Low or no water), other (Something else). hvac: no_cool (AC not cooling), no_heat (No heat), maintenance (Tune-up or maintenance), noise (Noise, smell or leak), thermostat (Thermostat), other (Something else)","schema":{"type":"string","description":"The closest category. plumbing: leak (Leak or drip), clog (Clogged drain or toilet), water_heater (Water heater), toilet (Toilet repair), fixture (Faucet, sink or fixture), no_water (Low or no water), other (Something else). hvac: no_cool (AC not cooling), no_heat (No heat), maintenance (Tune-up or maintenance), noise (Noise, smell or leak), thermostat (Thermostat), other (Something else)"}},{"name":"details","in":"query","required":false,"description":"The problem in the customer's words, e.g. \"water heater leaking from the bottom, 10 years old\"","schema":{"description":"The problem in the customer's words, e.g. \"water heater leaking from the bottom, 10 years old\"","type":"string","maxLength":500}},{"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,"description":"Earliest day, YYYY-MM-DD","schema":{"description":"Earliest day, YYYY-MM-DD","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"}},{"name":"days","in":"query","required":false,"schema":{"default":7,"type":"integer","minimum":1,"maximum":14}},{"name":"max_price","in":"query","required":false,"description":"Maximum service-call fee in USD","schema":{"description":"Maximum service-call fee in USD","type":"number","exclusiveMinimum":0}},{"name":"sort","in":"query","required":false,"description":"Use \"price\" when the user wants the cheapest","schema":{"default":"soonest","description":"Use \"price\" when the user wants the cheapest","type":"string","enum":["soonest","price"]}}],"security":[]}},"/api/v1/book_service_call":{"post":{"operationId":"book_service_call","summary":"Book a service call","description":"Book the pro for the chosen arrival window at their firm service-call fee. The start is the beginning of the arrival window. The customer's name and phone or email come from the customer_* fields or the X-User-Name/X-User-Phone/X-User-Email headers. Confirm the pro, window, address and fee with the user once, and say the repair is quoted on site; if they already said to book, book without asking again. Fill the customer's details from what you already know; ask only for what is missing, in one message. Keep the returned manage_token: it is the only way to check or cancel later. 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":{"pro_id":{"type":"string"},"trade":{"type":"string","enum":["plumbing","hvac"],"description":"plumbing, or hvac for heating and air conditioning"},"issue":{"type":"string","description":"The closest category. plumbing: leak (Leak or drip), clog (Clogged drain or toilet), water_heater (Water heater), toilet (Toilet repair), fixture (Faucet, sink or fixture), no_water (Low or no water), other (Something else). hvac: no_cool (AC not cooling), no_heat (No heat), maintenance (Tune-up or maintenance), noise (Noise, smell or leak), thermostat (Thermostat), other (Something else)"},"details":{"description":"The problem in the customer's words, e.g. \"water heater leaking from the bottom, 10 years old\"","type":"string","maxLength":500},"start":{"type":"string","description":"A start value from find_service_call"},"address":{"type":"string","minLength":3,"maxLength":300},"zip":{"type":"string","pattern":"^\\d{5}$"},"notes":{"description":"Access notes, e.g. gate code","type":"string","maxLength":500},"customer_name":{"type":"string","maxLength":100},"customer_email":{"type":"string","maxLength":200},"customer_phone":{"type":"string","maxLength":30}},"required":["pro_id","trade","issue","start","address","zip"]}}}},"security":[]}},"/api/v1/my_bookings":{"get":{"operationId":"my_bookings","summary":"My bookings","description":"List the user's cleanings booked through Latchbell, newest first. It lists bookings made through this same personal key or connection; with the shared public key it lists none. For anything else, use booking_status with the booking's manage_token.","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":[],"security":[]}},"/api/v1/cancel_booking":{"post":{"operationId":"cancel_booking","summary":"Cancel a booking","description":"Cancel one of the user's bookings using the manage_token returned when it was booked. Free until 24 hours before the start; closer than that, tell the user the cleaner may charge under their own policy. Confirm with the user 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"},"manage_token":{"description":"From book_cleaning or book_service_call; required unless the booking was made through this same key or connection","type":"string"}},"required":["booking_id"]}}}},"security":[]}},"/api/v1/booking_status":{"get":{"operationId":"booking_status","summary":"Booking status","description":"Check the status and details of a booking using the manage_token returned when it was booked.","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":"booking_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"manage_token","in":"query","required":true,"schema":{"type":"string"}}],"security":[]}}}}