repull 0.2.17 → 0.2.18

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +4 -4
  3. data/lib/repull/api/health_api.rb +0 -57
  4. data/lib/repull/api/migrate_api.rb +552 -0
  5. data/lib/repull/api/reservations_api.rb +2 -2
  6. data/lib/repull/models/airbnb_permits_response.rb +1 -1
  7. data/lib/repull/models/airbnb_permits_write_request.rb +1 -1
  8. data/lib/repull/models/airbnb_permits_write_request_permits_inner.rb +43 -4
  9. data/lib/repull/models/{airbnb_permits_write_request_permits_inner_answers_inner.rb → airbnb_permits_write_request_permits_inner_answers_value.rb} +22 -34
  10. data/lib/repull/models/check_migration_cutover200_response.rb +147 -0
  11. data/lib/repull/models/check_migration_cutover_request.rb +175 -0
  12. data/lib/repull/models/check_migration_cutover_request_reservations_inner.rb +216 -0
  13. data/lib/repull/models/connect_provider.rb +18 -5
  14. data/lib/repull/models/connect_session.rb +25 -5
  15. data/lib/repull/models/create_connect_session_request.rb +81 -5
  16. data/lib/repull/models/create_connect_session_request_copy.rb +251 -0
  17. data/lib/repull/models/create_connect_session_request_workspace.rb +196 -0
  18. data/lib/repull/models/delete_migration200_response.rb +147 -0
  19. data/lib/repull/models/delete_migration200_response_data.rb +165 -0
  20. data/lib/repull/models/get_migration200_response.rb +147 -0
  21. data/lib/repull/models/get_migration_channel_map200_response.rb +147 -0
  22. data/lib/repull/models/get_migration_report200_response.rb +147 -0
  23. data/lib/repull/models/list_migrations200_response.rb +158 -0
  24. data/lib/repull/models/listing_content_update_request.rb +10 -1
  25. data/lib/repull/models/listing_content_update_request_pricing.rb +235 -0
  26. data/lib/repull/models/listing_content_update_response.rb +1 -1
  27. data/lib/repull/models/listing_create_request.rb +24 -1
  28. data/lib/repull/models/listing_create_response.rb +14 -4
  29. data/lib/repull/models/migration.rb +303 -0
  30. data/lib/repull/models/migration_channel_map.rb +169 -0
  31. data/lib/repull/models/migration_channel_map_listings_inner.rb +208 -0
  32. data/lib/repull/models/migration_channel_map_listings_inner_airbnb.rb +156 -0
  33. data/lib/repull/models/migration_channel_map_listings_inner_booking.rb +156 -0
  34. data/lib/repull/models/migration_channel_map_sources_inner.rb +167 -0
  35. data/lib/repull/models/migration_completed_event.rb +309 -0
  36. data/lib/repull/models/migration_connections_inner.rb +195 -0
  37. data/lib/repull/models/migration_counts.rb +183 -0
  38. data/lib/repull/models/migration_cutover_check.rb +200 -0
  39. data/lib/repull/models/migration_cutover_check_mismatched_inner.rb +156 -0
  40. data/lib/repull/models/migration_failed_event.rb +309 -0
  41. data/lib/repull/models/migration_import_payload.rb +225 -0
  42. data/lib/repull/models/migration_import_payload_results_inner.rb +165 -0
  43. data/lib/repull/models/migration_import_run.rb +202 -0
  44. data/lib/repull/models/migration_import_run_results_inner.rb +165 -0
  45. data/lib/repull/models/migration_report.rb +179 -0
  46. data/lib/repull/models/migration_report_issues_inner.rb +229 -0
  47. data/lib/repull/models/migration_reservation_ref.rb +184 -0
  48. data/lib/repull/models/reservation.rb +12 -2
  49. data/lib/repull/models/run_migration_import202_response.rb +147 -0
  50. data/lib/repull/models/run_migration_import202_response_data.rb +158 -0
  51. data/lib/repull/models/run_migration_import202_response_data_queued_inner.rb +174 -0
  52. data/lib/repull/models/run_migration_import_request.rb +182 -0
  53. data/lib/repull/models/webhook_event.rb +4 -0
  54. data/lib/repull/models/webhook_event_type.rb +3 -1
  55. data/lib/repull/version.rb +1 -1
  56. data/lib/repull.rb +37 -1
  57. data/openapi/v1.json +3426 -2244
  58. metadata +39 -3
@@ -15,11 +15,19 @@ require 'time'
15
15
 
16
16
  module Repull
17
17
  class AirbnbPermitsWriteRequestPermitsInner < ApiModelBase
18
- # As named by the GET, e.g. the city or registry asking.
18
+ # As returned by the GET, e.g. `maui_county_hawaii`.
19
19
  attr_accessor :regulatory_body
20
20
 
21
+ # As returned by the GET.
21
22
  attr_accessor :regulation_type
22
23
 
24
+ # Echo the GET's `regulation_context` (e.g. `initial`) when present.
25
+ attr_accessor :regulation_context
26
+
27
+ # The `slug` of the flow you are answering, e.g. `existing_registration` or `exemption_claim`.
28
+ attr_accessor :flow_slug
29
+
30
+ # Keyed by each question's `answer_key`. Each value carries exactly one field, chosen by the question's `type`: TEXT → `text_value`, ATTESTATION → `attestation_value`, RADIO → `radio_value`, DATE → `date_value`, SELECT → `selected_options_value`.
23
31
  attr_accessor :answers
24
32
 
25
33
  # Attribute mapping from ruby-style variable name to JSON key.
@@ -27,6 +35,8 @@ module Repull
27
35
  {
28
36
  :'regulatory_body' => :'regulatory_body',
29
37
  :'regulation_type' => :'regulation_type',
38
+ :'regulation_context' => :'regulation_context',
39
+ :'flow_slug' => :'flow_slug',
30
40
  :'answers' => :'answers'
31
41
  }
32
42
  end
@@ -46,7 +56,9 @@ module Repull
46
56
  {
47
57
  :'regulatory_body' => :'String',
48
58
  :'regulation_type' => :'String',
49
- :'answers' => :'Array<AirbnbPermitsWriteRequestPermitsInnerAnswersInner>'
59
+ :'regulation_context' => :'String',
60
+ :'flow_slug' => :'String',
61
+ :'answers' => :'Hash<String, AirbnbPermitsWriteRequestPermitsInnerAnswersValue>'
50
62
  }
51
63
  end
52
64
 
@@ -84,8 +96,18 @@ module Repull
84
96
  self.regulation_type = nil
85
97
  end
86
98
 
99
+ if attributes.key?(:'regulation_context')
100
+ self.regulation_context = attributes[:'regulation_context']
101
+ end
102
+
103
+ if attributes.key?(:'flow_slug')
104
+ self.flow_slug = attributes[:'flow_slug']
105
+ else
106
+ self.flow_slug = nil
107
+ end
108
+
87
109
  if attributes.key?(:'answers')
88
- if (value = attributes[:'answers']).is_a?(Array)
110
+ if (value = attributes[:'answers']).is_a?(Hash)
89
111
  self.answers = value
90
112
  end
91
113
  else
@@ -106,6 +128,10 @@ module Repull
106
128
  invalid_properties.push('invalid value for "regulation_type", regulation_type cannot be nil.')
107
129
  end
108
130
 
131
+ if @flow_slug.nil?
132
+ invalid_properties.push('invalid value for "flow_slug", flow_slug cannot be nil.')
133
+ end
134
+
109
135
  if @answers.nil?
110
136
  invalid_properties.push('invalid value for "answers", answers cannot be nil.')
111
137
  end
@@ -123,6 +149,7 @@ module Repull
123
149
  warn '[DEPRECATED] the `valid?` method is obsolete'
124
150
  return false if @regulatory_body.nil?
125
151
  return false if @regulation_type.nil?
152
+ return false if @flow_slug.nil?
126
153
  return false if @answers.nil?
127
154
  return false if @answers.length < 1
128
155
  true
@@ -148,6 +175,16 @@ module Repull
148
175
  @regulation_type = regulation_type
149
176
  end
150
177
 
178
+ # Custom attribute writer method with validation
179
+ # @param [Object] flow_slug Value to be assigned
180
+ def flow_slug=(flow_slug)
181
+ if flow_slug.nil?
182
+ fail ArgumentError, 'flow_slug cannot be nil'
183
+ end
184
+
185
+ @flow_slug = flow_slug
186
+ end
187
+
151
188
  # Custom attribute writer method with validation
152
189
  # @param [Object] answers Value to be assigned
153
190
  def answers=(answers)
@@ -169,6 +206,8 @@ module Repull
169
206
  self.class == o.class &&
170
207
  regulatory_body == o.regulatory_body &&
171
208
  regulation_type == o.regulation_type &&
209
+ regulation_context == o.regulation_context &&
210
+ flow_slug == o.flow_slug &&
172
211
  answers == o.answers
173
212
  end
174
213
 
@@ -181,7 +220,7 @@ module Repull
181
220
  # Calculates hash code according to all attributes.
182
221
  # @return [Integer] Hash code
183
222
  def hash
184
- [regulatory_body, regulation_type, answers].hash
223
+ [regulatory_body, regulation_type, regulation_context, flow_slug, answers].hash
185
224
  end
186
225
 
187
226
  # Builds the object from hash
@@ -14,12 +14,13 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # Exactly one value field applies, decided by the question's `answer_type`.
18
- class AirbnbPermitsWriteRequestPermitsInnerAnswersInner < ApiModelBase
19
- attr_accessor :question_key
20
-
17
+ class AirbnbPermitsWriteRequestPermitsInnerAnswersValue < ApiModelBase
21
18
  attr_accessor :text_value
22
19
 
20
+ attr_accessor :attestation_value
21
+
22
+ attr_accessor :radio_value
23
+
23
24
  # ISO date, YYYY-MM-DD.
24
25
  attr_accessor :date_value
25
26
 
@@ -28,8 +29,9 @@ module Repull
28
29
  # Attribute mapping from ruby-style variable name to JSON key.
29
30
  def self.attribute_map
30
31
  {
31
- :'question_key' => :'question_key',
32
32
  :'text_value' => :'text_value',
33
+ :'attestation_value' => :'attestation_value',
34
+ :'radio_value' => :'radio_value',
33
35
  :'date_value' => :'date_value',
34
36
  :'selected_options_value' => :'selected_options_value'
35
37
  }
@@ -48,8 +50,9 @@ module Repull
48
50
  # Attribute type mapping.
49
51
  def self.openapi_types
50
52
  {
51
- :'question_key' => :'String',
52
53
  :'text_value' => :'String',
54
+ :'attestation_value' => :'Boolean',
55
+ :'radio_value' => :'String',
53
56
  :'date_value' => :'String',
54
57
  :'selected_options_value' => :'Array<String>'
55
58
  }
@@ -58,9 +61,6 @@ module Repull
58
61
  # List of attributes with nullable: true
59
62
  def self.openapi_nullable
60
63
  Set.new([
61
- :'text_value',
62
- :'date_value',
63
- :'selected_options_value'
64
64
  ])
65
65
  end
66
66
 
@@ -68,28 +68,30 @@ 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::AirbnbPermitsWriteRequestPermitsInnerAnswersInner` initialize method"
71
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::AirbnbPermitsWriteRequestPermitsInnerAnswersValue` 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::AirbnbPermitsWriteRequestPermitsInnerAnswersInner`. 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::AirbnbPermitsWriteRequestPermitsInnerAnswersValue`. 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?(:'question_key')
84
- self.question_key = attributes[:'question_key']
85
- else
86
- self.question_key = nil
87
- end
88
-
89
83
  if attributes.key?(:'text_value')
90
84
  self.text_value = attributes[:'text_value']
91
85
  end
92
86
 
87
+ if attributes.key?(:'attestation_value')
88
+ self.attestation_value = attributes[:'attestation_value']
89
+ end
90
+
91
+ if attributes.key?(:'radio_value')
92
+ self.radio_value = attributes[:'radio_value']
93
+ end
94
+
93
95
  if attributes.key?(:'date_value')
94
96
  self.date_value = attributes[:'date_value']
95
97
  end
@@ -106,10 +108,6 @@ module Repull
106
108
  def list_invalid_properties
107
109
  warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
108
110
  invalid_properties = Array.new
109
- if @question_key.nil?
110
- invalid_properties.push('invalid value for "question_key", question_key cannot be nil.')
111
- end
112
-
113
111
  invalid_properties
114
112
  end
115
113
 
@@ -117,27 +115,17 @@ module Repull
117
115
  # @return true if the model is valid
118
116
  def valid?
119
117
  warn '[DEPRECATED] the `valid?` method is obsolete'
120
- return false if @question_key.nil?
121
118
  true
122
119
  end
123
120
 
124
- # Custom attribute writer method with validation
125
- # @param [Object] question_key Value to be assigned
126
- def question_key=(question_key)
127
- if question_key.nil?
128
- fail ArgumentError, 'question_key cannot be nil'
129
- end
130
-
131
- @question_key = question_key
132
- end
133
-
134
121
  # Checks equality by comparing each attribute.
135
122
  # @param [Object] Object to be compared
136
123
  def ==(o)
137
124
  return true if self.equal?(o)
138
125
  self.class == o.class &&
139
- question_key == o.question_key &&
140
126
  text_value == o.text_value &&
127
+ attestation_value == o.attestation_value &&
128
+ radio_value == o.radio_value &&
141
129
  date_value == o.date_value &&
142
130
  selected_options_value == o.selected_options_value
143
131
  end
@@ -151,7 +139,7 @@ module Repull
151
139
  # Calculates hash code according to all attributes.
152
140
  # @return [Integer] Hash code
153
141
  def hash
154
- [question_key, text_value, date_value, selected_options_value].hash
142
+ [text_value, attestation_value, radio_value, date_value, selected_options_value].hash
155
143
  end
156
144
 
157
145
  # Builds the object from hash
@@ -0,0 +1,147 @@
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
+ Contact: ivan@vanio.ai
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 CheckMigrationCutover200Response < ApiModelBase
18
+ attr_accessor :data
19
+
20
+ # Attribute mapping from ruby-style variable name to JSON key.
21
+ def self.attribute_map
22
+ {
23
+ :'data' => :'data'
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
+ :'data' => :'MigrationCutoverCheck'
41
+ }
42
+ end
43
+
44
+ # List of attributes with nullable: true
45
+ def self.openapi_nullable
46
+ Set.new([
47
+ ])
48
+ end
49
+
50
+ # Initializes the object
51
+ # @param [Hash] attributes Model attributes in the form of hash
52
+ def initialize(attributes = {})
53
+ if (!attributes.is_a?(Hash))
54
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CheckMigrationCutover200Response` initialize method"
55
+ end
56
+
57
+ # check to see if the attribute exists and convert string to symbol for hash key
58
+ acceptable_attribute_map = self.class.acceptable_attribute_map
59
+ attributes = attributes.each_with_object({}) { |(k, v), h|
60
+ if (!acceptable_attribute_map.key?(k.to_sym))
61
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CheckMigrationCutover200Response`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
62
+ end
63
+ h[k.to_sym] = v
64
+ }
65
+
66
+ if attributes.key?(:'data')
67
+ self.data = attributes[:'data']
68
+ end
69
+ end
70
+
71
+ # Show invalid properties with the reasons. Usually used together with valid?
72
+ # @return Array for valid properties with the reasons
73
+ def list_invalid_properties
74
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
75
+ invalid_properties = Array.new
76
+ invalid_properties
77
+ end
78
+
79
+ # Check to see if the all the properties in the model are valid
80
+ # @return true if the model is valid
81
+ def valid?
82
+ warn '[DEPRECATED] the `valid?` method is obsolete'
83
+ true
84
+ end
85
+
86
+ # Checks equality by comparing each attribute.
87
+ # @param [Object] Object to be compared
88
+ def ==(o)
89
+ return true if self.equal?(o)
90
+ self.class == o.class &&
91
+ data == o.data
92
+ end
93
+
94
+ # @see the `==` method
95
+ # @param [Object] Object to be compared
96
+ def eql?(o)
97
+ self == o
98
+ end
99
+
100
+ # Calculates hash code according to all attributes.
101
+ # @return [Integer] Hash code
102
+ def hash
103
+ [data].hash
104
+ end
105
+
106
+ # Builds the object from hash
107
+ # @param [Hash] attributes Model attributes in the form of hash
108
+ # @return [Object] Returns the model itself
109
+ def self.build_from_hash(attributes)
110
+ return nil unless attributes.is_a?(Hash)
111
+ attributes = attributes.transform_keys(&:to_sym)
112
+ transformed_hash = {}
113
+ openapi_types.each_pair do |key, type|
114
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
115
+ transformed_hash["#{key}"] = nil
116
+ elsif type =~ /\AArray<(.*)>/i
117
+ # check to ensure the input is an array given that the attribute
118
+ # is documented as an array but the input is not
119
+ if attributes[attribute_map[key]].is_a?(Array)
120
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
121
+ end
122
+ elsif !attributes[attribute_map[key]].nil?
123
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
124
+ end
125
+ end
126
+ new(transformed_hash)
127
+ end
128
+
129
+ # Returns the object in the form of hash
130
+ # @return [Hash] Returns the object in the form of hash
131
+ def to_hash
132
+ hash = {}
133
+ self.class.attribute_map.each_pair do |attr, param|
134
+ value = self.send(attr)
135
+ if value.nil?
136
+ is_nullable = self.class.openapi_nullable.include?(attr)
137
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
138
+ end
139
+
140
+ hash[param] = _to_hash(value)
141
+ end
142
+ hash
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,175 @@
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
+ Contact: ivan@vanio.ai
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 CheckMigrationCutoverRequest < ApiModelBase
18
+ attr_accessor :reservations
19
+
20
+ # Attribute mapping from ruby-style variable name to JSON key.
21
+ def self.attribute_map
22
+ {
23
+ :'reservations' => :'reservations'
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
+ :'reservations' => :'Array<CheckMigrationCutoverRequestReservationsInner>'
41
+ }
42
+ end
43
+
44
+ # List of attributes with nullable: true
45
+ def self.openapi_nullable
46
+ Set.new([
47
+ ])
48
+ end
49
+
50
+ # Initializes the object
51
+ # @param [Hash] attributes Model attributes in the form of hash
52
+ def initialize(attributes = {})
53
+ if (!attributes.is_a?(Hash))
54
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CheckMigrationCutoverRequest` initialize method"
55
+ end
56
+
57
+ # check to see if the attribute exists and convert string to symbol for hash key
58
+ acceptable_attribute_map = self.class.acceptable_attribute_map
59
+ attributes = attributes.each_with_object({}) { |(k, v), h|
60
+ if (!acceptable_attribute_map.key?(k.to_sym))
61
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CheckMigrationCutoverRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
62
+ end
63
+ h[k.to_sym] = v
64
+ }
65
+
66
+ if attributes.key?(:'reservations')
67
+ if (value = attributes[:'reservations']).is_a?(Array)
68
+ self.reservations = value
69
+ end
70
+ else
71
+ self.reservations = nil
72
+ end
73
+ end
74
+
75
+ # Show invalid properties with the reasons. Usually used together with valid?
76
+ # @return Array for valid properties with the reasons
77
+ def list_invalid_properties
78
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
79
+ invalid_properties = Array.new
80
+ if @reservations.nil?
81
+ invalid_properties.push('invalid value for "reservations", reservations cannot be nil.')
82
+ end
83
+
84
+ if @reservations.length > 10000
85
+ invalid_properties.push('invalid value for "reservations", number of items must be less than or equal to 10000.')
86
+ end
87
+
88
+ invalid_properties
89
+ end
90
+
91
+ # Check to see if the all the properties in the model are valid
92
+ # @return true if the model is valid
93
+ def valid?
94
+ warn '[DEPRECATED] the `valid?` method is obsolete'
95
+ return false if @reservations.nil?
96
+ return false if @reservations.length > 10000
97
+ true
98
+ end
99
+
100
+ # Custom attribute writer method with validation
101
+ # @param [Object] reservations Value to be assigned
102
+ def reservations=(reservations)
103
+ if reservations.nil?
104
+ fail ArgumentError, 'reservations cannot be nil'
105
+ end
106
+
107
+ if reservations.length > 10000
108
+ fail ArgumentError, 'invalid value for "reservations", number of items must be less than or equal to 10000.'
109
+ end
110
+
111
+ @reservations = reservations
112
+ end
113
+
114
+ # Checks equality by comparing each attribute.
115
+ # @param [Object] Object to be compared
116
+ def ==(o)
117
+ return true if self.equal?(o)
118
+ self.class == o.class &&
119
+ reservations == o.reservations
120
+ end
121
+
122
+ # @see the `==` method
123
+ # @param [Object] Object to be compared
124
+ def eql?(o)
125
+ self == o
126
+ end
127
+
128
+ # Calculates hash code according to all attributes.
129
+ # @return [Integer] Hash code
130
+ def hash
131
+ [reservations].hash
132
+ end
133
+
134
+ # Builds the object from hash
135
+ # @param [Hash] attributes Model attributes in the form of hash
136
+ # @return [Object] Returns the model itself
137
+ def self.build_from_hash(attributes)
138
+ return nil unless attributes.is_a?(Hash)
139
+ attributes = attributes.transform_keys(&:to_sym)
140
+ transformed_hash = {}
141
+ openapi_types.each_pair do |key, type|
142
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
143
+ transformed_hash["#{key}"] = nil
144
+ elsif type =~ /\AArray<(.*)>/i
145
+ # check to ensure the input is an array given that the attribute
146
+ # is documented as an array but the input is not
147
+ if attributes[attribute_map[key]].is_a?(Array)
148
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
149
+ end
150
+ elsif !attributes[attribute_map[key]].nil?
151
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
152
+ end
153
+ end
154
+ new(transformed_hash)
155
+ end
156
+
157
+ # Returns the object in the form of hash
158
+ # @return [Hash] Returns the object in the form of hash
159
+ def to_hash
160
+ hash = {}
161
+ self.class.attribute_map.each_pair do |attr, param|
162
+ value = self.send(attr)
163
+ if value.nil?
164
+ is_nullable = self.class.openapi_nullable.include?(attr)
165
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
166
+ end
167
+
168
+ hash[param] = _to_hash(value)
169
+ end
170
+ hash
171
+ end
172
+
173
+ end
174
+
175
+ end