repull 0.2.0

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 (222) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/LICENSE +21 -0
  4. data/README.md +121 -0
  5. data/Rakefile +10 -0
  6. data/examples/connect_airbnb.rb +33 -0
  7. data/examples/quickstart.rb +22 -0
  8. data/lib/repull/api/ai_api.rb +86 -0
  9. data/lib/repull/api/airbnb_api.rb +1155 -0
  10. data/lib/repull/api/atlas_api.rb +194 -0
  11. data/lib/repull/api/availability_api.rb +167 -0
  12. data/lib/repull/api/billing_api.rb +139 -0
  13. data/lib/repull/api/booking_com_api.rb +617 -0
  14. data/lib/repull/api/connect_api.rb +672 -0
  15. data/lib/repull/api/conversations_api.rb +263 -0
  16. data/lib/repull/api/guests_api.rb +171 -0
  17. data/lib/repull/api/listings_api.rb +509 -0
  18. data/lib/repull/api/markets_api.rb +320 -0
  19. data/lib/repull/api/plumguide_api.rb +299 -0
  20. data/lib/repull/api/pricing_api.rb +453 -0
  21. data/lib/repull/api/properties_api.rb +166 -0
  22. data/lib/repull/api/reservations_api.rb +387 -0
  23. data/lib/repull/api/reviews_api.rb +208 -0
  24. data/lib/repull/api/schema_api.rb +347 -0
  25. data/lib/repull/api/system_api.rb +79 -0
  26. data/lib/repull/api/vrbo_api.rb +262 -0
  27. data/lib/repull/api/webhooks_api.rb +867 -0
  28. data/lib/repull/api_client.rb +397 -0
  29. data/lib/repull/api_error.rb +58 -0
  30. data/lib/repull/api_model_base.rb +88 -0
  31. data/lib/repull/configuration.rb +313 -0
  32. data/lib/repull/models/ai_operation.rb +158 -0
  33. data/lib/repull/models/airbnb_listing.rb +229 -0
  34. data/lib/repull/models/airbnb_listing_list_response.rb +158 -0
  35. data/lib/repull/models/airbnb_reservation.rb +224 -0
  36. data/lib/repull/models/airbnb_reservation_list_response.rb +158 -0
  37. data/lib/repull/models/airbnb_review.rb +222 -0
  38. data/lib/repull/models/airbnb_review_list_response.rb +158 -0
  39. data/lib/repull/models/airbnb_thread.rb +188 -0
  40. data/lib/repull/models/airbnb_thread_list_response.rb +158 -0
  41. data/lib/repull/models/booking_connect_listing_option.rb +201 -0
  42. data/lib/repull/models/booking_connect_room.rb +251 -0
  43. data/lib/repull/models/booking_connect_rooms_response.rb +274 -0
  44. data/lib/repull/models/booking_conversation.rb +178 -0
  45. data/lib/repull/models/booking_conversation_list_response.rb +158 -0
  46. data/lib/repull/models/booking_pricing_rate_update.rb +310 -0
  47. data/lib/repull/models/booking_pricing_rate_update_date_range.rb +190 -0
  48. data/lib/repull/models/booking_pricing_rate_update_restrictions.rb +179 -0
  49. data/lib/repull/models/booking_pricing_response.rb +157 -0
  50. data/lib/repull/models/booking_pricing_update_request.rb +176 -0
  51. data/lib/repull/models/booking_pricing_update_response.rb +199 -0
  52. data/lib/repull/models/booking_property.rb +188 -0
  53. data/lib/repull/models/booking_property_list_response.rb +158 -0
  54. data/lib/repull/models/booking_room_mapping.rb +177 -0
  55. data/lib/repull/models/booking_verify_hotel_request.rb +193 -0
  56. data/lib/repull/models/booking_verify_hotel_response.rb +285 -0
  57. data/lib/repull/models/bulk_pricing_failure.rb +177 -0
  58. data/lib/repull/models/bulk_pricing_item.rb +202 -0
  59. data/lib/repull/models/bulk_pricing_request.rb +212 -0
  60. data/lib/repull/models/bulk_pricing_response.rb +170 -0
  61. data/lib/repull/models/calendar_day.rb +174 -0
  62. data/lib/repull/models/calendar_response.rb +149 -0
  63. data/lib/repull/models/connect_host.rb +194 -0
  64. data/lib/repull/models/connect_provider.rb +365 -0
  65. data/lib/repull/models/connect_provider_list_response.rb +149 -0
  66. data/lib/repull/models/connect_session.rb +228 -0
  67. data/lib/repull/models/connect_status.rb +207 -0
  68. data/lib/repull/models/connection.rb +195 -0
  69. data/lib/repull/models/connection_list_response.rb +158 -0
  70. data/lib/repull/models/conversation.rb +257 -0
  71. data/lib/repull/models/conversation_detail.rb +284 -0
  72. data/lib/repull/models/conversation_guest.rb +178 -0
  73. data/lib/repull/models/conversation_guest_contact.rb +174 -0
  74. data/lib/repull/models/conversation_host.rb +186 -0
  75. data/lib/repull/models/conversation_list_response.rb +159 -0
  76. data/lib/repull/models/conversation_message_attachment.rb +174 -0
  77. data/lib/repull/models/create_ai_operation200_response.rb +156 -0
  78. data/lib/repull/models/create_billing_checkout_request.rb +181 -0
  79. data/lib/repull/models/create_connect_session_request.rb +189 -0
  80. data/lib/repull/models/create_connection_request.rb +225 -0
  81. data/lib/repull/models/create_reservation_request.rb +313 -0
  82. data/lib/repull/models/create_webhook_request.rb +211 -0
  83. data/lib/repull/models/custom_schema.rb +303 -0
  84. data/lib/repull/models/custom_schema_create.rb +253 -0
  85. data/lib/repull/models/custom_schema_create_response.rb +300 -0
  86. data/lib/repull/models/custom_schema_delete_response.rb +164 -0
  87. data/lib/repull/models/custom_schema_list_response.rb +159 -0
  88. data/lib/repull/models/custom_schema_summary.rb +300 -0
  89. data/lib/repull/models/custom_schema_update.rb +199 -0
  90. data/lib/repull/models/error.rb +165 -0
  91. data/lib/repull/models/error_error.rb +357 -0
  92. data/lib/repull/models/error_error_support.rb +167 -0
  93. data/lib/repull/models/get_health200_response.rb +156 -0
  94. data/lib/repull/models/guest.rb +271 -0
  95. data/lib/repull/models/guest_contact.rb +186 -0
  96. data/lib/repull/models/guest_flag.rb +179 -0
  97. data/lib/repull/models/guest_list_response.rb +159 -0
  98. data/lib/repull/models/guest_note.rb +187 -0
  99. data/lib/repull/models/guest_profile.rb +337 -0
  100. data/lib/repull/models/guest_reservations_summary.rb +175 -0
  101. data/lib/repull/models/listing.rb +216 -0
  102. data/lib/repull/models/listing_address.rb +158 -0
  103. data/lib/repull/models/listing_channel.rb +176 -0
  104. data/lib/repull/models/listing_comp.rb +272 -0
  105. data/lib/repull/models/listing_comp_nightly.rb +166 -0
  106. data/lib/repull/models/listing_comp_ratings.rb +157 -0
  107. data/lib/repull/models/listing_comps_response.rb +197 -0
  108. data/lib/repull/models/listing_content.rb +249 -0
  109. data/lib/repull/models/listing_create_request.rb +364 -0
  110. data/lib/repull/models/listing_create_response.rb +148 -0
  111. data/lib/repull/models/listing_generate_content_request.rb +192 -0
  112. data/lib/repull/models/listing_generate_content_response.rb +165 -0
  113. data/lib/repull/models/listing_list_response.rb +158 -0
  114. data/lib/repull/models/listing_pricing_apply_request.rb +193 -0
  115. data/lib/repull/models/listing_pricing_apply_response.rb +169 -0
  116. data/lib/repull/models/listing_pricing_history_entry.rb +212 -0
  117. data/lib/repull/models/listing_pricing_history_response.rb +159 -0
  118. data/lib/repull/models/listing_pricing_recommendation.rb +269 -0
  119. data/lib/repull/models/listing_pricing_response.rb +187 -0
  120. data/lib/repull/models/listing_pricing_response_comp_summary.rb +178 -0
  121. data/lib/repull/models/listing_pricing_response_date_range.rb +156 -0
  122. data/lib/repull/models/listing_pricing_response_listing.rb +191 -0
  123. data/lib/repull/models/listing_pricing_strategy.rb +317 -0
  124. data/lib/repull/models/listing_pricing_strategy_input.rb +264 -0
  125. data/lib/repull/models/listing_publish_airbnb_request.rb +171 -0
  126. data/lib/repull/models/listing_publish_response.rb +166 -0
  127. data/lib/repull/models/listing_publish_status_channel.rb +197 -0
  128. data/lib/repull/models/listing_publish_status_response.rb +158 -0
  129. data/lib/repull/models/listing_quality_tier.rb +175 -0
  130. data/lib/repull/models/listing_segment.rb +229 -0
  131. data/lib/repull/models/listing_segment_recommendation.rb +170 -0
  132. data/lib/repull/models/listing_segments_response.rb +230 -0
  133. data/lib/repull/models/listing_segments_response_scope.rb +159 -0
  134. data/lib/repull/models/map_connect_booking_rooms_request.rb +202 -0
  135. data/lib/repull/models/map_connect_booking_rooms_response.rb +243 -0
  136. data/lib/repull/models/market_browse_category.rb +158 -0
  137. data/lib/repull/models/market_browse_entry.rb +197 -0
  138. data/lib/repull/models/market_browse_featured.rb +188 -0
  139. data/lib/repull/models/market_browse_response.rb +158 -0
  140. data/lib/repull/models/market_calendar_day.rb +257 -0
  141. data/lib/repull/models/market_calendar_day_events_inner.rb +177 -0
  142. data/lib/repull/models/market_calendar_response.rb +178 -0
  143. data/lib/repull/models/market_detail_response.rb +269 -0
  144. data/lib/repull/models/market_detail_response_price_distribution_inner.rb +186 -0
  145. data/lib/repull/models/market_detail_response_property_type_mix_inner.rb +166 -0
  146. data/lib/repull/models/market_detail_response_supply_trend_inner.rb +156 -0
  147. data/lib/repull/models/market_detail_response_top_comps.rb +187 -0
  148. data/lib/repull/models/market_event.rb +257 -0
  149. data/lib/repull/models/market_my_listing.rb +242 -0
  150. data/lib/repull/models/market_summary.rb +259 -0
  151. data/lib/repull/models/market_top_comp.rb +275 -0
  152. data/lib/repull/models/markets_overview_response.rb +219 -0
  153. data/lib/repull/models/markets_overview_response_browse.rb +173 -0
  154. data/lib/repull/models/markets_overview_response_subscriptions.rb +157 -0
  155. data/lib/repull/models/markets_overview_response_totals.rb +165 -0
  156. data/lib/repull/models/message.rb +289 -0
  157. data/lib/repull/models/message_list_response.rb +159 -0
  158. data/lib/repull/models/pagination.rb +188 -0
  159. data/lib/repull/models/plumguide_listing.rb +167 -0
  160. data/lib/repull/models/plumguide_listing_list_response.rb +158 -0
  161. data/lib/repull/models/property.rb +271 -0
  162. data/lib/repull/models/property_list_response.rb +158 -0
  163. data/lib/repull/models/reservation.rb +457 -0
  164. data/lib/repull/models/reservation_list_response.rb +159 -0
  165. data/lib/repull/models/review.rb +357 -0
  166. data/lib/repull/models/review_category.rb +169 -0
  167. data/lib/repull/models/review_list_response.rb +159 -0
  168. data/lib/repull/models/review_response.rb +158 -0
  169. data/lib/repull/models/rotate_webhook_secret200_response.rb +165 -0
  170. data/lib/repull/models/select_connect_provider_request.rb +165 -0
  171. data/lib/repull/models/select_provider_response.rb +176 -0
  172. data/lib/repull/models/test_webhook_request.rb +165 -0
  173. data/lib/repull/models/update_availability_request.rb +149 -0
  174. data/lib/repull/models/update_listing_pricing_strategy200_response.rb +147 -0
  175. data/lib/repull/models/update_reservation_request.rb +174 -0
  176. data/lib/repull/models/update_webhook_request.rb +211 -0
  177. data/lib/repull/models/vrbo_listing.rb +167 -0
  178. data/lib/repull/models/vrbo_listing_list_response.rb +158 -0
  179. data/lib/repull/models/vrbo_reservation.rb +185 -0
  180. data/lib/repull/models/vrbo_reservation_list_response.rb +158 -0
  181. data/lib/repull/models/webhook_delivery.rb +243 -0
  182. data/lib/repull/models/webhook_delivery_detail.rb +261 -0
  183. data/lib/repull/models/webhook_delivery_list_response.rb +158 -0
  184. data/lib/repull/models/webhook_event_catalog.rb +149 -0
  185. data/lib/repull/models/webhook_event_catalog_domains_inner.rb +167 -0
  186. data/lib/repull/models/webhook_event_catalog_domains_inner_events_inner.rb +183 -0
  187. data/lib/repull/models/webhook_list_response.rb +158 -0
  188. data/lib/repull/models/webhook_subscription.rb +294 -0
  189. data/lib/repull/version.rb +15 -0
  190. data/lib/repull.rb +217 -0
  191. data/openapi/v1.json +8923 -0
  192. data/repull.gemspec +43 -0
  193. data/scripts/regen.sh +55 -0
  194. data/spec/api/ai_api_spec.rb +47 -0
  195. data/spec/api/airbnb_api_spec.rb +238 -0
  196. data/spec/api/availability_api_spec.rb +62 -0
  197. data/spec/api/billing_api_spec.rb +57 -0
  198. data/spec/api/booking_com_api_spec.rb +115 -0
  199. data/spec/api/connect_api_spec.rb +82 -0
  200. data/spec/api/conversations_api_spec.rb +68 -0
  201. data/spec/api/guests_api_spec.rb +59 -0
  202. data/spec/api/plumguide_api_spec.rb +85 -0
  203. data/spec/api/properties_api_spec.rb +60 -0
  204. data/spec/api/reservations_api_spec.rb +97 -0
  205. data/spec/api/system_api_spec.rb +45 -0
  206. data/spec/api/vrbo_api_spec.rb +55 -0
  207. data/spec/api/webhooks_api_spec.rb +69 -0
  208. data/spec/models/ai_operation_spec.rb +46 -0
  209. data/spec/models/calendar_day_spec.rb +54 -0
  210. data/spec/models/connect_host_spec.rb +60 -0
  211. data/spec/models/connect_status_spec.rb +76 -0
  212. data/spec/models/connection_spec.rb +70 -0
  213. data/spec/models/conversation_spec.rb +66 -0
  214. data/spec/models/error_error_spec.rb +54 -0
  215. data/spec/models/error_spec.rb +36 -0
  216. data/spec/models/guest_spec.rb +72 -0
  217. data/spec/models/message_spec.rb +70 -0
  218. data/spec/models/property_spec.rb +114 -0
  219. data/spec/models/reservation_spec.rb +128 -0
  220. data/spec/models/webhook_subscription_spec.rb +60 -0
  221. data/spec/spec_helper.rb +111 -0
  222. metadata +337 -0
@@ -0,0 +1,194 @@
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_test_YOUR_API_KEY ``` Sandbox keys start with `sk_test_`, production with `sk_live_`. ## 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`, `pro`, `enterprise`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resets_at` fields.
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 'cgi'
14
+
15
+ module Repull
16
+ class AtlasApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Atlas DNA segment intelligence for a listing
23
+ # Aggregates Atlas DNA segment signal (quality tier, design style, bedrooms) across the listing's geographic neighborhood (default: 5km radius) or the whole city, so consumers can answer: - What segments dominate my market? - Which segment does my listing match best? - What's the ADR uplift for moving up a tier? DNA coverage is still ramping — segments are scored asynchronously. Cities and radii without scored comps return `totalCompsAnalyzed: 0` plus a `low_dna_coverage` recommendation rather than fabricated data.
24
+ # @param id [Integer]
25
+ # @param [Hash] opts the optional parameters
26
+ # @option opts [String] :level `comp_set` (default) restricts to a `radius_km` bbox. `market` aggregates across the whole city. (default to 'comp_set')
27
+ # @option opts [Float] :radius_km Only used when `level=comp_set`. (default to 5)
28
+ # @return [ListingSegmentsResponse]
29
+ def get_listing_segments(id, opts = {})
30
+ data, _status_code, _headers = get_listing_segments_with_http_info(id, opts)
31
+ data
32
+ end
33
+
34
+ # Atlas DNA segment intelligence for a listing
35
+ # Aggregates Atlas DNA segment signal (quality tier, design style, bedrooms) across the listing's geographic neighborhood (default: 5km radius) or the whole city, so consumers can answer: - What segments dominate my market? - Which segment does my listing match best? - What's the ADR uplift for moving up a tier? DNA coverage is still ramping — segments are scored asynchronously. Cities and radii without scored comps return `totalCompsAnalyzed: 0` plus a `low_dna_coverage` recommendation rather than fabricated data.
36
+ # @param id [Integer]
37
+ # @param [Hash] opts the optional parameters
38
+ # @option opts [String] :level `comp_set` (default) restricts to a `radius_km` bbox. `market` aggregates across the whole city. (default to 'comp_set')
39
+ # @option opts [Float] :radius_km Only used when `level=comp_set`. (default to 5)
40
+ # @return [Array<(ListingSegmentsResponse, Integer, Hash)>] ListingSegmentsResponse data, response status code and response headers
41
+ def get_listing_segments_with_http_info(id, opts = {})
42
+ if @api_client.config.debugging
43
+ @api_client.config.logger.debug 'Calling API: AtlasApi.get_listing_segments ...'
44
+ end
45
+ # verify the required parameter 'id' is set
46
+ if @api_client.config.client_side_validation && id.nil?
47
+ fail ArgumentError, "Missing the required parameter 'id' when calling AtlasApi.get_listing_segments"
48
+ end
49
+ allowable_values = ["comp_set", "market"]
50
+ if @api_client.config.client_side_validation && opts[:'level'] && !allowable_values.include?(opts[:'level'])
51
+ fail ArgumentError, "invalid value for \"level\", must be one of #{allowable_values}"
52
+ end
53
+ if @api_client.config.client_side_validation && !opts[:'radius_km'].nil? && opts[:'radius_km'] > 50
54
+ fail ArgumentError, 'invalid value for "opts[:"radius_km"]" when calling AtlasApi.get_listing_segments, must be smaller than or equal to 50.'
55
+ end
56
+
57
+ if @api_client.config.client_side_validation && !opts[:'radius_km'].nil? && opts[:'radius_km'] < 0.5
58
+ fail ArgumentError, 'invalid value for "opts[:"radius_km"]" when calling AtlasApi.get_listing_segments, must be greater than or equal to 0.5.'
59
+ end
60
+
61
+ # resource path
62
+ local_var_path = '/v1/listings/{id}/segments'.sub('{id}', CGI.escape(id.to_s))
63
+
64
+ # query parameters
65
+ query_params = opts[:query_params] || {}
66
+ query_params[:'level'] = opts[:'level'] if !opts[:'level'].nil?
67
+ query_params[:'radius_km'] = opts[:'radius_km'] if !opts[:'radius_km'].nil?
68
+
69
+ # header parameters
70
+ header_params = opts[:header_params] || {}
71
+ # HTTP header 'Accept' (if needed)
72
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
73
+
74
+ # form parameters
75
+ form_params = opts[:form_params] || {}
76
+
77
+ # http body (model)
78
+ post_body = opts[:debug_body]
79
+
80
+ # return_type
81
+ return_type = opts[:debug_return_type] || 'ListingSegmentsResponse'
82
+
83
+ # auth_names
84
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
85
+
86
+ new_options = opts.merge(
87
+ :operation => :"AtlasApi.get_listing_segments",
88
+ :header_params => header_params,
89
+ :query_params => query_params,
90
+ :form_params => form_params,
91
+ :body => post_body,
92
+ :auth_names => auth_names,
93
+ :return_type => return_type
94
+ )
95
+
96
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
97
+ if @api_client.config.debugging
98
+ @api_client.config.logger.debug "API called: AtlasApi#get_listing_segments\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
99
+ end
100
+ return data, status_code, headers
101
+ end
102
+
103
+ # Comp set for a listing (with daily nightly pricing)
104
+ # Returns the actual comp set for a listing — the underlying competitor listings (with daily nightly pricing), not just the aggregated `compSummary` from `/pricing`. Each comp comes back with distance, bedrooms, ratings, lat/lng, platform link, and a per-day rate/availability series for the requested window. Powered by Atlas. Comps with no coordinates are excluded — there's no way to rank them by distance. Listings without coordinates return `data: []` and a `warning` field.
105
+ # @param id [Integer]
106
+ # @param [Hash] opts the optional parameters
107
+ # @option opts [Float] :radius_km Bbox + haversine on lat/lng. Default 5, max 50. (default to 5)
108
+ # @option opts [Integer] :limit Closest-first. Max 100. (default to 20)
109
+ # @option opts [Date] :start_date Defaults to today.
110
+ # @option opts [Date] :end_date Defaults to today + 30 days.
111
+ # @return [ListingCompsResponse]
112
+ def list_listing_comps(id, opts = {})
113
+ data, _status_code, _headers = list_listing_comps_with_http_info(id, opts)
114
+ data
115
+ end
116
+
117
+ # Comp set for a listing (with daily nightly pricing)
118
+ # Returns the actual comp set for a listing — the underlying competitor listings (with daily nightly pricing), not just the aggregated &#x60;compSummary&#x60; from &#x60;/pricing&#x60;. Each comp comes back with distance, bedrooms, ratings, lat/lng, platform link, and a per-day rate/availability series for the requested window. Powered by Atlas. Comps with no coordinates are excluded — there&#39;s no way to rank them by distance. Listings without coordinates return &#x60;data: []&#x60; and a &#x60;warning&#x60; field.
119
+ # @param id [Integer]
120
+ # @param [Hash] opts the optional parameters
121
+ # @option opts [Float] :radius_km Bbox + haversine on lat/lng. Default 5, max 50. (default to 5)
122
+ # @option opts [Integer] :limit Closest-first. Max 100. (default to 20)
123
+ # @option opts [Date] :start_date Defaults to today.
124
+ # @option opts [Date] :end_date Defaults to today + 30 days.
125
+ # @return [Array<(ListingCompsResponse, Integer, Hash)>] ListingCompsResponse data, response status code and response headers
126
+ def list_listing_comps_with_http_info(id, opts = {})
127
+ if @api_client.config.debugging
128
+ @api_client.config.logger.debug 'Calling API: AtlasApi.list_listing_comps ...'
129
+ end
130
+ # verify the required parameter 'id' is set
131
+ if @api_client.config.client_side_validation && id.nil?
132
+ fail ArgumentError, "Missing the required parameter 'id' when calling AtlasApi.list_listing_comps"
133
+ end
134
+ if @api_client.config.client_side_validation && !opts[:'radius_km'].nil? && opts[:'radius_km'] > 50
135
+ fail ArgumentError, 'invalid value for "opts[:"radius_km"]" when calling AtlasApi.list_listing_comps, must be smaller than or equal to 50.'
136
+ end
137
+
138
+ if @api_client.config.client_side_validation && !opts[:'radius_km'].nil? && opts[:'radius_km'] < 0.5
139
+ fail ArgumentError, 'invalid value for "opts[:"radius_km"]" when calling AtlasApi.list_listing_comps, must be greater than or equal to 0.5.'
140
+ end
141
+
142
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
143
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AtlasApi.list_listing_comps, must be smaller than or equal to 100.'
144
+ end
145
+
146
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
147
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AtlasApi.list_listing_comps, must be greater than or equal to 1.'
148
+ end
149
+
150
+ # resource path
151
+ local_var_path = '/v1/listings/{id}/comps'.sub('{id}', CGI.escape(id.to_s))
152
+
153
+ # query parameters
154
+ query_params = opts[:query_params] || {}
155
+ query_params[:'radius_km'] = opts[:'radius_km'] if !opts[:'radius_km'].nil?
156
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
157
+ query_params[:'startDate'] = opts[:'start_date'] if !opts[:'start_date'].nil?
158
+ query_params[:'endDate'] = opts[:'end_date'] if !opts[:'end_date'].nil?
159
+
160
+ # header parameters
161
+ header_params = opts[:header_params] || {}
162
+ # HTTP header 'Accept' (if needed)
163
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
164
+
165
+ # form parameters
166
+ form_params = opts[:form_params] || {}
167
+
168
+ # http body (model)
169
+ post_body = opts[:debug_body]
170
+
171
+ # return_type
172
+ return_type = opts[:debug_return_type] || 'ListingCompsResponse'
173
+
174
+ # auth_names
175
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
176
+
177
+ new_options = opts.merge(
178
+ :operation => :"AtlasApi.list_listing_comps",
179
+ :header_params => header_params,
180
+ :query_params => query_params,
181
+ :form_params => form_params,
182
+ :body => post_body,
183
+ :auth_names => auth_names,
184
+ :return_type => return_type
185
+ )
186
+
187
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
188
+ if @api_client.config.debugging
189
+ @api_client.config.logger.debug "API called: AtlasApi#list_listing_comps\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
190
+ end
191
+ return data, status_code, headers
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,167 @@
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_test_YOUR_API_KEY ``` Sandbox keys start with `sk_test_`, production with `sk_live_`. ## 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`, `pro`, `enterprise`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resets_at` fields.
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 'cgi'
14
+
15
+ module Repull
16
+ class AvailabilityApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Get availability calendar
23
+ # Returns day-by-day availability, pricing, and minimum stay for a property.
24
+ # @param property_id [Integer]
25
+ # @param start_date [Date]
26
+ # @param end_date [Date]
27
+ # @param [Hash] opts the optional parameters
28
+ # @return [CalendarResponse]
29
+ def get_availability(property_id, start_date, end_date, opts = {})
30
+ data, _status_code, _headers = get_availability_with_http_info(property_id, start_date, end_date, opts)
31
+ data
32
+ end
33
+
34
+ # Get availability calendar
35
+ # Returns day-by-day availability, pricing, and minimum stay for a property.
36
+ # @param property_id [Integer]
37
+ # @param start_date [Date]
38
+ # @param end_date [Date]
39
+ # @param [Hash] opts the optional parameters
40
+ # @return [Array<(CalendarResponse, Integer, Hash)>] CalendarResponse data, response status code and response headers
41
+ def get_availability_with_http_info(property_id, start_date, end_date, opts = {})
42
+ if @api_client.config.debugging
43
+ @api_client.config.logger.debug 'Calling API: AvailabilityApi.get_availability ...'
44
+ end
45
+ # verify the required parameter 'property_id' is set
46
+ if @api_client.config.client_side_validation && property_id.nil?
47
+ fail ArgumentError, "Missing the required parameter 'property_id' when calling AvailabilityApi.get_availability"
48
+ end
49
+ # verify the required parameter 'start_date' is set
50
+ if @api_client.config.client_side_validation && start_date.nil?
51
+ fail ArgumentError, "Missing the required parameter 'start_date' when calling AvailabilityApi.get_availability"
52
+ end
53
+ # verify the required parameter 'end_date' is set
54
+ if @api_client.config.client_side_validation && end_date.nil?
55
+ fail ArgumentError, "Missing the required parameter 'end_date' when calling AvailabilityApi.get_availability"
56
+ end
57
+ # resource path
58
+ local_var_path = '/v1/availability/{propertyId}'.sub('{propertyId}', CGI.escape(property_id.to_s))
59
+
60
+ # query parameters
61
+ query_params = opts[:query_params] || {}
62
+ query_params[:'startDate'] = start_date
63
+ query_params[:'endDate'] = end_date
64
+
65
+ # header parameters
66
+ header_params = opts[:header_params] || {}
67
+ # HTTP header 'Accept' (if needed)
68
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
69
+
70
+ # form parameters
71
+ form_params = opts[:form_params] || {}
72
+
73
+ # http body (model)
74
+ post_body = opts[:debug_body]
75
+
76
+ # return_type
77
+ return_type = opts[:debug_return_type] || 'CalendarResponse'
78
+
79
+ # auth_names
80
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
81
+
82
+ new_options = opts.merge(
83
+ :operation => :"AvailabilityApi.get_availability",
84
+ :header_params => header_params,
85
+ :query_params => query_params,
86
+ :form_params => form_params,
87
+ :body => post_body,
88
+ :auth_names => auth_names,
89
+ :return_type => return_type
90
+ )
91
+
92
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
93
+ if @api_client.config.debugging
94
+ @api_client.config.logger.debug "API called: AvailabilityApi#get_availability\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
95
+ end
96
+ return data, status_code, headers
97
+ end
98
+
99
+ # Update availability
100
+ # Update pricing, availability, and minimum stay for specific dates.
101
+ # @param property_id [Integer]
102
+ # @param [Hash] opts the optional parameters
103
+ # @option opts [UpdateAvailabilityRequest] :update_availability_request
104
+ # @return [nil]
105
+ def update_availability(property_id, opts = {})
106
+ update_availability_with_http_info(property_id, opts)
107
+ nil
108
+ end
109
+
110
+ # Update availability
111
+ # Update pricing, availability, and minimum stay for specific dates.
112
+ # @param property_id [Integer]
113
+ # @param [Hash] opts the optional parameters
114
+ # @option opts [UpdateAvailabilityRequest] :update_availability_request
115
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
116
+ def update_availability_with_http_info(property_id, opts = {})
117
+ if @api_client.config.debugging
118
+ @api_client.config.logger.debug 'Calling API: AvailabilityApi.update_availability ...'
119
+ end
120
+ # verify the required parameter 'property_id' is set
121
+ if @api_client.config.client_side_validation && property_id.nil?
122
+ fail ArgumentError, "Missing the required parameter 'property_id' when calling AvailabilityApi.update_availability"
123
+ end
124
+ # resource path
125
+ local_var_path = '/v1/availability/{propertyId}'.sub('{propertyId}', CGI.escape(property_id.to_s))
126
+
127
+ # query parameters
128
+ query_params = opts[:query_params] || {}
129
+
130
+ # header parameters
131
+ header_params = opts[:header_params] || {}
132
+ # HTTP header 'Content-Type'
133
+ content_type = @api_client.select_header_content_type(['application/json'])
134
+ if !content_type.nil?
135
+ header_params['Content-Type'] = content_type
136
+ end
137
+
138
+ # form parameters
139
+ form_params = opts[:form_params] || {}
140
+
141
+ # http body (model)
142
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(opts[:'update_availability_request'])
143
+
144
+ # return_type
145
+ return_type = opts[:debug_return_type]
146
+
147
+ # auth_names
148
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
149
+
150
+ new_options = opts.merge(
151
+ :operation => :"AvailabilityApi.update_availability",
152
+ :header_params => header_params,
153
+ :query_params => query_params,
154
+ :form_params => form_params,
155
+ :body => post_body,
156
+ :auth_names => auth_names,
157
+ :return_type => return_type
158
+ )
159
+
160
+ data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options)
161
+ if @api_client.config.debugging
162
+ @api_client.config.logger.debug "API called: AvailabilityApi#update_availability\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
163
+ end
164
+ return data, status_code, headers
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,139 @@
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_test_YOUR_API_KEY ``` Sandbox keys start with `sk_test_`, production with `sk_live_`. ## 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`, `pro`, `enterprise`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resets_at` fields.
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 'cgi'
14
+
15
+ module Repull
16
+ class BillingApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Create checkout session
23
+ # Redirect user to Stripe checkout.
24
+ # @param [Hash] opts the optional parameters
25
+ # @option opts [CreateBillingCheckoutRequest] :create_billing_checkout_request
26
+ # @return [nil]
27
+ def create_billing_checkout(opts = {})
28
+ create_billing_checkout_with_http_info(opts)
29
+ nil
30
+ end
31
+
32
+ # Create checkout session
33
+ # Redirect user to Stripe checkout.
34
+ # @param [Hash] opts the optional parameters
35
+ # @option opts [CreateBillingCheckoutRequest] :create_billing_checkout_request
36
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
37
+ def create_billing_checkout_with_http_info(opts = {})
38
+ if @api_client.config.debugging
39
+ @api_client.config.logger.debug 'Calling API: BillingApi.create_billing_checkout ...'
40
+ end
41
+ # resource path
42
+ local_var_path = '/v1/billing'
43
+
44
+ # query parameters
45
+ query_params = opts[:query_params] || {}
46
+
47
+ # header parameters
48
+ header_params = opts[:header_params] || {}
49
+ # HTTP header 'Content-Type'
50
+ content_type = @api_client.select_header_content_type(['application/json'])
51
+ if !content_type.nil?
52
+ header_params['Content-Type'] = content_type
53
+ end
54
+
55
+ # form parameters
56
+ form_params = opts[:form_params] || {}
57
+
58
+ # http body (model)
59
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(opts[:'create_billing_checkout_request'])
60
+
61
+ # return_type
62
+ return_type = opts[:debug_return_type]
63
+
64
+ # auth_names
65
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
66
+
67
+ new_options = opts.merge(
68
+ :operation => :"BillingApi.create_billing_checkout",
69
+ :header_params => header_params,
70
+ :query_params => query_params,
71
+ :form_params => form_params,
72
+ :body => post_body,
73
+ :auth_names => auth_names,
74
+ :return_type => return_type
75
+ )
76
+
77
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
78
+ if @api_client.config.debugging
79
+ @api_client.config.logger.debug "API called: BillingApi#create_billing_checkout\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
80
+ end
81
+ return data, status_code, headers
82
+ end
83
+
84
+ # Get plan and usage
85
+ # Fetch the current plan, usage counters, and billing-cycle reset date for this workspace. Use this to surface a \"you have used X / Y\" indicator in your dashboard.
86
+ # @param [Hash] opts the optional parameters
87
+ # @return [nil]
88
+ def get_billing(opts = {})
89
+ get_billing_with_http_info(opts)
90
+ nil
91
+ end
92
+
93
+ # Get plan and usage
94
+ # Fetch the current plan, usage counters, and billing-cycle reset date for this workspace. Use this to surface a \&quot;you have used X / Y\&quot; indicator in your dashboard.
95
+ # @param [Hash] opts the optional parameters
96
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
97
+ def get_billing_with_http_info(opts = {})
98
+ if @api_client.config.debugging
99
+ @api_client.config.logger.debug 'Calling API: BillingApi.get_billing ...'
100
+ end
101
+ # resource path
102
+ local_var_path = '/v1/billing'
103
+
104
+ # query parameters
105
+ query_params = opts[:query_params] || {}
106
+
107
+ # header parameters
108
+ header_params = opts[:header_params] || {}
109
+
110
+ # form parameters
111
+ form_params = opts[:form_params] || {}
112
+
113
+ # http body (model)
114
+ post_body = opts[:debug_body]
115
+
116
+ # return_type
117
+ return_type = opts[:debug_return_type]
118
+
119
+ # auth_names
120
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
121
+
122
+ new_options = opts.merge(
123
+ :operation => :"BillingApi.get_billing",
124
+ :header_params => header_params,
125
+ :query_params => query_params,
126
+ :form_params => form_params,
127
+ :body => post_body,
128
+ :auth_names => auth_names,
129
+ :return_type => return_type
130
+ )
131
+
132
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
133
+ if @api_client.config.debugging
134
+ @api_client.config.logger.debug "API called: BillingApi#get_billing\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
135
+ end
136
+ return data, status_code, headers
137
+ end
138
+ end
139
+ end