repull 0.2.25 → 0.2.27

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/airbnb_api.rb +38 -23
  3. data/lib/repull/api/booking_com_api.rb +9 -2
  4. data/lib/repull/api/connect_api.rb +6 -6
  5. data/lib/repull/api/conversations_api.rb +3 -0
  6. data/lib/repull/api/listings_api.rb +2 -2
  7. data/lib/repull/api/properties_api.rb +5 -2
  8. data/lib/repull/api/reservations_api.rb +96 -6
  9. data/lib/repull/api/reviews_api.rb +3 -0
  10. data/lib/repull/api/vrbo_api.rb +11 -4
  11. data/lib/repull/models/airbnb_listing.rb +13 -2
  12. data/lib/repull/models/airbnb_listing_details_write_request.rb +109 -5
  13. data/lib/repull/models/airbnb_listing_details_write_request_check_in_option.rb +1 -1
  14. data/lib/repull/models/airbnb_permits_write_request_permits_inner.rb +2 -2
  15. data/lib/repull/models/airbnb_pricing_write_request.rb +30 -2
  16. data/lib/repull/models/airbnb_pricing_write_request_fees_inner.rb +291 -0
  17. data/lib/repull/models/booking_property_listings_inner.rb +57 -1
  18. data/lib/repull/models/cancel_reservation200_response.rb +1 -1
  19. data/lib/repull/models/connect_session_completed_event.rb +281 -0
  20. data/lib/repull/models/connect_session_completed_payload.rb +210 -0
  21. data/lib/repull/models/connect_status.rb +35 -4
  22. data/lib/repull/models/connect_status_capabilities.rb +148 -0
  23. data/lib/repull/models/connection.rb +28 -5
  24. data/lib/repull/models/connection_action.rb +188 -0
  25. data/lib/repull/models/conversation.rb +12 -1
  26. data/lib/repull/models/conversation_detail.rb +12 -1
  27. data/lib/repull/models/create_connect_session_request.rb +40 -2
  28. data/lib/repull/models/create_connection_request.rb +46 -8
  29. data/lib/repull/models/error_error.rb +13 -1
  30. data/lib/repull/models/{cancel_reservation200_response_pms.rb → error_error_listings_inner.rb} +36 -30
  31. data/lib/repull/models/get_booking_extranet_login_status200_response.rb +2 -1
  32. data/lib/repull/models/list_listing_units200_response.rb +2 -1
  33. data/lib/repull/models/listing.rb +23 -2
  34. data/lib/repull/models/listing_capabilities.rb +148 -0
  35. data/lib/repull/models/property.rb +26 -3
  36. data/lib/repull/models/record_account.rb +170 -0
  37. data/lib/repull/models/reservation.rb +12 -1
  38. data/lib/repull/models/reservation_capabilities.rb +230 -0
  39. data/lib/repull/models/reservation_create_request.rb +172 -33
  40. data/lib/repull/models/reservation_create_response.rb +6 -6
  41. data/lib/repull/models/reservation_create_response_unit.rb +1 -1
  42. data/lib/repull/models/{reservation_create_response_pms.rb → reservation_pms_outcome.rb} +38 -8
  43. data/lib/repull/models/reservation_pms_outcome_quote.rb +168 -0
  44. data/lib/repull/models/{cancel_reservation200_response_pms_errors_inner.rb → reservation_pms_section_error.rb} +16 -16
  45. data/lib/repull/models/reservation_quote_request.rb +312 -0
  46. data/lib/repull/models/reservation_quote_response.rb +228 -0
  47. data/lib/repull/models/reservation_quote_response_breakdown.rb +175 -0
  48. data/lib/repull/models/reservation_update_response.rb +16 -6
  49. data/lib/repull/models/review.rb +12 -1
  50. data/lib/repull/models/submit_smoobu_credentials_request.rb +2 -5
  51. data/lib/repull/models/submit_smoobu_credentials_request_credentials.rb +193 -0
  52. data/lib/repull/models/update_airbnb_checkin_guide200_response.rb +147 -0
  53. data/lib/repull/models/update_airbnb_checkin_guide200_response_data.rb +177 -0
  54. data/lib/repull/models/{airbnb_permits_write_request_permits_inner_answers_value.rb → update_airbnb_checkin_guide200_response_data_steps_inner.rb} +24 -43
  55. data/lib/repull/models/update_airbnb_checkin_guide_request.rb +186 -0
  56. data/lib/repull/models/update_airbnb_checkin_guide_request_steps_inner.rb +182 -0
  57. data/lib/repull/models/webhook_event.rb +2 -0
  58. data/lib/repull/models/webhook_event_type.rb +2 -1
  59. data/lib/repull/version.rb +1 -1
  60. data/lib/repull.rb +21 -4
  61. data/openapi/v1.json +2221 -340
  62. data/scripts/regen.sh +1 -1
  63. metadata +23 -6
@@ -39,9 +39,17 @@ module Repull
39
39
  # PMS connections only: what the app may change in the PMS. Change it with `PATCH /v1/connect/{provider}/write-policy`.
40
40
  attr_accessor :write_policy
41
41
 
42
+ attr_accessor :capabilities
43
+
42
44
  # Vrbo only: the same freshness envelope the Airbnb read endpoints return, per account and in aggregate. Its reason is never_synced until a mapping is confirmed and importing while upcoming bookings come in.
43
45
  attr_accessor :data_freshness
44
46
 
47
+ # Smoobu only: set to `{ required: true, reason: \"reauth_required\", message }` when the connection still uses a legacy single API key, which Smoobu stops accepting on October 31, 2026. `null` once it is on an API key + secret.
48
+ attr_accessor :action
49
+
50
+ # Smoobu only: durable link to the hosted Smoobu form where the host pastes a new API key + secret. Submitting it updates this same connection (`id` unchanged). Present only when `action.required` is true.
51
+ attr_accessor :fix_url
52
+
45
53
  # Attribute mapping from ruby-style variable name to JSON key.
46
54
  def self.attribute_map
47
55
  {
@@ -54,7 +62,10 @@ module Repull
54
62
  :'host' => :'host',
55
63
  :'accounts' => :'accounts',
56
64
  :'write_policy' => :'writePolicy',
57
- :'data_freshness' => :'dataFreshness'
65
+ :'capabilities' => :'capabilities',
66
+ :'data_freshness' => :'dataFreshness',
67
+ :'action' => :'action',
68
+ :'fix_url' => :'fixUrl'
58
69
  }
59
70
  end
60
71
 
@@ -80,7 +91,10 @@ module Repull
80
91
  :'host' => :'ConnectHost',
81
92
  :'accounts' => :'Array<ConnectStatusAccountsInner>',
82
93
  :'write_policy' => :'PmsWritePolicy',
83
- :'data_freshness' => :'Object'
94
+ :'capabilities' => :'ConnectStatusCapabilities',
95
+ :'data_freshness' => :'Object',
96
+ :'action' => :'ConnectionAction',
97
+ :'fix_url' => :'String'
84
98
  }
85
99
  end
86
100
 
@@ -89,6 +103,8 @@ module Repull
89
103
  Set.new([
90
104
  :'external_account_id',
91
105
  :'host',
106
+ :'action',
107
+ :'fix_url'
92
108
  ])
93
109
  end
94
110
 
@@ -146,9 +162,21 @@ module Repull
146
162
  self.write_policy = attributes[:'write_policy']
147
163
  end
148
164
 
165
+ if attributes.key?(:'capabilities')
166
+ self.capabilities = attributes[:'capabilities']
167
+ end
168
+
149
169
  if attributes.key?(:'data_freshness')
150
170
  self.data_freshness = attributes[:'data_freshness']
151
171
  end
172
+
173
+ if attributes.key?(:'action')
174
+ self.action = attributes[:'action']
175
+ end
176
+
177
+ if attributes.key?(:'fix_url')
178
+ self.fix_url = attributes[:'fix_url']
179
+ end
152
180
  end
153
181
 
154
182
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -180,7 +208,10 @@ module Repull
180
208
  host == o.host &&
181
209
  accounts == o.accounts &&
182
210
  write_policy == o.write_policy &&
183
- data_freshness == o.data_freshness
211
+ capabilities == o.capabilities &&
212
+ data_freshness == o.data_freshness &&
213
+ action == o.action &&
214
+ fix_url == o.fix_url
184
215
  end
185
216
 
186
217
  # @see the `==` method
@@ -192,7 +223,7 @@ module Repull
192
223
  # Calculates hash code according to all attributes.
193
224
  # @return [Integer] Hash code
194
225
  def hash
195
- [connected, provider, id, status, external_account_id, created_at, host, accounts, write_policy, data_freshness].hash
226
+ [connected, provider, id, status, external_account_id, created_at, host, accounts, write_policy, capabilities, data_freshness, action, fix_url].hash
196
227
  end
197
228
 
198
229
  # Builds the object from hash
@@ -0,0 +1,148 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
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
+ # PMS providers only. `reservations`: which reservation writes the API performs on this connection's listings — the connector's support combined with `writePolicy`. When `connected` is false, what the connector supports once connected.
18
+ class ConnectStatusCapabilities < ApiModelBase
19
+ attr_accessor :reservations
20
+
21
+ # Attribute mapping from ruby-style variable name to JSON key.
22
+ def self.attribute_map
23
+ {
24
+ :'reservations' => :'reservations'
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
+ :'reservations' => :'ReservationCapabilities'
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::ConnectStatusCapabilities` 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::ConnectStatusCapabilities`. 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?(:'reservations')
68
+ self.reservations = attributes[:'reservations']
69
+ end
70
+ end
71
+
72
+ # Show invalid properties with the reasons. Usually used together with valid?
73
+ # @return Array for valid properties with the reasons
74
+ def list_invalid_properties
75
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
76
+ invalid_properties = Array.new
77
+ invalid_properties
78
+ end
79
+
80
+ # Check to see if the all the properties in the model are valid
81
+ # @return true if the model is valid
82
+ def valid?
83
+ warn '[DEPRECATED] the `valid?` method is obsolete'
84
+ true
85
+ end
86
+
87
+ # Checks equality by comparing each attribute.
88
+ # @param [Object] Object to be compared
89
+ def ==(o)
90
+ return true if self.equal?(o)
91
+ self.class == o.class &&
92
+ reservations == o.reservations
93
+ end
94
+
95
+ # @see the `==` method
96
+ # @param [Object] Object to be compared
97
+ def eql?(o)
98
+ self == o
99
+ end
100
+
101
+ # Calculates hash code according to all attributes.
102
+ # @return [Integer] Hash code
103
+ def hash
104
+ [reservations].hash
105
+ end
106
+
107
+ # Builds the object from hash
108
+ # @param [Hash] attributes Model attributes in the form of hash
109
+ # @return [Object] Returns the model itself
110
+ def self.build_from_hash(attributes)
111
+ return nil unless attributes.is_a?(Hash)
112
+ attributes = attributes.transform_keys(&:to_sym)
113
+ transformed_hash = {}
114
+ openapi_types.each_pair do |key, type|
115
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
116
+ transformed_hash["#{key}"] = nil
117
+ elsif type =~ /\AArray<(.*)>/i
118
+ # check to ensure the input is an array given that the attribute
119
+ # is documented as an array but the input is not
120
+ if attributes[attribute_map[key]].is_a?(Array)
121
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
122
+ end
123
+ elsif !attributes[attribute_map[key]].nil?
124
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
125
+ end
126
+ end
127
+ new(transformed_hash)
128
+ end
129
+
130
+ # Returns the object in the form of hash
131
+ # @return [Hash] Returns the object in the form of hash
132
+ def to_hash
133
+ hash = {}
134
+ self.class.attribute_map.each_pair do |attr, param|
135
+ value = self.send(attr)
136
+ if value.nil?
137
+ is_nullable = self.class.openapi_nullable.include?(attr)
138
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
139
+ end
140
+
141
+ hash[param] = _to_hash(value)
142
+ end
143
+ hash
144
+ end
145
+
146
+ end
147
+
148
+ end
@@ -19,6 +19,7 @@ module Repull
19
19
 
20
20
  attr_accessor :provider
21
21
 
22
+ # `active` — connected and working. `pending` — still settling. `needs_permissions` — connected but the host must grant more access before it works (see `action`/`fixUrl`). An `active` connection can also carry an `action` (e.g. a Smoobu legacy API key that must be replaced with a key + secret before October 31, 2026). `error` — the last operation failed. `disconnected` — revoked or superseded.
22
23
  attr_accessor :status
23
24
 
24
25
  attr_accessor :external_account_id
@@ -28,6 +29,12 @@ module Repull
28
29
  # Host metadata for the linked account. Currently populated for Airbnb only; null for other providers.
29
30
  attr_accessor :host
30
31
 
32
+ # Set when the host must do something before the connection works (e.g. grant the invited Booking.com Extranet user full access). `null` when no action is pending.
33
+ attr_accessor :action
34
+
35
+ # Durable link that reopens the hosted Connect flow bound to this account on the fix screen — send the host here to resolve `action`. Present only when `action.required` is true; `null` otherwise.
36
+ attr_accessor :fix_url
37
+
31
38
  # Attribute mapping from ruby-style variable name to JSON key.
32
39
  def self.attribute_map
33
40
  {
@@ -36,7 +43,9 @@ module Repull
36
43
  :'status' => :'status',
37
44
  :'external_account_id' => :'externalAccountId',
38
45
  :'created_at' => :'createdAt',
39
- :'host' => :'host'
46
+ :'host' => :'host',
47
+ :'action' => :'action',
48
+ :'fix_url' => :'fixUrl'
40
49
  }
41
50
  end
42
51
 
@@ -58,7 +67,9 @@ module Repull
58
67
  :'status' => :'String',
59
68
  :'external_account_id' => :'String',
60
69
  :'created_at' => :'Time',
61
- :'host' => :'ConnectHost'
70
+ :'host' => :'ConnectHost',
71
+ :'action' => :'ConnectionAction',
72
+ :'fix_url' => :'String'
62
73
  }
63
74
  end
64
75
 
@@ -66,7 +77,9 @@ module Repull
66
77
  def self.openapi_nullable
67
78
  Set.new([
68
79
  :'external_account_id',
69
- :'host'
80
+ :'host',
81
+ :'action',
82
+ :'fix_url'
70
83
  ])
71
84
  end
72
85
 
@@ -109,6 +122,14 @@ module Repull
109
122
  if attributes.key?(:'host')
110
123
  self.host = attributes[:'host']
111
124
  end
125
+
126
+ if attributes.key?(:'action')
127
+ self.action = attributes[:'action']
128
+ end
129
+
130
+ if attributes.key?(:'fix_url')
131
+ self.fix_url = attributes[:'fix_url']
132
+ end
112
133
  end
113
134
 
114
135
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -136,7 +157,9 @@ module Repull
136
157
  status == o.status &&
137
158
  external_account_id == o.external_account_id &&
138
159
  created_at == o.created_at &&
139
- host == o.host
160
+ host == o.host &&
161
+ action == o.action &&
162
+ fix_url == o.fix_url
140
163
  end
141
164
 
142
165
  # @see the `==` method
@@ -148,7 +171,7 @@ module Repull
148
171
  # Calculates hash code according to all attributes.
149
172
  # @return [Integer] Hash code
150
173
  def hash
151
- [id, provider, status, external_account_id, created_at, host].hash
174
+ [id, provider, status, external_account_id, created_at, host, action, fix_url].hash
152
175
  end
153
176
 
154
177
  # Builds the object from hash
@@ -0,0 +1,188 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+
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
+ # A host action a connection needs before it can work.
18
+ class ConnectionAction < ApiModelBase
19
+ # Whether a host action is pending.
20
+ attr_accessor :required
21
+
22
+ # Machine-readable reason, stable for programmatic handling.
23
+ attr_accessor :reason
24
+
25
+ # Host-facing one-liner describing what to do.
26
+ attr_accessor :message
27
+
28
+ # Attribute mapping from ruby-style variable name to JSON key.
29
+ def self.attribute_map
30
+ {
31
+ :'required' => :'required',
32
+ :'reason' => :'reason',
33
+ :'message' => :'message'
34
+ }
35
+ end
36
+
37
+ # Returns attribute mapping this model knows about
38
+ def self.acceptable_attribute_map
39
+ attribute_map
40
+ end
41
+
42
+ # Returns all the JSON keys this model knows about
43
+ def self.acceptable_attributes
44
+ acceptable_attribute_map.values
45
+ end
46
+
47
+ # Attribute type mapping.
48
+ def self.openapi_types
49
+ {
50
+ :'required' => :'Boolean',
51
+ :'reason' => :'String',
52
+ :'message' => :'String'
53
+ }
54
+ end
55
+
56
+ # List of attributes with nullable: true
57
+ def self.openapi_nullable
58
+ Set.new([
59
+ :'reason',
60
+ :'message'
61
+ ])
62
+ end
63
+
64
+ # Initializes the object
65
+ # @param [Hash] attributes Model attributes in the form of hash
66
+ def initialize(attributes = {})
67
+ if (!attributes.is_a?(Hash))
68
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::ConnectionAction` initialize method"
69
+ end
70
+
71
+ # check to see if the attribute exists and convert string to symbol for hash key
72
+ acceptable_attribute_map = self.class.acceptable_attribute_map
73
+ attributes = attributes.each_with_object({}) { |(k, v), h|
74
+ if (!acceptable_attribute_map.key?(k.to_sym))
75
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::ConnectionAction`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
76
+ end
77
+ h[k.to_sym] = v
78
+ }
79
+
80
+ if attributes.key?(:'required')
81
+ self.required = attributes[:'required']
82
+ else
83
+ self.required = nil
84
+ end
85
+
86
+ if attributes.key?(:'reason')
87
+ self.reason = attributes[:'reason']
88
+ end
89
+
90
+ if attributes.key?(:'message')
91
+ self.message = attributes[:'message']
92
+ end
93
+ end
94
+
95
+ # Show invalid properties with the reasons. Usually used together with valid?
96
+ # @return Array for valid properties with the reasons
97
+ def list_invalid_properties
98
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
99
+ invalid_properties = Array.new
100
+ if @required.nil?
101
+ invalid_properties.push('invalid value for "required", required cannot be nil.')
102
+ end
103
+
104
+ invalid_properties
105
+ end
106
+
107
+ # Check to see if the all the properties in the model are valid
108
+ # @return true if the model is valid
109
+ def valid?
110
+ warn '[DEPRECATED] the `valid?` method is obsolete'
111
+ return false if @required.nil?
112
+ true
113
+ end
114
+
115
+ # Custom attribute writer method with validation
116
+ # @param [Object] required Value to be assigned
117
+ def required=(required)
118
+ if required.nil?
119
+ fail ArgumentError, 'required cannot be nil'
120
+ end
121
+
122
+ @required = required
123
+ end
124
+
125
+ # Checks equality by comparing each attribute.
126
+ # @param [Object] Object to be compared
127
+ def ==(o)
128
+ return true if self.equal?(o)
129
+ self.class == o.class &&
130
+ required == o.required &&
131
+ reason == o.reason &&
132
+ message == o.message
133
+ end
134
+
135
+ # @see the `==` method
136
+ # @param [Object] Object to be compared
137
+ def eql?(o)
138
+ self == o
139
+ end
140
+
141
+ # Calculates hash code according to all attributes.
142
+ # @return [Integer] Hash code
143
+ def hash
144
+ [required, reason, message].hash
145
+ end
146
+
147
+ # Builds the object from hash
148
+ # @param [Hash] attributes Model attributes in the form of hash
149
+ # @return [Object] Returns the model itself
150
+ def self.build_from_hash(attributes)
151
+ return nil unless attributes.is_a?(Hash)
152
+ attributes = attributes.transform_keys(&:to_sym)
153
+ transformed_hash = {}
154
+ openapi_types.each_pair do |key, type|
155
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
156
+ transformed_hash["#{key}"] = nil
157
+ elsif type =~ /\AArray<(.*)>/i
158
+ # check to ensure the input is an array given that the attribute
159
+ # is documented as an array but the input is not
160
+ if attributes[attribute_map[key]].is_a?(Array)
161
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
162
+ end
163
+ elsif !attributes[attribute_map[key]].nil?
164
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
165
+ end
166
+ end
167
+ new(transformed_hash)
168
+ end
169
+
170
+ # Returns the object in the form of hash
171
+ # @return [Hash] Returns the object in the form of hash
172
+ def to_hash
173
+ hash = {}
174
+ self.class.attribute_map.each_pair do |attr, param|
175
+ value = self.send(attr)
176
+ if value.nil?
177
+ is_nullable = self.class.openapi_nullable.include?(attr)
178
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
179
+ end
180
+
181
+ hash[param] = _to_hash(value)
182
+ end
183
+ hash
184
+ end
185
+
186
+ end
187
+
188
+ end
@@ -16,6 +16,9 @@ require 'time'
16
16
  module Repull
17
17
  # Channel-agnostic message thread between the host workspace and a guest. Returned by `GET /v1/conversations`. The `id` is the internal Repull thread id (integer) — pass it back as the `{id}` path param on detail / messages calls.
18
18
  class Conversation < ApiModelBase
19
+ # The connected account this conversation belongs to. List endpoint.
20
+ attr_accessor :account
21
+
19
22
  attr_accessor :id
20
23
 
21
24
  attr_accessor :platform
@@ -49,6 +52,7 @@ module Repull
49
52
  # Attribute mapping from ruby-style variable name to JSON key.
50
53
  def self.attribute_map
51
54
  {
55
+ :'account' => :'account',
52
56
  :'id' => :'id',
53
57
  :'platform' => :'platform',
54
58
  :'external_thread_id' => :'externalThreadId',
@@ -78,6 +82,7 @@ module Repull
78
82
  # Attribute type mapping.
79
83
  def self.openapi_types
80
84
  {
85
+ :'account' => :'RecordAccount',
81
86
  :'id' => :'String',
82
87
  :'platform' => :'String',
83
88
  :'external_thread_id' => :'String',
@@ -97,6 +102,7 @@ module Repull
97
102
  # List of attributes with nullable: true
98
103
  def self.openapi_nullable
99
104
  Set.new([
105
+ :'account',
100
106
  :'platform',
101
107
  :'external_thread_id',
102
108
  :'guest_id',
@@ -124,6 +130,10 @@ module Repull
124
130
  h[k.to_sym] = v
125
131
  }
126
132
 
133
+ if attributes.key?(:'account')
134
+ self.account = attributes[:'account']
135
+ end
136
+
127
137
  if attributes.key?(:'id')
128
138
  self.id = attributes[:'id']
129
139
  end
@@ -197,6 +207,7 @@ module Repull
197
207
  def ==(o)
198
208
  return true if self.equal?(o)
199
209
  self.class == o.class &&
210
+ account == o.account &&
200
211
  id == o.id &&
201
212
  platform == o.platform &&
202
213
  external_thread_id == o.external_thread_id &&
@@ -221,7 +232,7 @@ module Repull
221
232
  # Calculates hash code according to all attributes.
222
233
  # @return [Integer] Hash code
223
234
  def hash
224
- [id, platform, external_thread_id, guest_id, listing_id, reservation_id, subject, last_message_at, last_message_preview, unread_count, status, created_at, updated_at].hash
235
+ [account, id, platform, external_thread_id, guest_id, listing_id, reservation_id, subject, last_message_at, last_message_preview, unread_count, status, created_at, updated_at].hash
225
236
  end
226
237
 
227
238
  # Builds the object from hash
@@ -16,6 +16,9 @@ require 'time'
16
16
  module Repull
17
17
  # Returned by `GET /v1/conversations/{id}`. Extends the list-row `Conversation` shape with expanded `host` + `guest` blocks so SDK consumers can render thread headers without an extra round-trip.
18
18
  class ConversationDetail < ApiModelBase
19
+ # The connected account this conversation belongs to. List endpoint.
20
+ attr_accessor :account
21
+
19
22
  attr_accessor :id
20
23
 
21
24
  attr_accessor :platform
@@ -55,6 +58,7 @@ module Repull
55
58
  # Attribute mapping from ruby-style variable name to JSON key.
56
59
  def self.attribute_map
57
60
  {
61
+ :'account' => :'account',
58
62
  :'id' => :'id',
59
63
  :'platform' => :'platform',
60
64
  :'external_thread_id' => :'externalThreadId',
@@ -87,6 +91,7 @@ module Repull
87
91
  # Attribute type mapping.
88
92
  def self.openapi_types
89
93
  {
94
+ :'account' => :'RecordAccount',
90
95
  :'id' => :'String',
91
96
  :'platform' => :'String',
92
97
  :'external_thread_id' => :'String',
@@ -109,6 +114,7 @@ module Repull
109
114
  # List of attributes with nullable: true
110
115
  def self.openapi_nullable
111
116
  Set.new([
117
+ :'account',
112
118
  :'platform',
113
119
  :'external_thread_id',
114
120
  :'guest_id',
@@ -145,6 +151,10 @@ module Repull
145
151
  h[k.to_sym] = v
146
152
  }
147
153
 
154
+ if attributes.key?(:'account')
155
+ self.account = attributes[:'account']
156
+ end
157
+
148
158
  if attributes.key?(:'id')
149
159
  self.id = attributes[:'id']
150
160
  end
@@ -230,6 +240,7 @@ module Repull
230
240
  def ==(o)
231
241
  return true if self.equal?(o)
232
242
  self.class == o.class &&
243
+ account == o.account &&
233
244
  id == o.id &&
234
245
  platform == o.platform &&
235
246
  external_thread_id == o.external_thread_id &&
@@ -257,7 +268,7 @@ module Repull
257
268
  # Calculates hash code according to all attributes.
258
269
  # @return [Integer] Hash code
259
270
  def hash
260
- [id, platform, external_thread_id, guest_id, listing_id, reservation_id, subject, last_message_at, last_message_preview, unread_count, status, created_at, updated_at, host, guest, capabilities].hash
271
+ [account, id, platform, external_thread_id, guest_id, listing_id, reservation_id, subject, last_message_at, last_message_preview, unread_count, status, created_at, updated_at, host, guest, capabilities].hash
261
272
  end
262
273
 
263
274
  # Builds the object from hash