{"openapi":"3.1.0","info":{"title":"Husly Rentals partner API","version":"1.0.0","description":"For travel agencies, channel managers and AI travel agents that sell one Husly rental business's cars, bikes, boats, gear or places to stay.\n\nThe business makes your key under Rentals > Integrations > Partner API. A key opens ONE site: its published catalogue and the bookings you made.\n\nPrices are integer cents. Times are UTC instants; a pick (`from`, `to`) is a date in the listing's own time zone.\n\nA booking you make is never paid on Husly: you settle with the renter. The business confirms it, or it is confirmed at once when the listing or your key says so.\n\nHolds: `hold: true` keeps the dates 20 minutes (at most 60) and tells nobody; `POST /bookings/{id}/confirm` books them. An unconfirmed hold lets the dates go by itself.\n\nLimits: 120 requests a minute and 60 new bookings an hour per key.\n\nMCP: the same API as a Model Context Protocol server at `POST /api/partner/v1/rentals/mcp` (Streamable HTTP, JSON answers, protocol 2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26), with the same key.\n\nWebhooks: every POST carries `X-Husly-Event`, `X-Husly-Timestamp` and `X-Husly-Signature: sha256=<hex>`, the HMAC-SHA256 of `<timestamp>.<body>` with your secret."},"servers":[{"url":"https://api.husly.app"}],"security":[{"bearer":[]}],"tags":[{"name":"Catalogue"},{"name":"Bookings"},{"name":"Webhook"},{"name":"MCP"}],"paths":{"/api/partner/v1/rentals/me":{"get":{"tags":["Catalogue"],"summary":"Your key, the site and its terms","responses":{"200":{"description":"The profile.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Profile"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/listings":{"get":{"tags":["Catalogue"],"summary":"The published catalogue","description":"With `from` and `to` only the listings free on those days are kept.","parameters":[{"name":"category","in":"query","required":false,"description":"A category slug.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"First day, YYYY-MM-DD.","schema":{"type":"string"}},{"name":"to","in":"query","required":false,"description":"Last day, YYYY-MM-DD.","schema":{"type":"string"}}],"responses":{"200":{"description":"The catalogue.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"object"}},"listings":{"type":"array","items":{"$ref":"#/components/schemas/ListingCard"}},"free":{"type":["object","null"]}}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/listings/{slug}":{"get":{"tags":["Catalogue"],"summary":"One listing","parameters":[{"name":"slug","in":"path","required":true,"description":"The listing's slug.","schema":{"type":"string"}}],"responses":{"200":{"description":"The listing.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Listing"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/listings/{slug}/calendar":{"get":{"tags":["Catalogue"],"summary":"Free and taken days","parameters":[{"name":"slug","in":"path","required":true,"description":"The listing's slug.","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"description":"First day, YYYY-MM-DD. Default today.","schema":{"type":"string"}},{"name":"days","in":"query","required":false,"description":"How many days, 1 to 62. Default 31.","schema":{"type":"integer"}}],"responses":{"200":{"description":"The days.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"days":{"type":"array","items":{"$ref":"#/components/schemas/CalendarDay"}}}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/listings/{slug}/quote":{"get":{"tags":["Catalogue"],"summary":"Price and availability for a range","parameters":[{"name":"slug","in":"path","required":true,"description":"The listing's slug.","schema":{"type":"string"}},{"name":"from","in":"query","required":true,"description":"`YYYY-MM-DD`, or `YYYY-MM-DDTHH:MM` for a rental priced by the hour, in the listing's time zone.","schema":{"type":"string"}},{"name":"to","in":"query","required":true,"description":"`YYYY-MM-DD`, or `YYYY-MM-DDTHH:MM` for a rental priced by the hour, in the listing's time zone.","schema":{"type":"string"}},{"name":"extras","in":"query","required":false,"description":"Add-ons as `id:qty,id:qty`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Both answers, even when one refuses.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Quote"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/bookings":{"get":{"tags":["Bookings"],"summary":"Your bookings","parameters":[{"name":"status","in":"query","required":false,"description":"held, pending, confirmed, cancelled, expired, completed or noshow.","schema":{"type":"string"}},{"name":"reference","in":"query","required":false,"description":"Your own reference.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"1 to 100. Default 50.","schema":{"type":"integer"}},{"name":"offset","in":"query","required":false,"description":"Default 0.","schema":{"type":"integer"}}],"responses":{"200":{"description":"Newest first.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"limit":{"type":"integer"},"offset":{"type":"integer"}}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}},"post":{"tags":["Bookings"],"summary":"Book, or hold the dates","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingRequest"}}}},"responses":{"200":{"description":"The same `reference` already holds dates: that booking.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}},"required":["data"]}}}},"201":{"description":"Booked or held.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"422":{"$ref":"#/components/responses/E422"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/bookings/{id}":{"get":{"tags":["Bookings"],"summary":"One of your bookings","parameters":[{"name":"id","in":"path","required":true,"description":"The booking id.","schema":{"type":"string"}}],"responses":{"200":{"description":"The booking.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/bookings/{id}/confirm":{"post":{"tags":["Bookings"],"summary":"Confirm a hold","parameters":[{"name":"id","in":"path","required":true,"description":"The booking id.","schema":{"type":"string"}}],"responses":{"200":{"description":"Booked: `confirmed`, or `pending` until the business answers.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/bookings/{id}/cancel":{"post":{"tags":["Bookings"],"summary":"Cancel a booking or let a hold go","parameters":[{"name":"id","in":"path","required":true,"description":"The booking id.","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":400}}}}}},"responses":{"200":{"description":"Cancelled (or expired, for a hold).","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Booking"}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"409":{"$ref":"#/components/responses/E409"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/webhook":{"get":{"tags":["Webhook"],"summary":"Your webhook","responses":{"200":{"description":"Null when none is set.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"webhook":{"oneOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"null"}]}}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}},"put":{"tags":["Webhook"],"summary":"Set the address","description":"The signing secret is in the answer when the webhook is new, or with `rotate: true`. Store it then.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string"},"rotate":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"},"secret":{"type":"string"}}}},"required":["data"]}}}},"201":{"description":"Created, with the secret.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"},"secret":{"type":"string"}}}},"required":["data"]}}}},"400":{"$ref":"#/components/responses/E400"},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}},"delete":{"tags":["Webhook"],"summary":"Remove it","responses":{"200":{"description":"Removed.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"webhook":{"type":"null"}}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/webhook/test":{"post":{"tags":["Webhook"],"summary":"Send a `ping`","responses":{"202":{"description":"Queued.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"deliveryId":{"type":"string"}}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"404":{"$ref":"#/components/responses/E404"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/mcp":{"post":{"tags":["MCP"],"summary":"The MCP server (JSON-RPC 2.0)","description":"Protocol 2026-07-28 (stateless: `server/discover`, `tools/list`, `tools/call`, with `MCP-Protocol-Version`, `Mcp-Method` and `Mcp-Name` headers) and 2025-03-26 to 2025-11-25 (`initialize`, `ping`, `tools/list`, `tools/call`). One message per request; a notification gets 202.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"The JSON-RPC answer.","content":{"application/json":{"schema":{"type":"object"}}}},"202":{"description":"A notification: no answer."},"401":{"$ref":"#/components/responses/E401"},"403":{"$ref":"#/components/responses/E403"},"429":{"$ref":"#/components/responses/E429"}}}},"/api/partner/v1/rentals/openapi.json":{"get":{"summary":"This document","security":[],"responses":{"200":{"description":"OpenAPI 3.1."}}}},"/api/partner/v1/rentals/docs":{"get":{"summary":"This document as a page","security":[],"responses":{"200":{"description":"HTML."}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer hsly_partner_...` (or `X-Api-Key`)."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"A stable code, e.g. `unavailable`."},"detail":{"type":"object"}},"required":["error"]},"Localized":{"type":"object","additionalProperties":{"type":"string"},"description":"Text by language, e.g. `{ \"et\": \"Ratas\", \"en\": \"Bike\" }`."},"Profile":{"type":"object","properties":{"partner":{"type":"object","properties":{"name":{"type":"string"},"canBook":{"type":"boolean"},"autoConfirm":{"type":"boolean"}}},"site":{"type":"object","properties":{"name":{"type":"string"},"url":{"type":["string","null"]}}},"settings":{"type":"object","properties":{"slugBase":{"type":"string"},"timezone":{"type":"string"},"vatRate":{"type":"number"},"vatIncluded":{"type":"boolean"},"termsUrl":{"type":"string","description":"The terms a renter agrees to. Empty = none."},"phoneRequired":{"type":"boolean"},"cancelPolicy":{"type":["object","null"],"properties":{"freeHours":{"type":"integer"},"lateFeePct":{"type":"integer"}}}}},"holds":{"type":"object","properties":{"defaultMinutes":{"type":"integer"},"maxMinutes":{"type":"integer"}}}}},"ListingCard":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"kind":{"type":"string"},"itemType":{"type":"string"},"title":{"$ref":"#/components/schemas/Localized"},"summary":{"oneOf":[{"$ref":"#/components/schemas/Localized"},{"type":"null"}]},"coverUrl":{"type":"string"},"categoryId":{"type":["string","null"]},"currency":{"type":"string"},"price":{"type":["object","null"],"description":"The lowest price of the shortest rental starting today."},"rating":{"type":["object","null"]},"url":{"type":["string","null"],"description":"The listing's page on the site."}}},"Listing":{"allOf":[{"$ref":"#/components/schemas/ListingCard"},{"type":"object","properties":{"content":{"oneOf":[{"$ref":"#/components/schemas/Localized"},{"type":"null"}]},"gallery":{"type":"array","items":{"type":"object"}},"specs":{"type":"array","items":{"type":"object"}},"amenities":{"type":"array","items":{"type":"object"}},"depositCents":{"type":"integer"},"minUnits":{"type":"integer"},"maxUnits":{"type":["integer","null"]},"leadHours":{"type":"integer"},"maxAdvanceDays":{"type":["integer","null"]},"openingHours":{"type":["object","null"]},"location":{"type":["object","null"]},"rates":{"type":"array","items":{"type":"object"},"description":"Price bands: unit, price, minimum, season, weekdays."},"extras":{"type":"array","items":{"type":"object"},"description":"Add-ons: `{ id, title, priceCents, chargeType, maxQty }`."},"questions":{"type":"array","items":{"type":"object"},"description":"What a booking asks beyond the dates; answer by `key`."}}}]},"CalendarDay":{"type":"object","properties":{"date":{"type":"string"},"state":{"type":"string","enum":["free","partial","busy"]}}},"Quote":{"type":"object","properties":{"availability":{"type":"object","properties":{"ok":{"type":"boolean"},"problem":{"type":"string"}}},"quote":{"type":"object","properties":{"ok":{"type":"boolean"},"totalCents":{"type":"integer"},"depositCents":{"type":"integer"},"currency":{"type":"string"},"lines":{"type":"array","items":{"type":"object"}}}}}},"Booking":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string","description":"The site's own reference, as in the renter's letters."},"status":{"type":"string","enum":["held","pending","confirmed","cancelled","expired","completed","noshow"]},"holdExpiresAt":{"type":["string","null"],"format":"date-time"},"reference":{"type":"string","description":"Your own reference."},"listing":{"type":"object","properties":{"slug":{"type":"string"},"title":{"$ref":"#/components/schemas/Localized"}}},"startsAt":{"type":"string","format":"date-time"},"endsAt":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"total":{"type":"object","properties":{"cents":{"type":"integer"},"currency":{"type":"string"}}},"depositCents":{"type":"integer"},"quote":{"type":["object","null"]},"guest":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"locale":{"type":"string"}}},"note":{"type":"string"},"cancelledAt":{"type":["string","null"],"format":"date-time"},"cancelReason":{"type":"string"},"cancelledBy":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"BookingRequest":{"type":"object","required":["listing","from","to","guest"],"properties":{"listing":{"type":"string","description":"The listing's slug."},"from":{"type":"string","description":"`YYYY-MM-DD`, or `YYYY-MM-DDTHH:MM` for a rental priced by the hour, in the listing's time zone."},"to":{"type":"string","description":"`YYYY-MM-DD`, or `YYYY-MM-DDTHH:MM` for a rental priced by the hour, in the listing's time zone."},"guest":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string","description":"Required when `settings.phoneRequired` is true (`GET /me`)."},"note":{"type":"string"}}},"termsAccepted":{"type":"boolean","description":"The guest agreed to `settings.termsUrl`. Required when it is set."},"locale":{"type":"string","description":"The guest's language for the letters (`et`, `en`). Default `en`."},"hold":{"type":"boolean","default":false,"description":"Hold the dates and confirm later."},"holdMinutes":{"type":"integer","minimum":5,"maximum":60,"default":20},"reference":{"type":"string","maxLength":64,"description":"Your booking number. Sending it again returns the same booking."},"extras":{"type":"object","additionalProperties":{"type":"integer"},"description":"`{ addOnId: quantity }`."},"answers":{"type":"object","description":"Answers to the listing's `questions`, by key."}}},"Webhook":{"type":"object","properties":{"url":{"type":"string"},"active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string","enum":["rental.booking.created","rental.booking.confirmed","rental.booking.cancelled"]}},"lastStatus":{"type":["integer","null"]},"lastError":{"type":["string","null"]},"lastDeliveryAt":{"type":["string","null"],"format":"date-time"},"disabledAt":{"type":["string","null"],"format":"date-time"}}}},"responses":{"E400":{"description":"The request does not read (for example `bad_range`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E401":{"description":"No key, a wrong key or a revoked key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E403":{"description":"The key is read-only (`read_only_key`) or the site's rentals are off (`rentals_off`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E404":{"description":"No such listing or booking for this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E409":{"description":"The dates are taken (`unavailable`), the hold ran out (`hold_expired`) or the rental has begun (`already_started`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E422":{"description":"The guest's details are missing (`invalid_guest`) or the range cannot be priced (`not_bookable`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"E429":{"description":"Too many requests. Wait the `Retry-After` seconds.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}