repull 0.2.26 → 0.2.27

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +38 -23
  3. data/lib/repull/api/booking_com_api.rb +9 -2
  4. data/lib/repull/api/connect_api.rb +4 -4
  5. data/lib/repull/api/listings_api.rb +2 -2
  6. data/lib/repull/api/properties_api.rb +2 -2
  7. data/lib/repull/api/reservations_api.rb +93 -6
  8. data/lib/repull/api/vrbo_api.rb +9 -2
  9. data/lib/repull/models/airbnb_listing.rb +13 -2
  10. data/lib/repull/models/airbnb_listing_details_write_request.rb +109 -5
  11. data/lib/repull/models/airbnb_listing_details_write_request_check_in_option.rb +1 -1
  12. data/lib/repull/models/airbnb_permits_write_request_permits_inner.rb +2 -2
  13. data/lib/repull/models/airbnb_pricing_write_request.rb +30 -2
  14. data/lib/repull/models/airbnb_pricing_write_request_fees_inner.rb +291 -0
  15. data/lib/repull/models/booking_property_listings_inner.rb +57 -1
  16. data/lib/repull/models/cancel_reservation200_response.rb +1 -1
  17. data/lib/repull/models/connect_status.rb +35 -4
  18. data/lib/repull/models/connect_status_capabilities.rb +148 -0
  19. data/lib/repull/models/connection.rb +28 -5
  20. data/lib/repull/models/connection_action.rb +188 -0
  21. data/lib/repull/models/create_connect_session_request.rb +39 -1
  22. data/lib/repull/models/create_connection_request.rb +39 -1
  23. data/lib/repull/models/error_error.rb +13 -1
  24. data/lib/repull/models/{cancel_reservation200_response_pms.rb → error_error_listings_inner.rb} +36 -30
  25. data/lib/repull/models/get_booking_extranet_login_status200_response.rb +2 -1
  26. data/lib/repull/models/list_listing_units200_response.rb +2 -1
  27. data/lib/repull/models/listing.rb +23 -2
  28. data/lib/repull/models/listing_capabilities.rb +148 -0
  29. data/lib/repull/models/property.rb +14 -3
  30. data/lib/repull/models/reservation_capabilities.rb +230 -0
  31. data/lib/repull/models/reservation_create_request.rb +172 -33
  32. data/lib/repull/models/reservation_create_response.rb +6 -6
  33. data/lib/repull/models/reservation_create_response_unit.rb +1 -1
  34. data/lib/repull/models/{reservation_create_response_pms.rb → reservation_pms_outcome.rb} +38 -8
  35. data/lib/repull/models/reservation_pms_outcome_quote.rb +168 -0
  36. data/lib/repull/models/{cancel_reservation200_response_pms_errors_inner.rb → reservation_pms_section_error.rb} +16 -16
  37. data/lib/repull/models/reservation_quote_request.rb +312 -0
  38. data/lib/repull/models/reservation_quote_response.rb +228 -0
  39. data/lib/repull/models/reservation_quote_response_breakdown.rb +175 -0
  40. data/lib/repull/models/reservation_update_response.rb +16 -6
  41. data/lib/repull/models/submit_smoobu_credentials_request.rb +2 -5
  42. data/lib/repull/models/submit_smoobu_credentials_request_credentials.rb +193 -0
  43. data/lib/repull/models/update_airbnb_checkin_guide200_response.rb +147 -0
  44. data/lib/repull/models/update_airbnb_checkin_guide200_response_data.rb +177 -0
  45. data/lib/repull/models/{airbnb_permits_write_request_permits_inner_answers_value.rb → update_airbnb_checkin_guide200_response_data_steps_inner.rb} +24 -43
  46. data/lib/repull/models/update_airbnb_checkin_guide_request.rb +186 -0
  47. data/lib/repull/models/update_airbnb_checkin_guide_request_steps_inner.rb +182 -0
  48. data/lib/repull/version.rb +1 -1
  49. data/lib/repull.rb +18 -4
  50. data/openapi/v1.json +2059 -335
  51. data/scripts/regen.sh +1 -1
  52. metadata +20 -6
@@ -14,8 +14,8 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # Mews or Cloudbeds listings only: the booking was made in the PMS first, and this is what it applied.
18
- class ReservationCreateResponsePms < ApiModelBase
17
+ # Present when the write was made in a PMS: what the PMS applied. `partial: true` means the booking exists in the PMS but the steps in `failedSections` (e.g. notes, a tentative state) did not apply — do not create it again.
18
+ class ReservationPmsOutcome < ApiModelBase
19
19
  attr_accessor :provider
20
20
 
21
21
  # The PMS's own id for the booking.
@@ -25,13 +25,22 @@ module Repull
25
25
 
26
26
  attr_accessor :errors
27
27
 
28
+ attr_accessor :partial
29
+
30
+ attr_accessor :failed_sections
31
+
32
+ attr_accessor :quote
33
+
28
34
  # Attribute mapping from ruby-style variable name to JSON key.
29
35
  def self.attribute_map
30
36
  {
31
37
  :'provider' => :'provider',
32
38
  :'reservation_id' => :'reservationId',
33
39
  :'applied' => :'applied',
34
- :'errors' => :'errors'
40
+ :'errors' => :'errors',
41
+ :'partial' => :'partial',
42
+ :'failed_sections' => :'failedSections',
43
+ :'quote' => :'quote'
35
44
  }
36
45
  end
37
46
 
@@ -51,13 +60,17 @@ module Repull
51
60
  :'provider' => :'String',
52
61
  :'reservation_id' => :'String',
53
62
  :'applied' => :'Array<String>',
54
- :'errors' => :'Array<CancelReservation200ResponsePmsErrorsInner>'
63
+ :'errors' => :'Array<ReservationPmsSectionError>',
64
+ :'partial' => :'Boolean',
65
+ :'failed_sections' => :'Array<ReservationPmsSectionError>',
66
+ :'quote' => :'ReservationPmsOutcomeQuote'
55
67
  }
56
68
  end
57
69
 
58
70
  # List of attributes with nullable: true
59
71
  def self.openapi_nullable
60
72
  Set.new([
73
+ :'reservation_id',
61
74
  ])
62
75
  end
63
76
 
@@ -65,14 +78,14 @@ module Repull
65
78
  # @param [Hash] attributes Model attributes in the form of hash
66
79
  def initialize(attributes = {})
67
80
  if (!attributes.is_a?(Hash))
68
- fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ReservationCreateResponsePms` initialize method"
81
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ReservationPmsOutcome` initialize method"
69
82
  end
70
83
 
71
84
  # check to see if the attribute exists and convert string to symbol for hash key
72
85
  acceptable_attribute_map = self.class.acceptable_attribute_map
73
86
  attributes = attributes.each_with_object({}) { |(k, v), h|
74
87
  if (!acceptable_attribute_map.key?(k.to_sym))
75
- fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ReservationCreateResponsePms`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
88
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ReservationPmsOutcome`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
76
89
  end
77
90
  h[k.to_sym] = v
78
91
  }
@@ -96,6 +109,20 @@ module Repull
96
109
  self.errors = value
97
110
  end
98
111
  end
112
+
113
+ if attributes.key?(:'partial')
114
+ self.partial = attributes[:'partial']
115
+ end
116
+
117
+ if attributes.key?(:'failed_sections')
118
+ if (value = attributes[:'failed_sections']).is_a?(Array)
119
+ self.failed_sections = value
120
+ end
121
+ end
122
+
123
+ if attributes.key?(:'quote')
124
+ self.quote = attributes[:'quote']
125
+ end
99
126
  end
100
127
 
101
128
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -121,7 +148,10 @@ module Repull
121
148
  provider == o.provider &&
122
149
  reservation_id == o.reservation_id &&
123
150
  applied == o.applied &&
124
- errors == o.errors
151
+ errors == o.errors &&
152
+ partial == o.partial &&
153
+ failed_sections == o.failed_sections &&
154
+ quote == o.quote
125
155
  end
126
156
 
127
157
  # @see the `==` method
@@ -133,7 +163,7 @@ module Repull
133
163
  # Calculates hash code according to all attributes.
134
164
  # @return [Integer] Hash code
135
165
  def hash
136
- [provider, reservation_id, applied, errors].hash
166
+ [provider, reservation_id, applied, errors, partial, failed_sections, quote].hash
137
167
  end
138
168
 
139
169
  # Builds the object from hash
@@ -0,0 +1,168 @@
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` | 50 | | `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
+ # Create only: the PMS quote the booking was priced from, when no `totalPrice` was sent.
18
+ class ReservationPmsOutcomeQuote < ApiModelBase
19
+ attr_accessor :available
20
+
21
+ attr_accessor :total
22
+
23
+ attr_accessor :currency
24
+
25
+ # Attribute mapping from ruby-style variable name to JSON key.
26
+ def self.attribute_map
27
+ {
28
+ :'available' => :'available',
29
+ :'total' => :'total',
30
+ :'currency' => :'currency'
31
+ }
32
+ end
33
+
34
+ # Returns attribute mapping this model knows about
35
+ def self.acceptable_attribute_map
36
+ attribute_map
37
+ end
38
+
39
+ # Returns all the JSON keys this model knows about
40
+ def self.acceptable_attributes
41
+ acceptable_attribute_map.values
42
+ end
43
+
44
+ # Attribute type mapping.
45
+ def self.openapi_types
46
+ {
47
+ :'available' => :'Boolean',
48
+ :'total' => :'Float',
49
+ :'currency' => :'String'
50
+ }
51
+ end
52
+
53
+ # List of attributes with nullable: true
54
+ def self.openapi_nullable
55
+ Set.new([
56
+ :'total',
57
+ :'currency'
58
+ ])
59
+ end
60
+
61
+ # Initializes the object
62
+ # @param [Hash] attributes Model attributes in the form of hash
63
+ def initialize(attributes = {})
64
+ if (!attributes.is_a?(Hash))
65
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ReservationPmsOutcomeQuote` initialize method"
66
+ end
67
+
68
+ # check to see if the attribute exists and convert string to symbol for hash key
69
+ acceptable_attribute_map = self.class.acceptable_attribute_map
70
+ attributes = attributes.each_with_object({}) { |(k, v), h|
71
+ if (!acceptable_attribute_map.key?(k.to_sym))
72
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ReservationPmsOutcomeQuote`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
73
+ end
74
+ h[k.to_sym] = v
75
+ }
76
+
77
+ if attributes.key?(:'available')
78
+ self.available = attributes[:'available']
79
+ end
80
+
81
+ if attributes.key?(:'total')
82
+ self.total = attributes[:'total']
83
+ end
84
+
85
+ if attributes.key?(:'currency')
86
+ self.currency = attributes[:'currency']
87
+ end
88
+ end
89
+
90
+ # Show invalid properties with the reasons. Usually used together with valid?
91
+ # @return Array for valid properties with the reasons
92
+ def list_invalid_properties
93
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
94
+ invalid_properties = Array.new
95
+ invalid_properties
96
+ end
97
+
98
+ # Check to see if the all the properties in the model are valid
99
+ # @return true if the model is valid
100
+ def valid?
101
+ warn '[DEPRECATED] the `valid?` method is obsolete'
102
+ true
103
+ end
104
+
105
+ # Checks equality by comparing each attribute.
106
+ # @param [Object] Object to be compared
107
+ def ==(o)
108
+ return true if self.equal?(o)
109
+ self.class == o.class &&
110
+ available == o.available &&
111
+ total == o.total &&
112
+ currency == o.currency
113
+ end
114
+
115
+ # @see the `==` method
116
+ # @param [Object] Object to be compared
117
+ def eql?(o)
118
+ self == o
119
+ end
120
+
121
+ # Calculates hash code according to all attributes.
122
+ # @return [Integer] Hash code
123
+ def hash
124
+ [available, total, currency].hash
125
+ end
126
+
127
+ # Builds the object from hash
128
+ # @param [Hash] attributes Model attributes in the form of hash
129
+ # @return [Object] Returns the model itself
130
+ def self.build_from_hash(attributes)
131
+ return nil unless attributes.is_a?(Hash)
132
+ attributes = attributes.transform_keys(&:to_sym)
133
+ transformed_hash = {}
134
+ openapi_types.each_pair do |key, type|
135
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
136
+ transformed_hash["#{key}"] = nil
137
+ elsif type =~ /\AArray<(.*)>/i
138
+ # check to ensure the input is an array given that the attribute
139
+ # is documented as an array but the input is not
140
+ if attributes[attribute_map[key]].is_a?(Array)
141
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
142
+ end
143
+ elsif !attributes[attribute_map[key]].nil?
144
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
145
+ end
146
+ end
147
+ new(transformed_hash)
148
+ end
149
+
150
+ # Returns the object in the form of hash
151
+ # @return [Hash] Returns the object in the form of hash
152
+ def to_hash
153
+ hash = {}
154
+ self.class.attribute_map.each_pair do |attr, param|
155
+ value = self.send(attr)
156
+ if value.nil?
157
+ is_nullable = self.class.openapi_nullable.include?(attr)
158
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
159
+ end
160
+
161
+ hash[param] = _to_hash(value)
162
+ end
163
+ hash
164
+ end
165
+
166
+ end
167
+
168
+ end
@@ -14,19 +14,19 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- class CancelReservation200ResponsePmsErrorsInner < ApiModelBase
17
+ class ReservationPmsSectionError < ApiModelBase
18
18
  attr_accessor :section
19
19
 
20
- attr_accessor :message
21
-
22
20
  attr_accessor :code
23
21
 
22
+ attr_accessor :message
23
+
24
24
  # Attribute mapping from ruby-style variable name to JSON key.
25
25
  def self.attribute_map
26
26
  {
27
27
  :'section' => :'section',
28
- :'message' => :'message',
29
- :'code' => :'code'
28
+ :'code' => :'code',
29
+ :'message' => :'message'
30
30
  }
31
31
  end
32
32
 
@@ -44,8 +44,8 @@ module Repull
44
44
  def self.openapi_types
45
45
  {
46
46
  :'section' => :'String',
47
- :'message' => :'String',
48
- :'code' => :'String'
47
+ :'code' => :'String',
48
+ :'message' => :'String'
49
49
  }
50
50
  end
51
51
 
@@ -59,14 +59,14 @@ module Repull
59
59
  # @param [Hash] attributes Model attributes in the form of hash
60
60
  def initialize(attributes = {})
61
61
  if (!attributes.is_a?(Hash))
62
- fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CancelReservation200ResponsePmsErrorsInner` initialize method"
62
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ReservationPmsSectionError` initialize method"
63
63
  end
64
64
 
65
65
  # check to see if the attribute exists and convert string to symbol for hash key
66
66
  acceptable_attribute_map = self.class.acceptable_attribute_map
67
67
  attributes = attributes.each_with_object({}) { |(k, v), h|
68
68
  if (!acceptable_attribute_map.key?(k.to_sym))
69
- fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CancelReservation200ResponsePmsErrorsInner`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
69
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ReservationPmsSectionError`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
70
70
  end
71
71
  h[k.to_sym] = v
72
72
  }
@@ -75,13 +75,13 @@ module Repull
75
75
  self.section = attributes[:'section']
76
76
  end
77
77
 
78
- if attributes.key?(:'message')
79
- self.message = attributes[:'message']
80
- end
81
-
82
78
  if attributes.key?(:'code')
83
79
  self.code = attributes[:'code']
84
80
  end
81
+
82
+ if attributes.key?(:'message')
83
+ self.message = attributes[:'message']
84
+ end
85
85
  end
86
86
 
87
87
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -105,8 +105,8 @@ module Repull
105
105
  return true if self.equal?(o)
106
106
  self.class == o.class &&
107
107
  section == o.section &&
108
- message == o.message &&
109
- code == o.code
108
+ code == o.code &&
109
+ message == o.message
110
110
  end
111
111
 
112
112
  # @see the `==` method
@@ -118,7 +118,7 @@ module Repull
118
118
  # Calculates hash code according to all attributes.
119
119
  # @return [Integer] Hash code
120
120
  def hash
121
- [section, message, code].hash
121
+ [section, code, message].hash
122
122
  end
123
123
 
124
124
  # Builds the object from hash
@@ -0,0 +1,312 @@
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` | 50 | | `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
+ class ReservationQuoteRequest < ApiModelBase
18
+ attr_accessor :listing_id
19
+
20
+ attr_accessor :check_in
21
+
22
+ # Must be after `checkIn`.
23
+ attr_accessor :check_out
24
+
25
+ attr_accessor :adults
26
+
27
+ attr_accessor :children
28
+
29
+ # Total guests, when you do not split adults and children.
30
+ attr_accessor :guest_count
31
+
32
+ # Quote one unit (`GET /v1/listings/{id}` → `units[].id`).
33
+ attr_accessor :unit_id
34
+
35
+ # Attribute mapping from ruby-style variable name to JSON key.
36
+ def self.attribute_map
37
+ {
38
+ :'listing_id' => :'listingId',
39
+ :'check_in' => :'checkIn',
40
+ :'check_out' => :'checkOut',
41
+ :'adults' => :'adults',
42
+ :'children' => :'children',
43
+ :'guest_count' => :'guestCount',
44
+ :'unit_id' => :'unitId'
45
+ }
46
+ end
47
+
48
+ # Returns attribute mapping this model knows about
49
+ def self.acceptable_attribute_map
50
+ attribute_map
51
+ end
52
+
53
+ # Returns all the JSON keys this model knows about
54
+ def self.acceptable_attributes
55
+ acceptable_attribute_map.values
56
+ end
57
+
58
+ # Attribute type mapping.
59
+ def self.openapi_types
60
+ {
61
+ :'listing_id' => :'Integer',
62
+ :'check_in' => :'Date',
63
+ :'check_out' => :'Date',
64
+ :'adults' => :'Integer',
65
+ :'children' => :'Integer',
66
+ :'guest_count' => :'Integer',
67
+ :'unit_id' => :'String'
68
+ }
69
+ end
70
+
71
+ # List of attributes with nullable: true
72
+ def self.openapi_nullable
73
+ Set.new([
74
+ ])
75
+ end
76
+
77
+ # Initializes the object
78
+ # @param [Hash] attributes Model attributes in the form of hash
79
+ def initialize(attributes = {})
80
+ if (!attributes.is_a?(Hash))
81
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ReservationQuoteRequest` initialize method"
82
+ end
83
+
84
+ # check to see if the attribute exists and convert string to symbol for hash key
85
+ acceptable_attribute_map = self.class.acceptable_attribute_map
86
+ attributes = attributes.each_with_object({}) { |(k, v), h|
87
+ if (!acceptable_attribute_map.key?(k.to_sym))
88
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ReservationQuoteRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
89
+ end
90
+ h[k.to_sym] = v
91
+ }
92
+
93
+ if attributes.key?(:'listing_id')
94
+ self.listing_id = attributes[:'listing_id']
95
+ else
96
+ self.listing_id = nil
97
+ end
98
+
99
+ if attributes.key?(:'check_in')
100
+ self.check_in = attributes[:'check_in']
101
+ else
102
+ self.check_in = nil
103
+ end
104
+
105
+ if attributes.key?(:'check_out')
106
+ self.check_out = attributes[:'check_out']
107
+ else
108
+ self.check_out = nil
109
+ end
110
+
111
+ if attributes.key?(:'adults')
112
+ self.adults = attributes[:'adults']
113
+ end
114
+
115
+ if attributes.key?(:'children')
116
+ self.children = attributes[:'children']
117
+ end
118
+
119
+ if attributes.key?(:'guest_count')
120
+ self.guest_count = attributes[:'guest_count']
121
+ end
122
+
123
+ if attributes.key?(:'unit_id')
124
+ self.unit_id = attributes[:'unit_id']
125
+ end
126
+ end
127
+
128
+ # Show invalid properties with the reasons. Usually used together with valid?
129
+ # @return Array for valid properties with the reasons
130
+ def list_invalid_properties
131
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
132
+ invalid_properties = Array.new
133
+ if @listing_id.nil?
134
+ invalid_properties.push('invalid value for "listing_id", listing_id cannot be nil.')
135
+ end
136
+
137
+ if @check_in.nil?
138
+ invalid_properties.push('invalid value for "check_in", check_in cannot be nil.')
139
+ end
140
+
141
+ if @check_out.nil?
142
+ invalid_properties.push('invalid value for "check_out", check_out cannot be nil.')
143
+ end
144
+
145
+ if !@adults.nil? && @adults < 1
146
+ invalid_properties.push('invalid value for "adults", must be greater than or equal to 1.')
147
+ end
148
+
149
+ if !@children.nil? && @children < 0
150
+ invalid_properties.push('invalid value for "children", must be greater than or equal to 0.')
151
+ end
152
+
153
+ if !@guest_count.nil? && @guest_count < 1
154
+ invalid_properties.push('invalid value for "guest_count", must be greater than or equal to 1.')
155
+ end
156
+
157
+ invalid_properties
158
+ end
159
+
160
+ # Check to see if the all the properties in the model are valid
161
+ # @return true if the model is valid
162
+ def valid?
163
+ warn '[DEPRECATED] the `valid?` method is obsolete'
164
+ return false if @listing_id.nil?
165
+ return false if @check_in.nil?
166
+ return false if @check_out.nil?
167
+ return false if !@adults.nil? && @adults < 1
168
+ return false if !@children.nil? && @children < 0
169
+ return false if !@guest_count.nil? && @guest_count < 1
170
+ true
171
+ end
172
+
173
+ # Custom attribute writer method with validation
174
+ # @param [Object] listing_id Value to be assigned
175
+ def listing_id=(listing_id)
176
+ if listing_id.nil?
177
+ fail ArgumentError, 'listing_id cannot be nil'
178
+ end
179
+
180
+ @listing_id = listing_id
181
+ end
182
+
183
+ # Custom attribute writer method with validation
184
+ # @param [Object] check_in Value to be assigned
185
+ def check_in=(check_in)
186
+ if check_in.nil?
187
+ fail ArgumentError, 'check_in cannot be nil'
188
+ end
189
+
190
+ @check_in = check_in
191
+ end
192
+
193
+ # Custom attribute writer method with validation
194
+ # @param [Object] check_out Value to be assigned
195
+ def check_out=(check_out)
196
+ if check_out.nil?
197
+ fail ArgumentError, 'check_out cannot be nil'
198
+ end
199
+
200
+ @check_out = check_out
201
+ end
202
+
203
+ # Custom attribute writer method with validation
204
+ # @param [Object] adults Value to be assigned
205
+ def adults=(adults)
206
+ if adults.nil?
207
+ fail ArgumentError, 'adults cannot be nil'
208
+ end
209
+
210
+ if adults < 1
211
+ fail ArgumentError, 'invalid value for "adults", must be greater than or equal to 1.'
212
+ end
213
+
214
+ @adults = adults
215
+ end
216
+
217
+ # Custom attribute writer method with validation
218
+ # @param [Object] children Value to be assigned
219
+ def children=(children)
220
+ if children.nil?
221
+ fail ArgumentError, 'children cannot be nil'
222
+ end
223
+
224
+ if children < 0
225
+ fail ArgumentError, 'invalid value for "children", must be greater than or equal to 0.'
226
+ end
227
+
228
+ @children = children
229
+ end
230
+
231
+ # Custom attribute writer method with validation
232
+ # @param [Object] guest_count Value to be assigned
233
+ def guest_count=(guest_count)
234
+ if guest_count.nil?
235
+ fail ArgumentError, 'guest_count cannot be nil'
236
+ end
237
+
238
+ if guest_count < 1
239
+ fail ArgumentError, 'invalid value for "guest_count", must be greater than or equal to 1.'
240
+ end
241
+
242
+ @guest_count = guest_count
243
+ end
244
+
245
+ # Checks equality by comparing each attribute.
246
+ # @param [Object] Object to be compared
247
+ def ==(o)
248
+ return true if self.equal?(o)
249
+ self.class == o.class &&
250
+ listing_id == o.listing_id &&
251
+ check_in == o.check_in &&
252
+ check_out == o.check_out &&
253
+ adults == o.adults &&
254
+ children == o.children &&
255
+ guest_count == o.guest_count &&
256
+ unit_id == o.unit_id
257
+ end
258
+
259
+ # @see the `==` method
260
+ # @param [Object] Object to be compared
261
+ def eql?(o)
262
+ self == o
263
+ end
264
+
265
+ # Calculates hash code according to all attributes.
266
+ # @return [Integer] Hash code
267
+ def hash
268
+ [listing_id, check_in, check_out, adults, children, guest_count, unit_id].hash
269
+ end
270
+
271
+ # Builds the object from hash
272
+ # @param [Hash] attributes Model attributes in the form of hash
273
+ # @return [Object] Returns the model itself
274
+ def self.build_from_hash(attributes)
275
+ return nil unless attributes.is_a?(Hash)
276
+ attributes = attributes.transform_keys(&:to_sym)
277
+ transformed_hash = {}
278
+ openapi_types.each_pair do |key, type|
279
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
280
+ transformed_hash["#{key}"] = nil
281
+ elsif type =~ /\AArray<(.*)>/i
282
+ # check to ensure the input is an array given that the attribute
283
+ # is documented as an array but the input is not
284
+ if attributes[attribute_map[key]].is_a?(Array)
285
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
286
+ end
287
+ elsif !attributes[attribute_map[key]].nil?
288
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
289
+ end
290
+ end
291
+ new(transformed_hash)
292
+ end
293
+
294
+ # Returns the object in the form of hash
295
+ # @return [Hash] Returns the object in the form of hash
296
+ def to_hash
297
+ hash = {}
298
+ self.class.attribute_map.each_pair do |attr, param|
299
+ value = self.send(attr)
300
+ if value.nil?
301
+ is_nullable = self.class.openapi_nullable.include?(attr)
302
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
303
+ end
304
+
305
+ hash[param] = _to_hash(value)
306
+ end
307
+ hash
308
+ end
309
+
310
+ end
311
+
312
+ end