repull 0.2.38 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/repull/api/availability_api.rb +2 -2
- data/lib/repull/api/listings_api.rb +4 -4
- data/lib/repull/models/list_listing_units200_response_data_inner.rb +17 -5
- data/lib/repull/models/listing.rb +14 -4
- data/lib/repull/models/listing_parent.rb +159 -0
- data/lib/repull/models/listing_units_inner.rb +17 -4
- data/lib/repull/models/property_availability_day.rb +17 -5
- data/lib/repull/models/property_availability_unit.rb +193 -0
- data/lib/repull/models/reservation_unit.rb +1 -1
- data/lib/repull/version.rb +1 -1
- data/lib/repull.rb +2 -0
- data/openapi/v1.json +77 -12
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 05f8dcbf939588a31a5e66d89b6009ae1ec20c56f90f172f664b3cb3317657b4
|
|
4
|
+
data.tar.gz: c2083ff59fa0e0df5010ba404f922b6b94551120d7887f13df6e587543be9c77
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8176bc233194e36040c4f103241232c72545b716c1619d3e16bf2ed0c0f9725da0ff1b3df772237f70a519b679266432bd844e268c25955418805e140a9710f5
|
|
7
|
+
data.tar.gz: 152383381f99200bd26b99008c17400dd415390d7a50871e29f7af166105be9739337598317467f7a4ccad7e4067b3d49c013d3a09a264c4e274b53a5d5d3c4f
|
|
@@ -88,7 +88,7 @@ module Repull
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# Get property availability
|
|
91
|
-
# Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape — `{ date, available, price, minNights, availableUnits }` — projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the
|
|
91
|
+
# Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape — `{ date, available, price, minNights, availableUnits }` — projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the units left for a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing). On such a listing each day also carries `units: [{ id, available }]` — which unit is free that night, with the ids of the listing's `units[]` and a reservation's `unit`. The `from` and `to` query params are **required** (ISO `YYYY-MM-DD`, inclusive) — omitting or malforming either returns 422. The window is capped at 366 days; longer ranges are truncated to the first 366 days. **`days` contains only the dates we actually hold calendar data for.** Requested dates with no calendar row are listed in `coverage.missingDates` — their availability is unknown. Never treat a missing date as bookable: this endpoint deliberately does not synthesise availability, because a fabricated open date can be double-booked. A property with no calendar still returns a real 200 (`days: []`, every date in `coverage.missingDates`), never a 404 — 404 means the property id does not exist or belongs to a different workspace. This endpoint is read-only, and the projected per-date shape carries **availability, units left, price, and min-nights only** — it does NOT expose max-stay, closed-to-arrival (CTA), closed-to-departure (CTD), or the dedicated stop-sell flag. To read or write that full restriction set on Booking.com use the channel routes: `GET`/`PUT /v1/channels/booking/availability` (with the room + rate ids from `GET /v1/channels/booking/properties/{id}/rooms`). To **write** calendar values use `PUT /v1/availability/{propertyId}` (or `PATCH /v1/availability/batch`), which updates the property calendar and pushes to its connected channels; channel-only settings stay on the channel routes. Returns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
|
|
92
92
|
# @param property_id [Integer] Repull property id (equal to `listings.id`; the same integer used as `propertyId` on availability and `listingId` on reservations).
|
|
93
93
|
# @param from [Date] Start of the window (inclusive), ISO `YYYY-MM-DD`. Required — missing/malformed returns 422. `startDate` is accepted as an alias.
|
|
94
94
|
# @param to [Date] End of the window (inclusive), ISO `YYYY-MM-DD`. Required — missing/malformed returns 422. `endDate` is accepted as an alias.
|
|
@@ -100,7 +100,7 @@ module Repull
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Get property availability
|
|
103
|
-
# Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape — `{ date, available, price, minNights, availableUnits }` — projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the
|
|
103
|
+
# Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape — `{ date, available, price, minNights, availableUnits }` — projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the units left for a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing). On such a listing each day also carries `units: [{ id, available }]` — which unit is free that night, with the ids of the listing's `units[]` and a reservation's `unit`. The `from` and `to` query params are **required** (ISO `YYYY-MM-DD`, inclusive) — omitting or malforming either returns 422. The window is capped at 366 days; longer ranges are truncated to the first 366 days. **`days` contains only the dates we actually hold calendar data for.** Requested dates with no calendar row are listed in `coverage.missingDates` — their availability is unknown. Never treat a missing date as bookable: this endpoint deliberately does not synthesise availability, because a fabricated open date can be double-booked. A property with no calendar still returns a real 200 (`days: []`, every date in `coverage.missingDates`), never a 404 — 404 means the property id does not exist or belongs to a different workspace. This endpoint is read-only, and the projected per-date shape carries **availability, units left, price, and min-nights only** — it does NOT expose max-stay, closed-to-arrival (CTA), closed-to-departure (CTD), or the dedicated stop-sell flag. To read or write that full restriction set on Booking.com use the channel routes: `GET`/`PUT /v1/channels/booking/availability` (with the room + rate ids from `GET /v1/channels/booking/properties/{id}/rooms`). To **write** calendar values use `PUT /v1/availability/{propertyId}` (or `PATCH /v1/availability/batch`), which updates the property calendar and pushes to its connected channels; channel-only settings stay on the channel routes. Returns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
|
|
104
104
|
# @param property_id [Integer] Repull property id (equal to `listings.id`; the same integer used as `propertyId` on availability and `listingId` on reservations).
|
|
105
105
|
# @param from [Date] Start of the window (inclusive), ISO `YYYY-MM-DD`. Required — missing/malformed returns 422. `startDate` is accepted as an alias.
|
|
106
106
|
# @param to [Date] End of the window (inclusive), ISO `YYYY-MM-DD`. Required — missing/malformed returns 422. `endDate` is accepted as an alias.
|
|
@@ -689,8 +689,8 @@ module Repull
|
|
|
689
689
|
return data, status_code, headers
|
|
690
690
|
end
|
|
691
691
|
|
|
692
|
-
# List a listing's units
|
|
693
|
-
# The
|
|
692
|
+
# List a listing's units
|
|
693
|
+
# The units under a listing. - **Hotel-model PMS (Mews, Cloudbeds):** the listing is a room type and these are its physical rooms. Prices, restrictions and availability are set on the room type, and each reservation is assigned one of these rooms (`reservation.unit.id`). A room can belong to more than one room type. - **Guesty multi-unit listing:** the listing is the parent channels sell, and these are its units. Each unit is also a listing of its own (`listingId`), with its own calendar and stays; a stay on a unit carries `reservation.unit`. Which unit is free on which night: `units` per day on `GET /v1/availability/{propertyId}`. Any other listing is a single home, which is its own unit, and returns an empty list.
|
|
694
694
|
# @param id [Integer] Listing id.
|
|
695
695
|
# @param [Hash] opts the optional parameters
|
|
696
696
|
# @return [ListListingUnits200Response]
|
|
@@ -699,8 +699,8 @@ module Repull
|
|
|
699
699
|
data
|
|
700
700
|
end
|
|
701
701
|
|
|
702
|
-
# List a listing's units
|
|
703
|
-
# The
|
|
702
|
+
# List a listing's units
|
|
703
|
+
# The units under a listing. - **Hotel-model PMS (Mews, Cloudbeds):** the listing is a room type and these are its physical rooms. Prices, restrictions and availability are set on the room type, and each reservation is assigned one of these rooms (`reservation.unit.id`). A room can belong to more than one room type. - **Guesty multi-unit listing:** the listing is the parent channels sell, and these are its units. Each unit is also a listing of its own (`listingId`), with its own calendar and stays; a stay on a unit carries `reservation.unit`. Which unit is free on which night: `units` per day on `GET /v1/availability/{propertyId}`. Any other listing is a single home, which is its own unit, and returns an empty list.
|
|
704
704
|
# @param id [Integer] Listing id.
|
|
705
705
|
# @param [Hash] opts the optional parameters
|
|
706
706
|
# @return [Array<(ListListingUnits200Response, Integer, Hash)>] ListListingUnits200Response data, response status code and response headers
|
|
@@ -15,7 +15,7 @@ require 'time'
|
|
|
15
15
|
|
|
16
16
|
module Repull
|
|
17
17
|
class ListListingUnits200ResponseDataInner < ApiModelBase
|
|
18
|
-
# The PMS's id for the
|
|
18
|
+
# The PMS's id for the unit; `reservation.unit.id` and the per-night `units[].id` on `GET /v1/availability/{propertyId}` refer to it.
|
|
19
19
|
attr_accessor :id
|
|
20
20
|
|
|
21
21
|
attr_accessor :name
|
|
@@ -30,8 +30,12 @@ module Repull
|
|
|
30
30
|
|
|
31
31
|
attr_accessor :floor
|
|
32
32
|
|
|
33
|
+
# The PMS the unit comes from: `cloudbeds`, `mews`, `guesty`, …
|
|
33
34
|
attr_accessor :source
|
|
34
35
|
|
|
36
|
+
# The unit's own Repull listing id when the unit is a listing too (a Guesty multi-unit listing's units, each with its own calendar and stays), else null.
|
|
37
|
+
attr_accessor :listing_id
|
|
38
|
+
|
|
35
39
|
# Attribute mapping from ruby-style variable name to JSON key.
|
|
36
40
|
def self.attribute_map
|
|
37
41
|
{
|
|
@@ -41,7 +45,8 @@ module Repull
|
|
|
41
45
|
:'parent_id' => :'parentId',
|
|
42
46
|
:'housekeeping_status' => :'housekeepingStatus',
|
|
43
47
|
:'floor' => :'floor',
|
|
44
|
-
:'source' => :'source'
|
|
48
|
+
:'source' => :'source',
|
|
49
|
+
:'listing_id' => :'listingId'
|
|
45
50
|
}
|
|
46
51
|
end
|
|
47
52
|
|
|
@@ -64,7 +69,8 @@ module Repull
|
|
|
64
69
|
:'parent_id' => :'String',
|
|
65
70
|
:'housekeeping_status' => :'String',
|
|
66
71
|
:'floor' => :'String',
|
|
67
|
-
:'source' => :'String'
|
|
72
|
+
:'source' => :'String',
|
|
73
|
+
:'listing_id' => :'String'
|
|
68
74
|
}
|
|
69
75
|
end
|
|
70
76
|
|
|
@@ -74,6 +80,7 @@ module Repull
|
|
|
74
80
|
:'parent_id',
|
|
75
81
|
:'housekeeping_status',
|
|
76
82
|
:'floor',
|
|
83
|
+
:'listing_id'
|
|
77
84
|
])
|
|
78
85
|
end
|
|
79
86
|
|
|
@@ -120,6 +127,10 @@ module Repull
|
|
|
120
127
|
if attributes.key?(:'source')
|
|
121
128
|
self.source = attributes[:'source']
|
|
122
129
|
end
|
|
130
|
+
|
|
131
|
+
if attributes.key?(:'listing_id')
|
|
132
|
+
self.listing_id = attributes[:'listing_id']
|
|
133
|
+
end
|
|
123
134
|
end
|
|
124
135
|
|
|
125
136
|
# Show invalid properties with the reasons. Usually used together with valid?
|
|
@@ -148,7 +159,8 @@ module Repull
|
|
|
148
159
|
parent_id == o.parent_id &&
|
|
149
160
|
housekeeping_status == o.housekeeping_status &&
|
|
150
161
|
floor == o.floor &&
|
|
151
|
-
source == o.source
|
|
162
|
+
source == o.source &&
|
|
163
|
+
listing_id == o.listing_id
|
|
152
164
|
end
|
|
153
165
|
|
|
154
166
|
# @see the `==` method
|
|
@@ -160,7 +172,7 @@ module Repull
|
|
|
160
172
|
# Calculates hash code according to all attributes.
|
|
161
173
|
# @return [Integer] Hash code
|
|
162
174
|
def hash
|
|
163
|
-
[id, name, active, parent_id, housekeeping_status, floor, source].hash
|
|
175
|
+
[id, name, active, parent_id, housekeeping_status, floor, source, listing_id].hash
|
|
164
176
|
end
|
|
165
177
|
|
|
166
178
|
# Builds the object from hash
|
|
@@ -18,15 +18,17 @@ module Repull
|
|
|
18
18
|
class Listing < ApiModelBase
|
|
19
19
|
attr_accessor :capabilities
|
|
20
20
|
|
|
21
|
-
# `room_type` when the listing sells several
|
|
21
|
+
# `room_type` when the listing sells several units (a hotel room type or a Guesty multi-unit listing: `unitCount` > 1) or is a hotel PMS room type; `home` otherwise. See the Hotels & multi-unit guide.
|
|
22
22
|
attr_accessor :kind
|
|
23
23
|
|
|
24
|
+
attr_accessor :parent
|
|
25
|
+
|
|
24
26
|
attr_accessor :hotel
|
|
25
27
|
|
|
26
|
-
# How many
|
|
28
|
+
# How many units (rooms) this listing sells: the hotel PMS rooms of the room type, the units of a Guesty multi-unit listing, the room count on Booking.com, or 1 for a home. Units left per night are `availableUnits` on `GET /v1/availability/{propertyId}`, and which unit is free is `units` there.
|
|
27
29
|
attr_accessor :unit_count
|
|
28
30
|
|
|
29
|
-
# `GET /v1/listings/{id}` only. The
|
|
31
|
+
# `GET /v1/listings/{id}` only. The units under the listing: a Mews or Cloudbeds room type's rooms, or a Guesty multi-unit listing's units; empty for a single home. Same items as `GET /v1/listings/{id}/units`.
|
|
30
32
|
attr_accessor :units
|
|
31
33
|
|
|
32
34
|
# Repull listing id
|
|
@@ -87,6 +89,7 @@ module Repull
|
|
|
87
89
|
{
|
|
88
90
|
:'capabilities' => :'capabilities',
|
|
89
91
|
:'kind' => :'kind',
|
|
92
|
+
:'parent' => :'parent',
|
|
90
93
|
:'hotel' => :'hotel',
|
|
91
94
|
:'unit_count' => :'unitCount',
|
|
92
95
|
:'units' => :'units',
|
|
@@ -127,6 +130,7 @@ module Repull
|
|
|
127
130
|
{
|
|
128
131
|
:'capabilities' => :'ListingCapabilities',
|
|
129
132
|
:'kind' => :'String',
|
|
133
|
+
:'parent' => :'ListingParent',
|
|
130
134
|
:'hotel' => :'ListingHotel',
|
|
131
135
|
:'unit_count' => :'Integer',
|
|
132
136
|
:'units' => :'Array<ListingUnitsInner>',
|
|
@@ -155,6 +159,7 @@ module Repull
|
|
|
155
159
|
# List of attributes with nullable: true
|
|
156
160
|
def self.openapi_nullable
|
|
157
161
|
Set.new([
|
|
162
|
+
:'parent',
|
|
158
163
|
:'hotel',
|
|
159
164
|
:'public_name',
|
|
160
165
|
:'thumbnail_url',
|
|
@@ -190,6 +195,10 @@ module Repull
|
|
|
190
195
|
self.kind = attributes[:'kind']
|
|
191
196
|
end
|
|
192
197
|
|
|
198
|
+
if attributes.key?(:'parent')
|
|
199
|
+
self.parent = attributes[:'parent']
|
|
200
|
+
end
|
|
201
|
+
|
|
193
202
|
if attributes.key?(:'hotel')
|
|
194
203
|
self.hotel = attributes[:'hotel']
|
|
195
204
|
end
|
|
@@ -334,6 +343,7 @@ module Repull
|
|
|
334
343
|
self.class == o.class &&
|
|
335
344
|
capabilities == o.capabilities &&
|
|
336
345
|
kind == o.kind &&
|
|
346
|
+
parent == o.parent &&
|
|
337
347
|
hotel == o.hotel &&
|
|
338
348
|
unit_count == o.unit_count &&
|
|
339
349
|
units == o.units &&
|
|
@@ -367,7 +377,7 @@ module Repull
|
|
|
367
377
|
# Calculates hash code according to all attributes.
|
|
368
378
|
# @return [Integer] Hash code
|
|
369
379
|
def hash
|
|
370
|
-
[capabilities, kind, hotel, unit_count, units, id, name, public_name, address, thumbnail_url, status, channels, amenities, content, details, rooms, policies, rules, pricing, fees, taxes, photos, created_at, updated_at].hash
|
|
380
|
+
[capabilities, kind, parent, hotel, unit_count, units, id, name, public_name, address, thumbnail_url, status, channels, amenities, content, details, rooms, policies, rules, pricing, fees, taxes, photos, created_at, updated_at].hash
|
|
371
381
|
end
|
|
372
382
|
|
|
373
383
|
# Builds the object from hash
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
#Repull API
|
|
3
|
+
|
|
4
|
+
#The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | unlimited (10 included, then billed per listing) | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
|
|
5
|
+
|
|
6
|
+
The version of the OpenAPI document: 1.0.0
|
|
7
|
+
|
|
8
|
+
Generated by: https://openapi-generator.tech
|
|
9
|
+
Generator version: 7.22.0
|
|
10
|
+
|
|
11
|
+
=end
|
|
12
|
+
|
|
13
|
+
require 'date'
|
|
14
|
+
require 'time'
|
|
15
|
+
|
|
16
|
+
module Repull
|
|
17
|
+
# The multi-unit listing this listing is one unit of (a Guesty multi-unit listing's unit, which has its own calendar and stays). The parent lists it in `units[]` with this listing's id as `units[].listingId`. `null` otherwise.
|
|
18
|
+
class ListingParent < ApiModelBase
|
|
19
|
+
# Repull listing id of the multi-unit listing.
|
|
20
|
+
attr_accessor :id
|
|
21
|
+
|
|
22
|
+
attr_accessor :name
|
|
23
|
+
|
|
24
|
+
# Attribute mapping from ruby-style variable name to JSON key.
|
|
25
|
+
def self.attribute_map
|
|
26
|
+
{
|
|
27
|
+
:'id' => :'id',
|
|
28
|
+
:'name' => :'name'
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns attribute mapping this model knows about
|
|
33
|
+
def self.acceptable_attribute_map
|
|
34
|
+
attribute_map
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Returns all the JSON keys this model knows about
|
|
38
|
+
def self.acceptable_attributes
|
|
39
|
+
acceptable_attribute_map.values
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Attribute type mapping.
|
|
43
|
+
def self.openapi_types
|
|
44
|
+
{
|
|
45
|
+
:'id' => :'String',
|
|
46
|
+
:'name' => :'String'
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# List of attributes with nullable: true
|
|
51
|
+
def self.openapi_nullable
|
|
52
|
+
Set.new([
|
|
53
|
+
:'name'
|
|
54
|
+
])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Initializes the object
|
|
58
|
+
# @param [Hash] attributes Model attributes in the form of hash
|
|
59
|
+
def initialize(attributes = {})
|
|
60
|
+
if (!attributes.is_a?(Hash))
|
|
61
|
+
fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ListingParent` initialize method"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# check to see if the attribute exists and convert string to symbol for hash key
|
|
65
|
+
acceptable_attribute_map = self.class.acceptable_attribute_map
|
|
66
|
+
attributes = attributes.each_with_object({}) { |(k, v), h|
|
|
67
|
+
if (!acceptable_attribute_map.key?(k.to_sym))
|
|
68
|
+
fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ListingParent`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
|
|
69
|
+
end
|
|
70
|
+
h[k.to_sym] = v
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if attributes.key?(:'id')
|
|
74
|
+
self.id = attributes[:'id']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if attributes.key?(:'name')
|
|
78
|
+
self.name = attributes[:'name']
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Show invalid properties with the reasons. Usually used together with valid?
|
|
83
|
+
# @return Array for valid properties with the reasons
|
|
84
|
+
def list_invalid_properties
|
|
85
|
+
warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
|
|
86
|
+
invalid_properties = Array.new
|
|
87
|
+
invalid_properties
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Check to see if the all the properties in the model are valid
|
|
91
|
+
# @return true if the model is valid
|
|
92
|
+
def valid?
|
|
93
|
+
warn '[DEPRECATED] the `valid?` method is obsolete'
|
|
94
|
+
true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Checks equality by comparing each attribute.
|
|
98
|
+
# @param [Object] Object to be compared
|
|
99
|
+
def ==(o)
|
|
100
|
+
return true if self.equal?(o)
|
|
101
|
+
self.class == o.class &&
|
|
102
|
+
id == o.id &&
|
|
103
|
+
name == o.name
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @see the `==` method
|
|
107
|
+
# @param [Object] Object to be compared
|
|
108
|
+
def eql?(o)
|
|
109
|
+
self == o
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Calculates hash code according to all attributes.
|
|
113
|
+
# @return [Integer] Hash code
|
|
114
|
+
def hash
|
|
115
|
+
[id, name].hash
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Builds the object from hash
|
|
119
|
+
# @param [Hash] attributes Model attributes in the form of hash
|
|
120
|
+
# @return [Object] Returns the model itself
|
|
121
|
+
def self.build_from_hash(attributes)
|
|
122
|
+
return nil unless attributes.is_a?(Hash)
|
|
123
|
+
attributes = attributes.transform_keys(&:to_sym)
|
|
124
|
+
transformed_hash = {}
|
|
125
|
+
openapi_types.each_pair do |key, type|
|
|
126
|
+
if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
|
|
127
|
+
transformed_hash["#{key}"] = nil
|
|
128
|
+
elsif type =~ /\AArray<(.*)>/i
|
|
129
|
+
# check to ensure the input is an array given that the attribute
|
|
130
|
+
# is documented as an array but the input is not
|
|
131
|
+
if attributes[attribute_map[key]].is_a?(Array)
|
|
132
|
+
transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
|
|
133
|
+
end
|
|
134
|
+
elsif !attributes[attribute_map[key]].nil?
|
|
135
|
+
transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
new(transformed_hash)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Returns the object in the form of hash
|
|
142
|
+
# @return [Hash] Returns the object in the form of hash
|
|
143
|
+
def to_hash
|
|
144
|
+
hash = {}
|
|
145
|
+
self.class.attribute_map.each_pair do |attr, param|
|
|
146
|
+
value = self.send(attr)
|
|
147
|
+
if value.nil?
|
|
148
|
+
is_nullable = self.class.openapi_nullable.include?(attr)
|
|
149
|
+
next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
hash[param] = _to_hash(value)
|
|
153
|
+
end
|
|
154
|
+
hash
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
end
|
|
@@ -15,6 +15,7 @@ require 'time'
|
|
|
15
15
|
|
|
16
16
|
module Repull
|
|
17
17
|
class ListingUnitsInner < ApiModelBase
|
|
18
|
+
# Unit id — what `reservation.unit.id` and the per-night `units[].id` on availability refer to.
|
|
18
19
|
attr_accessor :id
|
|
19
20
|
|
|
20
21
|
attr_accessor :name
|
|
@@ -27,8 +28,12 @@ module Repull
|
|
|
27
28
|
|
|
28
29
|
attr_accessor :floor
|
|
29
30
|
|
|
31
|
+
# The PMS the unit comes from: `cloudbeds`, `mews`, `guesty`, …
|
|
30
32
|
attr_accessor :source
|
|
31
33
|
|
|
34
|
+
# The unit's own Repull listing id when the unit is a listing too (a Guesty multi-unit listing's units, each with its own calendar), else `null`.
|
|
35
|
+
attr_accessor :listing_id
|
|
36
|
+
|
|
32
37
|
# Attribute mapping from ruby-style variable name to JSON key.
|
|
33
38
|
def self.attribute_map
|
|
34
39
|
{
|
|
@@ -38,7 +43,8 @@ module Repull
|
|
|
38
43
|
:'parent_id' => :'parentId',
|
|
39
44
|
:'housekeeping_status' => :'housekeepingStatus',
|
|
40
45
|
:'floor' => :'floor',
|
|
41
|
-
:'source' => :'source'
|
|
46
|
+
:'source' => :'source',
|
|
47
|
+
:'listing_id' => :'listingId'
|
|
42
48
|
}
|
|
43
49
|
end
|
|
44
50
|
|
|
@@ -61,7 +67,8 @@ module Repull
|
|
|
61
67
|
:'parent_id' => :'String',
|
|
62
68
|
:'housekeeping_status' => :'String',
|
|
63
69
|
:'floor' => :'String',
|
|
64
|
-
:'source' => :'String'
|
|
70
|
+
:'source' => :'String',
|
|
71
|
+
:'listing_id' => :'String'
|
|
65
72
|
}
|
|
66
73
|
end
|
|
67
74
|
|
|
@@ -71,6 +78,7 @@ module Repull
|
|
|
71
78
|
:'parent_id',
|
|
72
79
|
:'housekeeping_status',
|
|
73
80
|
:'floor',
|
|
81
|
+
:'listing_id'
|
|
74
82
|
])
|
|
75
83
|
end
|
|
76
84
|
|
|
@@ -117,6 +125,10 @@ module Repull
|
|
|
117
125
|
if attributes.key?(:'source')
|
|
118
126
|
self.source = attributes[:'source']
|
|
119
127
|
end
|
|
128
|
+
|
|
129
|
+
if attributes.key?(:'listing_id')
|
|
130
|
+
self.listing_id = attributes[:'listing_id']
|
|
131
|
+
end
|
|
120
132
|
end
|
|
121
133
|
|
|
122
134
|
# Show invalid properties with the reasons. Usually used together with valid?
|
|
@@ -145,7 +157,8 @@ module Repull
|
|
|
145
157
|
parent_id == o.parent_id &&
|
|
146
158
|
housekeeping_status == o.housekeeping_status &&
|
|
147
159
|
floor == o.floor &&
|
|
148
|
-
source == o.source
|
|
160
|
+
source == o.source &&
|
|
161
|
+
listing_id == o.listing_id
|
|
149
162
|
end
|
|
150
163
|
|
|
151
164
|
# @see the `==` method
|
|
@@ -157,7 +170,7 @@ module Repull
|
|
|
157
170
|
# Calculates hash code according to all attributes.
|
|
158
171
|
# @return [Integer] Hash code
|
|
159
172
|
def hash
|
|
160
|
-
[id, name, active, parent_id, housekeeping_status, floor, source].hash
|
|
173
|
+
[id, name, active, parent_id, housekeeping_status, floor, source, listing_id].hash
|
|
161
174
|
end
|
|
162
175
|
|
|
163
176
|
# Builds the object from hash
|
|
@@ -28,9 +28,12 @@ module Repull
|
|
|
28
28
|
# Minimum-stay requirement for a stay starting on this date. Falls back to the listing-level default min-nights when the calendar row carries none.
|
|
29
29
|
attr_accessor :min_nights
|
|
30
30
|
|
|
31
|
-
# Units still sellable that night. 1 or 0 for a single home. For a
|
|
31
|
+
# Units still sellable that night. 1 or 0 for a single home. For a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing) the units left, e.g. 3 of 5 — see `GET /v1/listings/{id}/units`. `available` is false whenever this is 0.
|
|
32
32
|
attr_accessor :available_units
|
|
33
33
|
|
|
34
|
+
# Only on a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing): each active unit and whether it is free that night. A unit is taken when a stay is on it, a room block covers it, or (for a unit that is a listing of its own) its own calendar is closed. Ids are the listing's `units[].id` and a reservation's `unit.id`.
|
|
35
|
+
attr_accessor :units
|
|
36
|
+
|
|
34
37
|
# Attribute mapping from ruby-style variable name to JSON key.
|
|
35
38
|
def self.attribute_map
|
|
36
39
|
{
|
|
@@ -38,7 +41,8 @@ module Repull
|
|
|
38
41
|
:'available' => :'available',
|
|
39
42
|
:'price' => :'price',
|
|
40
43
|
:'min_nights' => :'minNights',
|
|
41
|
-
:'available_units' => :'availableUnits'
|
|
44
|
+
:'available_units' => :'availableUnits',
|
|
45
|
+
:'units' => :'units'
|
|
42
46
|
}
|
|
43
47
|
end
|
|
44
48
|
|
|
@@ -59,7 +63,8 @@ module Repull
|
|
|
59
63
|
:'available' => :'Boolean',
|
|
60
64
|
:'price' => :'Float',
|
|
61
65
|
:'min_nights' => :'Integer',
|
|
62
|
-
:'available_units' => :'Integer'
|
|
66
|
+
:'available_units' => :'Integer',
|
|
67
|
+
:'units' => :'Array<PropertyAvailabilityUnit>'
|
|
63
68
|
}
|
|
64
69
|
end
|
|
65
70
|
|
|
@@ -114,6 +119,12 @@ module Repull
|
|
|
114
119
|
else
|
|
115
120
|
self.available_units = nil
|
|
116
121
|
end
|
|
122
|
+
|
|
123
|
+
if attributes.key?(:'units')
|
|
124
|
+
if (value = attributes[:'units']).is_a?(Array)
|
|
125
|
+
self.units = value
|
|
126
|
+
end
|
|
127
|
+
end
|
|
117
128
|
end
|
|
118
129
|
|
|
119
130
|
# Show invalid properties with the reasons. Usually used together with valid?
|
|
@@ -224,7 +235,8 @@ module Repull
|
|
|
224
235
|
available == o.available &&
|
|
225
236
|
price == o.price &&
|
|
226
237
|
min_nights == o.min_nights &&
|
|
227
|
-
available_units == o.available_units
|
|
238
|
+
available_units == o.available_units &&
|
|
239
|
+
units == o.units
|
|
228
240
|
end
|
|
229
241
|
|
|
230
242
|
# @see the `==` method
|
|
@@ -236,7 +248,7 @@ module Repull
|
|
|
236
248
|
# Calculates hash code according to all attributes.
|
|
237
249
|
# @return [Integer] Hash code
|
|
238
250
|
def hash
|
|
239
|
-
[date, available, price, min_nights, available_units].hash
|
|
251
|
+
[date, available, price, min_nights, available_units, units].hash
|
|
240
252
|
end
|
|
241
253
|
|
|
242
254
|
# Builds the object from hash
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
#Repull API
|
|
3
|
+
|
|
4
|
+
#The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | unlimited (10 included, then billed per listing) | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
|
|
5
|
+
|
|
6
|
+
The version of the OpenAPI document: 1.0.0
|
|
7
|
+
|
|
8
|
+
Generated by: https://openapi-generator.tech
|
|
9
|
+
Generator version: 7.22.0
|
|
10
|
+
|
|
11
|
+
=end
|
|
12
|
+
|
|
13
|
+
require 'date'
|
|
14
|
+
require 'time'
|
|
15
|
+
|
|
16
|
+
module Repull
|
|
17
|
+
# One unit on one night.
|
|
18
|
+
class PropertyAvailabilityUnit < ApiModelBase
|
|
19
|
+
# The unit id: `units[].id` on the listing, `unit.id` on a reservation.
|
|
20
|
+
attr_accessor :id
|
|
21
|
+
|
|
22
|
+
# Whether this unit is free that night.
|
|
23
|
+
attr_accessor :available
|
|
24
|
+
|
|
25
|
+
# Attribute mapping from ruby-style variable name to JSON key.
|
|
26
|
+
def self.attribute_map
|
|
27
|
+
{
|
|
28
|
+
:'id' => :'id',
|
|
29
|
+
:'available' => :'available'
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Returns attribute mapping this model knows about
|
|
34
|
+
def self.acceptable_attribute_map
|
|
35
|
+
attribute_map
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns all the JSON keys this model knows about
|
|
39
|
+
def self.acceptable_attributes
|
|
40
|
+
acceptable_attribute_map.values
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Attribute type mapping.
|
|
44
|
+
def self.openapi_types
|
|
45
|
+
{
|
|
46
|
+
:'id' => :'String',
|
|
47
|
+
:'available' => :'Boolean'
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# List of attributes with nullable: true
|
|
52
|
+
def self.openapi_nullable
|
|
53
|
+
Set.new([
|
|
54
|
+
])
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Initializes the object
|
|
58
|
+
# @param [Hash] attributes Model attributes in the form of hash
|
|
59
|
+
def initialize(attributes = {})
|
|
60
|
+
if (!attributes.is_a?(Hash))
|
|
61
|
+
fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::PropertyAvailabilityUnit` initialize method"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# check to see if the attribute exists and convert string to symbol for hash key
|
|
65
|
+
acceptable_attribute_map = self.class.acceptable_attribute_map
|
|
66
|
+
attributes = attributes.each_with_object({}) { |(k, v), h|
|
|
67
|
+
if (!acceptable_attribute_map.key?(k.to_sym))
|
|
68
|
+
fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::PropertyAvailabilityUnit`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
|
|
69
|
+
end
|
|
70
|
+
h[k.to_sym] = v
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if attributes.key?(:'id')
|
|
74
|
+
self.id = attributes[:'id']
|
|
75
|
+
else
|
|
76
|
+
self.id = nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
if attributes.key?(:'available')
|
|
80
|
+
self.available = attributes[:'available']
|
|
81
|
+
else
|
|
82
|
+
self.available = nil
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Show invalid properties with the reasons. Usually used together with valid?
|
|
87
|
+
# @return Array for valid properties with the reasons
|
|
88
|
+
def list_invalid_properties
|
|
89
|
+
warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
|
|
90
|
+
invalid_properties = Array.new
|
|
91
|
+
if @id.nil?
|
|
92
|
+
invalid_properties.push('invalid value for "id", id cannot be nil.')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
if @available.nil?
|
|
96
|
+
invalid_properties.push('invalid value for "available", available cannot be nil.')
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
invalid_properties
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Check to see if the all the properties in the model are valid
|
|
103
|
+
# @return true if the model is valid
|
|
104
|
+
def valid?
|
|
105
|
+
warn '[DEPRECATED] the `valid?` method is obsolete'
|
|
106
|
+
return false if @id.nil?
|
|
107
|
+
return false if @available.nil?
|
|
108
|
+
true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Custom attribute writer method with validation
|
|
112
|
+
# @param [Object] id Value to be assigned
|
|
113
|
+
def id=(id)
|
|
114
|
+
if id.nil?
|
|
115
|
+
fail ArgumentError, 'id cannot be nil'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
@id = id
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Custom attribute writer method with validation
|
|
122
|
+
# @param [Object] available Value to be assigned
|
|
123
|
+
def available=(available)
|
|
124
|
+
if available.nil?
|
|
125
|
+
fail ArgumentError, 'available cannot be nil'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@available = available
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Checks equality by comparing each attribute.
|
|
132
|
+
# @param [Object] Object to be compared
|
|
133
|
+
def ==(o)
|
|
134
|
+
return true if self.equal?(o)
|
|
135
|
+
self.class == o.class &&
|
|
136
|
+
id == o.id &&
|
|
137
|
+
available == o.available
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @see the `==` method
|
|
141
|
+
# @param [Object] Object to be compared
|
|
142
|
+
def eql?(o)
|
|
143
|
+
self == o
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Calculates hash code according to all attributes.
|
|
147
|
+
# @return [Integer] Hash code
|
|
148
|
+
def hash
|
|
149
|
+
[id, available].hash
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Builds the object from hash
|
|
153
|
+
# @param [Hash] attributes Model attributes in the form of hash
|
|
154
|
+
# @return [Object] Returns the model itself
|
|
155
|
+
def self.build_from_hash(attributes)
|
|
156
|
+
return nil unless attributes.is_a?(Hash)
|
|
157
|
+
attributes = attributes.transform_keys(&:to_sym)
|
|
158
|
+
transformed_hash = {}
|
|
159
|
+
openapi_types.each_pair do |key, type|
|
|
160
|
+
if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
|
|
161
|
+
transformed_hash["#{key}"] = nil
|
|
162
|
+
elsif type =~ /\AArray<(.*)>/i
|
|
163
|
+
# check to ensure the input is an array given that the attribute
|
|
164
|
+
# is documented as an array but the input is not
|
|
165
|
+
if attributes[attribute_map[key]].is_a?(Array)
|
|
166
|
+
transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
|
|
167
|
+
end
|
|
168
|
+
elsif !attributes[attribute_map[key]].nil?
|
|
169
|
+
transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
new(transformed_hash)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Returns the object in the form of hash
|
|
176
|
+
# @return [Hash] Returns the object in the form of hash
|
|
177
|
+
def to_hash
|
|
178
|
+
hash = {}
|
|
179
|
+
self.class.attribute_map.each_pair do |attr, param|
|
|
180
|
+
value = self.send(attr)
|
|
181
|
+
if value.nil?
|
|
182
|
+
is_nullable = self.class.openapi_nullable.include?(attr)
|
|
183
|
+
next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
hash[param] = _to_hash(value)
|
|
187
|
+
end
|
|
188
|
+
hash
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
end
|
|
@@ -14,7 +14,7 @@ require 'date'
|
|
|
14
14
|
require 'time'
|
|
15
15
|
|
|
16
16
|
module Repull
|
|
17
|
-
# The
|
|
17
|
+
# The unit the stay is on: the room assigned by a hotel-model PMS (Mews, Cloudbeds) where the listing is a room type, or the unit of a Guesty multi-unit listing (the stay's `listingId` is then that unit's own listing). Matches the listing's `units[].id` and the per-night `units` on availability. `null` when no unit is assigned yet, and for every listing that is a single home.
|
|
18
18
|
class ReservationUnit < ApiModelBase
|
|
19
19
|
# The room id, as listed by `GET /v1/listings/{id}/units`.
|
|
20
20
|
attr_accessor :id
|
data/lib/repull/version.rb
CHANGED
data/lib/repull.rb
CHANGED
|
@@ -389,6 +389,7 @@ require 'repull/models/listing_hotel'
|
|
|
389
389
|
require 'repull/models/listing_list_response'
|
|
390
390
|
require 'repull/models/listing_market_state_request'
|
|
391
391
|
require 'repull/models/listing_market_state_response'
|
|
392
|
+
require 'repull/models/listing_parent'
|
|
392
393
|
require 'repull/models/listing_photo'
|
|
393
394
|
require 'repull/models/listing_photo_delete_request'
|
|
394
395
|
require 'repull/models/listing_photo_delete_response'
|
|
@@ -548,6 +549,7 @@ require 'repull/models/property'
|
|
|
548
549
|
require 'repull/models/property_availability'
|
|
549
550
|
require 'repull/models/property_availability_coverage'
|
|
550
551
|
require 'repull/models/property_availability_day'
|
|
552
|
+
require 'repull/models/property_availability_unit'
|
|
551
553
|
require 'repull/models/property_list_response'
|
|
552
554
|
require 'repull/models/publish_section_error'
|
|
553
555
|
require 'repull/models/quote'
|
data/openapi/v1.json
CHANGED
|
@@ -338,8 +338,45 @@
|
|
|
338
338
|
"availableUnits": {
|
|
339
339
|
"type": "integer",
|
|
340
340
|
"minimum": 0,
|
|
341
|
-
"description": "Units still sellable that night. 1 or 0 for a single home. For a
|
|
341
|
+
"description": "Units still sellable that night. 1 or 0 for a single home. For a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing) the units left, e.g. 3 of 5 \u2014 see `GET /v1/listings/{id}/units`. `available` is false whenever this is 0.",
|
|
342
342
|
"example": 1
|
|
343
|
+
},
|
|
344
|
+
"units": {
|
|
345
|
+
"type": "array",
|
|
346
|
+
"description": "Only on a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing): each active unit and whether it is free that night. A unit is taken when a stay is on it, a room block covers it, or (for a unit that is a listing of its own) its own calendar is closed. Ids are the listing's `units[].id` and a reservation's `unit.id`.",
|
|
347
|
+
"items": {
|
|
348
|
+
"$ref": "#/components/schemas/PropertyAvailabilityUnit"
|
|
349
|
+
},
|
|
350
|
+
"example": [
|
|
351
|
+
{
|
|
352
|
+
"id": "unit_a",
|
|
353
|
+
"available": true
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"id": "unit_b",
|
|
357
|
+
"available": false
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
"PropertyAvailabilityUnit": {
|
|
364
|
+
"type": "object",
|
|
365
|
+
"description": "One unit on one night.",
|
|
366
|
+
"required": [
|
|
367
|
+
"id",
|
|
368
|
+
"available"
|
|
369
|
+
],
|
|
370
|
+
"properties": {
|
|
371
|
+
"id": {
|
|
372
|
+
"type": "string",
|
|
373
|
+
"description": "The unit id: `units[].id` on the listing, `unit.id` on a reservation.",
|
|
374
|
+
"example": "unit_b"
|
|
375
|
+
},
|
|
376
|
+
"available": {
|
|
377
|
+
"type": "boolean",
|
|
378
|
+
"description": "Whether this unit is free that night.",
|
|
379
|
+
"example": false
|
|
343
380
|
}
|
|
344
381
|
}
|
|
345
382
|
},
|
|
@@ -813,7 +850,7 @@
|
|
|
813
850
|
"unit": {
|
|
814
851
|
"type": "object",
|
|
815
852
|
"nullable": true,
|
|
816
|
-
"description": "The
|
|
853
|
+
"description": "The unit the stay is on: the room assigned by a hotel-model PMS (Mews, Cloudbeds) where the listing is a room type, or the unit of a Guesty multi-unit listing (the stay's `listingId` is then that unit's own listing). Matches the listing's `units[].id` and the per-night `units` on availability. `null` when no unit is assigned yet, and for every listing that is a single home.",
|
|
817
854
|
"properties": {
|
|
818
855
|
"id": {
|
|
819
856
|
"type": "string",
|
|
@@ -10298,7 +10335,22 @@
|
|
|
10298
10335
|
},
|
|
10299
10336
|
"kind": {
|
|
10300
10337
|
"type": "string",
|
|
10301
|
-
"description": "`room_type` when the listing sells several
|
|
10338
|
+
"description": "`room_type` when the listing sells several units (a hotel room type or a Guesty multi-unit listing: `unitCount` > 1) or is a hotel PMS room type; `home` otherwise. See the Hotels & multi-unit guide."
|
|
10339
|
+
},
|
|
10340
|
+
"parent": {
|
|
10341
|
+
"type": "object",
|
|
10342
|
+
"nullable": true,
|
|
10343
|
+
"description": "The multi-unit listing this listing is one unit of (a Guesty multi-unit listing's unit, which has its own calendar and stays). The parent lists it in `units[]` with this listing's id as `units[].listingId`. `null` otherwise.",
|
|
10344
|
+
"properties": {
|
|
10345
|
+
"id": {
|
|
10346
|
+
"type": "string",
|
|
10347
|
+
"description": "Repull listing id of the multi-unit listing."
|
|
10348
|
+
},
|
|
10349
|
+
"name": {
|
|
10350
|
+
"type": "string",
|
|
10351
|
+
"nullable": true
|
|
10352
|
+
}
|
|
10353
|
+
}
|
|
10302
10354
|
},
|
|
10303
10355
|
"hotel": {
|
|
10304
10356
|
"type": "object",
|
|
@@ -10326,16 +10378,17 @@
|
|
|
10326
10378
|
"unitCount": {
|
|
10327
10379
|
"type": "integer",
|
|
10328
10380
|
"minimum": 1,
|
|
10329
|
-
"description": "How many
|
|
10381
|
+
"description": "How many units (rooms) this listing sells: the hotel PMS rooms of the room type, the units of a Guesty multi-unit listing, the room count on Booking.com, or 1 for a home. Units left per night are `availableUnits` on `GET /v1/availability/{propertyId}`, and which unit is free is `units` there."
|
|
10330
10382
|
},
|
|
10331
10383
|
"units": {
|
|
10332
10384
|
"type": "array",
|
|
10333
|
-
"description": "`GET /v1/listings/{id}` only. The
|
|
10385
|
+
"description": "`GET /v1/listings/{id}` only. The units under the listing: a Mews or Cloudbeds room type's rooms, or a Guesty multi-unit listing's units; empty for a single home. Same items as `GET /v1/listings/{id}/units`.",
|
|
10334
10386
|
"items": {
|
|
10335
10387
|
"type": "object",
|
|
10336
10388
|
"properties": {
|
|
10337
10389
|
"id": {
|
|
10338
|
-
"type": "string"
|
|
10390
|
+
"type": "string",
|
|
10391
|
+
"description": "Unit id \u2014 what `reservation.unit.id` and the per-night `units[].id` on availability refer to."
|
|
10339
10392
|
},
|
|
10340
10393
|
"name": {
|
|
10341
10394
|
"type": "string"
|
|
@@ -10356,7 +10409,13 @@
|
|
|
10356
10409
|
"nullable": true
|
|
10357
10410
|
},
|
|
10358
10411
|
"source": {
|
|
10359
|
-
"type": "string"
|
|
10412
|
+
"type": "string",
|
|
10413
|
+
"description": "The PMS the unit comes from: `cloudbeds`, `mews`, `guesty`, \u2026"
|
|
10414
|
+
},
|
|
10415
|
+
"listingId": {
|
|
10416
|
+
"type": "string",
|
|
10417
|
+
"nullable": true,
|
|
10418
|
+
"description": "The unit's own Repull listing id when the unit is a listing too (a Guesty multi-unit listing's units, each with its own calendar), else `null`."
|
|
10360
10419
|
}
|
|
10361
10420
|
}
|
|
10362
10421
|
}
|
|
@@ -16829,7 +16888,7 @@
|
|
|
16829
16888
|
"get": {
|
|
16830
16889
|
"operationId": "get_availability",
|
|
16831
16890
|
"summary": "Get property availability",
|
|
16832
|
-
"description": "Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape \u2014 `{ date, available, price, minNights, availableUnits }` \u2014 projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the
|
|
16891
|
+
"description": "Channel-agnostic day-by-day availability calendar for a property over a date window. Returns a thin per-date shape \u2014 `{ date, available, price, minNights, availableUnits }` \u2014 projected from the property calendar. `availableUnits` is 1 or 0 for a single home, and the units left for a listing with units (a Mews or Cloudbeds room type, a Guesty multi-unit listing). On such a listing each day also carries `units: [{ id, available }]` \u2014 which unit is free that night, with the ids of the listing's `units[]` and a reservation's `unit`.\n\nThe `from` and `to` query params are **required** (ISO `YYYY-MM-DD`, inclusive) \u2014 omitting or malforming either returns 422. The window is capped at 366 days; longer ranges are truncated to the first 366 days.\n\n**`days` contains only the dates we actually hold calendar data for.** Requested dates with no calendar row are listed in `coverage.missingDates` \u2014 their availability is unknown. Never treat a missing date as bookable: this endpoint deliberately does not synthesise availability, because a fabricated open date can be double-booked. A property with no calendar still returns a real 200 (`days: []`, every date in `coverage.missingDates`), never a 404 \u2014 404 means the property id does not exist or belongs to a different workspace.\n\nThis endpoint is read-only, and the projected per-date shape carries **availability, units left, price, and min-nights only** \u2014 it does NOT expose max-stay, closed-to-arrival (CTA), closed-to-departure (CTD), or the dedicated stop-sell flag. To read or write that full restriction set on Booking.com use the channel routes: `GET`/`PUT /v1/channels/booking/availability` (with the room + rate ids from `GET /v1/channels/booking/properties/{id}/rooms`). To **write** calendar values use `PUT /v1/availability/{propertyId}` (or `PATCH /v1/availability/batch`), which updates the property calendar and pushes to its connected channels; channel-only settings stay on the channel routes.\n\nReturns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.",
|
|
16833
16892
|
"tags": [
|
|
16834
16893
|
"Availability"
|
|
16835
16894
|
],
|
|
@@ -35940,8 +35999,8 @@
|
|
|
35940
35999
|
"/v1/listings/{id}/units": {
|
|
35941
36000
|
"get": {
|
|
35942
36001
|
"operationId": "listListingUnits",
|
|
35943
|
-
"summary": "List a listing's units
|
|
35944
|
-
"description": "The
|
|
36002
|
+
"summary": "List a listing's units",
|
|
36003
|
+
"description": "The units under a listing.\n\n- **Hotel-model PMS (Mews, Cloudbeds):** the listing is a room type and these are its physical rooms. Prices, restrictions and availability are set on the room type, and each reservation is assigned one of these rooms (`reservation.unit.id`). A room can belong to more than one room type.\n- **Guesty multi-unit listing:** the listing is the parent channels sell, and these are its units. Each unit is also a listing of its own (`listingId`), with its own calendar and stays; a stay on a unit carries `reservation.unit`.\n\nWhich unit is free on which night: `units` per day on `GET /v1/availability/{propertyId}`. Any other listing is a single home, which is its own unit, and returns an empty list.",
|
|
35945
36004
|
"tags": [
|
|
35946
36005
|
"Listings"
|
|
35947
36006
|
],
|
|
@@ -35978,7 +36037,7 @@
|
|
|
35978
36037
|
"properties": {
|
|
35979
36038
|
"id": {
|
|
35980
36039
|
"type": "string",
|
|
35981
|
-
"description": "The PMS's id for the
|
|
36040
|
+
"description": "The PMS's id for the unit; `reservation.unit.id` and the per-night `units[].id` on `GET /v1/availability/{propertyId}` refer to it."
|
|
35982
36041
|
},
|
|
35983
36042
|
"name": {
|
|
35984
36043
|
"type": "string",
|
|
@@ -36003,7 +36062,13 @@
|
|
|
36003
36062
|
},
|
|
36004
36063
|
"source": {
|
|
36005
36064
|
"type": "string",
|
|
36006
|
-
"example": "mews"
|
|
36065
|
+
"example": "mews",
|
|
36066
|
+
"description": "The PMS the unit comes from: `cloudbeds`, `mews`, `guesty`, \u2026"
|
|
36067
|
+
},
|
|
36068
|
+
"listingId": {
|
|
36069
|
+
"type": "string",
|
|
36070
|
+
"nullable": true,
|
|
36071
|
+
"description": "The unit's own Repull listing id when the unit is a listing too (a Guesty multi-unit listing's units, each with its own calendar and stays), else null."
|
|
36007
36072
|
}
|
|
36008
36073
|
}
|
|
36009
36074
|
}
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: repull
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.39
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ivan Nikolov
|
|
@@ -469,6 +469,7 @@ files:
|
|
|
469
469
|
- lib/repull/models/listing_list_response.rb
|
|
470
470
|
- lib/repull/models/listing_market_state_request.rb
|
|
471
471
|
- lib/repull/models/listing_market_state_response.rb
|
|
472
|
+
- lib/repull/models/listing_parent.rb
|
|
472
473
|
- lib/repull/models/listing_photo.rb
|
|
473
474
|
- lib/repull/models/listing_photo_delete_request.rb
|
|
474
475
|
- lib/repull/models/listing_photo_delete_response.rb
|
|
@@ -628,6 +629,7 @@ files:
|
|
|
628
629
|
- lib/repull/models/property_availability.rb
|
|
629
630
|
- lib/repull/models/property_availability_coverage.rb
|
|
630
631
|
- lib/repull/models/property_availability_day.rb
|
|
632
|
+
- lib/repull/models/property_availability_unit.rb
|
|
631
633
|
- lib/repull/models/property_list_response.rb
|
|
632
634
|
- lib/repull/models/publish_section_error.rb
|
|
633
635
|
- lib/repull/models/quote.rb
|