{"openapi":"3.1.0","info":{"title":"RentalCal Public Booking API","version":"1.0.0","summary":"Describe, price and request a stay at a RentalCal property.","description":"Everything a guest can see on a host's booking page, for programs.\nNo authentication for reads. A booking request needs only the guest's name and email.\n\nTypical flow: resolve the property (from its site address or an id the host gave you),\nread the listing, check availability, get a quote for the exact dates and party,\nshow the guest the total and any rental agreement, then POST a booking request.\nWhen the host takes payment up front the response carries a Stripe Checkout URL;\nthe request is only held once that is paid. Otherwise the host reviews the request\nand the guest hears back by email.\n\nNever submit a request the guest has not confirmed: dates, party size, the total,\nand the agreement when one is returned. Quotes are not holds.","contact":{"name":"RentalCal","url":"https://www.rentalcal.com"}},"servers":[{"url":"https://api.rentalcal.com"}],"paths":{"/public/v1/properties":{"get":{"operationId":"resolveProperty","summary":"Find a property id from its booking site address","parameters":[{"name":"site","in":"query","required":true,"description":"The host's site address, e.g. `cabin.rentalcal.com` or `www.thecabin.com`. A full URL (`https://cabin.rentalcal.com/`) or the slug alone (`cabin`) also works.","schema":{"type":"string"}}],"responses":{"200":{"description":"The property behind that site.","content":{"application/json":{"schema":{"type":"object","required":["propertyId","name"],"properties":{"propertyId":{"type":"string"},"name":{"type":"string"}}}}}},"400":{"description":"`site` missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No published site at that address.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/properties/{propertyId}":{"get":{"operationId":"getProperty","summary":"The listing: description, photos, rules, base prices","parameters":[{"$ref":"#/components/parameters/propertyId"}],"responses":{"200":{"description":"The listing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}},"404":{"description":"Unknown or inactive property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/properties/{propertyId}/availability":{"get":{"operationId":"getAvailability","summary":"Taken nights and open gaps in a date window","parameters":[{"$ref":"#/components/parameters/propertyId"},{"name":"from","in":"query","description":"Window start (inclusive). Defaults to today (UTC).","schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD"}},{"name":"to","in":"query","description":"Window end (exclusive). Defaults to `from` + `days`. At most 365 days after `from`.","schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD"}},{"name":"days","in":"query","description":"Window length when `to` is omitted. Default 90, max 365.","schema":{"type":"integer","minimum":1,"maximum":365}}],"responses":{"200":{"description":"Availability inside the window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Availability"}}}},"400":{"description":"Bad dates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or inactive property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/properties/{propertyId}/quote":{"get":{"operationId":"getQuote","summary":"Price a stay for exact dates and party","description":"Runs the same checks a booking request would (minimum stay, availability, occupancy, pets, promo code) and returns the full breakdown. A quote is not a hold.","parameters":[{"$ref":"#/components/parameters/propertyId"},{"name":"checkIn","in":"query","required":true,"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Arrival date."}},{"name":"checkOut","in":"query","required":true,"schema":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Departure date. Nights are counted from checkIn up to but not including checkOut."}},{"name":"guests","in":"query","required":true,"description":"Adults plus children.","schema":{"type":"integer","minimum":1}},{"name":"pets","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"promo","in":"query","description":"A promo code the guest has. An invalid code fails the quote rather than being ignored.","schema":{"type":"string"}}],"responses":{"200":{"description":"The stay can be requested at this price.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"The stay breaks a rule: `min_nights`, `max_guests`, `max_pets`, `pets_not_allowed`, `invalid_dates`, `invalid_promo`. The message is safe to show the guest.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or inactive property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`dates_unavailable`: a night in the range is taken, or `booking_requests_disabled`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/properties/{propertyId}/booking-requests":{"post":{"operationId":"createBookingRequest","summary":"Request the stay","description":"Creates a pending booking request. The dates are held against other requests from this moment. If `checkout` is returned the host requires payment up front: send the guest to `checkout.url` (Stripe Checkout, which accepts Link) before it expires, or the request lapses. If `checkout` is null the host reviews the request and replies to the guest's email. When the listing returned a `rentalAgreement`, pass `agreementAccepted: true` and its `version` after the guest has read it.","parameters":[{"$ref":"#/components/parameters/propertyId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequestInput"}}}},"responses":{"201":{"description":"Request created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}}},"400":{"description":"`invalid_payload`, `invalid_email`, `agreement_required`, or any quote failure code.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Unknown or inactive property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`dates_unavailable`, `booking_requests_disabled`, or `host_limit_reached` (the host's plan has hit its monthly request cap; suggest contacting them directly).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Body over 32 KB.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited, per caller, per guest email and per property.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/public/v1/booking-requests/{bookingId}":{"get":{"operationId":"getBookingRequest","summary":"Where a request stands","parameters":[{"name":"bookingId","in":"path","required":true,"schema":{"type":"string"}},{"name":"email","in":"query","required":true,"description":"The guest email the request was made with.","schema":{"type":"string","format":"email"}}],"responses":{"200":{"description":"The request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingStatus"}}}},"400":{"description":"`email` missing.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No request with that id for that email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"parameters":{"propertyId":{"name":"propertyId","in":"path","required":true,"description":"RentalCal property id, as returned by `resolveProperty` or embedded in the host's booking widget.","schema":{"type":"string"}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string","description":"Written for the guest. Safe to relay."}}}}},"DateRange":{"type":"object","required":["from","to"],"properties":{"from":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"First night (inclusive)."},"to":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Checkout day (exclusive)."}}},"Property":{"type":"object","required":["id","name","timezone","minNights","maxGuests","currency","pricing","booking"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"tagline":{"type":["string","null"]},"description":{"type":["string","null"]},"location":{"type":["string","null"],"description":"As the host describes it, e.g. `Tofino, BC`."},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2."},"timezone":{"type":"string","description":"IANA zone."},"checkInTime":{"type":"string","description":"e.g. `15:00`."},"checkOutTime":{"type":"string"},"minNights":{"type":"integer"},"maxGuests":{"type":"integer"},"bedrooms":{"type":["number","null"]},"bathrooms":{"type":["number","null"]},"petsAllowed":{"type":"boolean"},"maxPets":{"type":["integer","null"],"description":"0 when pets are not allowed; null means allowed with no stated cap."},"amenities":{"type":"array","items":{"type":"string"}},"houseRules":{"type":["string","null"]},"images":{"type":"object","properties":{"hero":{"type":["string","null"],"format":"uri"},"gallery":{"type":"array","items":{"type":"string","format":"uri"}}}},"cancellationPolicy":{"type":["object","null"],"description":"How a cancellation is refunded. `summary` is what to tell the guest; `rules` is the same thing as numbers when the policy is simple: cancel at least `daysBeforeCheckIn` days out and `refundPercent` of what was paid is returned.","properties":{"summary":{"type":"string"},"rules":{"type":"array","items":{"type":"object","properties":{"daysBeforeCheckIn":{"type":"integer"},"refundPercent":{"type":"number"}}}}}},"paymentSchedule":{"type":["object","null"],"description":"When the host collects the money. `depositPercent` is due at booking unless `depositDueDaysBeforeArrival` is set; the balance is due `balanceDueDaysBeforeArrival` days before check-in (0 = on arrival). 100 means paid in full at booking.","properties":{"depositPercent":{"type":"number"},"depositDueDaysBeforeArrival":{"type":"integer"},"balanceDueDaysBeforeArrival":{"type":"integer"},"summary":{"type":"string"}}},"damageDepositCents":{"type":"integer","description":"Refundable deposit the host requires, on top of the total. 0 when none. The quote says whether it is held at booking. In the smallest unit of the listing's currency (cents for USD/CAD)."},"faq":{"type":"array","description":"The host's own answers to common questions.","items":{"type":"object","properties":{"question":{"type":"string"},"answer":{"type":"string"}}}},"site":{"type":["object","null"],"description":"The host's booking site, when published.","properties":{"url":{"type":"string","format":"uri"}}},"currency":{"type":"string","description":"ISO 4217."},"pricing":{"type":"object","description":"Base rates. Use `getQuote` for a real total: seasonal rates, fees and taxes apply.","properties":{"pricePerNight":{"type":"integer","description":"Base nightly rate. In the smallest unit of the listing's currency (cents for USD/CAD)."},"cleaningFee":{"type":"integer","description":"Once per stay. In the smallest unit of the listing's currency (cents for USD/CAD)."},"weekendPricePerNight":{"type":["integer","null"],"description":"Friday and Saturday nights. In the smallest unit of the listing's currency (cents for USD/CAD)."},"weeklyDiscountPercent":{"type":["number","null"]},"monthlyDiscountPercent":{"type":["number","null"]},"petFeeCents":{"type":["integer","null"],"description":"Per pet, per stay. In the smallest unit of the listing's currency (cents for USD/CAD)."},"includedGuests":{"type":["integer","null"]},"extraGuestFeeCents":{"type":["integer","null"],"description":"Per guest above includedGuests, per night. In the smallest unit of the listing's currency (cents for USD/CAD)."}}},"booking":{"type":"object","properties":{"acceptsRequests":{"type":"boolean"},"rentalAgreement":{"type":["object","null"],"description":"When present the guest must read it and the request must carry `agreementAccepted` with this `version`.","properties":{"version":{"type":"integer"},"bodyMarkdown":{"type":"string"}}}}}}},"Availability":{"type":"object","required":["window","minNights","unavailable","openGaps"],"properties":{"window":{"type":"object","properties":{"from":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive."},"to":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Exclusive."}}},"minNights":{"type":"integer"},"seasonalMinNights":{"type":"array","description":"Overrides by check-in date.","items":{"type":"object","properties":{"startDate":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"endDate":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Inclusive."},"minNights":{"type":"integer"}}}},"unavailable":{"type":"array","items":{"$ref":"#/components/schemas/DateRange"},"description":"Nights already booked or blocked."},"openGaps":{"type":"array","description":"Runs of free nights that a quote would accept a stay in. Seasonal minimums and gap fill are applied, and no gap starts before today.","items":{"type":"object","properties":{"start":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"First free night."},"end":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Latest checkout."},"nights":{"type":"integer","description":"Free nights in the gap."}}}}}},"Quote":{"type":"object","required":["checkIn","checkOut","nights","currency","breakdown","payment"],"properties":{"checkIn":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"checkOut":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"nights":{"type":"integer"},"guestCount":{"type":"integer"},"petCount":{"type":"integer"},"currency":{"type":"string"},"minNights":{"type":"integer","description":"For this check-in date."},"breakdown":{"type":"object","properties":{"nightly":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"price":{"type":"integer","description":"That night. In the smallest unit of the listing's currency (cents for USD/CAD)."}}}},"subtotal":{"type":"integer","description":"All nights before discounts. In the smallest unit of the listing's currency (cents for USD/CAD)."},"discount":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["weekly","monthly","promo"]},"percent":{"type":["number","null"]},"amount":{"type":"integer","description":"Taken off the subtotal. In the smallest unit of the listing's currency (cents for USD/CAD)."},"code":{"type":["string","null"]}}},"fees":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"amount":{"type":"integer","description":" In the smallest unit of the listing's currency (cents for USD/CAD)."}}}},"taxes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"rate":{"type":"number","description":"Percent."},"amount":{"type":"integer","description":" In the smallest unit of the listing's currency (cents for USD/CAD)."}}}},"cleaningFee":{"type":"integer","description":" In the smallest unit of the listing's currency (cents for USD/CAD)."},"damageWaiverFee":{"type":"integer","description":"Non-refundable damage protection, when the host charges one. 0 otherwise. In the smallest unit of the listing's currency (cents for USD/CAD)."},"total":{"type":"integer","description":"What the guest pays. Quote this number. In the smallest unit of the listing's currency (cents for USD/CAD)."}}},"payment":{"type":"object","properties":{"required":{"type":"boolean","description":"True: the request must be paid through the returned Checkout URL to be held."},"mode":{"type":"string","enum":["none","instant","request_hold"],"description":"`instant`: paid and confirmed at once. `request_hold`: the card is authorised and the host approves or declines. `none`: the host replies by email."},"damageDepositCents":{"type":"integer","description":"Refundable deposit the host requires, on top of the total. 0 when none. In the smallest unit of the listing's currency (cents for USD/CAD)."},"damageDepositHeldAtBooking":{"type":"boolean","description":"True: RentalCal holds the deposit on the guest's card through Checkout and releases it after the stay. False: the host arranges it with the guest directly."}}},"agreementRequired":{"type":"boolean"}}},"BookingRequestInput":{"type":"object","required":["checkIn","checkOut","guestName","guestEmail","guestCount"],"properties":{"checkIn":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"checkOut":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"guestName":{"type":"string","maxLength":200},"guestEmail":{"type":"string","format":"email"},"guestPhone":{"type":"string","maxLength":40},"guestCount":{"type":"integer","minimum":1},"petCount":{"type":"integer","minimum":0},"message":{"type":"string","maxLength":2000,"description":"A note to the host, in the guest's words."},"promoCode":{"type":"string"},"agreementAccepted":{"type":"boolean"},"agreementVersion":{"type":"integer"},"listOptIn":{"type":"boolean","description":"Only when the guest explicitly agreed to hear from the host about offers."},"source":{"type":"string","enum":["mcp"],"description":"Where the guest came from, when a RentalCal surface sent them: the `rc_source` value on a booking link. Anything else is ignored."},"returnUrl":{"type":"string","format":"uri","description":"https page to return the guest to after Stripe Checkout. Optional."}}},"BookingRequest":{"type":"object","required":["id","status","paymentStatus","checkIn","checkOut","total","currency"],"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending"]},"paymentStatus":{"type":"string","enum":["not_required","awaiting_payment"]},"paymentMode":{"type":"string","enum":["none","instant","request_hold"]},"checkIn":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"checkOut":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"nights":{"type":"integer"},"total":{"type":"integer","description":"The amount due, or the estimate the host will confirm. In the smallest unit of the listing's currency (cents for USD/CAD)."},"currency":{"type":"string"},"checkout":{"type":["object","null"],"description":"Present when payment is required. Send the guest here.","properties":{"url":{"type":"string","format":"uri"},"expiresAt":{"type":"integer","description":"Unix milliseconds."}}},"next":{"type":"string","description":"What happens now, in words for the guest."}}},"BookingStatus":{"type":"object","properties":{"id":{"type":"string"},"propertyId":{"type":"string"},"propertyName":{"type":["string","null"]},"status":{"type":"string","enum":["pending","confirmed","declined","cancelled"]},"paymentStatus":{"type":"string","enum":["not_required","awaiting_payment","hold_placed","paid","refunded","expired","hold_released"]},"checkIn":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"checkOut":{"type":"string","format":"date","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":""},"nights":{"type":"integer"},"guestCount":{"type":"integer"},"total":{"type":"integer","description":" In the smallest unit of the listing's currency (cents for USD/CAD)."},"currency":{"type":["string","null"]},"requestedAt":{"type":"integer","description":"Unix milliseconds."}}}}}}