repull 0.2.34 → 0.2.36

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/connect_api.rb +6 -6
  3. data/lib/repull/api/health_api.rb +3 -3
  4. data/lib/repull/api/listings_api.rb +8 -8
  5. data/lib/repull/api/migrate_api.rb +68 -0
  6. data/lib/repull/models/{calendar_updated_event.rb → booking_unacknowledged_event.rb} +4 -4
  7. data/lib/repull/models/booking_unacknowledged_payload.rb +229 -0
  8. data/lib/repull/models/channel_degraded_event.rb +279 -0
  9. data/lib/repull/models/channel_degraded_payload.rb +174 -0
  10. data/lib/repull/models/channel_degraded_payload_object.rb +262 -0
  11. data/lib/repull/models/channel_sync_failed_event.rb +280 -0
  12. data/lib/repull/models/channel_sync_failure_payload.rb +214 -0
  13. data/lib/repull/models/channel_sync_failure_payload_failures_inner.rb +213 -0
  14. data/lib/repull/models/channel_sync_failure_payload_object.rb +289 -0
  15. data/lib/repull/models/charge_per.rb +42 -0
  16. data/lib/repull/models/connect_session_completed_payload.rb +1 -1
  17. data/lib/repull/models/connect_status_accounts_inner.rb +12 -1
  18. data/lib/repull/models/create_connect_session_request.rb +26 -2
  19. data/lib/repull/models/create_connection_request.rb +3 -23
  20. data/lib/repull/models/{plumguide_listing_list_response.rb → import_from_channex200_response.rb} +8 -19
  21. data/lib/repull/models/import_from_channex200_response_data.rb +324 -0
  22. data/lib/repull/models/import_from_channex200_response_data_channels_inner.rb +263 -0
  23. data/lib/repull/models/import_from_channex200_response_data_properties_inner.rb +222 -0
  24. data/lib/repull/models/import_from_channex200_response_data_summary.rb +201 -0
  25. data/lib/repull/models/import_from_channex200_response_data_summary_bookings.rb +165 -0
  26. data/lib/repull/models/import_from_channex200_response_data_summary_properties.rb +165 -0
  27. data/lib/repull/models/import_from_channex_request.rb +223 -0
  28. data/lib/repull/models/list_connection_units200_response.rb +12 -1
  29. data/lib/repull/models/list_connection_units200_response_units_inner.rb +1 -0
  30. data/lib/repull/models/listing.rb +82 -1
  31. data/lib/repull/models/listing_address.rb +62 -5
  32. data/lib/repull/models/listing_content_photo.rb +206 -0
  33. data/lib/repull/models/listing_fee.rb +229 -0
  34. data/lib/repull/models/listing_policies.rb +257 -0
  35. data/lib/repull/models/{calendar_updated_payload.rb → listing_policies_check_in.rb} +33 -38
  36. data/lib/repull/models/listing_policies_check_out.rb +148 -0
  37. data/lib/repull/models/{calendar_updated_payload_range.rb → listing_policies_quiet_hours.rb} +5 -5
  38. data/lib/repull/models/listing_pricing_settings.rb +233 -0
  39. data/lib/repull/models/listing_room.rb +190 -0
  40. data/lib/repull/models/{plumguide_listing.rb → listing_room_beds_inner.rb} +16 -27
  41. data/lib/repull/models/listing_rules.rb +212 -0
  42. data/lib/repull/models/listing_tax.rb +241 -0
  43. data/lib/repull/models/message.rb +24 -1
  44. data/lib/repull/models/postal_address.rb +216 -0
  45. data/lib/repull/models/property.rb +12 -2
  46. data/lib/repull/models/reservation_thread_assigned_event.rb +279 -0
  47. data/lib/repull/models/reservation_thread_assigned_payload.rb +174 -0
  48. data/lib/repull/models/reservation_thread_assigned_payload_object.rb +249 -0
  49. data/lib/repull/models/webhook_event.rb +8 -2
  50. data/lib/repull/models/webhook_event_type.rb +5 -2
  51. data/lib/repull/version.rb +1 -1
  52. data/lib/repull.rb +33 -6
  53. data/openapi/v1.json +1712 -352
  54. metadata +35 -10
  55. data/lib/repull/api/plumguide_api.rb +0 -552
  56. data/spec/api/plumguide_api_spec.rb +0 -85
@@ -0,0 +1,257 @@
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
+ class ListingPolicies < ApiModelBase
18
+ attr_accessor :cancellation_policy
19
+
20
+ attr_accessor :house_rules
21
+
22
+ attr_accessor :check_in
23
+
24
+ attr_accessor :check_out
25
+
26
+ attr_accessor :allows_pets
27
+
28
+ attr_accessor :allows_smoking
29
+
30
+ attr_accessor :allows_events
31
+
32
+ attr_accessor :allows_children
33
+
34
+ attr_accessor :allows_infants
35
+
36
+ attr_accessor :children_min_age
37
+
38
+ attr_accessor :quiet_hours
39
+
40
+ # In the listing currency.
41
+ attr_accessor :security_deposit
42
+
43
+ # Attribute mapping from ruby-style variable name to JSON key.
44
+ def self.attribute_map
45
+ {
46
+ :'cancellation_policy' => :'cancellationPolicy',
47
+ :'house_rules' => :'houseRules',
48
+ :'check_in' => :'checkIn',
49
+ :'check_out' => :'checkOut',
50
+ :'allows_pets' => :'allowsPets',
51
+ :'allows_smoking' => :'allowsSmoking',
52
+ :'allows_events' => :'allowsEvents',
53
+ :'allows_children' => :'allowsChildren',
54
+ :'allows_infants' => :'allowsInfants',
55
+ :'children_min_age' => :'childrenMinAge',
56
+ :'quiet_hours' => :'quietHours',
57
+ :'security_deposit' => :'securityDeposit'
58
+ }
59
+ end
60
+
61
+ # Returns attribute mapping this model knows about
62
+ def self.acceptable_attribute_map
63
+ attribute_map
64
+ end
65
+
66
+ # Returns all the JSON keys this model knows about
67
+ def self.acceptable_attributes
68
+ acceptable_attribute_map.values
69
+ end
70
+
71
+ # Attribute type mapping.
72
+ def self.openapi_types
73
+ {
74
+ :'cancellation_policy' => :'String',
75
+ :'house_rules' => :'String',
76
+ :'check_in' => :'ListingPoliciesCheckIn',
77
+ :'check_out' => :'ListingPoliciesCheckOut',
78
+ :'allows_pets' => :'Boolean',
79
+ :'allows_smoking' => :'Boolean',
80
+ :'allows_events' => :'Boolean',
81
+ :'allows_children' => :'Boolean',
82
+ :'allows_infants' => :'Boolean',
83
+ :'children_min_age' => :'Integer',
84
+ :'quiet_hours' => :'ListingPoliciesQuietHours',
85
+ :'security_deposit' => :'Float'
86
+ }
87
+ end
88
+
89
+ # List of attributes with nullable: true
90
+ def self.openapi_nullable
91
+ Set.new([
92
+ :'cancellation_policy',
93
+ :'house_rules',
94
+ :'allows_pets',
95
+ :'allows_smoking',
96
+ :'allows_events',
97
+ :'allows_children',
98
+ :'allows_infants',
99
+ :'children_min_age',
100
+ :'quiet_hours',
101
+ :'security_deposit'
102
+ ])
103
+ end
104
+
105
+ # Initializes the object
106
+ # @param [Hash] attributes Model attributes in the form of hash
107
+ def initialize(attributes = {})
108
+ if (!attributes.is_a?(Hash))
109
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ListingPolicies` initialize method"
110
+ end
111
+
112
+ # check to see if the attribute exists and convert string to symbol for hash key
113
+ acceptable_attribute_map = self.class.acceptable_attribute_map
114
+ attributes = attributes.each_with_object({}) { |(k, v), h|
115
+ if (!acceptable_attribute_map.key?(k.to_sym))
116
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ListingPolicies`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
117
+ end
118
+ h[k.to_sym] = v
119
+ }
120
+
121
+ if attributes.key?(:'cancellation_policy')
122
+ self.cancellation_policy = attributes[:'cancellation_policy']
123
+ end
124
+
125
+ if attributes.key?(:'house_rules')
126
+ self.house_rules = attributes[:'house_rules']
127
+ end
128
+
129
+ if attributes.key?(:'check_in')
130
+ self.check_in = attributes[:'check_in']
131
+ end
132
+
133
+ if attributes.key?(:'check_out')
134
+ self.check_out = attributes[:'check_out']
135
+ end
136
+
137
+ if attributes.key?(:'allows_pets')
138
+ self.allows_pets = attributes[:'allows_pets']
139
+ end
140
+
141
+ if attributes.key?(:'allows_smoking')
142
+ self.allows_smoking = attributes[:'allows_smoking']
143
+ end
144
+
145
+ if attributes.key?(:'allows_events')
146
+ self.allows_events = attributes[:'allows_events']
147
+ end
148
+
149
+ if attributes.key?(:'allows_children')
150
+ self.allows_children = attributes[:'allows_children']
151
+ end
152
+
153
+ if attributes.key?(:'allows_infants')
154
+ self.allows_infants = attributes[:'allows_infants']
155
+ end
156
+
157
+ if attributes.key?(:'children_min_age')
158
+ self.children_min_age = attributes[:'children_min_age']
159
+ end
160
+
161
+ if attributes.key?(:'quiet_hours')
162
+ self.quiet_hours = attributes[:'quiet_hours']
163
+ end
164
+
165
+ if attributes.key?(:'security_deposit')
166
+ self.security_deposit = attributes[:'security_deposit']
167
+ end
168
+ end
169
+
170
+ # Show invalid properties with the reasons. Usually used together with valid?
171
+ # @return Array for valid properties with the reasons
172
+ def list_invalid_properties
173
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
174
+ invalid_properties = Array.new
175
+ invalid_properties
176
+ end
177
+
178
+ # Check to see if the all the properties in the model are valid
179
+ # @return true if the model is valid
180
+ def valid?
181
+ warn '[DEPRECATED] the `valid?` method is obsolete'
182
+ true
183
+ end
184
+
185
+ # Checks equality by comparing each attribute.
186
+ # @param [Object] Object to be compared
187
+ def ==(o)
188
+ return true if self.equal?(o)
189
+ self.class == o.class &&
190
+ cancellation_policy == o.cancellation_policy &&
191
+ house_rules == o.house_rules &&
192
+ check_in == o.check_in &&
193
+ check_out == o.check_out &&
194
+ allows_pets == o.allows_pets &&
195
+ allows_smoking == o.allows_smoking &&
196
+ allows_events == o.allows_events &&
197
+ allows_children == o.allows_children &&
198
+ allows_infants == o.allows_infants &&
199
+ children_min_age == o.children_min_age &&
200
+ quiet_hours == o.quiet_hours &&
201
+ security_deposit == o.security_deposit
202
+ end
203
+
204
+ # @see the `==` method
205
+ # @param [Object] Object to be compared
206
+ def eql?(o)
207
+ self == o
208
+ end
209
+
210
+ # Calculates hash code according to all attributes.
211
+ # @return [Integer] Hash code
212
+ def hash
213
+ [cancellation_policy, house_rules, check_in, check_out, allows_pets, allows_smoking, allows_events, allows_children, allows_infants, children_min_age, quiet_hours, security_deposit].hash
214
+ end
215
+
216
+ # Builds the object from hash
217
+ # @param [Hash] attributes Model attributes in the form of hash
218
+ # @return [Object] Returns the model itself
219
+ def self.build_from_hash(attributes)
220
+ return nil unless attributes.is_a?(Hash)
221
+ attributes = attributes.transform_keys(&:to_sym)
222
+ transformed_hash = {}
223
+ openapi_types.each_pair do |key, type|
224
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
225
+ transformed_hash["#{key}"] = nil
226
+ elsif type =~ /\AArray<(.*)>/i
227
+ # check to ensure the input is an array given that the attribute
228
+ # is documented as an array but the input is not
229
+ if attributes[attribute_map[key]].is_a?(Array)
230
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
231
+ end
232
+ elsif !attributes[attribute_map[key]].nil?
233
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
234
+ end
235
+ end
236
+ new(transformed_hash)
237
+ end
238
+
239
+ # Returns the object in the form of hash
240
+ # @return [Hash] Returns the object in the form of hash
241
+ def to_hash
242
+ hash = {}
243
+ self.class.attribute_map.each_pair do |attr, param|
244
+ value = self.send(attr)
245
+ if value.nil?
246
+ is_nullable = self.class.openapi_nullable.include?(attr)
247
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
248
+ end
249
+
250
+ hash[param] = _to_hash(value)
251
+ end
252
+ hash
253
+ end
254
+
255
+ end
256
+
257
+ end
@@ -14,26 +14,23 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # Payload for `calendar.updated`. Availability or pricing for a listing was updated.
18
- class CalendarUpdatedPayload < ApiModelBase
19
- attr_accessor :listing_id
17
+ class ListingPoliciesCheckIn < ApiModelBase
18
+ # `lockbox`, `smartlock`, `keypad`, `doorman`, `host_greets`, `building_staff`, `other`.
19
+ attr_accessor :method
20
20
 
21
- attr_accessor :range
21
+ attr_accessor :instructions
22
22
 
23
- attr_accessor :affected_dates
23
+ attr_accessor :from
24
24
 
25
- attr_accessor :pricing_changed
26
-
27
- attr_accessor :availability_changed
25
+ attr_accessor :_until
28
26
 
29
27
  # Attribute mapping from ruby-style variable name to JSON key.
30
28
  def self.attribute_map
31
29
  {
32
- :'listing_id' => :'listingId',
33
- :'range' => :'range',
34
- :'affected_dates' => :'affectedDates',
35
- :'pricing_changed' => :'pricingChanged',
36
- :'availability_changed' => :'availabilityChanged'
30
+ :'method' => :'method',
31
+ :'instructions' => :'instructions',
32
+ :'from' => :'from',
33
+ :'_until' => :'until'
37
34
  }
38
35
  end
39
36
 
@@ -50,17 +47,20 @@ module Repull
50
47
  # Attribute type mapping.
51
48
  def self.openapi_types
52
49
  {
53
- :'listing_id' => :'Integer',
54
- :'range' => :'CalendarUpdatedPayloadRange',
55
- :'affected_dates' => :'Integer',
56
- :'pricing_changed' => :'Boolean',
57
- :'availability_changed' => :'Boolean'
50
+ :'method' => :'String',
51
+ :'instructions' => :'String',
52
+ :'from' => :'String',
53
+ :'_until' => :'String'
58
54
  }
59
55
  end
60
56
 
61
57
  # List of attributes with nullable: true
62
58
  def self.openapi_nullable
63
59
  Set.new([
60
+ :'method',
61
+ :'instructions',
62
+ :'from',
63
+ :'_until'
64
64
  ])
65
65
  end
66
66
 
@@ -68,36 +68,32 @@ module Repull
68
68
  # @param [Hash] attributes Model attributes in the form of hash
69
69
  def initialize(attributes = {})
70
70
  if (!attributes.is_a?(Hash))
71
- fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CalendarUpdatedPayload` initialize method"
71
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ListingPoliciesCheckIn` initialize method"
72
72
  end
73
73
 
74
74
  # check to see if the attribute exists and convert string to symbol for hash key
75
75
  acceptable_attribute_map = self.class.acceptable_attribute_map
76
76
  attributes = attributes.each_with_object({}) { |(k, v), h|
77
77
  if (!acceptable_attribute_map.key?(k.to_sym))
78
- fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CalendarUpdatedPayload`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
78
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ListingPoliciesCheckIn`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
79
79
  end
80
80
  h[k.to_sym] = v
81
81
  }
82
82
 
83
- if attributes.key?(:'listing_id')
84
- self.listing_id = attributes[:'listing_id']
85
- end
86
-
87
- if attributes.key?(:'range')
88
- self.range = attributes[:'range']
83
+ if attributes.key?(:'method')
84
+ self.method = attributes[:'method']
89
85
  end
90
86
 
91
- if attributes.key?(:'affected_dates')
92
- self.affected_dates = attributes[:'affected_dates']
87
+ if attributes.key?(:'instructions')
88
+ self.instructions = attributes[:'instructions']
93
89
  end
94
90
 
95
- if attributes.key?(:'pricing_changed')
96
- self.pricing_changed = attributes[:'pricing_changed']
91
+ if attributes.key?(:'from')
92
+ self.from = attributes[:'from']
97
93
  end
98
94
 
99
- if attributes.key?(:'availability_changed')
100
- self.availability_changed = attributes[:'availability_changed']
95
+ if attributes.key?(:'_until')
96
+ self._until = attributes[:'_until']
101
97
  end
102
98
  end
103
99
 
@@ -121,11 +117,10 @@ module Repull
121
117
  def ==(o)
122
118
  return true if self.equal?(o)
123
119
  self.class == o.class &&
124
- listing_id == o.listing_id &&
125
- range == o.range &&
126
- affected_dates == o.affected_dates &&
127
- pricing_changed == o.pricing_changed &&
128
- availability_changed == o.availability_changed
120
+ method == o.method &&
121
+ instructions == o.instructions &&
122
+ from == o.from &&
123
+ _until == o._until
129
124
  end
130
125
 
131
126
  # @see the `==` method
@@ -137,7 +132,7 @@ module Repull
137
132
  # Calculates hash code according to all attributes.
138
133
  # @return [Integer] Hash code
139
134
  def hash
140
- [listing_id, range, affected_dates, pricing_changed, availability_changed].hash
135
+ [method, instructions, from, _until].hash
141
136
  end
142
137
 
143
138
  # Builds the object from hash
@@ -0,0 +1,148 @@
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
+ class ListingPoliciesCheckOut < ApiModelBase
18
+ attr_accessor :_until
19
+
20
+ # Attribute mapping from ruby-style variable name to JSON key.
21
+ def self.attribute_map
22
+ {
23
+ :'_until' => :'until'
24
+ }
25
+ end
26
+
27
+ # Returns attribute mapping this model knows about
28
+ def self.acceptable_attribute_map
29
+ attribute_map
30
+ end
31
+
32
+ # Returns all the JSON keys this model knows about
33
+ def self.acceptable_attributes
34
+ acceptable_attribute_map.values
35
+ end
36
+
37
+ # Attribute type mapping.
38
+ def self.openapi_types
39
+ {
40
+ :'_until' => :'String'
41
+ }
42
+ end
43
+
44
+ # List of attributes with nullable: true
45
+ def self.openapi_nullable
46
+ Set.new([
47
+ :'_until'
48
+ ])
49
+ end
50
+
51
+ # Initializes the object
52
+ # @param [Hash] attributes Model attributes in the form of hash
53
+ def initialize(attributes = {})
54
+ if (!attributes.is_a?(Hash))
55
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ListingPoliciesCheckOut` initialize method"
56
+ end
57
+
58
+ # check to see if the attribute exists and convert string to symbol for hash key
59
+ acceptable_attribute_map = self.class.acceptable_attribute_map
60
+ attributes = attributes.each_with_object({}) { |(k, v), h|
61
+ if (!acceptable_attribute_map.key?(k.to_sym))
62
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ListingPoliciesCheckOut`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
63
+ end
64
+ h[k.to_sym] = v
65
+ }
66
+
67
+ if attributes.key?(:'_until')
68
+ self._until = attributes[:'_until']
69
+ end
70
+ end
71
+
72
+ # Show invalid properties with the reasons. Usually used together with valid?
73
+ # @return Array for valid properties with the reasons
74
+ def list_invalid_properties
75
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
76
+ invalid_properties = Array.new
77
+ invalid_properties
78
+ end
79
+
80
+ # Check to see if the all the properties in the model are valid
81
+ # @return true if the model is valid
82
+ def valid?
83
+ warn '[DEPRECATED] the `valid?` method is obsolete'
84
+ true
85
+ end
86
+
87
+ # Checks equality by comparing each attribute.
88
+ # @param [Object] Object to be compared
89
+ def ==(o)
90
+ return true if self.equal?(o)
91
+ self.class == o.class &&
92
+ _until == o._until
93
+ end
94
+
95
+ # @see the `==` method
96
+ # @param [Object] Object to be compared
97
+ def eql?(o)
98
+ self == o
99
+ end
100
+
101
+ # Calculates hash code according to all attributes.
102
+ # @return [Integer] Hash code
103
+ def hash
104
+ [_until].hash
105
+ end
106
+
107
+ # Builds the object from hash
108
+ # @param [Hash] attributes Model attributes in the form of hash
109
+ # @return [Object] Returns the model itself
110
+ def self.build_from_hash(attributes)
111
+ return nil unless attributes.is_a?(Hash)
112
+ attributes = attributes.transform_keys(&:to_sym)
113
+ transformed_hash = {}
114
+ openapi_types.each_pair do |key, type|
115
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
116
+ transformed_hash["#{key}"] = nil
117
+ elsif type =~ /\AArray<(.*)>/i
118
+ # check to ensure the input is an array given that the attribute
119
+ # is documented as an array but the input is not
120
+ if attributes[attribute_map[key]].is_a?(Array)
121
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
122
+ end
123
+ elsif !attributes[attribute_map[key]].nil?
124
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
125
+ end
126
+ end
127
+ new(transformed_hash)
128
+ end
129
+
130
+ # Returns the object in the form of hash
131
+ # @return [Hash] Returns the object in the form of hash
132
+ def to_hash
133
+ hash = {}
134
+ self.class.attribute_map.each_pair do |attr, param|
135
+ value = self.send(attr)
136
+ if value.nil?
137
+ is_nullable = self.class.openapi_nullable.include?(attr)
138
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
139
+ end
140
+
141
+ hash[param] = _to_hash(value)
142
+ end
143
+ hash
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -14,7 +14,7 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- class CalendarUpdatedPayloadRange < ApiModelBase
17
+ class ListingPoliciesQuietHours < ApiModelBase
18
18
  attr_accessor :start
19
19
 
20
20
  attr_accessor :_end
@@ -40,8 +40,8 @@ module Repull
40
40
  # Attribute type mapping.
41
41
  def self.openapi_types
42
42
  {
43
- :'start' => :'Date',
44
- :'_end' => :'Date'
43
+ :'start' => :'String',
44
+ :'_end' => :'String'
45
45
  }
46
46
  end
47
47
 
@@ -55,14 +55,14 @@ module Repull
55
55
  # @param [Hash] attributes Model attributes in the form of hash
56
56
  def initialize(attributes = {})
57
57
  if (!attributes.is_a?(Hash))
58
- fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CalendarUpdatedPayloadRange` initialize method"
58
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ListingPoliciesQuietHours` initialize method"
59
59
  end
60
60
 
61
61
  # check to see if the attribute exists and convert string to symbol for hash key
62
62
  acceptable_attribute_map = self.class.acceptable_attribute_map
63
63
  attributes = attributes.each_with_object({}) { |(k, v), h|
64
64
  if (!acceptable_attribute_map.key?(k.to_sym))
65
- fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CalendarUpdatedPayloadRange`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
65
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ListingPoliciesQuietHours`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
66
66
  end
67
67
  h[k.to_sym] = v
68
68
  }