repull 0.2.20 → 0.2.21

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71d3ad6c9468a7b360f86f98812fba7773eb3237af66ad61ada50df72db13f72
4
- data.tar.gz: '09cb8aa5cc955b5e6ebf5250261c625a27072c45c04a6552775a8b7ffe0faeb8'
3
+ metadata.gz: 9ae04570f4fcd2cdf1feb8fc95a1ff6700b136cd1732aab138f5997e9b2b5585
4
+ data.tar.gz: 80968fad4134b4b87d6b18b965dae6a29eff61e2f96e77f0d48c137c836d9f20
5
5
  SHA512:
6
- metadata.gz: ac80b60656604b2b775998387d999e7b36f64ac5706b6311b44d67f4f4c642e223a8d3124f4ce6d3a38b375a33a6b39c94bc9e241cb505d3bdc295adfa438401
7
- data.tar.gz: 92346a3067ed2b39810fe08d3190b5ccc6c4feb30b02c5ba34068ef80878c5c38ca216eaf21803279b195cd1f9be1805a021df2ce1bbcedb96d2ad31e29e6442
6
+ metadata.gz: 624fc79360d49dc82445c9d29e8db4297e9225f22a3de83e3c5ed7be6a120efa4538a2249a407b1f5e74c704d3de67668579dba605c8259401bd924afe53d63c
7
+ data.tar.gz: ff2676bc32a7982783d8b32eef11b92a120095f9dee811f86fe5106a15fd2a67a4e89efc808185d3a7e672eef98ffc43cbbee0c30c3192fd9c36ecf5e3a5babf
@@ -437,6 +437,69 @@ module Repull
437
437
  return data, status_code, headers
438
438
  end
439
439
 
440
+ # Get a listing's channel markups
441
+ # The markup each channel adds to this listing's price. A listing's price on a channel is its own nightly price plus the channel's markup: a $200 night with a 35% Airbnb markup is sent to Airbnb as $270. The calendar keeps the listing's own price (`GET /v1/availability/{propertyId}` returns it); the markup is added only when a price is sent to the channel. - **Airbnb** — one markup per listing. - **Booking.com** — one markup per **property**, shared by every listing priced through it (`listingIds` names them). Returns `404 not_found` for a listing that does not exist or is not in this workspace, and `403 listing_inactive` for an inactive one.
442
+ # @param id [String] Repull listing id.
443
+ # @param [Hash] opts the optional parameters
444
+ # @return [GetListingMarkups200Response]
445
+ def get_listing_markups(id, opts = {})
446
+ data, _status_code, _headers = get_listing_markups_with_http_info(id, opts)
447
+ data
448
+ end
449
+
450
+ # Get a listing's channel markups
451
+ # The markup each channel adds to this listing's price. A listing's price on a channel is its own nightly price plus the channel's markup: a $200 night with a 35% Airbnb markup is sent to Airbnb as $270. The calendar keeps the listing's own price (`GET /v1/availability/{propertyId}` returns it); the markup is added only when a price is sent to the channel. - **Airbnb** — one markup per listing. - **Booking.com** — one markup per **property**, shared by every listing priced through it (`listingIds` names them). Returns `404 not_found` for a listing that does not exist or is not in this workspace, and `403 listing_inactive` for an inactive one.
452
+ # @param id [String] Repull listing id.
453
+ # @param [Hash] opts the optional parameters
454
+ # @return [Array<(GetListingMarkups200Response, Integer, Hash)>] GetListingMarkups200Response data, response status code and response headers
455
+ def get_listing_markups_with_http_info(id, opts = {})
456
+ if @api_client.config.debugging
457
+ @api_client.config.logger.debug 'Calling API: ListingsApi.get_listing_markups ...'
458
+ end
459
+ # verify the required parameter 'id' is set
460
+ if @api_client.config.client_side_validation && id.nil?
461
+ fail ArgumentError, "Missing the required parameter 'id' when calling ListingsApi.get_listing_markups"
462
+ end
463
+ # resource path
464
+ local_var_path = '/v1/listings/{id}/markups'.sub('{id}', CGI.escape(id.to_s))
465
+
466
+ # query parameters
467
+ query_params = opts[:query_params] || {}
468
+
469
+ # header parameters
470
+ header_params = opts[:header_params] || {}
471
+ # HTTP header 'Accept' (if needed)
472
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
473
+
474
+ # form parameters
475
+ form_params = opts[:form_params] || {}
476
+
477
+ # http body (model)
478
+ post_body = opts[:debug_body]
479
+
480
+ # return_type
481
+ return_type = opts[:debug_return_type] || 'GetListingMarkups200Response'
482
+
483
+ # auth_names
484
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
485
+
486
+ new_options = opts.merge(
487
+ :operation => :"ListingsApi.get_listing_markups",
488
+ :header_params => header_params,
489
+ :query_params => query_params,
490
+ :form_params => form_params,
491
+ :body => post_body,
492
+ :auth_names => auth_names,
493
+ :return_type => return_type
494
+ )
495
+
496
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
497
+ if @api_client.config.debugging
498
+ @api_client.config.logger.debug "API called: ListingsApi#get_listing_markups\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
499
+ end
500
+ return data, status_code, headers
501
+ end
502
+
440
503
  # Per-channel publish status
441
504
  # Returns connection state and sync activity per channel. `channels` is sync activity (empty until first push). `connections` is connection state (populated as soon as a channel is linked). Recommended polling cadence: at most once per 30s per listing — for bulk views, prefer `GET /v1/listings` and filter client-side. **When a push fails, this endpoint says why.** `channels[].pushError` carries the channel's own reason for the last failed push, verbatim — `\"Links and contact info can't be shared\"`, `\"Check-in start time must be before end time\"`, `\"property_type_group must be one of […]\"`. It is free text written by the channel, so render it next to the retry button rather than parsing it. `null` when the last push succeeded or none has run; pair it with `pushStatus` to tell those two apart. **It also says what you will not be allowed to change.** The `airbnb` entry in `connections` carries `lockedFields` — attributes Airbnb has locked on this listing. Airbnb does not refuse a write to one: it answers 200, reports the field as locked, and applies nothing, so a locked write is indistinguishable from a successful one unless you looked first. Read it before you let someone edit. Airbnb-only; no other channel has the concept, and no other entry carries the field. Returns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
442
505
  # @param id [Integer]
@@ -884,6 +947,80 @@ module Repull
884
947
  return data, status_code, headers
885
948
  end
886
949
 
950
+ # Set a listing's markup on a channel
951
+ # Set the markup one channel adds to this listing's price. When the value changes, the affected listings' prices are re-sent to that channel straight away (`pricesResent`); nothing else is sent. A listing's price on a channel is its own nightly price plus the channel's markup: a $200 night with a 35% Airbnb markup is sent to Airbnb as $270. The calendar keeps the listing's own price (`GET /v1/availability/{propertyId}` returns it); the markup is added only when a price is sent to the channel. - **Airbnb** — one markup per listing. - **Booking.com** — one markup per **property**, shared by every listing priced through it (`listingIds` names them). Returns `404 not_found` for a listing that does not exist or is not in this workspace, and `403 listing_inactive` for an inactive one. On Booking.com the markup belongs to the property, so setting it reprices every listing on that property (`affectedListingIds`). A listing on more than one property must name one with `hotelId`; without it the request is refused with `409 ambiguous_booking_mapping` listing the candidates, rather than repricing a property it guessed. `markupPercent` is a percentage — `15` for 15%. A value between 0 and 1 is refused as a probable fraction, with the number to send instead.
952
+ # @param id [String] Repull listing id.
953
+ # @param set_listing_markup_request [SetListingMarkupRequest]
954
+ # @param [Hash] opts the optional parameters
955
+ # @return [nil]
956
+ def set_listing_markup(id, set_listing_markup_request, opts = {})
957
+ set_listing_markup_with_http_info(id, set_listing_markup_request, opts)
958
+ nil
959
+ end
960
+
961
+ # Set a listing&#39;s markup on a channel
962
+ # Set the markup one channel adds to this listing&#39;s price. When the value changes, the affected listings&#39; prices are re-sent to that channel straight away (&#x60;pricesResent&#x60;); nothing else is sent. A listing&#39;s price on a channel is its own nightly price plus the channel&#39;s markup: a $200 night with a 35% Airbnb markup is sent to Airbnb as $270. The calendar keeps the listing&#39;s own price (&#x60;GET /v1/availability/{propertyId}&#x60; returns it); the markup is added only when a price is sent to the channel. - **Airbnb** — one markup per listing. - **Booking.com** — one markup per **property**, shared by every listing priced through it (&#x60;listingIds&#x60; names them). Returns &#x60;404 not_found&#x60; for a listing that does not exist or is not in this workspace, and &#x60;403 listing_inactive&#x60; for an inactive one. On Booking.com the markup belongs to the property, so setting it reprices every listing on that property (&#x60;affectedListingIds&#x60;). A listing on more than one property must name one with &#x60;hotelId&#x60;; without it the request is refused with &#x60;409 ambiguous_booking_mapping&#x60; listing the candidates, rather than repricing a property it guessed. &#x60;markupPercent&#x60; is a percentage — &#x60;15&#x60; for 15%. A value between 0 and 1 is refused as a probable fraction, with the number to send instead.
963
+ # @param id [String] Repull listing id.
964
+ # @param set_listing_markup_request [SetListingMarkupRequest]
965
+ # @param [Hash] opts the optional parameters
966
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
967
+ def set_listing_markup_with_http_info(id, set_listing_markup_request, opts = {})
968
+ if @api_client.config.debugging
969
+ @api_client.config.logger.debug 'Calling API: ListingsApi.set_listing_markup ...'
970
+ end
971
+ # verify the required parameter 'id' is set
972
+ if @api_client.config.client_side_validation && id.nil?
973
+ fail ArgumentError, "Missing the required parameter 'id' when calling ListingsApi.set_listing_markup"
974
+ end
975
+ # verify the required parameter 'set_listing_markup_request' is set
976
+ if @api_client.config.client_side_validation && set_listing_markup_request.nil?
977
+ fail ArgumentError, "Missing the required parameter 'set_listing_markup_request' when calling ListingsApi.set_listing_markup"
978
+ end
979
+ # resource path
980
+ local_var_path = '/v1/listings/{id}/markups'.sub('{id}', CGI.escape(id.to_s))
981
+
982
+ # query parameters
983
+ query_params = opts[:query_params] || {}
984
+
985
+ # header parameters
986
+ header_params = opts[:header_params] || {}
987
+ # HTTP header 'Accept' (if needed)
988
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
989
+ # HTTP header 'Content-Type'
990
+ content_type = @api_client.select_header_content_type(['application/json'])
991
+ if !content_type.nil?
992
+ header_params['Content-Type'] = content_type
993
+ end
994
+
995
+ # form parameters
996
+ form_params = opts[:form_params] || {}
997
+
998
+ # http body (model)
999
+ post_body = opts[:debug_body] || @api_client.object_to_http_body(set_listing_markup_request)
1000
+
1001
+ # return_type
1002
+ return_type = opts[:debug_return_type]
1003
+
1004
+ # auth_names
1005
+ auth_names = opts[:debug_auth_names] || ['bearerAuth']
1006
+
1007
+ new_options = opts.merge(
1008
+ :operation => :"ListingsApi.set_listing_markup",
1009
+ :header_params => header_params,
1010
+ :query_params => query_params,
1011
+ :form_params => form_params,
1012
+ :body => post_body,
1013
+ :auth_names => auth_names,
1014
+ :return_type => return_type
1015
+ )
1016
+
1017
+ data, status_code, headers = @api_client.call_api(:PUT, local_var_path, new_options)
1018
+ if @api_client.config.debugging
1019
+ @api_client.config.logger.debug "API called: ListingsApi#set_listing_markup\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1020
+ end
1021
+ return data, status_code, headers
1022
+ end
1023
+
887
1024
  # Activate or deactivate listings in bulk
888
1025
  # Sets up to 500 listings active or inactive in one call. Send `{ \"listingIds\": [\"4118\", \"4119\"], \"active\": false }` to deactivate them, or `\"active\": true` to activate them. An inactive listing is not counted toward your plan's listing limit or billed. It is NOT deleted and the upstream channel (Airbnb / Booking.com / your PMS) is never touched — its data keeps syncing, so it is complete the moment you activate it again. Until then it cannot be read, changed, or receive webhooks. **All or nothing.** Nothing changes unless the whole request can be applied: - If any id is not one of your listings, the call returns `404` naming those ids. - If activating would take you over your plan's listing limit, the call returns `402 listings_limit_exceeded`. Only listings that are currently inactive count toward the new total, so re-sending ids that are already active never trips the limit. Deactivating is always allowed, including when your account is already over its limit — it is how you get back under it. **Idempotent.** Ids already in the requested state are returned in `unchanged`; ids this call changed are returned in `updated`. For a single listing, `PATCH /v1/listings/{id}` does the same.
889
1026
  # @param listing_status_batch_request [ListingStatusBatchRequest]
@@ -19,8 +19,8 @@ module Repull
19
19
  def initialize(api_client = ApiClient.default)
20
20
  @api_client = api_client
21
21
  end
22
- # Get property details
23
- # Fetch a single property by Repull id. Property ids are workspace-scoped — an id from one workspace is not valid in another. 404 means the id does not exist OR belongs to a different workspace. **Optional expansions:** Pass `?include=amenities` to enrich the response with the property's amenities (sourced from the unified `listings_amenities` table). Returns `[]` when the property has no amenity rows. The default response stays lean; consumers must opt in. Returns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
22
+ # Get property details (older name for /v1/listings/{id})
23
+ # **`/v1/properties` is the older name for `/v1/listings`** — the same listings, the same ids. It stays for existing integrations; new code should use `/v1/listings`, which is where create, content, publishing and markups live. Fetch a single property by Repull id. Property ids are workspace-scoped — an id from one workspace is not valid in another. 404 means the id does not exist OR belongs to a different workspace. **Optional expansions:** Pass `?include=amenities` to enrich the response with the property's amenities (sourced from the unified `listings_amenities` table). Returns `[]` when the property has no amenity rows. The default response stays lean; consumers must opt in. Returns `403 listing_inactive` when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
24
24
  # @param id [Integer]
25
25
  # @param [Hash] opts the optional parameters
26
26
  # @option opts [String] :include Comma-separated optional expansions. Currently supported: &#x60;amenities&#x60;. Unknown values return 422.
@@ -30,8 +30,8 @@ module Repull
30
30
  data
31
31
  end
32
32
 
33
- # Get property details
34
- # Fetch a single property by Repull id. Property ids are workspace-scoped — an id from one workspace is not valid in another. 404 means the id does not exist OR belongs to a different workspace. **Optional expansions:** Pass &#x60;?include&#x3D;amenities&#x60; to enrich the response with the property&#39;s amenities (sourced from the unified &#x60;listings_amenities&#x60; table). Returns &#x60;[]&#x60; when the property has no amenity rows. The default response stays lean; consumers must opt in. Returns &#x60;403 listing_inactive&#x60; when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
33
+ # Get property details (older name for /v1/listings/{id})
34
+ # **&#x60;/v1/properties&#x60; is the older name for &#x60;/v1/listings&#x60;** — the same listings, the same ids. It stays for existing integrations; new code should use &#x60;/v1/listings&#x60;, which is where create, content, publishing and markups live. Fetch a single property by Repull id. Property ids are workspace-scoped — an id from one workspace is not valid in another. 404 means the id does not exist OR belongs to a different workspace. **Optional expansions:** Pass &#x60;?include&#x3D;amenities&#x60; to enrich the response with the property&#39;s amenities (sourced from the unified &#x60;listings_amenities&#x60; table). Returns &#x60;[]&#x60; when the property has no amenity rows. The default response stays lean; consumers must opt in. Returns &#x60;403 listing_inactive&#x60; when the listing is inactive. An inactive listing keeps syncing, but cannot be read or changed through the API until it is activated.
35
35
  # @param id [Integer]
36
36
  # @param [Hash] opts the optional parameters
37
37
  # @option opts [String] :include Comma-separated optional expansions. Currently supported: &#x60;amenities&#x60;. Unknown values return 422.
@@ -89,8 +89,8 @@ module Repull
89
89
  return data, status_code, headers
90
90
  end
91
91
 
92
- # List properties
93
- # Cursor-paginated list of properties for the authenticated workspace. Walk pages with `?cursor=<pagination.nextCursor>`; stop when `pagination.hasMore` is `false`. Cursor is opaque base64 — do not parse it. `?offset=` is also accepted as a first-class alias for shallow paging (0..10000) — see the `offset` parameter below. Mutually exclusive with `cursor`. Filters: `q` (substring on name/street/city), `status` (active|inactive|all), `lifecycle_status` (exact match on the listing's lifecycle state). Other unknown params (e.g. `?search=` or `?propertyId=`) are rejected with 422 — no silent unfiltered results. **Incremental sync (only changes since last poll):** pass `?updated_since=<ISO8601>` to receive only properties changed at or after that instant. Each property carries `updatedAt` — the last row of the final page is your next watermark. `updated_since` changes the page ordering to `updatedAt ASC, id ASC` (and the cursor with it); see the parameter description. `GET /v1/listings` does NOT yet accept `updated_since` — use this endpoint for property-side incremental sync. **Inactive properties:** an inactive property keeps syncing, but cannot be read or changed through the API until it is activated. They are only listed when `status` asks for them, and then with `id`, `name`, `status`, `lifecycleStatus`, `channels` and `updatedAt` only — enough to choose what to activate with `PATCH /v1/listings/{id}`.
92
+ # List properties (older name for /v1/listings)
93
+ # **`/v1/properties` is the older name for `/v1/listings`** — the same listings, the same ids. It stays for existing integrations; new code should use `/v1/listings`, which is where create, content, publishing and markups live. Cursor-paginated list of properties for the authenticated workspace. Walk pages with `?cursor=<pagination.nextCursor>`; stop when `pagination.hasMore` is `false`. Cursor is opaque base64 — do not parse it. `?offset=` is also accepted as a first-class alias for shallow paging (0..10000) — see the `offset` parameter below. Mutually exclusive with `cursor`. Filters: `q` (substring on name/street/city), `status` (active|inactive|all), `lifecycle_status` (exact match on the listing's lifecycle state). Other unknown params (e.g. `?search=` or `?propertyId=`) are rejected with 422 — no silent unfiltered results. **Incremental sync (only changes since last poll):** pass `?updated_since=<ISO8601>` to receive only properties changed at or after that instant. Each property carries `updatedAt` — the last row of the final page is your next watermark. `updated_since` changes the page ordering to `updatedAt ASC, id ASC` (and the cursor with it); see the parameter description. `GET /v1/listings` does NOT yet accept `updated_since` — use this endpoint for property-side incremental sync. **Inactive properties:** an inactive property keeps syncing, but cannot be read or changed through the API until it is activated. They are only listed when `status` asks for them, and then with `id`, `name`, `status`, `lifecycleStatus`, `channels` and `updatedAt` only — enough to choose what to activate with `PATCH /v1/listings/{id}`.
94
94
  # @param [Hash] opts the optional parameters
95
95
  # @option opts [Integer] :limit Page size (max 100). Requests over the cap return 422. (default to 50)
96
96
  # @option opts [String] :cursor Opaque cursor returned in the previous response&#39;s &#x60;pagination.nextCursor&#x60;. Omit to fetch the first page.
@@ -107,8 +107,8 @@ module Repull
107
107
  data
108
108
  end
109
109
 
110
- # List properties
111
- # Cursor-paginated list of properties for the authenticated workspace. Walk pages with &#x60;?cursor&#x3D;&lt;pagination.nextCursor&gt;&#x60;; stop when &#x60;pagination.hasMore&#x60; is &#x60;false&#x60;. Cursor is opaque base64 — do not parse it. &#x60;?offset&#x3D;&#x60; is also accepted as a first-class alias for shallow paging (0..10000) — see the &#x60;offset&#x60; parameter below. Mutually exclusive with &#x60;cursor&#x60;. Filters: &#x60;q&#x60; (substring on name/street/city), &#x60;status&#x60; (active|inactive|all), &#x60;lifecycle_status&#x60; (exact match on the listing&#39;s lifecycle state). Other unknown params (e.g. &#x60;?search&#x3D;&#x60; or &#x60;?propertyId&#x3D;&#x60;) are rejected with 422 — no silent unfiltered results. **Incremental sync (only changes since last poll):** pass &#x60;?updated_since&#x3D;&lt;ISO8601&gt;&#x60; to receive only properties changed at or after that instant. Each property carries &#x60;updatedAt&#x60; — the last row of the final page is your next watermark. &#x60;updated_since&#x60; changes the page ordering to &#x60;updatedAt ASC, id ASC&#x60; (and the cursor with it); see the parameter description. &#x60;GET /v1/listings&#x60; does NOT yet accept &#x60;updated_since&#x60; — use this endpoint for property-side incremental sync. **Inactive properties:** an inactive property keeps syncing, but cannot be read or changed through the API until it is activated. They are only listed when &#x60;status&#x60; asks for them, and then with &#x60;id&#x60;, &#x60;name&#x60;, &#x60;status&#x60;, &#x60;lifecycleStatus&#x60;, &#x60;channels&#x60; and &#x60;updatedAt&#x60; only — enough to choose what to activate with &#x60;PATCH /v1/listings/{id}&#x60;.
110
+ # List properties (older name for /v1/listings)
111
+ # **&#x60;/v1/properties&#x60; is the older name for &#x60;/v1/listings&#x60;** — the same listings, the same ids. It stays for existing integrations; new code should use &#x60;/v1/listings&#x60;, which is where create, content, publishing and markups live. Cursor-paginated list of properties for the authenticated workspace. Walk pages with &#x60;?cursor&#x3D;&lt;pagination.nextCursor&gt;&#x60;; stop when &#x60;pagination.hasMore&#x60; is &#x60;false&#x60;. Cursor is opaque base64 — do not parse it. &#x60;?offset&#x3D;&#x60; is also accepted as a first-class alias for shallow paging (0..10000) — see the &#x60;offset&#x60; parameter below. Mutually exclusive with &#x60;cursor&#x60;. Filters: &#x60;q&#x60; (substring on name/street/city), &#x60;status&#x60; (active|inactive|all), &#x60;lifecycle_status&#x60; (exact match on the listing&#39;s lifecycle state). Other unknown params (e.g. &#x60;?search&#x3D;&#x60; or &#x60;?propertyId&#x3D;&#x60;) are rejected with 422 — no silent unfiltered results. **Incremental sync (only changes since last poll):** pass &#x60;?updated_since&#x3D;&lt;ISO8601&gt;&#x60; to receive only properties changed at or after that instant. Each property carries &#x60;updatedAt&#x60; — the last row of the final page is your next watermark. &#x60;updated_since&#x60; changes the page ordering to &#x60;updatedAt ASC, id ASC&#x60; (and the cursor with it); see the parameter description. &#x60;GET /v1/listings&#x60; does NOT yet accept &#x60;updated_since&#x60; — use this endpoint for property-side incremental sync. **Inactive properties:** an inactive property keeps syncing, but cannot be read or changed through the API until it is activated. They are only listed when &#x60;status&#x60; asks for them, and then with &#x60;id&#x60;, &#x60;name&#x60;, &#x60;status&#x60;, &#x60;lifecycleStatus&#x60;, &#x60;channels&#x60; and &#x60;updatedAt&#x60; only — enough to choose what to activate with &#x60;PATCH /v1/listings/{id}&#x60;.
112
112
  # @param [Hash] opts the optional parameters
113
113
  # @option opts [Integer] :limit Page size (max 100). Requests over the cap return 422. (default to 50)
114
114
  # @option opts [String] :cursor Opaque cursor returned in the previous response&#39;s &#x60;pagination.nextCursor&#x60;. Omit to fetch the first page.
@@ -40,7 +40,7 @@ module Repull
40
40
 
41
41
  attr_accessor :primary
42
42
 
43
- # Decimal markup (e.g. \"1.10\" for +10%).
43
+ # The Airbnb markup as a fraction: \"0.35\" = +35% on the listing's own price. Read or set it as a percentage with `/v1/listings/{id}/markups`.
44
44
  attr_accessor :markup
45
45
 
46
46
  # Airbnb's own API sync decision for THIS listing, as Airbnb reports it. Airbnb authorises sync one listing at a time, so a connected account can still contain listings it will not accept writes for. - `sync_all` — Repull manages content, rates and availability. - `sync_rates_and_availability` — Repull manages rates and availability; listing content is managed by the host on Airbnb. - `none` — the listing is **not** connected to Repull on Airbnb's side. Every write to it is refused with `403 listing_not_api_connected`; reconnecting the Airbnb account does not change this, the host must switch the listing on in Airbnb. `null` when the listing has not synced yet. Not to be confused with `syncEnabled`, which is a Repull-side flag and says nothing about what Airbnb accepts.
@@ -28,6 +28,7 @@ module Repull
28
28
 
29
29
  attr_accessor :booking_url
30
30
 
31
+ # The Booking.com markup on this property, as a fraction: \"0.18\" = +18%, shared by every listing on the property. Read or set it as a percentage with `/v1/listings/{id}/markups`.
31
32
  attr_accessor :markup
32
33
 
33
34
  attr_accessor :sync_category
@@ -0,0 +1,170 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+ Contact: ivan@vanio.ai
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Repull
17
+ class GetListingMarkups200Response < ApiModelBase
18
+ # Repull listing id.
19
+ attr_accessor :id
20
+
21
+ attr_accessor :airbnb
22
+
23
+ attr_accessor :booking
24
+
25
+ # Attribute mapping from ruby-style variable name to JSON key.
26
+ def self.attribute_map
27
+ {
28
+ :'id' => :'id',
29
+ :'airbnb' => :'airbnb',
30
+ :'booking' => :'booking'
31
+ }
32
+ end
33
+
34
+ # Returns attribute mapping this model knows about
35
+ def self.acceptable_attribute_map
36
+ attribute_map
37
+ end
38
+
39
+ # Returns all the JSON keys this model knows about
40
+ def self.acceptable_attributes
41
+ acceptable_attribute_map.values
42
+ end
43
+
44
+ # Attribute type mapping.
45
+ def self.openapi_types
46
+ {
47
+ :'id' => :'String',
48
+ :'airbnb' => :'Array<GetListingMarkups200ResponseAirbnbInner>',
49
+ :'booking' => :'Array<GetListingMarkups200ResponseBookingInner>'
50
+ }
51
+ end
52
+
53
+ # List of attributes with nullable: true
54
+ def self.openapi_nullable
55
+ Set.new([
56
+ ])
57
+ end
58
+
59
+ # Initializes the object
60
+ # @param [Hash] attributes Model attributes in the form of hash
61
+ def initialize(attributes = {})
62
+ if (!attributes.is_a?(Hash))
63
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::GetListingMarkups200Response` initialize method"
64
+ end
65
+
66
+ # check to see if the attribute exists and convert string to symbol for hash key
67
+ acceptable_attribute_map = self.class.acceptable_attribute_map
68
+ attributes = attributes.each_with_object({}) { |(k, v), h|
69
+ if (!acceptable_attribute_map.key?(k.to_sym))
70
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::GetListingMarkups200Response`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
71
+ end
72
+ h[k.to_sym] = v
73
+ }
74
+
75
+ if attributes.key?(:'id')
76
+ self.id = attributes[:'id']
77
+ end
78
+
79
+ if attributes.key?(:'airbnb')
80
+ if (value = attributes[:'airbnb']).is_a?(Array)
81
+ self.airbnb = value
82
+ end
83
+ end
84
+
85
+ if attributes.key?(:'booking')
86
+ if (value = attributes[:'booking']).is_a?(Array)
87
+ self.booking = value
88
+ end
89
+ end
90
+ end
91
+
92
+ # Show invalid properties with the reasons. Usually used together with valid?
93
+ # @return Array for valid properties with the reasons
94
+ def list_invalid_properties
95
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
96
+ invalid_properties = Array.new
97
+ invalid_properties
98
+ end
99
+
100
+ # Check to see if the all the properties in the model are valid
101
+ # @return true if the model is valid
102
+ def valid?
103
+ warn '[DEPRECATED] the `valid?` method is obsolete'
104
+ true
105
+ end
106
+
107
+ # Checks equality by comparing each attribute.
108
+ # @param [Object] Object to be compared
109
+ def ==(o)
110
+ return true if self.equal?(o)
111
+ self.class == o.class &&
112
+ id == o.id &&
113
+ airbnb == o.airbnb &&
114
+ booking == o.booking
115
+ end
116
+
117
+ # @see the `==` method
118
+ # @param [Object] Object to be compared
119
+ def eql?(o)
120
+ self == o
121
+ end
122
+
123
+ # Calculates hash code according to all attributes.
124
+ # @return [Integer] Hash code
125
+ def hash
126
+ [id, airbnb, booking].hash
127
+ end
128
+
129
+ # Builds the object from hash
130
+ # @param [Hash] attributes Model attributes in the form of hash
131
+ # @return [Object] Returns the model itself
132
+ def self.build_from_hash(attributes)
133
+ return nil unless attributes.is_a?(Hash)
134
+ attributes = attributes.transform_keys(&:to_sym)
135
+ transformed_hash = {}
136
+ openapi_types.each_pair do |key, type|
137
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
138
+ transformed_hash["#{key}"] = nil
139
+ elsif type =~ /\AArray<(.*)>/i
140
+ # check to ensure the input is an array given that the attribute
141
+ # is documented as an array but the input is not
142
+ if attributes[attribute_map[key]].is_a?(Array)
143
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
144
+ end
145
+ elsif !attributes[attribute_map[key]].nil?
146
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
147
+ end
148
+ end
149
+ new(transformed_hash)
150
+ end
151
+
152
+ # Returns the object in the form of hash
153
+ # @return [Hash] Returns the object in the form of hash
154
+ def to_hash
155
+ hash = {}
156
+ self.class.attribute_map.each_pair do |attr, param|
157
+ value = self.send(attr)
158
+ if value.nil?
159
+ is_nullable = self.class.openapi_nullable.include?(attr)
160
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
161
+ end
162
+
163
+ hash[param] = _to_hash(value)
164
+ end
165
+ hash
166
+ end
167
+
168
+ end
169
+
170
+ end
@@ -0,0 +1,159 @@
1
+ =begin
2
+ #Repull API
3
+
4
+ #The unified API for vacation rental tech. Connect to 50+ PMS platforms and 4 OTA channels through one REST API. Built-in AI operations for guest communication, pricing, and listing optimization. ## Designed for AI agents Every error response on this API includes machine-parseable fields so an LLM (Claude in MCP, Cursor, Cline, GPT, etc.) can self-recover without escalating to a human: - `error.code` — stable string identifier (e.g. `invalid_params`, `rate_limit_exceeded`) - `error.message` — human-readable cause - `error.fix` — exact recovery steps (e.g. \"Pass `check_in_after` as ISO 8601: `?check_in_after=2026-01-15`\") - `error.docs_url` — link to the canonical write-up at `https://repull.dev/docs/errors/{code}` - `error.request_id` — id to correlate with server-side logs - `error.field` / `error.value_received` / `error.valid_values` / `error.did_you_mean` — when the error is parameter-specific - `error.retry_after` — seconds to wait before retrying (rate-limit + transient upstream) `Access-Control-Expose-Headers` lists `x-request-id` and the `X-RateLimit-*` family so browsers can read them on cross-origin responses. ## Quick Start 1. Get an API key at https://repull.dev/dashboard 2. Connect a PMS: `POST /v1/connect/{provider}` 3. List properties: `GET /v1/properties` 4. Get reservations: `GET /v1/reservations` ## Authentication All requests require a Bearer token: ``` Authorization: Bearer sk_live_YOUR_API_KEY ``` ## Request Correlation (X-Request-ID) Every response carries an `X-Request-ID` header, e.g. `X-Request-ID: req_01HXY...`. Include this id in support tickets and bug reports — we can trace the full request lifecycle (auth, rate limit, handler, downstream calls, log row) from a single id. You may set the header on the inbound request to forward your own trace id; we will echo it back instead of generating a new one. Accepted format: `^[\\\\w.-]{1,128}$`. The id is also embedded in error envelopes as `request_id` so server-side log diffs work even when the response headers are stripped by an intermediate proxy. ## Rate Limits The public API enforces a per-API-key sliding-window rate limit on top of the per-tier monthly + daily-AI quotas. **Default policy:** 600 requests per 60 seconds, per API key. Sliding window — there is no fixed-minute boundary you can burst across. Every response includes: | Header | Meaning | |---|---| | `X-RateLimit-Limit` | Requests permitted in the current window. | | `X-RateLimit-Remaining` | Requests left in the current window after this call. | | `X-RateLimit-Reset` | Unix epoch (seconds) when the next slot opens. | | `X-RateLimit-Policy` | Machine-readable policy descriptor, e.g. `600;w=60`. | | `Retry-After` | Seconds to wait before retrying. **Only present on 429 responses.** | **On 429 (rate_limit_exceeded):** the response body matches the standard error envelope with `code: \"rate_limit_exceeded\"`, plus `limit`, `window_seconds`, `retry_after`, and `request_id` fields. SDKs MUST honor `Retry-After` and use exponential backoff with jitter on subsequent retries — never a tight loop. Recommended backoff: ``` sleep_ms = (Retry-After * 1000) + random(0..250) ``` Monthly + daily-AI tier quotas (`free`, `starter`, `custom`) are enforced separately and also surface as 429s; they include `tier`, `scope`, and `resetsAt` fields. ## Plan Limits (402 — `listings_limit_exceeded`) The Repull API also enforces a per-tier cap on **active listings**: | Tier | Active listings cap | |---|---| | `free` | 3 | | `starter` | 50 | | `custom` | unlimited | When a customer's active-listing count is above their tier cap, the API returns **`402 Payment Required`** with `error.code = \"listings_limit_exceeded\"` on every route EXCEPT: - `/v1/health` — uptime probes are never gated. - `/v1/usage/*` — so dashboards can render the over-cap state. - Any `DELETE` — so the customer can trim listings to get back under the cap without paying. Unlike 429, 402 is NOT a \"wait and retry\" condition — `Retry-After` is not set. The only paths back to 200 are: 1. `DELETE` enough listings to come back under the cap, or 2. Upgrade at `https://repull.dev/dashboard/billing`. The server-side usage cache is 60s, so the first 200 after an upgrade may take up to a minute. The envelope mirrors `rate_limit_exceeded` for SDK ergonomics: `tier`, `limit`, `active_listings`, `upgrade_url`, plus the standard `code` / `message` / `fix` / `docs_url` / `request_id`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+ Contact: ivan@vanio.ai
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.22.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Repull
17
+ class GetListingMarkups200ResponseAirbnbInner < ApiModelBase
18
+ # Airbnb listing id.
19
+ attr_accessor :airbnb_id
20
+
21
+ # Percent added to the listing's price on Airbnb. 35 = +35%. `null` = none.
22
+ attr_accessor :markup_percent
23
+
24
+ # Attribute mapping from ruby-style variable name to JSON key.
25
+ def self.attribute_map
26
+ {
27
+ :'airbnb_id' => :'airbnbId',
28
+ :'markup_percent' => :'markupPercent'
29
+ }
30
+ end
31
+
32
+ # Returns attribute mapping this model knows about
33
+ def self.acceptable_attribute_map
34
+ attribute_map
35
+ end
36
+
37
+ # Returns all the JSON keys this model knows about
38
+ def self.acceptable_attributes
39
+ acceptable_attribute_map.values
40
+ end
41
+
42
+ # Attribute type mapping.
43
+ def self.openapi_types
44
+ {
45
+ :'airbnb_id' => :'String',
46
+ :'markup_percent' => :'Float'
47
+ }
48
+ end
49
+
50
+ # List of attributes with nullable: true
51
+ def self.openapi_nullable
52
+ Set.new([
53
+ :'markup_percent'
54
+ ])
55
+ end
56
+
57
+ # Initializes the object
58
+ # @param [Hash] attributes Model attributes in the form of hash
59
+ def initialize(attributes = {})
60
+ if (!attributes.is_a?(Hash))
61
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Repull::GetListingMarkups200ResponseAirbnbInner` initialize method"
62
+ end
63
+
64
+ # check to see if the attribute exists and convert string to symbol for hash key
65
+ acceptable_attribute_map = self.class.acceptable_attribute_map
66
+ attributes = attributes.each_with_object({}) { |(k, v), h|
67
+ if (!acceptable_attribute_map.key?(k.to_sym))
68
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Repull::GetListingMarkups200ResponseAirbnbInner`. Please check the name to make sure it's valid. List of attributes: " + acceptable_attribute_map.keys.inspect
69
+ end
70
+ h[k.to_sym] = v
71
+ }
72
+
73
+ if attributes.key?(:'airbnb_id')
74
+ self.airbnb_id = attributes[:'airbnb_id']
75
+ end
76
+
77
+ if attributes.key?(:'markup_percent')
78
+ self.markup_percent = attributes[:'markup_percent']
79
+ end
80
+ end
81
+
82
+ # Show invalid properties with the reasons. Usually used together with valid?
83
+ # @return Array for valid properties with the reasons
84
+ def list_invalid_properties
85
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
86
+ invalid_properties = Array.new
87
+ invalid_properties
88
+ end
89
+
90
+ # Check to see if the all the properties in the model are valid
91
+ # @return true if the model is valid
92
+ def valid?
93
+ warn '[DEPRECATED] the `valid?` method is obsolete'
94
+ true
95
+ end
96
+
97
+ # Checks equality by comparing each attribute.
98
+ # @param [Object] Object to be compared
99
+ def ==(o)
100
+ return true if self.equal?(o)
101
+ self.class == o.class &&
102
+ airbnb_id == o.airbnb_id &&
103
+ markup_percent == o.markup_percent
104
+ end
105
+
106
+ # @see the `==` method
107
+ # @param [Object] Object to be compared
108
+ def eql?(o)
109
+ self == o
110
+ end
111
+
112
+ # Calculates hash code according to all attributes.
113
+ # @return [Integer] Hash code
114
+ def hash
115
+ [airbnb_id, markup_percent].hash
116
+ end
117
+
118
+ # Builds the object from hash
119
+ # @param [Hash] attributes Model attributes in the form of hash
120
+ # @return [Object] Returns the model itself
121
+ def self.build_from_hash(attributes)
122
+ return nil unless attributes.is_a?(Hash)
123
+ attributes = attributes.transform_keys(&:to_sym)
124
+ transformed_hash = {}
125
+ openapi_types.each_pair do |key, type|
126
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
127
+ transformed_hash["#{key}"] = nil
128
+ elsif type =~ /\AArray<(.*)>/i
129
+ # check to ensure the input is an array given that the attribute
130
+ # is documented as an array but the input is not
131
+ if attributes[attribute_map[key]].is_a?(Array)
132
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
133
+ end
134
+ elsif !attributes[attribute_map[key]].nil?
135
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
136
+ end
137
+ end
138
+ new(transformed_hash)
139
+ end
140
+
141
+ # Returns the object in the form of hash
142
+ # @return [Hash] Returns the object in the form of hash
143
+ def to_hash
144
+ hash = {}
145
+ self.class.attribute_map.each_pair do |attr, param|
146
+ value = self.send(attr)
147
+ if value.nil?
148
+ is_nullable = self.class.openapi_nullable.include?(attr)
149
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
150
+ end
151
+
152
+ hash[param] = _to_hash(value)
153
+ end
154
+ hash
155
+ end
156
+
157
+ end
158
+
159
+ end