repull 0.2.30 → 0.2.31

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +2 -2
  3. data/lib/repull/api/billing_api.rb +68 -9
  4. data/lib/repull/api/connect_api.rb +139 -0
  5. data/lib/repull/api/conversations_api.rb +5 -2
  6. data/lib/repull/api/health_api.rb +2 -2
  7. data/lib/repull/api/hotels_api.rb +142 -0
  8. data/lib/repull/api/workspaces_api.rb +96 -0
  9. data/lib/repull/configuration.rb +0 -4
  10. data/lib/repull/models/availability_batch_write_request.rb +30 -1
  11. data/lib/repull/models/availability_write_request.rb +30 -1
  12. data/lib/repull/models/availability_write_settings.rb +33 -4
  13. data/lib/repull/models/connect_provider.rb +2 -2
  14. data/lib/repull/models/connect_session.rb +12 -2
  15. data/lib/repull/models/create_billing_checkout200_response.rb +156 -0
  16. data/lib/repull/models/create_billing_checkout_request.rb +2 -2
  17. data/lib/repull/models/create_connect_session_request.rb +13 -1
  18. data/lib/repull/models/create_connect_session_request_workspace.rb +1 -1
  19. data/lib/repull/models/get_billing200_response.rb +183 -0
  20. data/lib/repull/models/get_billing200_response_limits.rb +160 -0
  21. data/lib/repull/models/get_billing200_response_used.rb +156 -0
  22. data/lib/repull/models/get_channel_health200_response_vrbo.rb +11 -1
  23. data/lib/repull/models/get_hotel200_response.rb +252 -0
  24. data/lib/repull/models/get_hotel200_response_room_types_inner.rb +168 -0
  25. data/lib/repull/models/get_usage_summary200_response.rb +10 -1
  26. data/lib/repull/models/get_usage_summary200_response_credits.rb +186 -0
  27. data/lib/repull/models/list_hotels200_response.rb +158 -0
  28. data/lib/repull/models/list_hotels200_response_data_inner.rb +241 -0
  29. data/lib/repull/models/list_workspaces200_response.rb +158 -0
  30. data/lib/repull/models/listing.rb +50 -1
  31. data/lib/repull/models/listing_hotel.rb +202 -0
  32. data/lib/repull/models/record_account.rb +15 -5
  33. data/lib/repull/models/reservation_message_received_payload.rb +12 -1
  34. data/lib/repull/models/reservation_message_sent_payload.rb +12 -1
  35. data/lib/repull/models/reservation_message_updated_payload.rb +12 -1
  36. data/lib/repull/models/select_provider_response.rb +1 -1
  37. data/lib/repull/models/workspace.rb +265 -0
  38. data/lib/repull/version.rb +1 -1
  39. data/lib/repull.rb +14 -0
  40. data/openapi/v1.json +633 -18
  41. metadata +16 -2
@@ -0,0 +1,96 @@
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 'cgi'
14
+
15
+ module Repull
16
+ class WorkspacesApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # List workspaces
23
+ # Every sub-workspace your workspace has opened, newest first: one per customer of yours, created by `POST /v1/connect` with `workspace` (or by Repull Migrate). Read or write a workspace's data by sending its `workspaceId` as `X-Workspace-Id`. Its webhook events arrive on your own webhook endpoints, tagged with `workspaceId`. Page with `pagination.nextCursor`.
24
+ # @param [Hash] opts the optional parameters
25
+ # @option opts [Integer] :limit (default to 25)
26
+ # @option opts [String] :cursor `pagination.nextCursor` from the previous page.
27
+ # @option opts [String] :external_ref Only the workspace you tagged with this `externalRef` (your own id for the customer).
28
+ # @return [ListWorkspaces200Response]
29
+ def list_workspaces(opts = {})
30
+ data, _status_code, _headers = list_workspaces_with_http_info(opts)
31
+ data
32
+ end
33
+
34
+ # List workspaces
35
+ # Every sub-workspace your workspace has opened, newest first: one per customer of yours, created by `POST /v1/connect` with `workspace` (or by Repull Migrate). Read or write a workspace's data by sending its `workspaceId` as `X-Workspace-Id`. Its webhook events arrive on your own webhook endpoints, tagged with `workspaceId`. Page with `pagination.nextCursor`.
36
+ # @param [Hash] opts the optional parameters
37
+ # @option opts [Integer] :limit (default to 25)
38
+ # @option opts [String] :cursor `pagination.nextCursor` from the previous page.
39
+ # @option opts [String] :external_ref Only the workspace you tagged with this `externalRef` (your own id for the customer).
40
+ # @return [Array<(ListWorkspaces200Response, Integer, Hash)>] ListWorkspaces200Response data, response status code and response headers
41
+ def list_workspaces_with_http_info(opts = {})
42
+ if @api_client.config.debugging
43
+ @api_client.config.logger.debug 'Calling API: WorkspacesApi.list_workspaces ...'
44
+ end
45
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
46
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling WorkspacesApi.list_workspaces, must be smaller than or equal to 100.'
47
+ end
48
+
49
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
50
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling WorkspacesApi.list_workspaces, must be greater than or equal to 1.'
51
+ end
52
+
53
+ # resource path
54
+ local_var_path = '/v1/workspaces'
55
+
56
+ # query parameters
57
+ query_params = opts[:query_params] || {}
58
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
59
+ query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil?
60
+ query_params[:'externalRef'] = opts[:'external_ref'] if !opts[:'external_ref'].nil?
61
+
62
+ # header parameters
63
+ header_params = opts[:header_params] || {}
64
+ # HTTP header 'Accept' (if needed)
65
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
66
+
67
+ # form parameters
68
+ form_params = opts[:form_params] || {}
69
+
70
+ # http body (model)
71
+ post_body = opts[:debug_body]
72
+
73
+ # return_type
74
+ return_type = opts[:debug_return_type] || 'ListWorkspaces200Response'
75
+
76
+ # auth_names
77
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
78
+
79
+ new_options = opts.merge(
80
+ :operation => :"WorkspacesApi.list_workspaces",
81
+ :header_params => header_params,
82
+ :query_params => query_params,
83
+ :form_params => form_params,
84
+ :body => post_body,
85
+ :auth_names => auth_names,
86
+ :return_type => return_type
87
+ )
88
+
89
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
90
+ if @api_client.config.debugging
91
+ @api_client.config.logger.debug "API called: WorkspacesApi#list_workspaces\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
92
+ end
93
+ return data, status_code, headers
94
+ end
95
+ end
96
+ end
@@ -261,10 +261,6 @@ module Repull
261
261
  {
262
262
  url: "https://api.repull.dev",
263
263
  description: "Production",
264
- },
265
- {
266
- url: "http://repull.localhost:3000/api/repull",
267
- description: "Local development",
268
264
  }
269
265
  ]
270
266
  end
@@ -25,6 +25,9 @@ module Repull
25
25
 
26
26
  attr_accessor :max_nights
27
27
 
28
+ # Rooms to sell on these dates, for a listing that sells several identical rooms (`unitCount` > 1, e.g. a Booking.com room type). Up to the listing's `unitCount`. Refused with `409 managed_by_pms` on a hotel PMS room type (Cloudbeds, Mews) — its PMS owns the room count.
29
+ attr_accessor :available_units
30
+
28
31
  # ISO dates. Capped at 731 — Airbnb refuses calendar writes spanning more.
29
32
  attr_accessor :dates
30
33
 
@@ -37,6 +40,7 @@ module Repull
37
40
  :'price' => :'price',
38
41
  :'min_nights' => :'minNights',
39
42
  :'max_nights' => :'maxNights',
43
+ :'available_units' => :'availableUnits',
40
44
  :'dates' => :'dates',
41
45
  :'property_ids' => :'propertyIds'
42
46
  }
@@ -59,6 +63,7 @@ module Repull
59
63
  :'price' => :'Float',
60
64
  :'min_nights' => :'Integer',
61
65
  :'max_nights' => :'Integer',
66
+ :'available_units' => :'Integer',
62
67
  :'dates' => :'Array<Date>',
63
68
  :'property_ids' => :'Array<Integer>'
64
69
  }
@@ -109,6 +114,10 @@ module Repull
109
114
  self.max_nights = attributes[:'max_nights']
110
115
  end
111
116
 
117
+ if attributes.key?(:'available_units')
118
+ self.available_units = attributes[:'available_units']
119
+ end
120
+
112
121
  if attributes.key?(:'dates')
113
122
  if (value = attributes[:'dates']).is_a?(Array)
114
123
  self.dates = value
@@ -139,6 +148,10 @@ module Repull
139
148
  invalid_properties.push('invalid value for "max_nights", must be greater than or equal to 1.')
140
149
  end
141
150
 
151
+ if !@available_units.nil? && @available_units < 0
152
+ invalid_properties.push('invalid value for "available_units", must be greater than or equal to 0.')
153
+ end
154
+
142
155
  if @dates.nil?
143
156
  invalid_properties.push('invalid value for "dates", dates cannot be nil.')
144
157
  end
@@ -164,6 +177,7 @@ module Repull
164
177
  warn '[DEPRECATED] the `valid?` method is obsolete'
165
178
  return false if !@min_nights.nil? && @min_nights < 1
166
179
  return false if !@max_nights.nil? && @max_nights < 1
180
+ return false if !@available_units.nil? && @available_units < 0
167
181
  return false if @dates.nil?
168
182
  return false if @dates.length > 731
169
183
  return false if @property_ids.nil?
@@ -199,6 +213,20 @@ module Repull
199
213
  @max_nights = max_nights
200
214
  end
201
215
 
216
+ # Custom attribute writer method with validation
217
+ # @param [Object] available_units Value to be assigned
218
+ def available_units=(available_units)
219
+ if available_units.nil?
220
+ fail ArgumentError, 'available_units cannot be nil'
221
+ end
222
+
223
+ if available_units < 0
224
+ fail ArgumentError, 'invalid value for "available_units", must be greater than or equal to 0.'
225
+ end
226
+
227
+ @available_units = available_units
228
+ end
229
+
202
230
  # Custom attribute writer method with validation
203
231
  # @param [Object] dates Value to be assigned
204
232
  def dates=(dates)
@@ -236,6 +264,7 @@ module Repull
236
264
  price == o.price &&
237
265
  min_nights == o.min_nights &&
238
266
  max_nights == o.max_nights &&
267
+ available_units == o.available_units &&
239
268
  dates == o.dates &&
240
269
  property_ids == o.property_ids
241
270
  end
@@ -249,7 +278,7 @@ module Repull
249
278
  # Calculates hash code according to all attributes.
250
279
  # @return [Integer] Hash code
251
280
  def hash
252
- [available, price, min_nights, max_nights, dates, property_ids].hash
281
+ [available, price, min_nights, max_nights, available_units, dates, property_ids].hash
253
282
  end
254
283
 
255
284
  # Builds the object from hash
@@ -25,6 +25,9 @@ module Repull
25
25
 
26
26
  attr_accessor :max_nights
27
27
 
28
+ # Rooms to sell on these dates, for a listing that sells several identical rooms (`unitCount` > 1, e.g. a Booking.com room type). Up to the listing's `unitCount`. Refused with `409 managed_by_pms` on a hotel PMS room type (Cloudbeds, Mews) — its PMS owns the room count.
29
+ attr_accessor :available_units
30
+
28
31
  # ISO dates. Capped at 731 — Airbnb refuses calendar writes spanning more.
29
32
  attr_accessor :dates
30
33
 
@@ -35,6 +38,7 @@ module Repull
35
38
  :'price' => :'price',
36
39
  :'min_nights' => :'minNights',
37
40
  :'max_nights' => :'maxNights',
41
+ :'available_units' => :'availableUnits',
38
42
  :'dates' => :'dates'
39
43
  }
40
44
  end
@@ -56,6 +60,7 @@ module Repull
56
60
  :'price' => :'Float',
57
61
  :'min_nights' => :'Integer',
58
62
  :'max_nights' => :'Integer',
63
+ :'available_units' => :'Integer',
59
64
  :'dates' => :'Array<Date>'
60
65
  }
61
66
  end
@@ -105,6 +110,10 @@ module Repull
105
110
  self.max_nights = attributes[:'max_nights']
106
111
  end
107
112
 
113
+ if attributes.key?(:'available_units')
114
+ self.available_units = attributes[:'available_units']
115
+ end
116
+
108
117
  if attributes.key?(:'dates')
109
118
  if (value = attributes[:'dates']).is_a?(Array)
110
119
  self.dates = value
@@ -127,6 +136,10 @@ module Repull
127
136
  invalid_properties.push('invalid value for "max_nights", must be greater than or equal to 1.')
128
137
  end
129
138
 
139
+ if !@available_units.nil? && @available_units < 0
140
+ invalid_properties.push('invalid value for "available_units", must be greater than or equal to 0.')
141
+ end
142
+
130
143
  if @dates.nil?
131
144
  invalid_properties.push('invalid value for "dates", dates cannot be nil.')
132
145
  end
@@ -144,6 +157,7 @@ module Repull
144
157
  warn '[DEPRECATED] the `valid?` method is obsolete'
145
158
  return false if !@min_nights.nil? && @min_nights < 1
146
159
  return false if !@max_nights.nil? && @max_nights < 1
160
+ return false if !@available_units.nil? && @available_units < 0
147
161
  return false if @dates.nil?
148
162
  return false if @dates.length > 731
149
163
  true
@@ -177,6 +191,20 @@ module Repull
177
191
  @max_nights = max_nights
178
192
  end
179
193
 
194
+ # Custom attribute writer method with validation
195
+ # @param [Object] available_units Value to be assigned
196
+ def available_units=(available_units)
197
+ if available_units.nil?
198
+ fail ArgumentError, 'available_units cannot be nil'
199
+ end
200
+
201
+ if available_units < 0
202
+ fail ArgumentError, 'invalid value for "available_units", must be greater than or equal to 0.'
203
+ end
204
+
205
+ @available_units = available_units
206
+ end
207
+
180
208
  # Custom attribute writer method with validation
181
209
  # @param [Object] dates Value to be assigned
182
210
  def dates=(dates)
@@ -200,6 +228,7 @@ module Repull
200
228
  price == o.price &&
201
229
  min_nights == o.min_nights &&
202
230
  max_nights == o.max_nights &&
231
+ available_units == o.available_units &&
203
232
  dates == o.dates
204
233
  end
205
234
 
@@ -212,7 +241,7 @@ module Repull
212
241
  # Calculates hash code according to all attributes.
213
242
  # @return [Integer] Hash code
214
243
  def hash
215
- [available, price, min_nights, max_nights, dates].hash
244
+ [available, price, min_nights, max_nights, available_units, dates].hash
216
245
  end
217
246
 
218
247
  # Builds the object from hash
@@ -26,13 +26,17 @@ module Repull
26
26
 
27
27
  attr_accessor :max_nights
28
28
 
29
+ # Rooms to sell on these dates, for a listing that sells several identical rooms (`unitCount` > 1, e.g. a Booking.com room type). Up to the listing's `unitCount`. Refused with `409 managed_by_pms` on a hotel PMS room type (Cloudbeds, Mews) — its PMS owns the room count.
30
+ attr_accessor :available_units
31
+
29
32
  # Attribute mapping from ruby-style variable name to JSON key.
30
33
  def self.attribute_map
31
34
  {
32
35
  :'available' => :'available',
33
36
  :'price' => :'price',
34
37
  :'min_nights' => :'minNights',
35
- :'max_nights' => :'maxNights'
38
+ :'max_nights' => :'maxNights',
39
+ :'available_units' => :'availableUnits'
36
40
  }
37
41
  end
38
42
 
@@ -52,7 +56,8 @@ module Repull
52
56
  :'available' => :'Boolean',
53
57
  :'price' => :'Float',
54
58
  :'min_nights' => :'Integer',
55
- :'max_nights' => :'Integer'
59
+ :'max_nights' => :'Integer',
60
+ :'available_units' => :'Integer'
56
61
  }
57
62
  end
58
63
 
@@ -93,6 +98,10 @@ module Repull
93
98
  if attributes.key?(:'max_nights')
94
99
  self.max_nights = attributes[:'max_nights']
95
100
  end
101
+
102
+ if attributes.key?(:'available_units')
103
+ self.available_units = attributes[:'available_units']
104
+ end
96
105
  end
97
106
 
98
107
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -108,6 +117,10 @@ module Repull
108
117
  invalid_properties.push('invalid value for "max_nights", must be greater than or equal to 1.')
109
118
  end
110
119
 
120
+ if !@available_units.nil? && @available_units < 0
121
+ invalid_properties.push('invalid value for "available_units", must be greater than or equal to 0.')
122
+ end
123
+
111
124
  invalid_properties
112
125
  end
113
126
 
@@ -117,6 +130,7 @@ module Repull
117
130
  warn '[DEPRECATED] the `valid?` method is obsolete'
118
131
  return false if !@min_nights.nil? && @min_nights < 1
119
132
  return false if !@max_nights.nil? && @max_nights < 1
133
+ return false if !@available_units.nil? && @available_units < 0
120
134
  true
121
135
  end
122
136
 
@@ -148,6 +162,20 @@ module Repull
148
162
  @max_nights = max_nights
149
163
  end
150
164
 
165
+ # Custom attribute writer method with validation
166
+ # @param [Object] available_units Value to be assigned
167
+ def available_units=(available_units)
168
+ if available_units.nil?
169
+ fail ArgumentError, 'available_units cannot be nil'
170
+ end
171
+
172
+ if available_units < 0
173
+ fail ArgumentError, 'invalid value for "available_units", must be greater than or equal to 0.'
174
+ end
175
+
176
+ @available_units = available_units
177
+ end
178
+
151
179
  # Checks equality by comparing each attribute.
152
180
  # @param [Object] Object to be compared
153
181
  def ==(o)
@@ -156,7 +184,8 @@ module Repull
156
184
  available == o.available &&
157
185
  price == o.price &&
158
186
  min_nights == o.min_nights &&
159
- max_nights == o.max_nights
187
+ max_nights == o.max_nights &&
188
+ available_units == o.available_units
160
189
  end
161
190
 
162
191
  # @see the `==` method
@@ -168,7 +197,7 @@ module Repull
168
197
  # Calculates hash code according to all attributes.
169
198
  # @return [Integer] Hash code
170
199
  def hash
171
- [available, price, min_nights, max_nights].hash
200
+ [available, price, min_nights, max_nights, available_units].hash
172
201
  end
173
202
 
174
203
  # Builds the object from hash
@@ -24,10 +24,10 @@ module Repull
24
24
  # Channel category — OTAs are listing marketplaces; PMSes are property management systems.
25
25
  attr_accessor :category
26
26
 
27
- # How the host is connected. `oauth`: provider-side consent screen. `credentials`: hosted form collects API keys. `activation`: push-only handshake (Vrbo). `claim`: connectivity-provider designation in the channel's Extranet (Booking.com).
27
+ # How the host is connected. `oauth`: provider-side consent screen. `credentials`: hosted form collects API keys. `activation`: push-only handshake. `claim`: connectivity-provider designation in the channel's Extranet (Booking.com). `extranet`: the host signs in with their Booking.com Extranet user.
28
28
  attr_accessor :connect_pattern
29
29
 
30
- # Pickers should hide / disable `coming-soon` cards. `beta` cards are clickable but show a Beta pill.
30
+ # Pickers should hide / disable `coming-soon` cards. `beta` cards are clickable but show a Beta pill. `request-only` is a PMS Repull doesn't connect yet, listed only with `includeRequestOnly`: picking it collects the host's access details for the Repull team, and the session ends as `requested` instead of connecting.
31
31
  attr_accessor :status
32
32
 
33
33
  # Logo URL — Clearbit stand-in until self-hosted SVGs land.
@@ -28,7 +28,10 @@ module Repull
28
28
  # Present only on a Repull Migrate session.
29
29
  attr_accessor :purpose
30
30
 
31
- # Repull Migrate only: the workspace the property manager's data lands in. Read it with `X-Workspace-Id`, track it with `GET /v1/migrations/{workspaceId}`.
31
+ # Present (`true`) when the session also lists PMSes Repull doesn't connect yet.
32
+ attr_accessor :include_request_only
33
+
34
+ # Present when the session lands on a sub-workspace (`workspace` or `X-Workspace-Id` on the request): the workspace the connection lands in. Read and write it with `X-Workspace-Id`; on a migrate session, track the import with `GET /v1/migrations/{workspaceId}`.
32
35
  attr_accessor :workspace_id
33
36
 
34
37
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -39,6 +42,7 @@ module Repull
39
42
  :'expires_at' => :'expiresAt',
40
43
  :'state' => :'state',
41
44
  :'purpose' => :'purpose',
45
+ :'include_request_only' => :'includeRequestOnly',
42
46
  :'workspace_id' => :'workspaceId'
43
47
  }
44
48
  end
@@ -61,6 +65,7 @@ module Repull
61
65
  :'expires_at' => :'Time',
62
66
  :'state' => :'String',
63
67
  :'purpose' => :'String',
68
+ :'include_request_only' => :'Boolean',
64
69
  :'workspace_id' => :'String'
65
70
  }
66
71
  end
@@ -114,6 +119,10 @@ module Repull
114
119
  self.purpose = attributes[:'purpose']
115
120
  end
116
121
 
122
+ if attributes.key?(:'include_request_only')
123
+ self.include_request_only = attributes[:'include_request_only']
124
+ end
125
+
117
126
  if attributes.key?(:'workspace_id')
118
127
  self.workspace_id = attributes[:'workspace_id']
119
128
  end
@@ -189,6 +198,7 @@ module Repull
189
198
  expires_at == o.expires_at &&
190
199
  state == o.state &&
191
200
  purpose == o.purpose &&
201
+ include_request_only == o.include_request_only &&
192
202
  workspace_id == o.workspace_id
193
203
  end
194
204
 
@@ -201,7 +211,7 @@ module Repull
201
211
  # Calculates hash code according to all attributes.
202
212
  # @return [Integer] Hash code
203
213
  def hash
204
- [session_id, url, expires_at, state, purpose, workspace_id].hash
214
+ [session_id, url, expires_at, state, purpose, include_request_only, workspace_id].hash
205
215
  end
206
216
 
207
217
  # Builds the object from hash
@@ -0,0 +1,156 @@
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 CreateBillingCheckout200Response < ApiModelBase
18
+ attr_accessor :plan
19
+
20
+ attr_accessor :checkout_url
21
+
22
+ # Attribute mapping from ruby-style variable name to JSON key.
23
+ def self.attribute_map
24
+ {
25
+ :'plan' => :'plan',
26
+ :'checkout_url' => :'checkoutUrl'
27
+ }
28
+ end
29
+
30
+ # Returns attribute mapping this model knows about
31
+ def self.acceptable_attribute_map
32
+ attribute_map
33
+ end
34
+
35
+ # Returns all the JSON keys this model knows about
36
+ def self.acceptable_attributes
37
+ acceptable_attribute_map.values
38
+ end
39
+
40
+ # Attribute type mapping.
41
+ def self.openapi_types
42
+ {
43
+ :'plan' => :'String',
44
+ :'checkout_url' => :'String'
45
+ }
46
+ end
47
+
48
+ # List of attributes with nullable: true
49
+ def self.openapi_nullable
50
+ Set.new([
51
+ ])
52
+ end
53
+
54
+ # Initializes the object
55
+ # @param [Hash] attributes Model attributes in the form of hash
56
+ def initialize(attributes = {})
57
+ if (!attributes.is_a?(Hash))
58
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CreateBillingCheckout200Response` initialize method"
59
+ end
60
+
61
+ # check to see if the attribute exists and convert string to symbol for hash key
62
+ acceptable_attribute_map = self.class.acceptable_attribute_map
63
+ attributes = attributes.each_with_object({}) { |(k, v), h|
64
+ if (!acceptable_attribute_map.key?(k.to_sym))
65
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CreateBillingCheckout200Response`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
66
+ end
67
+ h[k.to_sym] = v
68
+ }
69
+
70
+ if attributes.key?(:'plan')
71
+ self.plan = attributes[:'plan']
72
+ end
73
+
74
+ if attributes.key?(:'checkout_url')
75
+ self.checkout_url = attributes[:'checkout_url']
76
+ end
77
+ end
78
+
79
+ # Show invalid properties with the reasons. Usually used together with valid?
80
+ # @return Array for valid properties with the reasons
81
+ def list_invalid_properties
82
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
83
+ invalid_properties = Array.new
84
+ invalid_properties
85
+ end
86
+
87
+ # Check to see if the all the properties in the model are valid
88
+ # @return true if the model is valid
89
+ def valid?
90
+ warn '[DEPRECATED] the `valid?` method is obsolete'
91
+ true
92
+ end
93
+
94
+ # Checks equality by comparing each attribute.
95
+ # @param [Object] Object to be compared
96
+ def ==(o)
97
+ return true if self.equal?(o)
98
+ self.class == o.class &&
99
+ plan == o.plan &&
100
+ checkout_url == o.checkout_url
101
+ end
102
+
103
+ # @see the `==` method
104
+ # @param [Object] Object to be compared
105
+ def eql?(o)
106
+ self == o
107
+ end
108
+
109
+ # Calculates hash code according to all attributes.
110
+ # @return [Integer] Hash code
111
+ def hash
112
+ [plan, checkout_url].hash
113
+ end
114
+
115
+ # Builds the object from hash
116
+ # @param [Hash] attributes Model attributes in the form of hash
117
+ # @return [Object] Returns the model itself
118
+ def self.build_from_hash(attributes)
119
+ return nil unless attributes.is_a?(Hash)
120
+ attributes = attributes.transform_keys(&:to_sym)
121
+ transformed_hash = {}
122
+ openapi_types.each_pair do |key, type|
123
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
124
+ transformed_hash["#{key}"] = nil
125
+ elsif type =~ /\AArray<(.*)>/i
126
+ # check to ensure the input is an array given that the attribute
127
+ # is documented as an array but the input is not
128
+ if attributes[attribute_map[key]].is_a?(Array)
129
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
130
+ end
131
+ elsif !attributes[attribute_map[key]].nil?
132
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
133
+ end
134
+ end
135
+ new(transformed_hash)
136
+ end
137
+
138
+ # Returns the object in the form of hash
139
+ # @return [Hash] Returns the object in the form of hash
140
+ def to_hash
141
+ hash = {}
142
+ self.class.attribute_map.each_pair do |attr, param|
143
+ value = self.send(attr)
144
+ if value.nil?
145
+ is_nullable = self.class.openapi_nullable.include?(attr)
146
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
147
+ end
148
+
149
+ hash[param] = _to_hash(value)
150
+ end
151
+ hash
152
+ end
153
+
154
+ end
155
+
156
+ end
@@ -102,7 +102,7 @@ module Repull
102
102
  # @return true if the model is valid
103
103
  def valid?
104
104
  warn '[DEPRECATED] the `valid?` method is obsolete'
105
- plan_validator = EnumAttributeValidator.new('String', ["starter", "growth", "scale"])
105
+ plan_validator = EnumAttributeValidator.new('String', ["starter"])
106
106
  return false unless plan_validator.valid?(@plan)
107
107
  true
108
108
  end
@@ -110,7 +110,7 @@ module Repull
110
110
  # Custom attribute writer method checking allowed values (enum).
111
111
  # @param [Object] plan Object to be assigned
112
112
  def plan=(plan)
113
- validator = EnumAttributeValidator.new('String', ["starter", "growth", "scale"])
113
+ validator = EnumAttributeValidator.new('String', ["starter"])
114
114
  unless validator.valid?(plan)
115
115
  fail ArgumentError, "invalid value for \"plan\", must be one of #{validator.allowable_values}."
116
116
  end