repull 0.2.32 → 0.2.34

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/repull/api/connections_api.rb +77 -0
  4. data/lib/repull/api/promotions_api.rb +574 -0
  5. data/lib/repull/api/resolutions_api.rb +236 -0
  6. data/lib/repull/api/transactions_api.rb +73 -0
  7. data/lib/repull/models/account_import.rb +237 -0
  8. data/lib/repull/models/account_import_completed_event.rb +282 -0
  9. data/lib/repull/models/account_import_counts.rb +193 -0
  10. data/lib/repull/models/account_import_failed_event.rb +282 -0
  11. data/lib/repull/models/account_import_finished_payload.rb +224 -0
  12. data/lib/repull/models/account_import_stage.rb +177 -0
  13. data/lib/repull/models/account_import_started_event.rb +282 -0
  14. data/lib/repull/models/account_import_started_payload.rb +177 -0
  15. data/lib/repull/models/connection.rb +16 -5
  16. data/lib/repull/models/list_airbnb_resolutions200_response.rb +192 -0
  17. data/lib/repull/models/list_connection_imports200_response.rb +158 -0
  18. data/lib/repull/models/listing_updated_payload.rb +2 -2
  19. data/lib/repull/models/promotion.rb +344 -0
  20. data/lib/repull/models/promotion_applies_to.rb +161 -0
  21. data/lib/repull/models/promotion_bulk_request.rb +236 -0
  22. data/lib/repull/models/promotion_bulk_response.rb +158 -0
  23. data/lib/repull/models/promotion_bulk_response_data_inner.rb +174 -0
  24. data/lib/repull/models/promotion_bulk_response_data_inner_error.rb +165 -0
  25. data/lib/repull/models/promotion_bulk_response_summary.rb +156 -0
  26. data/lib/repull/models/promotion_catalog.rb +158 -0
  27. data/lib/repull/models/promotion_catalog_channels_inner.rb +222 -0
  28. data/lib/repull/models/promotion_catalog_entry.rb +265 -0
  29. data/lib/repull/models/promotion_catalog_field.rb +205 -0
  30. data/lib/repull/models/promotion_catalog_field_options_inner.rb +156 -0
  31. data/lib/repull/models/promotion_catalog_response.rb +147 -0
  32. data/lib/repull/models/promotion_create_request.rb +258 -0
  33. data/lib/repull/models/promotion_delete_response.rb +147 -0
  34. data/lib/repull/models/promotion_delete_response_data.rb +156 -0
  35. data/lib/repull/models/promotion_discount.rb +191 -0
  36. data/lib/repull/models/promotion_input.rb +241 -0
  37. data/lib/repull/models/promotion_list_response.rb +170 -0
  38. data/lib/repull/models/promotion_refresh_response.rb +170 -0
  39. data/lib/repull/models/promotion_response.rb +147 -0
  40. data/lib/repull/models/promotion_rules.rb +290 -0
  41. data/lib/repull/models/promotion_rules_blackout_dates_quota.rb +156 -0
  42. data/lib/repull/models/promotion_rules_booking_window.rb +230 -0
  43. data/lib/repull/models/promotion_rules_last_minute.rb +218 -0
  44. data/lib/repull/models/promotion_rules_stay_window.rb +211 -0
  45. data/lib/repull/models/promotion_type.rb +47 -0
  46. data/lib/repull/models/promotion_update_request.rb +175 -0
  47. data/lib/repull/models/promotion_usage.rb +193 -0
  48. data/lib/repull/models/promotion_usage_revenue.rb +156 -0
  49. data/lib/repull/models/refresh_airbnb_resolutions200_response.rb +219 -0
  50. data/lib/repull/models/refresh_airbnb_resolutions200_response_accounts_inner.rb +200 -0
  51. data/lib/repull/models/refresh_airbnb_resolutions200_response_accounts_inner_error.rb +156 -0
  52. data/lib/repull/models/refresh_airbnb_resolutions_request.rb +192 -0
  53. data/lib/repull/models/reservation_money_line.rb +14 -4
  54. data/lib/repull/models/resolution.rb +329 -0
  55. data/lib/repull/models/transaction_payout.rb +17 -6
  56. data/lib/repull/models/webhook_event.rb +6 -0
  57. data/lib/repull/models/webhook_event_type.rb +4 -1
  58. data/lib/repull/version.rb +1 -1
  59. data/lib/repull.rb +47 -0
  60. data/openapi/v1.json +2211 -385
  61. data/scripts/regen.sh +1 -1
  62. metadata +49 -2
@@ -0,0 +1,236 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | unlimited (10 included, then billed per listing) | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'cgi'
14
+
15
+ module Repull
16
+ class ResolutionsApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Get an Airbnb Resolution Center case
23
+ # One Airbnb Resolution Center case. `{id}` is the Airbnb resolution id (the `resolutionId` from the list, also a transaction's `reference`). Read-only (see the list endpoint). The amounts moved are in the transactions ledger — join `resolutionId` to a transaction's `reference`. 404 when it isn't in this workspace (POST the list endpoint to refresh first).
24
+ # @param id [String] The Airbnb resolution id.
25
+ # @param [Hash] opts the optional parameters
26
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: `native` (default), `calry`, `calry-v1`. Custom: any schema name created via `POST /v1/schema/custom`. Unknown / inactive schema names fall back to `native`.
27
+ # @return [Resolution]
28
+ def get_airbnb_resolution(id, opts = {})
29
+ data, _status_code, _headers = get_airbnb_resolution_with_http_info(id, opts)
30
+ data
31
+ end
32
+
33
+ # Get an Airbnb Resolution Center case
34
+ # One Airbnb Resolution Center case. `{id}` is the Airbnb resolution id (the `resolutionId` from the list, also a transaction's `reference`). Read-only (see the list endpoint). The amounts moved are in the transactions ledger — join `resolutionId` to a transaction's `reference`. 404 when it isn't in this workspace (POST the list endpoint to refresh first).
35
+ # @param id [String] The Airbnb resolution id.
36
+ # @param [Hash] opts the optional parameters
37
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: `native` (default), `calry`, `calry-v1`. Custom: any schema name created via `POST /v1/schema/custom`. Unknown / inactive schema names fall back to `native`.
38
+ # @return [Array<(Resolution, Integer, Hash)>] Resolution data, response status code and response headers
39
+ def get_airbnb_resolution_with_http_info(id, opts = {})
40
+ if @api_client.config.debugging
41
+ @api_client.config.logger.debug 'Calling API: ResolutionsApi.get_airbnb_resolution ...'
42
+ end
43
+ # verify the required parameter 'id' is set
44
+ if @api_client.config.client_side_validation && id.nil?
45
+ fail ArgumentError, "Missing the required parameter 'id' when calling ResolutionsApi.get_airbnb_resolution"
46
+ end
47
+ # resource path
48
+ local_var_path = '/v1/channels/airbnb/resolutions/{id}'.sub('{id}', CGI.escape(id.to_s))
49
+
50
+ # query parameters
51
+ query_params = opts[:query_params] || {}
52
+
53
+ # header parameters
54
+ header_params = opts[:header_params] || {}
55
+ # HTTP header 'Accept' (if needed)
56
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
57
+ header_params[:'X-Schema'] = opts[:'x_schema'] if !opts[:'x_schema'].nil?
58
+
59
+ # form parameters
60
+ form_params = opts[:form_params] || {}
61
+
62
+ # http body (model)
63
+ post_body = opts[:debug_body]
64
+
65
+ # return_type
66
+ return_type = opts[:debug_return_type] || 'Resolution'
67
+
68
+ # auth_names
69
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
70
+
71
+ new_options = opts.merge(
72
+ :operation => :"ResolutionsApi.get_airbnb_resolution",
73
+ :header_params => header_params,
74
+ :query_params => query_params,
75
+ :form_params => form_params,
76
+ :body => post_body,
77
+ :auth_names => auth_names,
78
+ :return_type => return_type
79
+ )
80
+
81
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
82
+ if @api_client.config.debugging
83
+ @api_client.config.logger.debug "API called: ResolutionsApi#get_airbnb_resolution\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
84
+ end
85
+ return data, status_code, headers
86
+ end
87
+
88
+ # List Airbnb Resolution Center cases
89
+ # The host's Airbnb Resolution Center cases (AirCover / damage, extra-guest, refund and change requests), open and closed. Served from the database; `POST` this path to refresh from Airbnb. READ-ONLY upstream — Airbnb's Resolutions API is read-only; a claim is created, evidence added and status changed only in Airbnb's Resolution Center UI, not via API. Use this for visibility and action tracking (`suggestedActionText`). The money a resolution moved is in the transactions ledger as `Resolution Payout` / `Resolution Adjustment` rows sharing the same reference — join on `resolutionId`. Cursor-paginated, newest first.
90
+ # @param [Hash] opts the optional parameters
91
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
92
+ # @option opts [String] :status Filter by lifecycle: &#x60;open&#x60; (still needs action) or &#x60;closed&#x60; (settled). Omit for all.
93
+ # @option opts [Integer] :limit Page size (default 50, max 200).
94
+ # @option opts [String] :cursor Opaque pagination cursor from a previous &#x60;pagination.nextCursor&#x60;.
95
+ # @return [ListAirbnbResolutions200Response]
96
+ def list_airbnb_resolutions(opts = {})
97
+ data, _status_code, _headers = list_airbnb_resolutions_with_http_info(opts)
98
+ data
99
+ end
100
+
101
+ # List Airbnb Resolution Center cases
102
+ # The host&#39;s Airbnb Resolution Center cases (AirCover / damage, extra-guest, refund and change requests), open and closed. Served from the database; &#x60;POST&#x60; this path to refresh from Airbnb. READ-ONLY upstream — Airbnb&#39;s Resolutions API is read-only; a claim is created, evidence added and status changed only in Airbnb&#39;s Resolution Center UI, not via API. Use this for visibility and action tracking (&#x60;suggestedActionText&#x60;). The money a resolution moved is in the transactions ledger as &#x60;Resolution Payout&#x60; / &#x60;Resolution Adjustment&#x60; rows sharing the same reference — join on &#x60;resolutionId&#x60;. Cursor-paginated, newest first.
103
+ # @param [Hash] opts the optional parameters
104
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
105
+ # @option opts [String] :status Filter by lifecycle: &#x60;open&#x60; (still needs action) or &#x60;closed&#x60; (settled). Omit for all.
106
+ # @option opts [Integer] :limit Page size (default 50, max 200).
107
+ # @option opts [String] :cursor Opaque pagination cursor from a previous &#x60;pagination.nextCursor&#x60;.
108
+ # @return [Array<(ListAirbnbResolutions200Response, Integer, Hash)>] ListAirbnbResolutions200Response data, response status code and response headers
109
+ def list_airbnb_resolutions_with_http_info(opts = {})
110
+ if @api_client.config.debugging
111
+ @api_client.config.logger.debug 'Calling API: ResolutionsApi.list_airbnb_resolutions ...'
112
+ end
113
+ allowable_values = ["open", "closed"]
114
+ if @api_client.config.client_side_validation && opts[:'status'] && !allowable_values.include?(opts[:'status'])
115
+ fail ArgumentError, "invalid value for \"status\", must be one of #{allowable_values}"
116
+ end
117
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 200
118
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling ResolutionsApi.list_airbnb_resolutions, must be smaller than or equal to 200.'
119
+ end
120
+
121
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
122
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling ResolutionsApi.list_airbnb_resolutions, must be greater than or equal to 1.'
123
+ end
124
+
125
+ # resource path
126
+ local_var_path = '/v1/channels/airbnb/resolutions'
127
+
128
+ # query parameters
129
+ query_params = opts[:query_params] || {}
130
+ query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
131
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
132
+ query_params[:'cursor'] = opts[:'cursor'] if !opts[:'cursor'].nil?
133
+
134
+ # header parameters
135
+ header_params = opts[:header_params] || {}
136
+ # HTTP header 'Accept' (if needed)
137
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
138
+ header_params[:'X-Schema'] = opts[:'x_schema'] if !opts[:'x_schema'].nil?
139
+
140
+ # form parameters
141
+ form_params = opts[:form_params] || {}
142
+
143
+ # http body (model)
144
+ post_body = opts[:debug_body]
145
+
146
+ # return_type
147
+ return_type = opts[:debug_return_type] || 'ListAirbnbResolutions200Response'
148
+
149
+ # auth_names
150
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
151
+
152
+ new_options = opts.merge(
153
+ :operation => :"ResolutionsApi.list_airbnb_resolutions",
154
+ :header_params => header_params,
155
+ :query_params => query_params,
156
+ :form_params => form_params,
157
+ :body => post_body,
158
+ :auth_names => auth_names,
159
+ :return_type => return_type
160
+ )
161
+
162
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
163
+ if @api_client.config.debugging
164
+ @api_client.config.logger.debug "API called: ResolutionsApi#list_airbnb_resolutions\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
165
+ end
166
+ return data, status_code, headers
167
+ end
168
+
169
+ # Refresh Airbnb resolutions from Airbnb
170
+ # Pulls the latest Resolution Center cases from Airbnb (read-only) and stores them, then read with GET. Optionally scope to one `account_id` and/or `status`.
171
+ # @param [Hash] opts the optional parameters
172
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
173
+ # @option opts [RefreshAirbnbResolutionsRequest] :refresh_airbnb_resolutions_request
174
+ # @return [RefreshAirbnbResolutions200Response]
175
+ def refresh_airbnb_resolutions(opts = {})
176
+ data, _status_code, _headers = refresh_airbnb_resolutions_with_http_info(opts)
177
+ data
178
+ end
179
+
180
+ # Refresh Airbnb resolutions from Airbnb
181
+ # Pulls the latest Resolution Center cases from Airbnb (read-only) and stores them, then read with GET. Optionally scope to one &#x60;account_id&#x60; and/or &#x60;status&#x60;.
182
+ # @param [Hash] opts the optional parameters
183
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
184
+ # @option opts [RefreshAirbnbResolutionsRequest] :refresh_airbnb_resolutions_request
185
+ # @return [Array<(RefreshAirbnbResolutions200Response, Integer, Hash)>] RefreshAirbnbResolutions200Response data, response status code and response headers
186
+ def refresh_airbnb_resolutions_with_http_info(opts = {})
187
+ if @api_client.config.debugging
188
+ @api_client.config.logger.debug 'Calling API: ResolutionsApi.refresh_airbnb_resolutions ...'
189
+ end
190
+ # resource path
191
+ local_var_path = '/v1/channels/airbnb/resolutions'
192
+
193
+ # query parameters
194
+ query_params = opts[:query_params] || {}
195
+
196
+ # header parameters
197
+ header_params = opts[:header_params] || {}
198
+ # HTTP header 'Accept' (if needed)
199
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
200
+ # HTTP header 'Content-Type'
201
+ content_type = @api_client.select_header_content_type(['application/json'])
202
+ if !content_type.nil?
203
+ header_params['Content-Type'] = content_type
204
+ end
205
+ header_params[:'X-Schema'] = opts[:'x_schema'] if !opts[:'x_schema'].nil?
206
+
207
+ # form parameters
208
+ form_params = opts[:form_params] || {}
209
+
210
+ # http body (model)
211
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(opts[:'refresh_airbnb_resolutions_request'])
212
+
213
+ # return_type
214
+ return_type = opts[:debug_return_type] || 'RefreshAirbnbResolutions200Response'
215
+
216
+ # auth_names
217
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
218
+
219
+ new_options = opts.merge(
220
+ :operation => :"ResolutionsApi.refresh_airbnb_resolutions",
221
+ :header_params => header_params,
222
+ :query_params => query_params,
223
+ :form_params => form_params,
224
+ :body => post_body,
225
+ :auth_names => auth_names,
226
+ :return_type => return_type
227
+ )
228
+
229
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path, new_options)
230
+ if @api_client.config.debugging
231
+ @api_client.config.logger.debug "API called: ResolutionsApi#refresh_airbnb_resolutions\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
232
+ end
233
+ return data, status_code, headers
234
+ end
235
+ end
236
+ end
@@ -19,6 +19,79 @@ module Repull
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
+ # Download the original payout statement
23
+ # Downloads the channel's ORIGINAL payout statement — the authoritative settlement record a host reconciles against — for a payout transaction. `{id}` is the payout `transactionId` (e.g. `booking_payout_5463377687_bank_transfer`). Repull serves the stored bytes itself (no channel session), so unlike the channel's own link it never expires or leaks a session. PDF by default; pass `?format=csv` for the CSV remittance. Returns `404` when no statement was captured for the payout (e.g. an upcoming payout, or a channel that has none).
24
+ # @param id [String] The payout transaction id (&#x60;transactionId&#x60;).
25
+ # @param [Hash] opts the optional parameters
26
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
27
+ # @option opts [String] :format Statement format. (default to 'pdf')
28
+ # @return [File]
29
+ def get_transaction_statement(id, opts = {})
30
+ data, _status_code, _headers = get_transaction_statement_with_http_info(id, opts)
31
+ data
32
+ end
33
+
34
+ # Download the original payout statement
35
+ # Downloads the channel&#39;s ORIGINAL payout statement — the authoritative settlement record a host reconciles against — for a payout transaction. &#x60;{id}&#x60; is the payout &#x60;transactionId&#x60; (e.g. &#x60;booking_payout_5463377687_bank_transfer&#x60;). Repull serves the stored bytes itself (no channel session), so unlike the channel&#39;s own link it never expires or leaks a session. PDF by default; pass &#x60;?format&#x3D;csv&#x60; for the CSV remittance. Returns &#x60;404&#x60; when no statement was captured for the payout (e.g. an upcoming payout, or a channel that has none).
36
+ # @param id [String] The payout transaction id (&#x60;transactionId&#x60;).
37
+ # @param [Hash] opts the optional parameters
38
+ # @option opts [String] :x_schema Apply a custom or built-in schema to transform the response. Built-in: &#x60;native&#x60; (default), &#x60;calry&#x60;, &#x60;calry-v1&#x60;. Custom: any schema name created via &#x60;POST /v1/schema/custom&#x60;. Unknown / inactive schema names fall back to &#x60;native&#x60;.
39
+ # @option opts [String] :format Statement format. (default to 'pdf')
40
+ # @return [Array<(File, Integer, Hash)>] File data, response status code and response headers
41
+ def get_transaction_statement_with_http_info(id, opts = {})
42
+ if @api_client.config.debugging
43
+ @api_client.config.logger.debug 'Calling API: TransactionsApi.get_transaction_statement ...'
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 TransactionsApi.get_transaction_statement"
48
+ end
49
+ allowable_values = ["pdf", "csv"]
50
+ if @api_client.config.client_side_validation && opts[:'format'] && !allowable_values.include?(opts[:'format'])
51
+ fail ArgumentError, "invalid value for \"format\", must be one of #{allowable_values}"
52
+ end
53
+ # resource path
54
+ local_var_path = '/v1/transactions/{id}/statement'.sub('{id}', CGI.escape(id.to_s))
55
+
56
+ # query parameters
57
+ query_params = opts[:query_params] || {}
58
+ query_params[:'format'] = opts[:'format'] if !opts[:'format'].nil?
59
+
60
+ # header parameters
61
+ header_params = opts[:header_params] || {}
62
+ # HTTP header 'Accept' (if needed)
63
+ header_params['Accept'] = @api_client.select_header_accept(['application/pdf', 'text/csv', 'application/json']) unless header_params['Accept']
64
+ header_params[:'X-Schema'] = opts[:'x_schema'] if !opts[:'x_schema'].nil?
65
+
66
+ # form parameters
67
+ form_params = opts[:form_params] || {}
68
+
69
+ # http body (model)
70
+ post_body = opts[:debug_body]
71
+
72
+ # return_type
73
+ return_type = opts[:debug_return_type] || 'File'
74
+
75
+ # auth_names
76
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
77
+
78
+ new_options = opts.merge(
79
+ :operation => :"TransactionsApi.get_transaction_statement",
80
+ :header_params => header_params,
81
+ :query_params => query_params,
82
+ :form_params => form_params,
83
+ :body => post_body,
84
+ :auth_names => auth_names,
85
+ :return_type => return_type
86
+ )
87
+
88
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
89
+ if @api_client.config.debugging
90
+ @api_client.config.logger.debug "API called: TransactionsApi#get_transaction_statement\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
91
+ end
92
+ return data, status_code, headers
93
+ end
94
+
22
95
  # List transactions & payments (unified ledger)
23
96
  # Every channel's money in one ledger: payouts that reached the host, reservation charges, refunds and adjustments, across Airbnb, Booking.com and Vrbo. Each row carries a normalized `platform` — `booking` always means Booking.com however the account was connected (official API or extranet); there is no `booking_extranet`, and `?platform=booking` matches all of them. A payout row's `amount` is the net the host received; `grossAmount` is what the guest paid and `fees` the deductions (commission, payments service fee, processing fee). Cursor-paginated, newest first.
24
97
  # @param [Hash] opts the optional parameters
@@ -0,0 +1,237 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | unlimited (10 included, then billed per listing) | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Repull
17
+ # One import run of a connected account: the first sync after connecting, a room or unit mapped later, a reconnect or a re-sync.
18
+ class AccountImport < ApiModelBase
19
+ # The import id — the `importId` on `account.import.*` webhooks.
20
+ attr_accessor :id
21
+
22
+ attr_accessor :status
23
+
24
+ attr_accessor :trigger
25
+
26
+ # `completed` imports only: `partial` when some units failed (see `stages`).
27
+ attr_accessor :outcome
28
+
29
+ # `failed` imports only: why it stopped — e.g. `needs_permissions`, `duplicate_account`, `stalled`.
30
+ attr_accessor :reason
31
+
32
+ attr_accessor :started_at
33
+
34
+ attr_accessor :last_activity_at
35
+
36
+ attr_accessor :completed_at
37
+
38
+ attr_accessor :stages
39
+
40
+ attr_accessor :counts
41
+
42
+ # Attribute mapping from ruby-style variable name to JSON key.
43
+ def self.attribute_map
44
+ {
45
+ :'id' => :'id',
46
+ :'status' => :'status',
47
+ :'trigger' => :'trigger',
48
+ :'outcome' => :'outcome',
49
+ :'reason' => :'reason',
50
+ :'started_at' => :'startedAt',
51
+ :'last_activity_at' => :'lastActivityAt',
52
+ :'completed_at' => :'completedAt',
53
+ :'stages' => :'stages',
54
+ :'counts' => :'counts'
55
+ }
56
+ end
57
+
58
+ # Returns attribute mapping this model knows about
59
+ def self.acceptable_attribute_map
60
+ attribute_map
61
+ end
62
+
63
+ # Returns all the JSON keys this model knows about
64
+ def self.acceptable_attributes
65
+ acceptable_attribute_map.values
66
+ end
67
+
68
+ # Attribute type mapping.
69
+ def self.openapi_types
70
+ {
71
+ :'id' => :'String',
72
+ :'status' => :'String',
73
+ :'trigger' => :'String',
74
+ :'outcome' => :'String',
75
+ :'reason' => :'String',
76
+ :'started_at' => :'Time',
77
+ :'last_activity_at' => :'Time',
78
+ :'completed_at' => :'Time',
79
+ :'stages' => :'Array<AccountImportStage>',
80
+ :'counts' => :'AccountImportCounts'
81
+ }
82
+ end
83
+
84
+ # List of attributes with nullable: true
85
+ def self.openapi_nullable
86
+ Set.new([
87
+ :'outcome',
88
+ :'reason',
89
+ :'completed_at',
90
+ ])
91
+ end
92
+
93
+ # Initializes the object
94
+ # @param [Hash] attributes Model attributes in the form of hash
95
+ def initialize(attributes = {})
96
+ if (!attributes.is_a?(Hash))
97
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::AccountImport` initialize method"
98
+ end
99
+
100
+ # check to see if the attribute exists and convert string to symbol for hash key
101
+ acceptable_attribute_map = self.class.acceptable_attribute_map
102
+ attributes = attributes.each_with_object({}) { |(k, v), h|
103
+ if (!acceptable_attribute_map.key?(k.to_sym))
104
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::AccountImport`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
105
+ end
106
+ h[k.to_sym] = v
107
+ }
108
+
109
+ if attributes.key?(:'id')
110
+ self.id = attributes[:'id']
111
+ end
112
+
113
+ if attributes.key?(:'status')
114
+ self.status = attributes[:'status']
115
+ end
116
+
117
+ if attributes.key?(:'trigger')
118
+ self.trigger = attributes[:'trigger']
119
+ end
120
+
121
+ if attributes.key?(:'outcome')
122
+ self.outcome = attributes[:'outcome']
123
+ end
124
+
125
+ if attributes.key?(:'reason')
126
+ self.reason = attributes[:'reason']
127
+ end
128
+
129
+ if attributes.key?(:'started_at')
130
+ self.started_at = attributes[:'started_at']
131
+ end
132
+
133
+ if attributes.key?(:'last_activity_at')
134
+ self.last_activity_at = attributes[:'last_activity_at']
135
+ end
136
+
137
+ if attributes.key?(:'completed_at')
138
+ self.completed_at = attributes[:'completed_at']
139
+ end
140
+
141
+ if attributes.key?(:'stages')
142
+ if (value = attributes[:'stages']).is_a?(Array)
143
+ self.stages = value
144
+ end
145
+ end
146
+
147
+ if attributes.key?(:'counts')
148
+ self.counts = attributes[:'counts']
149
+ end
150
+ end
151
+
152
+ # Show invalid properties with the reasons. Usually used together with valid?
153
+ # @return Array for valid properties with the reasons
154
+ def list_invalid_properties
155
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
156
+ invalid_properties = Array.new
157
+ invalid_properties
158
+ end
159
+
160
+ # Check to see if the all the properties in the model are valid
161
+ # @return true if the model is valid
162
+ def valid?
163
+ warn '[DEPRECATED] the `valid?` method is obsolete'
164
+ true
165
+ end
166
+
167
+ # Checks equality by comparing each attribute.
168
+ # @param [Object] Object to be compared
169
+ def ==(o)
170
+ return true if self.equal?(o)
171
+ self.class == o.class &&
172
+ id == o.id &&
173
+ status == o.status &&
174
+ trigger == o.trigger &&
175
+ outcome == o.outcome &&
176
+ reason == o.reason &&
177
+ started_at == o.started_at &&
178
+ last_activity_at == o.last_activity_at &&
179
+ completed_at == o.completed_at &&
180
+ stages == o.stages &&
181
+ counts == o.counts
182
+ end
183
+
184
+ # @see the `==` method
185
+ # @param [Object] Object to be compared
186
+ def eql?(o)
187
+ self == o
188
+ end
189
+
190
+ # Calculates hash code according to all attributes.
191
+ # @return [Integer] Hash code
192
+ def hash
193
+ [id, status, trigger, outcome, reason, started_at, last_activity_at, completed_at, stages, counts].hash
194
+ end
195
+
196
+ # Builds the object from hash
197
+ # @param [Hash] attributes Model attributes in the form of hash
198
+ # @return [Object] Returns the model itself
199
+ def self.build_from_hash(attributes)
200
+ return nil unless attributes.is_a?(Hash)
201
+ attributes = attributes.transform_keys(&:to_sym)
202
+ transformed_hash = {}
203
+ openapi_types.each_pair do |key, type|
204
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
205
+ transformed_hash["#{key}"] = nil
206
+ elsif type =~ /\AArray<(.*)>/i
207
+ # check to ensure the input is an array given that the attribute
208
+ # is documented as an array but the input is not
209
+ if attributes[attribute_map[key]].is_a?(Array)
210
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
211
+ end
212
+ elsif !attributes[attribute_map[key]].nil?
213
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
214
+ end
215
+ end
216
+ new(transformed_hash)
217
+ end
218
+
219
+ # Returns the object in the form of hash
220
+ # @return [Hash] Returns the object in the form of hash
221
+ def to_hash
222
+ hash = {}
223
+ self.class.attribute_map.each_pair do |attr, param|
224
+ value = self.send(attr)
225
+ if value.nil?
226
+ is_nullable = self.class.openapi_nullable.include?(attr)
227
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
228
+ end
229
+
230
+ hash[param] = _to_hash(value)
231
+ end
232
+ hash
233
+ end
234
+
235
+ end
236
+
237
+ end