repull 0.2.16 → 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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +10 -10
  3. data/lib/repull/api/booking_com_api.rb +86 -2
  4. data/lib/repull/api/conversations_api.rb +8 -8
  5. data/lib/repull/api/health_api.rb +0 -57
  6. data/lib/repull/api/listings_api.rb +182 -12
  7. data/lib/repull/api/migrate_api.rb +552 -0
  8. data/lib/repull/api/reservations_api.rb +6 -6
  9. data/lib/repull/models/airbnb_permits_response.rb +1 -1
  10. data/lib/repull/models/airbnb_permits_write_request.rb +1 -1
  11. data/lib/repull/models/airbnb_permits_write_request_permits_inner.rb +43 -4
  12. data/lib/repull/models/{airbnb_permits_write_request_permits_inner_answers_inner.rb → airbnb_permits_write_request_permits_inner_answers_value.rb} +22 -34
  13. data/lib/repull/models/airbnb_publish_result.rb +41 -2
  14. data/lib/repull/models/booking_property_action_request.rb +176 -0
  15. data/lib/repull/models/booking_property_action_response.rb +281 -0
  16. data/lib/repull/models/booking_publish_result.rb +245 -0
  17. data/lib/repull/models/booking_publish_section_error.rb +220 -0
  18. data/lib/repull/models/booking_setup_request.rb +44 -5
  19. data/lib/repull/models/booking_setup_request_legal_entity.rb +235 -0
  20. data/lib/repull/models/channel_market_state_item.rb +291 -0
  21. data/lib/repull/models/check_migration_cutover200_response.rb +147 -0
  22. data/lib/repull/models/check_migration_cutover_request.rb +175 -0
  23. data/lib/repull/models/check_migration_cutover_request_reservations_inner.rb +216 -0
  24. data/lib/repull/models/connect_provider.rb +18 -5
  25. data/lib/repull/models/connect_session.rb +25 -5
  26. data/lib/repull/models/create_connect_session_request.rb +81 -5
  27. data/lib/repull/models/create_connect_session_request_copy.rb +251 -0
  28. data/lib/repull/models/create_connect_session_request_workspace.rb +196 -0
  29. data/lib/repull/models/delete_migration200_response.rb +147 -0
  30. data/lib/repull/models/delete_migration200_response_data.rb +165 -0
  31. data/lib/repull/models/error_error.rb +11 -1
  32. data/lib/repull/models/get_migration200_response.rb +147 -0
  33. data/lib/repull/models/get_migration_channel_map200_response.rb +147 -0
  34. data/lib/repull/models/get_migration_report200_response.rb +147 -0
  35. data/lib/repull/models/inquiry_updated_payload.rb +1 -1
  36. data/lib/repull/models/list_inquiries200_response_data_inner.rb +1 -1
  37. data/lib/repull/models/list_migrations200_response.rb +158 -0
  38. data/lib/repull/models/listing_address_readiness.rb +171 -0
  39. data/lib/repull/models/listing_content_update_request.rb +10 -1
  40. data/lib/repull/models/listing_content_update_request_address.rb +40 -3
  41. data/lib/repull/models/listing_content_update_request_pricing.rb +235 -0
  42. data/lib/repull/models/listing_content_update_response.rb +1 -1
  43. data/lib/repull/models/listing_create_request.rb +71 -2
  44. data/lib/repull/models/listing_create_response.rb +14 -4
  45. data/lib/repull/models/listing_market_state_request.rb +149 -0
  46. data/lib/repull/models/listing_market_state_response.rb +221 -0
  47. data/lib/repull/models/listing_publish_booking_request.rb +149 -0
  48. data/lib/repull/models/{listing_publish_response.rb → listing_publish_booking_response.rb} +4 -5
  49. data/lib/repull/models/listing_publish_status_channel.rb +32 -1
  50. data/lib/repull/models/listing_publish_status_connection.rb +17 -5
  51. data/lib/repull/models/listing_publish_status_response.rb +13 -1
  52. data/lib/repull/models/migration.rb +303 -0
  53. data/lib/repull/models/migration_channel_map.rb +169 -0
  54. data/lib/repull/models/migration_channel_map_listings_inner.rb +208 -0
  55. data/lib/repull/models/migration_channel_map_listings_inner_airbnb.rb +156 -0
  56. data/lib/repull/models/migration_channel_map_listings_inner_booking.rb +156 -0
  57. data/lib/repull/models/migration_channel_map_sources_inner.rb +167 -0
  58. data/lib/repull/models/migration_completed_event.rb +309 -0
  59. data/lib/repull/models/migration_connections_inner.rb +195 -0
  60. data/lib/repull/models/migration_counts.rb +183 -0
  61. data/lib/repull/models/migration_cutover_check.rb +200 -0
  62. data/lib/repull/models/migration_cutover_check_mismatched_inner.rb +156 -0
  63. data/lib/repull/models/migration_failed_event.rb +309 -0
  64. data/lib/repull/models/migration_import_payload.rb +225 -0
  65. data/lib/repull/models/migration_import_payload_results_inner.rb +165 -0
  66. data/lib/repull/models/migration_import_run.rb +202 -0
  67. data/lib/repull/models/migration_import_run_results_inner.rb +165 -0
  68. data/lib/repull/models/migration_report.rb +179 -0
  69. data/lib/repull/models/migration_report_issues_inner.rb +229 -0
  70. data/lib/repull/models/migration_reservation_ref.rb +184 -0
  71. data/lib/repull/models/reservation.rb +12 -2
  72. data/lib/repull/models/reservation_request_updated_payload.rb +1 -1
  73. data/lib/repull/models/run_migration_import202_response.rb +147 -0
  74. data/lib/repull/models/run_migration_import202_response_data.rb +158 -0
  75. data/lib/repull/models/run_migration_import202_response_data_queued_inner.rb +174 -0
  76. data/lib/repull/models/run_migration_import_request.rb +182 -0
  77. data/lib/repull/models/webhook_event.rb +4 -0
  78. data/lib/repull/models/webhook_event_type.rb +3 -1
  79. data/lib/repull/version.rb +1 -1
  80. data/lib/repull.rb +48 -2
  81. data/openapi/v1.json +2398 -433
  82. data/scripts/regen.sh +1 -1
  83. metadata +50 -4
@@ -15,7 +15,7 @@ require 'time'
15
15
 
16
16
  module Repull
17
17
  class AirbnbPermitsResponse < ApiModelBase
18
- # The live permit flows from Airbnb — present only with `?source=live`, `null` otherwise. Each flow names its `regulatory_body`, `regulation_type`, `status`, and the `question_key` / `answer_type` / `options` of every question you have to answer, plus the answers already on file.
18
+ # The live permit flows from Airbnb — present only with `?source=live`, `null` otherwise. Each flow names its `regulatory_body`, `regulation_type`, `status`, its `flows[]` with the `answer_key` / `type` / `choices` of every question you have to answer, plus the answers already on file.
19
19
  attr_accessor :permits
20
20
 
21
21
  # Permits as last mirrored by the sync worker: body, type, status, number. The RESULT of a permit, not the questions.
@@ -14,7 +14,7 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # Answer the regulatory permit questions Airbnb asks for this listing. Read them first with `?source=live` on the GET — Airbnb refuses a `question_key` it did not ask for on this listing.
17
+ # Answer the regulatory permit questions Airbnb asks for this listing, in Airbnb's Listing Permits shape. Read them first with `?source=live` on the GET: each permit lists its `flows[]`, and each flow its `questions[]` with an `answer_key` and a `type`.
18
18
  class AirbnbPermitsWriteRequest < ApiModelBase
19
19
  attr_accessor :permits
20
20
 
@@ -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
@@ -14,17 +14,23 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- # A publish is not one call to Airbnb: it is up to eight independent ones (details, description, amenities, rooms, policies, photos, pricing, checkout_tasks), each of which can fail on its own. A PARTIAL publish is normal — what succeeded stays applied; there is no rollback.
17
+ # A publish is not one call to Airbnb: it is up to eight independent ones (details, description, amenities, rooms, policies, photos, pricing, checkout_tasks), each of which can fail on its own. A PARTIAL publish is normal — what succeeded stays applied; there is no rollback. **Content landing and the listing being live are two different answers.** `published` is about content; `live` is about whether the listing takes bookings. Read both.
18
18
  class AirbnbPublishResult < ApiModelBase
19
19
  # True only when EVERY attempted section reached Airbnb.
20
20
  attr_accessor :published
21
21
 
22
+ # Whether the listing is active and bookable on Airbnb — that is, whether activation was actually performed and succeeded. `published: true` with `live: false` is a real and common outcome: every content section landed, but the listing was never activated, because activation is skipped when instant-booking cannot be confirmed to be off. `warnings` says why. **Absent is not `false`.** The field is omitted entirely when activation was never part of the operation — publishing to an already-mapped Airbnb listing updates content and activates nothing, so there is nothing to report. Only treat the listing as not-live when `live` is present and false.
23
+ attr_accessor :live
24
+
22
25
  # Sections that landed on Airbnb.
23
26
  attr_accessor :sections
24
27
 
25
28
  # Per-section failures. Empty when `published` is true.
26
29
  attr_accessor :errors
27
30
 
31
+ # Steps that failed WITHOUT failing the publish — optional work the push carried on past, each in the push's own words. These used to be swallowed silently, so the only sign of one was a listing that was somehow not quite right afterwards. A publish can be `published: true` and still carry warnings; read them before concluding nothing needs doing.
32
+ attr_accessor :warnings
33
+
28
34
  # Set when the publish never started at all (no connection, address missing, subscription gate).
29
35
  attr_accessor :reason
30
36
 
@@ -35,8 +41,10 @@ module Repull
35
41
  def self.attribute_map
36
42
  {
37
43
  :'published' => :'published',
44
+ :'live' => :'live',
38
45
  :'sections' => :'sections',
39
46
  :'errors' => :'errors',
47
+ :'warnings' => :'warnings',
40
48
  :'reason' => :'reason',
41
49
  :'locked_fields' => :'lockedFields'
42
50
  }
@@ -56,8 +64,10 @@ module Repull
56
64
  def self.openapi_types
57
65
  {
58
66
  :'published' => :'Boolean',
67
+ :'live' => :'Boolean',
59
68
  :'sections' => :'Array<String>',
60
69
  :'errors' => :'Array<PublishSectionError>',
70
+ :'warnings' => :'Array<String>',
61
71
  :'reason' => :'String',
62
72
  :'locked_fields' => :'Array<String>'
63
73
  }
@@ -91,6 +101,10 @@ module Repull
91
101
  self.published = nil
92
102
  end
93
103
 
104
+ if attributes.key?(:'live')
105
+ self.live = attributes[:'live']
106
+ end
107
+
94
108
  if attributes.key?(:'sections')
95
109
  if (value = attributes[:'sections']).is_a?(Array)
96
110
  self.sections = value
@@ -107,6 +121,14 @@ module Repull
107
121
  self.errors = nil
108
122
  end
109
123
 
124
+ if attributes.key?(:'warnings')
125
+ if (value = attributes[:'warnings']).is_a?(Array)
126
+ self.warnings = value
127
+ end
128
+ else
129
+ self.warnings = nil
130
+ end
131
+
110
132
  if attributes.key?(:'reason')
111
133
  self.reason = attributes[:'reason']
112
134
  end
@@ -137,6 +159,10 @@ module Repull
137
159
  invalid_properties.push('invalid value for "errors", errors cannot be nil.')
138
160
  end
139
161
 
162
+ if @warnings.nil?
163
+ invalid_properties.push('invalid value for "warnings", warnings cannot be nil.')
164
+ end
165
+
140
166
  if @locked_fields.nil?
141
167
  invalid_properties.push('invalid value for "locked_fields", locked_fields cannot be nil.')
142
168
  end
@@ -151,6 +177,7 @@ module Repull
151
177
  return false if @published.nil?
152
178
  return false if @sections.nil?
153
179
  return false if @errors.nil?
180
+ return false if @warnings.nil?
154
181
  return false if @locked_fields.nil?
155
182
  true
156
183
  end
@@ -185,6 +212,16 @@ module Repull
185
212
  @errors = errors
186
213
  end
187
214
 
215
+ # Custom attribute writer method with validation
216
+ # @param [Object] warnings Value to be assigned
217
+ def warnings=(warnings)
218
+ if warnings.nil?
219
+ fail ArgumentError, 'warnings cannot be nil'
220
+ end
221
+
222
+ @warnings = warnings
223
+ end
224
+
188
225
  # Custom attribute writer method with validation
189
226
  # @param [Object] locked_fields Value to be assigned
190
227
  def locked_fields=(locked_fields)
@@ -201,8 +238,10 @@ module Repull
201
238
  return true if self.equal?(o)
202
239
  self.class == o.class &&
203
240
  published == o.published &&
241
+ live == o.live &&
204
242
  sections == o.sections &&
205
243
  errors == o.errors &&
244
+ warnings == o.warnings &&
206
245
  reason == o.reason &&
207
246
  locked_fields == o.locked_fields
208
247
  end
@@ -216,7 +255,7 @@ module Repull
216
255
  # Calculates hash code according to all attributes.
217
256
  # @return [Integer] Hash code
218
257
  def hash
219
- [published, sections, errors, reason, locked_fields].hash
258
+ [published, live, sections, errors, warnings, reason, locked_fields].hash
220
259
  end
221
260
 
222
261
  # Builds the object from hash
@@ -0,0 +1,176 @@
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
+ # Take this listing's Booking.com property off sale, or put it back.
18
+ class BookingPropertyActionRequest < ApiModelBase
19
+ # `unlist` closes the room's availability across the whole forward window, so the property stops selling. `relist` re-syncs the real calendar: dates that are genuinely blocked (a reservation, an owner stay) stay blocked, and only the closure `unlist` wrote lifts. They are not mirror images, and that is deliberate.
20
+ attr_accessor :action
21
+
22
+ # Booking.com property to act on, for a listing mapped to more than one. Without it the request is refused with `409 ambiguous_booking_mapping` and nothing is written. `?hotel_id=` means the same thing; the body wins if you send both.
23
+ attr_accessor :hotel_id
24
+
25
+ # Attribute mapping from ruby-style variable name to JSON key.
26
+ def self.attribute_map
27
+ {
28
+ :'action' => :'action',
29
+ :'hotel_id' => :'hotelId'
30
+ }
31
+ end
32
+
33
+ # Returns attribute mapping this model knows about
34
+ def self.acceptable_attribute_map
35
+ attribute_map
36
+ end
37
+
38
+ # Returns all the JSON keys this model knows about
39
+ def self.acceptable_attributes
40
+ acceptable_attribute_map.values
41
+ end
42
+
43
+ # Attribute type mapping.
44
+ def self.openapi_types
45
+ {
46
+ :'action' => :'String',
47
+ :'hotel_id' => :'String'
48
+ }
49
+ end
50
+
51
+ # List of attributes with nullable: true
52
+ def self.openapi_nullable
53
+ Set.new([
54
+ ])
55
+ end
56
+
57
+ # Initializes the object
58
+ # @param [Hash] attributes Model attributes in the form of hash
59
+ def initialize(attributes = {})
60
+ if (!attributes.is_a?(Hash))
61
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::BookingPropertyActionRequest` initialize method"
62
+ end
63
+
64
+ # check to see if the attribute exists and convert string to symbol for hash key
65
+ acceptable_attribute_map = self.class.acceptable_attribute_map
66
+ attributes = attributes.each_with_object({}) { |(k, v), h|
67
+ if (!acceptable_attribute_map.key?(k.to_sym))
68
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::BookingPropertyActionRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
69
+ end
70
+ h[k.to_sym] = v
71
+ }
72
+
73
+ if attributes.key?(:'action')
74
+ self.action = attributes[:'action']
75
+ else
76
+ self.action = nil
77
+ end
78
+
79
+ if attributes.key?(:'hotel_id')
80
+ self.hotel_id = attributes[:'hotel_id']
81
+ end
82
+ end
83
+
84
+ # Show invalid properties with the reasons. Usually used together with valid?
85
+ # @return Array for valid properties with the reasons
86
+ def list_invalid_properties
87
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
88
+ invalid_properties = Array.new
89
+ if @action.nil?
90
+ invalid_properties.push('invalid value for "action", action cannot be nil.')
91
+ end
92
+
93
+ invalid_properties
94
+ end
95
+
96
+ # Check to see if the all the properties in the model are valid
97
+ # @return true if the model is valid
98
+ def valid?
99
+ warn '[DEPRECATED] the `valid?` method is obsolete'
100
+ return false if @action.nil?
101
+ true
102
+ end
103
+
104
+ # Custom attribute writer method with validation
105
+ # @param [Object] action Value to be assigned
106
+ def action=(action)
107
+ if action.nil?
108
+ fail ArgumentError, 'action cannot be nil'
109
+ end
110
+
111
+ @action = action
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
+ action == o.action &&
120
+ hotel_id == o.hotel_id
121
+ end
122
+
123
+ # @see the `==` method
124
+ # @param [Object] Object to be compared
125
+ def eql?(o)
126
+ self == o
127
+ end
128
+
129
+ # Calculates hash code according to all attributes.
130
+ # @return [Integer] Hash code
131
+ def hash
132
+ [action, hotel_id].hash
133
+ end
134
+
135
+ # Builds the object from hash
136
+ # @param [Hash] attributes Model attributes in the form of hash
137
+ # @return [Object] Returns the model itself
138
+ def self.build_from_hash(attributes)
139
+ return nil unless attributes.is_a?(Hash)
140
+ attributes = attributes.transform_keys(&:to_sym)
141
+ transformed_hash = {}
142
+ openapi_types.each_pair do |key, type|
143
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
144
+ transformed_hash["#{key}"] = nil
145
+ elsif type =~ /\AArray<(.*)>/i
146
+ # check to ensure the input is an array given that the attribute
147
+ # is documented as an array but the input is not
148
+ if attributes[attribute_map[key]].is_a?(Array)
149
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
150
+ end
151
+ elsif !attributes[attribute_map[key]].nil?
152
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
153
+ end
154
+ end
155
+ new(transformed_hash)
156
+ end
157
+
158
+ # Returns the object in the form of hash
159
+ # @return [Hash] Returns the object in the form of hash
160
+ def to_hash
161
+ hash = {}
162
+ self.class.attribute_map.each_pair do |attr, param|
163
+ value = self.send(attr)
164
+ if value.nil?
165
+ is_nullable = self.class.openapi_nullable.include?(attr)
166
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
167
+ end
168
+
169
+ hash[param] = _to_hash(value)
170
+ end
171
+ hash
172
+ end
173
+
174
+ end
175
+
176
+ end