repull 0.2.8 → 0.2.10

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +1443 -182
  3. data/lib/repull/api/availability_api.rb +24 -92
  4. data/lib/repull/api/billing_api.rb +186 -15
  5. data/lib/repull/api/booking_com_api.rb +532 -41
  6. data/lib/repull/api/listings_api.rb +74 -0
  7. data/lib/repull/api/plumguide_api.rb +245 -0
  8. data/lib/repull/api/reservations_api.rb +0 -197
  9. data/lib/repull/api/sandbox_api.rb +136 -0
  10. data/lib/repull/api/vrbo_api.rb +0 -126
  11. data/lib/repull/models/acknowledge_booking_reservations_request.rb +176 -0
  12. data/lib/repull/models/{create_studio_project201_response_data.rb → airbnb_alteration.rb} +44 -52
  13. data/lib/repull/models/airbnb_amenity.rb +171 -0
  14. data/lib/repull/models/{get_studio_deployment200_response.rb → airbnb_availability_write_request.rb} +52 -10
  15. data/lib/repull/models/airbnb_calendar_operation.rb +250 -0
  16. data/lib/repull/models/airbnb_listing_action_request.rb +198 -0
  17. data/lib/repull/models/{update_availability_request.rb → airbnb_pricing_write_request.rb} +100 -11
  18. data/lib/repull/models/booking_availability_update.rb +276 -0
  19. data/lib/repull/models/booking_availability_update_request.rb +230 -0
  20. data/lib/repull/models/{generate_studio_completion_request_project_id.rb → booking_availability_update_request_property_id.rb} +2 -2
  21. data/lib/repull/models/booking_availability_update_request_updates_inner.rb +104 -0
  22. data/lib/repull/models/booking_pricing_rate_update.rb +1 -0
  23. data/lib/repull/models/booking_pricing_rate_update_restrictions.rb +65 -6
  24. data/lib/repull/models/booking_setup_request.rb +220 -0
  25. data/lib/repull/models/create_airbnb_alteration_request.rb +195 -0
  26. data/lib/repull/models/{create_studio_deployment201_response_data.rb → create_airbnb_offer_request.rb} +42 -30
  27. data/lib/repull/models/{create_studio_project_request.rb → create_booking_webhook_request.rb} +36 -47
  28. data/lib/repull/models/{generate_studio_completion200_response.rb → delete_airbnb_listing_photo200_response.rb} +10 -10
  29. data/lib/repull/models/{delete_studio_project200_response.rb → get_airbnb_alteration200_response.rb} +50 -7
  30. data/lib/repull/models/{delete_studio_project_file200_response.rb → get_airbnb_listing_quality200_response.rb} +37 -7
  31. data/lib/repull/models/{create_studio_project_generation201_response.rb → get_airbnb_listing_settings200_response.rb} +37 -7
  32. data/lib/repull/models/{delete_studio_deployment200_response.rb → get_airbnb_thread200_response.rb} +50 -7
  33. data/lib/repull/models/{list_studio_deployments200_response.rb → get_usage_logs200_response.rb} +17 -8
  34. data/lib/repull/models/get_usage_logs200_response_data_inner.rb +264 -0
  35. data/lib/repull/models/{create_studio_deployment201_response.rb → get_usage_logs200_response_pagination.rb} +29 -10
  36. data/lib/repull/models/get_usage_summary200_response.rb +232 -0
  37. data/lib/repull/models/get_usage_summary200_response_breakdown_inner.rb +183 -0
  38. data/lib/repull/models/{delete_studio_deployment200_response_data.rb → get_usage_summary200_response_limits.rb} +18 -16
  39. data/lib/repull/models/{delete_studio_project200_response_data.rb → get_usage_summary200_response_remaining.rb} +18 -16
  40. data/lib/repull/models/{create_studio_project_generation201_response_data.rb → get_usage_summary200_response_status_distribution.rb} +31 -22
  41. data/lib/repull/models/{upsert_studio_project_file_request.rb → get_usage_summary200_response_timeline_inner.rb} +28 -28
  42. data/lib/repull/models/{create_studio_project_generation_request.rb → get_usage_summary200_response_totals.rb} +28 -28
  43. data/lib/repull/models/{create_studio_project201_response.rb → get_usage_summary200_response_used.rb} +19 -10
  44. data/lib/repull/models/get_usage_tier200_response.rb +183 -0
  45. data/lib/repull/models/{list_studio_projects200_response.rb → get_usage_tier200_response_limits.rb} +31 -12
  46. data/lib/repull/models/{update_reservation_request.rb → get_usage_tier200_response_remaining.rb} +25 -31
  47. data/lib/repull/models/{get_studio_project200_response.rb → get_usage_tier200_response_used.rb} +28 -10
  48. data/lib/repull/models/list_airbnb_alterations200_response.rb +192 -0
  49. data/lib/repull/models/list_airbnb_listing_amenities200_response.rb +190 -0
  50. data/lib/repull/models/{create_studio_deployment_request.rb → list_airbnb_listing_amenities200_response_data.rb} +23 -28
  51. data/lib/repull/models/{list_studio_project_files200_response.rb → list_airbnb_transactions200_response.rb} +50 -7
  52. data/lib/repull/models/listing_content_update_request.rb +243 -0
  53. data/lib/repull/models/{upsert_studio_project_file200_response.rb → listing_content_update_request_address.rb} +53 -10
  54. data/lib/repull/models/listing_content_update_request_amenities.rb +105 -0
  55. data/lib/repull/models/listing_content_update_request_amenities_one_of_inner.rb +196 -0
  56. data/lib/repull/models/listing_content_update_request_occupancy.rb +179 -0
  57. data/lib/repull/{api/ai_api.rb → models/listing_content_update_request_photos_inner.rb} +77 -59
  58. data/lib/repull/models/{generate_studio_completion200_response_data.rb → listing_content_update_request_photos_inner_one_of.rb} +81 -66
  59. data/lib/repull/models/listing_content_update_request_policies.rb +253 -0
  60. data/lib/repull/models/{create_ai_operation200_response.rb → listing_content_update_response.rb} +32 -16
  61. data/lib/repull/models/map_airbnb_listing_request.rb +215 -0
  62. data/lib/repull/models/{create_reservation_request.rb → map_airbnb_listing_response.rb} +137 -116
  63. data/lib/repull/models/property.rb +42 -69
  64. data/lib/repull/models/property_availability.rb +222 -0
  65. data/lib/repull/models/property_availability_day.rb +247 -0
  66. data/lib/repull/models/reservation.rb +28 -1
  67. data/lib/repull/models/sandbox_fixture_ref.rb +193 -0
  68. data/lib/repull/models/{delete_studio_project_file200_response_data.rb → sandbox_reset_result.rb} +72 -11
  69. data/lib/repull/models/sandbox_reset_result_deleted.rb +216 -0
  70. data/lib/repull/models/sandbox_seed_result.rb +278 -0
  71. data/lib/repull/models/{update_studio_project_request.rb → update_airbnb_message_request.rb} +43 -24
  72. data/lib/repull/models/update_booking_charges_request.rb +194 -0
  73. data/lib/repull/version.rb +1 -1
  74. data/lib/repull.rb +59 -34
  75. data/openapi/v1.json +3956 -2522
  76. metadata +61 -36
  77. data/lib/repull/api/studio_api.rb +0 -1094
  78. data/lib/repull/models/generate_studio_completion_request.rb +0 -305
  79. data/lib/repull/models/upsert_studio_project_file200_response_data.rb +0 -147
@@ -0,0 +1,136 @@
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`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resets_at` 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 'cgi'
14
+
15
+ module Repull
16
+ class SandboxApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Reset sandbox fixtures
23
+ # Clear the authenticated test customer's sandbox fixtures. **Requires a test key (`sk_test_*`)** — live or legacy keys are rejected with 403. Deletes ONLY the customer's rows in the isolated sandbox data space — it can never touch real inventory (`listings`, `reservations`, connections). Idempotent: resetting an empty sandbox returns zero counts (still 200). Returns per-resource deletion counts.
24
+ # @param [Hash] opts the optional parameters
25
+ # @return [SandboxResetResult]
26
+ def reset_sandbox(opts = {})
27
+ data, _status_code, _headers = reset_sandbox_with_http_info(opts)
28
+ data
29
+ end
30
+
31
+ # Reset sandbox fixtures
32
+ # Clear the authenticated test customer's sandbox fixtures. **Requires a test key (`sk_test_*`)** — live or legacy keys are rejected with 403. Deletes ONLY the customer's rows in the isolated sandbox data space — it can never touch real inventory (`listings`, `reservations`, connections). Idempotent: resetting an empty sandbox returns zero counts (still 200). Returns per-resource deletion counts.
33
+ # @param [Hash] opts the optional parameters
34
+ # @return [Array<(SandboxResetResult, Integer, Hash)>] SandboxResetResult data, response status code and response headers
35
+ def reset_sandbox_with_http_info(opts = {})
36
+ if @api_client.config.debugging
37
+ @api_client.config.logger.debug 'Calling API: SandboxApi.reset_sandbox ...'
38
+ end
39
+ # resource path
40
+ local_var_path = '/v1/sandbox/reset'
41
+
42
+ # query parameters
43
+ query_params = opts[:query_params] || {}
44
+
45
+ # header parameters
46
+ header_params = opts[:header_params] || {}
47
+ # HTTP header 'Accept' (if needed)
48
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
49
+
50
+ # form parameters
51
+ form_params = opts[:form_params] || {}
52
+
53
+ # http body (model)
54
+ post_body = opts[:debug_body]
55
+
56
+ # return_type
57
+ return_type = opts[:debug_return_type] || 'SandboxResetResult'
58
+
59
+ # auth_names
60
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
61
+
62
+ new_options = opts.merge(
63
+ :operation => :"SandboxApi.reset_sandbox",
64
+ :header_params => header_params,
65
+ :query_params => query_params,
66
+ :form_params => form_params,
67
+ :body => post_body,
68
+ :auth_names => auth_names,
69
+ :return_type => return_type
70
+ )
71
+
72
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
73
+ if @api_client.config.debugging
74
+ @api_client.config.logger.debug "API called: SandboxApi#reset_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
75
+ end
76
+ return data, status_code, headers
77
+ end
78
+
79
+ # Seed sandbox fixtures
80
+ # Provision a deterministic set of test fixtures for contract testing WITHOUT live provider accounts. **Requires a test key (`sk_test_*`)** — live or legacy keys are rejected with 403. Seeds, scoped to the authenticated test customer: 3 sample listings, 5 reservations across the lifecycle (created / modified / cancelled / date-changed / pending), and 2 fake connected provider accounts (Airbnb + Booking.com) so the pairing + connection-status flows are testable without real OAuth. Idempotent — re-seeding upserts the same rows and returns the same ids. The seeded rows are visible ONLY under a test key, via the normal read endpoints (`GET /v1/listings`, `/v1/reservations`, `/v1/connect`, `/v1/channels/airbnb/listings`, `/v1/channels/airbnb/connection`). They live in a data space fully isolated from live inventory.
81
+ # @param [Hash] opts the optional parameters
82
+ # @return [SandboxSeedResult]
83
+ def seed_sandbox(opts = {})
84
+ data, _status_code, _headers = seed_sandbox_with_http_info(opts)
85
+ data
86
+ end
87
+
88
+ # Seed sandbox fixtures
89
+ # Provision a deterministic set of test fixtures for contract testing WITHOUT live provider accounts. **Requires a test key (&#x60;sk_test_*&#x60;)** — live or legacy keys are rejected with 403. Seeds, scoped to the authenticated test customer: 3 sample listings, 5 reservations across the lifecycle (created / modified / cancelled / date-changed / pending), and 2 fake connected provider accounts (Airbnb + Booking.com) so the pairing + connection-status flows are testable without real OAuth. Idempotent — re-seeding upserts the same rows and returns the same ids. The seeded rows are visible ONLY under a test key, via the normal read endpoints (&#x60;GET /v1/listings&#x60;, &#x60;/v1/reservations&#x60;, &#x60;/v1/connect&#x60;, &#x60;/v1/channels/airbnb/listings&#x60;, &#x60;/v1/channels/airbnb/connection&#x60;). They live in a data space fully isolated from live inventory.
90
+ # @param [Hash] opts the optional parameters
91
+ # @return [Array<(SandboxSeedResult, Integer, Hash)>] SandboxSeedResult data, response status code and response headers
92
+ def seed_sandbox_with_http_info(opts = {})
93
+ if @api_client.config.debugging
94
+ @api_client.config.logger.debug 'Calling API: SandboxApi.seed_sandbox ...'
95
+ end
96
+ # resource path
97
+ local_var_path = '/v1/sandbox/seed'
98
+
99
+ # query parameters
100
+ query_params = opts[:query_params] || {}
101
+
102
+ # header parameters
103
+ header_params = opts[:header_params] || {}
104
+ # HTTP header 'Accept' (if needed)
105
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
106
+
107
+ # form parameters
108
+ form_params = opts[:form_params] || {}
109
+
110
+ # http body (model)
111
+ post_body = opts[:debug_body]
112
+
113
+ # return_type
114
+ return_type = opts[:debug_return_type] || 'SandboxSeedResult'
115
+
116
+ # auth_names
117
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
118
+
119
+ new_options = opts.merge(
120
+ :operation => :"SandboxApi.seed_sandbox",
121
+ :header_params => header_params,
122
+ :query_params => query_params,
123
+ :form_params => form_params,
124
+ :body => post_body,
125
+ :auth_names => auth_names,
126
+ :return_type => return_type
127
+ )
128
+
129
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
130
+ if @api_client.config.debugging
131
+ @api_client.config.logger.debug "API called: SandboxApi#seed_sandbox\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
132
+ end
133
+ return data, status_code, headers
134
+ end
135
+ end
136
+ end
@@ -19,69 +19,6 @@ module Repull
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
- # Get VRBO pricing (501 — agency model)
23
- # VRBO uses the agency model — VRBO PULLS rates from `/api/webhooks/vrbo/listings-xml/rates/{listing}/{unit}` rather than accepting a push API. This endpoint is declared for symmetry with the other channel-pricing routes but currently returns **501 Not Implemented** with a pointer at the public rate URL VRBO consumes. Use `GET /v1/listings/{id}/calendar` (once wired) to inspect the underlying source-of-truth. When the listings-XML rate-builder is ported into this repo, this endpoint will return the parsed rates VRBO sees.
24
- # @param id [Integer] Vanio listing ID — resolved to a VRBO listing/unit via the workspace mapping.
25
- # @param [Hash] opts the optional parameters
26
- # @return [nil]
27
- def get_vrbo_listing_pricing(id, opts = {})
28
- get_vrbo_listing_pricing_with_http_info(id, opts)
29
- nil
30
- end
31
-
32
- # Get VRBO pricing (501 — agency model)
33
- # VRBO uses the agency model — VRBO PULLS rates from &#x60;/api/webhooks/vrbo/listings-xml/rates/{listing}/{unit}&#x60; rather than accepting a push API. This endpoint is declared for symmetry with the other channel-pricing routes but currently returns **501 Not Implemented** with a pointer at the public rate URL VRBO consumes. Use &#x60;GET /v1/listings/{id}/calendar&#x60; (once wired) to inspect the underlying source-of-truth. When the listings-XML rate-builder is ported into this repo, this endpoint will return the parsed rates VRBO sees.
34
- # @param id [Integer] Vanio listing ID — resolved to a VRBO listing/unit via the workspace mapping.
35
- # @param [Hash] opts the optional parameters
36
- # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
37
- def get_vrbo_listing_pricing_with_http_info(id, opts = {})
38
- if @api_client.config.debugging
39
- @api_client.config.logger.debug 'Calling API: VRBOApi.get_vrbo_listing_pricing ...'
40
- end
41
- # verify the required parameter 'id' is set
42
- if @api_client.config.client_side_validation && id.nil?
43
- fail ArgumentError, "Missing the required parameter 'id' when calling VRBOApi.get_vrbo_listing_pricing"
44
- end
45
- # resource path
46
- local_var_path = '/v1/channels/vrbo/listings/{id}/pricing'.sub('{id}', CGI.escape(id.to_s))
47
-
48
- # query parameters
49
- query_params = opts[:query_params] || {}
50
-
51
- # header parameters
52
- header_params = opts[:header_params] || {}
53
- # HTTP header 'Accept' (if needed)
54
- header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
55
-
56
- # form parameters
57
- form_params = opts[:form_params] || {}
58
-
59
- # http body (model)
60
- post_body = opts[:debug_body]
61
-
62
- # return_type
63
- return_type = opts[:debug_return_type]
64
-
65
- # auth_names
66
- auth_names = opts[:debug_auth_names] || ['bearerAuth']
67
-
68
- new_options = opts.merge(
69
- :operation => :"VRBOApi.get_vrbo_listing_pricing",
70
- :header_params => header_params,
71
- :query_params => query_params,
72
- :form_params => form_params,
73
- :body => post_body,
74
- :auth_names => auth_names,
75
- :return_type => return_type
76
- )
77
-
78
- data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
79
- if @api_client.config.debugging
80
- @api_client.config.logger.debug "API called: VRBOApi#get_vrbo_listing_pricing\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
81
- end
82
- return data, status_code, headers
83
- end
84
-
85
22
  # List VRBO listings
86
23
  # List VRBO listings this workspace owns. VRBO is agency-model — Repull reads listings via the public iCal/HTTP feeds.
87
24
  # @param [Hash] opts the optional parameters
@@ -223,68 +160,5 @@ module Repull
223
160
  end
224
161
  return data, status_code, headers
225
162
  end
226
-
227
- # Update VRBO pricing (501 — no push API exists)
228
- # VRBO has no rate-push API. To change what VRBO sees, update the underlying Vanio calendar/pricing-settings (e.g. `PUT /v1/listings/{id}/calendar` once wired) — VRBO will pick up the change on its next pull. This endpoint always returns **501** rather than fake-stubbing a successful push the SDK would silently swallow.
229
- # @param id [Integer]
230
- # @param [Hash] opts the optional parameters
231
- # @return [nil]
232
- def update_vrbo_listing_pricing(id, opts = {})
233
- update_vrbo_listing_pricing_with_http_info(id, opts)
234
- nil
235
- end
236
-
237
- # Update VRBO pricing (501 — no push API exists)
238
- # VRBO has no rate-push API. To change what VRBO sees, update the underlying Vanio calendar/pricing-settings (e.g. &#x60;PUT /v1/listings/{id}/calendar&#x60; once wired) — VRBO will pick up the change on its next pull. This endpoint always returns **501** rather than fake-stubbing a successful push the SDK would silently swallow.
239
- # @param id [Integer]
240
- # @param [Hash] opts the optional parameters
241
- # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
242
- def update_vrbo_listing_pricing_with_http_info(id, opts = {})
243
- if @api_client.config.debugging
244
- @api_client.config.logger.debug 'Calling API: VRBOApi.update_vrbo_listing_pricing ...'
245
- end
246
- # verify the required parameter 'id' is set
247
- if @api_client.config.client_side_validation && id.nil?
248
- fail ArgumentError, "Missing the required parameter 'id' when calling VRBOApi.update_vrbo_listing_pricing"
249
- end
250
- # resource path
251
- local_var_path = '/v1/channels/vrbo/listings/{id}/pricing'.sub('{id}', CGI.escape(id.to_s))
252
-
253
- # query parameters
254
- query_params = opts[:query_params] || {}
255
-
256
- # header parameters
257
- header_params = opts[:header_params] || {}
258
- # HTTP header 'Accept' (if needed)
259
- header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
260
-
261
- # form parameters
262
- form_params = opts[:form_params] || {}
263
-
264
- # http body (model)
265
- post_body = opts[:debug_body]
266
-
267
- # return_type
268
- return_type = opts[:debug_return_type]
269
-
270
- # auth_names
271
- auth_names = opts[:debug_auth_names] || ['bearerAuth']
272
-
273
- new_options = opts.merge(
274
- :operation => :"VRBOApi.update_vrbo_listing_pricing",
275
- :header_params => header_params,
276
- :query_params => query_params,
277
- :form_params => form_params,
278
- :body => post_body,
279
- :auth_names => auth_names,
280
- :return_type => return_type
281
- )
282
-
283
- data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options)
284
- if @api_client.config.debugging
285
- @api_client.config.logger.debug "API called: VRBOApi#update_vrbo_listing_pricing\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
286
- end
287
- return data, status_code, headers
288
- end
289
163
  end
290
164
  end
@@ -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_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`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resets_at` 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 AcknowledgeBookingReservationsRequest < ApiModelBase
18
+ # Booking.com reservation ids to acknowledge.
19
+ attr_accessor :reservation_ids
20
+
21
+ # Attribute mapping from ruby-style variable name to JSON key.
22
+ def self.attribute_map
23
+ {
24
+ :'reservation_ids' => :'reservation_ids'
25
+ }
26
+ end
27
+
28
+ # Returns attribute mapping this model knows about
29
+ def self.acceptable_attribute_map
30
+ attribute_map
31
+ end
32
+
33
+ # Returns all the JSON keys this model knows about
34
+ def self.acceptable_attributes
35
+ acceptable_attribute_map.values
36
+ end
37
+
38
+ # Attribute type mapping.
39
+ def self.openapi_types
40
+ {
41
+ :'reservation_ids' => :'Array<String>'
42
+ }
43
+ end
44
+
45
+ # List of attributes with nullable: true
46
+ def self.openapi_nullable
47
+ Set.new([
48
+ ])
49
+ end
50
+
51
+ # Initializes the object
52
+ # @param [Hash] attributes Model attributes in the form of hash
53
+ def initialize(attributes = {})
54
+ if (!attributes.is_a?(Hash))
55
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::AcknowledgeBookingReservationsRequest` initialize method"
56
+ end
57
+
58
+ # check to see if the attribute exists and convert string to symbol for hash key
59
+ acceptable_attribute_map = self.class.acceptable_attribute_map
60
+ attributes = attributes.each_with_object({}) { |(k, v), h|
61
+ if (!acceptable_attribute_map.key?(k.to_sym))
62
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::AcknowledgeBookingReservationsRequest`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
63
+ end
64
+ h[k.to_sym] = v
65
+ }
66
+
67
+ if attributes.key?(:'reservation_ids')
68
+ if (value = attributes[:'reservation_ids']).is_a?(Array)
69
+ self.reservation_ids = value
70
+ end
71
+ else
72
+ self.reservation_ids = nil
73
+ end
74
+ end
75
+
76
+ # Show invalid properties with the reasons. Usually used together with valid?
77
+ # @return Array for valid properties with the reasons
78
+ def list_invalid_properties
79
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
80
+ invalid_properties = Array.new
81
+ if @reservation_ids.nil?
82
+ invalid_properties.push('invalid value for "reservation_ids", reservation_ids cannot be nil.')
83
+ end
84
+
85
+ if @reservation_ids.length < 1
86
+ invalid_properties.push('invalid value for "reservation_ids", number of items must be greater than or equal to 1.')
87
+ end
88
+
89
+ invalid_properties
90
+ end
91
+
92
+ # Check to see if the all the properties in the model are valid
93
+ # @return true if the model is valid
94
+ def valid?
95
+ warn '[DEPRECATED] the `valid?` method is obsolete'
96
+ return false if @reservation_ids.nil?
97
+ return false if @reservation_ids.length < 1
98
+ true
99
+ end
100
+
101
+ # Custom attribute writer method with validation
102
+ # @param [Object] reservation_ids Value to be assigned
103
+ def reservation_ids=(reservation_ids)
104
+ if reservation_ids.nil?
105
+ fail ArgumentError, 'reservation_ids cannot be nil'
106
+ end
107
+
108
+ if reservation_ids.length < 1
109
+ fail ArgumentError, 'invalid value for "reservation_ids", number of items must be greater than or equal to 1.'
110
+ end
111
+
112
+ @reservation_ids = reservation_ids
113
+ end
114
+
115
+ # Checks equality by comparing each attribute.
116
+ # @param [Object] Object to be compared
117
+ def ==(o)
118
+ return true if self.equal?(o)
119
+ self.class == o.class &&
120
+ reservation_ids == o.reservation_ids
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
+ [reservation_ids].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
@@ -14,41 +14,29 @@ require 'date'
14
14
  require 'time'
15
15
 
16
16
  module Repull
17
- class CreateStudioProject201ResponseData < ApiModelBase
18
- attr_accessor :id
17
+ # An Airbnb reservation alteration request (date change, guest-count change, or price change), mirrored locally in `reservation_alterations`. Additional Airbnb-side fields may be present.
18
+ class AirbnbAlteration < ApiModelBase
19
+ # Airbnb alteration id.
20
+ attr_accessor :alteration_id
19
21
 
20
- attr_accessor :slug
22
+ # Repull reservation id the alteration belongs to.
23
+ attr_accessor :reservation_id
21
24
 
22
- attr_accessor :status
25
+ attr_accessor :platform
23
26
 
24
- class EnumAttributeValidator
25
- attr_reader :datatype
26
- attr_reader :allowable_values
27
-
28
- def initialize(datatype, allowable_values)
29
- @allowable_values = allowable_values.map do |value|
30
- case datatype.to_s
31
- when /Integer/i
32
- value.to_i
33
- when /Float/i
34
- value.to_f
35
- else
36
- value
37
- end
38
- end
39
- end
27
+ # Alteration status (e.g. `pending`).
28
+ attr_accessor :status
40
29
 
41
- def valid?(value)
42
- !value || allowable_values.include?(value)
43
- end
44
- end
30
+ attr_accessor :created_at
45
31
 
46
32
  # Attribute mapping from ruby-style variable name to JSON key.
47
33
  def self.attribute_map
48
34
  {
49
- :'id' => :'id',
50
- :'slug' => :'slug',
51
- :'status' => :'status'
35
+ :'alteration_id' => :'alterationId',
36
+ :'reservation_id' => :'reservationId',
37
+ :'platform' => :'platform',
38
+ :'status' => :'status',
39
+ :'created_at' => :'createdAt'
52
40
  }
53
41
  end
54
42
 
@@ -65,15 +53,21 @@ module Repull
65
53
  # Attribute type mapping.
66
54
  def self.openapi_types
67
55
  {
68
- :'id' => :'String',
69
- :'slug' => :'String',
70
- :'status' => :'String'
56
+ :'alteration_id' => :'String',
57
+ :'reservation_id' => :'Integer',
58
+ :'platform' => :'String',
59
+ :'status' => :'String',
60
+ :'created_at' => :'Time'
71
61
  }
72
62
  end
73
63
 
74
64
  # List of attributes with nullable: true
75
65
  def self.openapi_nullable
76
66
  Set.new([
67
+ :'alteration_id',
68
+ :'reservation_id',
69
+ :'status',
70
+ :'created_at'
77
71
  ])
78
72
  end
79
73
 
@@ -81,29 +75,37 @@ module Repull
81
75
  # @param [Hash] attributes Model attributes in the form of hash
82
76
  def initialize(attributes = {})
83
77
  if (!attributes.is_a?(Hash))
84
- fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::CreateStudioProject201ResponseData` initialize method"
78
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::AirbnbAlteration` initialize method"
85
79
  end
86
80
 
87
81
  # check to see if the attribute exists and convert string to symbol for hash key
88
82
  acceptable_attribute_map = self.class.acceptable_attribute_map
89
83
  attributes = attributes.each_with_object({}) { |(k, v), h|
90
84
  if (!acceptable_attribute_map.key?(k.to_sym))
91
- fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::CreateStudioProject201ResponseData`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
85
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::AirbnbAlteration`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
92
86
  end
93
87
  h[k.to_sym] = v
94
88
  }
95
89
 
96
- if attributes.key?(:'id')
97
- self.id = attributes[:'id']
90
+ if attributes.key?(:'alteration_id')
91
+ self.alteration_id = attributes[:'alteration_id']
98
92
  end
99
93
 
100
- if attributes.key?(:'slug')
101
- self.slug = attributes[:'slug']
94
+ if attributes.key?(:'reservation_id')
95
+ self.reservation_id = attributes[:'reservation_id']
96
+ end
97
+
98
+ if attributes.key?(:'platform')
99
+ self.platform = attributes[:'platform']
102
100
  end
103
101
 
104
102
  if attributes.key?(:'status')
105
103
  self.status = attributes[:'status']
106
104
  end
105
+
106
+ if attributes.key?(:'created_at')
107
+ self.created_at = attributes[:'created_at']
108
+ end
107
109
  end
108
110
 
109
111
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -118,29 +120,19 @@ module Repull
118
120
  # @return true if the model is valid
119
121
  def valid?
120
122
  warn '[DEPRECATED] the `valid?` method is obsolete'
121
- status_validator = EnumAttributeValidator.new('String', ["draft", "building", "live", "archived"])
122
- return false unless status_validator.valid?(@status)
123
123
  true
124
124
  end
125
125
 
126
- # Custom attribute writer method checking allowed values (enum).
127
- # @param [Object] status Object to be assigned
128
- def status=(status)
129
- validator = EnumAttributeValidator.new('String', ["draft", "building", "live", "archived"])
130
- unless validator.valid?(status)
131
- fail ArgumentError, "invalid value for \"status\", must be one of #{validator.allowable_values}."
132
- end
133
- @status = status
134
- end
135
-
136
126
  # Checks equality by comparing each attribute.
137
127
  # @param [Object] Object to be compared
138
128
  def ==(o)
139
129
  return true if self.equal?(o)
140
130
  self.class == o.class &&
141
- id == o.id &&
142
- slug == o.slug &&
143
- status == o.status
131
+ alteration_id == o.alteration_id &&
132
+ reservation_id == o.reservation_id &&
133
+ platform == o.platform &&
134
+ status == o.status &&
135
+ created_at == o.created_at
144
136
  end
145
137
 
146
138
  # @see the `==` method
@@ -152,7 +144,7 @@ module Repull
152
144
  # Calculates hash code according to all attributes.
153
145
  # @return [Integer] Hash code
154
146
  def hash
155
- [id, slug, status].hash
147
+ [alteration_id, reservation_id, platform, status, created_at].hash
156
148
  end
157
149
 
158
150
  # Builds the object from hash