repull 0.2.26 → 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.
- checksums.yaml +4 -4
- data/lib/repull/api/airbnb_api.rb +38 -23
- data/lib/repull/api/booking_com_api.rb +9 -2
- data/lib/repull/api/connect_api.rb +4 -4
- data/lib/repull/api/listings_api.rb +2 -2
- data/lib/repull/api/properties_api.rb +2 -2
- data/lib/repull/api/reservations_api.rb +93 -6
- data/lib/repull/api/vrbo_api.rb +9 -2
- data/lib/repull/models/airbnb_listing.rb +13 -2
- data/lib/repull/models/airbnb_listing_details_write_request.rb +109 -5
- data/lib/repull/models/airbnb_listing_details_write_request_check_in_option.rb +1 -1
- data/lib/repull/models/airbnb_permits_write_request_permits_inner.rb +2 -2
- data/lib/repull/models/airbnb_pricing_write_request.rb +30 -2
- data/lib/repull/models/airbnb_pricing_write_request_fees_inner.rb +291 -0
- data/lib/repull/models/booking_property_listings_inner.rb +57 -1
- data/lib/repull/models/cancel_reservation200_response.rb +1 -1
- data/lib/repull/models/connect_status.rb +35 -4
- data/lib/repull/models/connect_status_capabilities.rb +148 -0
- data/lib/repull/models/connection.rb +28 -5
- data/lib/repull/models/connection_action.rb +188 -0
- data/lib/repull/models/create_connect_session_request.rb +39 -1
- data/lib/repull/models/create_connection_request.rb +39 -1
- data/lib/repull/models/error_error.rb +13 -1
- data/lib/repull/models/{cancel_reservation200_response_pms.rb → error_error_listings_inner.rb} +36 -30
- data/lib/repull/models/get_booking_extranet_login_status200_response.rb +2 -1
- data/lib/repull/models/list_listing_units200_response.rb +2 -1
- data/lib/repull/models/listing.rb +23 -2
- data/lib/repull/models/listing_capabilities.rb +148 -0
- data/lib/repull/models/property.rb +14 -3
- data/lib/repull/models/reservation_capabilities.rb +230 -0
- data/lib/repull/models/reservation_create_request.rb +172 -33
- data/lib/repull/models/reservation_create_response.rb +6 -6
- data/lib/repull/models/reservation_create_response_unit.rb +1 -1
- data/lib/repull/models/{reservation_create_response_pms.rb → reservation_pms_outcome.rb} +38 -8
- data/lib/repull/models/reservation_pms_outcome_quote.rb +168 -0
- data/lib/repull/models/{cancel_reservation200_response_pms_errors_inner.rb → reservation_pms_section_error.rb} +16 -16
- data/lib/repull/models/reservation_quote_request.rb +312 -0
- data/lib/repull/models/reservation_quote_response.rb +228 -0
- data/lib/repull/models/reservation_quote_response_breakdown.rb +175 -0
- data/lib/repull/models/reservation_update_response.rb +16 -6
- data/lib/repull/models/submit_smoobu_credentials_request.rb +2 -5
- data/lib/repull/models/submit_smoobu_credentials_request_credentials.rb +193 -0
- data/lib/repull/models/update_airbnb_checkin_guide200_response.rb +147 -0
- data/lib/repull/models/update_airbnb_checkin_guide200_response_data.rb +177 -0
- data/lib/repull/models/{airbnb_permits_write_request_permits_inner_answers_value.rb → update_airbnb_checkin_guide200_response_data_steps_inner.rb} +24 -43
- data/lib/repull/models/update_airbnb_checkin_guide_request.rb +186 -0
- data/lib/repull/models/update_airbnb_checkin_guide_request_steps_inner.rb +182 -0
- data/lib/repull/version.rb +1 -1
- data/lib/repull.rb +18 -4
- data/openapi/v1.json +2059 -335
- data/scripts/regen.sh +1 -1
- metadata +20 -6
data/openapi/v1.json
CHANGED
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"schemas": {
|
|
152
152
|
"Property": {
|
|
153
153
|
"type": "object",
|
|
154
|
-
"description": "A vacation rental property in your Repull workspace. Backed by the core `listings` row \u2014 enriched per-PMS fields (bedrooms, property type, provider id, etc.) live in provider-specific detail tables and are NOT returned here.\n\nField availability differs by endpoint:\n- `channels` is returned by the list endpoint (`GET /v1/properties`) only.\n- `latitude`, `longitude`, `createdAt`, and `amenities` are returned by the detail endpoint (`GET /v1/properties/{id}`) only. `amenities` requires `?include=amenities`.\n\nAn **inactive** property (`status: inactive`) appears only in the list endpoint, and only when `?status=inactive|all` asks for it. Such a row carries identity fields only \u2014 `id`, `name`, `status`, `lifecycleStatus`, `channels`, `accounts`, `updatedAt` \u2014 so every other field is absent until the property is activated. Every other endpoint answers `403 listing_inactive` for it.",
|
|
154
|
+
"description": "A vacation rental property in your Repull workspace. Backed by the core `listings` row \u2014 enriched per-PMS fields (bedrooms, property type, provider id, etc.) live in provider-specific detail tables and are NOT returned here.\n\nField availability differs by endpoint:\n- `channels` is returned by the list endpoint (`GET /v1/properties`) only.\n- `latitude`, `longitude`, `createdAt`, and `amenities` are returned by the detail endpoint (`GET /v1/properties/{id}`) only. `amenities` requires `?include=amenities`.\n\nAn **inactive** property (`status: inactive`) appears only in the list endpoint, and only when `?status=inactive|all` asks for it. Such a row carries identity fields only \u2014 `id`, `name`, `city`, `status`, `inactiveReason`, `lifecycleStatus`, `channels`, `accounts`, `updatedAt` \u2014 so every other field is absent until the property is activated. `inactiveReason` is `plan_limit` (held back by the plan; activating needs a free slot or an upgrade), `unlisted_on_airbnb`, or `deactivated` (switched off by you). Every other endpoint answers `403 listing_inactive` for it.",
|
|
155
155
|
"properties": {
|
|
156
156
|
"accounts": {
|
|
157
157
|
"type": "array",
|
|
@@ -166,9 +166,15 @@
|
|
|
166
166
|
},
|
|
167
167
|
"name": {
|
|
168
168
|
"type": "string",
|
|
169
|
-
"description": "Property name",
|
|
169
|
+
"description": "Property name \u2014 the host's internal nickname.",
|
|
170
170
|
"example": "Oceanview Suite #3"
|
|
171
171
|
},
|
|
172
|
+
"publicName": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"nullable": true,
|
|
175
|
+
"description": "The title guests see on the channel (e.g. the Airbnb listing title). `name` is the host's internal nickname for the listing; show `publicName` in anything a guest or end user reads. Present on inactive rows too.",
|
|
176
|
+
"example": "Centre Oxford bright single room D"
|
|
177
|
+
},
|
|
172
178
|
"address": {
|
|
173
179
|
"type": "string",
|
|
174
180
|
"nullable": true,
|
|
@@ -1470,7 +1476,8 @@
|
|
|
1470
1476
|
},
|
|
1471
1477
|
"status": {
|
|
1472
1478
|
"type": "string",
|
|
1473
|
-
"example": "active"
|
|
1479
|
+
"example": "active",
|
|
1480
|
+
"description": "`active` \u2014 connected and working. `pending` \u2014 still settling. `needs_permissions` \u2014 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` \u2014 the last operation failed. `disconnected` \u2014 revoked or superseded."
|
|
1474
1481
|
},
|
|
1475
1482
|
"externalAccountId": {
|
|
1476
1483
|
"type": "string",
|
|
@@ -1488,6 +1495,45 @@
|
|
|
1488
1495
|
}
|
|
1489
1496
|
],
|
|
1490
1497
|
"description": "Host metadata for the linked account. Currently populated for Airbnb only; null for other providers."
|
|
1498
|
+
},
|
|
1499
|
+
"action": {
|
|
1500
|
+
"nullable": true,
|
|
1501
|
+
"allOf": [
|
|
1502
|
+
{
|
|
1503
|
+
"$ref": "#/components/schemas/ConnectionAction"
|
|
1504
|
+
}
|
|
1505
|
+
],
|
|
1506
|
+
"description": "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."
|
|
1507
|
+
},
|
|
1508
|
+
"fixUrl": {
|
|
1509
|
+
"type": "string",
|
|
1510
|
+
"format": "uri",
|
|
1511
|
+
"nullable": true,
|
|
1512
|
+
"description": "Durable link that reopens the hosted Connect flow bound to this account on the fix screen \u2014 send the host here to resolve `action`. Present only when `action.required` is true; `null` otherwise."
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
},
|
|
1516
|
+
"ConnectionAction": {
|
|
1517
|
+
"type": "object",
|
|
1518
|
+
"description": "A host action a connection needs before it can work.",
|
|
1519
|
+
"required": [
|
|
1520
|
+
"required"
|
|
1521
|
+
],
|
|
1522
|
+
"properties": {
|
|
1523
|
+
"required": {
|
|
1524
|
+
"type": "boolean",
|
|
1525
|
+
"description": "Whether a host action is pending."
|
|
1526
|
+
},
|
|
1527
|
+
"reason": {
|
|
1528
|
+
"type": "string",
|
|
1529
|
+
"nullable": true,
|
|
1530
|
+
"example": "needs_permissions",
|
|
1531
|
+
"description": "Machine-readable reason, stable for programmatic handling."
|
|
1532
|
+
},
|
|
1533
|
+
"message": {
|
|
1534
|
+
"type": "string",
|
|
1535
|
+
"nullable": true,
|
|
1536
|
+
"description": "Host-facing one-liner describing what to do."
|
|
1491
1537
|
}
|
|
1492
1538
|
}
|
|
1493
1539
|
},
|
|
@@ -2123,9 +2169,33 @@
|
|
|
2123
2169
|
],
|
|
2124
2170
|
"description": "PMS connections only: what the app may change in the PMS. Change it with `PATCH /v1/connect/{provider}/write-policy`."
|
|
2125
2171
|
},
|
|
2172
|
+
"capabilities": {
|
|
2173
|
+
"type": "object",
|
|
2174
|
+
"description": "PMS providers only. `reservations`: which reservation writes the API performs on this connection's listings \u2014 the connector's support combined with `writePolicy`. When `connected` is false, what the connector supports once connected.",
|
|
2175
|
+
"properties": {
|
|
2176
|
+
"reservations": {
|
|
2177
|
+
"$ref": "#/components/schemas/ReservationCapabilities"
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
},
|
|
2126
2181
|
"dataFreshness": {
|
|
2127
2182
|
"type": "object",
|
|
2128
2183
|
"description": "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."
|
|
2184
|
+
},
|
|
2185
|
+
"action": {
|
|
2186
|
+
"nullable": true,
|
|
2187
|
+
"allOf": [
|
|
2188
|
+
{
|
|
2189
|
+
"$ref": "#/components/schemas/ConnectionAction"
|
|
2190
|
+
}
|
|
2191
|
+
],
|
|
2192
|
+
"description": "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."
|
|
2193
|
+
},
|
|
2194
|
+
"fixUrl": {
|
|
2195
|
+
"type": "string",
|
|
2196
|
+
"format": "uri",
|
|
2197
|
+
"nullable": true,
|
|
2198
|
+
"description": "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."
|
|
2129
2199
|
}
|
|
2130
2200
|
}
|
|
2131
2201
|
},
|
|
@@ -5377,9 +5447,15 @@
|
|
|
5377
5447
|
},
|
|
5378
5448
|
"name": {
|
|
5379
5449
|
"type": "string",
|
|
5380
|
-
"description": "
|
|
5450
|
+
"description": "The host's internal nickname for the listing.",
|
|
5381
5451
|
"example": "Oceanview Villa"
|
|
5382
5452
|
},
|
|
5453
|
+
"publicName": {
|
|
5454
|
+
"type": "string",
|
|
5455
|
+
"nullable": true,
|
|
5456
|
+
"description": "The title guests see on the channel (e.g. the Airbnb listing title). `name` is the host's internal nickname for the listing; show `publicName` in anything a guest or end user reads. Present on inactive rows too.",
|
|
5457
|
+
"example": "Centre Oxford bright single room D"
|
|
5458
|
+
},
|
|
5383
5459
|
"city": {
|
|
5384
5460
|
"type": "string",
|
|
5385
5461
|
"nullable": true,
|
|
@@ -5581,7 +5657,30 @@
|
|
|
5581
5657
|
},
|
|
5582
5658
|
"name": {
|
|
5583
5659
|
"type": "string",
|
|
5584
|
-
"nullable": true
|
|
5660
|
+
"nullable": true,
|
|
5661
|
+
"description": "The host's internal nickname for the listing."
|
|
5662
|
+
},
|
|
5663
|
+
"publicName": {
|
|
5664
|
+
"type": "string",
|
|
5665
|
+
"nullable": true,
|
|
5666
|
+
"description": "The title guests see on the channel; show this to end users."
|
|
5667
|
+
},
|
|
5668
|
+
"status": {
|
|
5669
|
+
"type": "string",
|
|
5670
|
+
"enum": [
|
|
5671
|
+
"active",
|
|
5672
|
+
"inactive"
|
|
5673
|
+
],
|
|
5674
|
+
"description": "Inactive listings appear only with `?status=inactive|all`, with identity fields only."
|
|
5675
|
+
},
|
|
5676
|
+
"inactiveReason": {
|
|
5677
|
+
"type": "string",
|
|
5678
|
+
"enum": [
|
|
5679
|
+
"plan_limit",
|
|
5680
|
+
"unlisted_on_airbnb",
|
|
5681
|
+
"deactivated"
|
|
5682
|
+
],
|
|
5683
|
+
"description": "On inactive listings only: why it is inactive."
|
|
5585
5684
|
},
|
|
5586
5685
|
"city": {
|
|
5587
5686
|
"type": "string",
|
|
@@ -6554,6 +6653,28 @@
|
|
|
6554
6653
|
"4118"
|
|
6555
6654
|
]
|
|
6556
6655
|
},
|
|
6656
|
+
"listings": {
|
|
6657
|
+
"type": "array",
|
|
6658
|
+
"description": "The same inactive listings with their names, so you can show the user which ones to activate. Present on `code: \"listing_inactive\"` (HTTP 403). `name` is null only when the request did not resolve it.",
|
|
6659
|
+
"items": {
|
|
6660
|
+
"type": "object",
|
|
6661
|
+
"required": [
|
|
6662
|
+
"id",
|
|
6663
|
+
"name"
|
|
6664
|
+
],
|
|
6665
|
+
"properties": {
|
|
6666
|
+
"id": {
|
|
6667
|
+
"type": "string",
|
|
6668
|
+
"example": "4118"
|
|
6669
|
+
},
|
|
6670
|
+
"name": {
|
|
6671
|
+
"type": "string",
|
|
6672
|
+
"nullable": true,
|
|
6673
|
+
"example": "R-Sable 1302"
|
|
6674
|
+
}
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
},
|
|
6557
6678
|
"listing_id": {
|
|
6558
6679
|
"type": "string",
|
|
6559
6680
|
"description": "The single Repull listing the error is about. Present on `code: \"listing_not_api_connected\"` (HTTP 403).",
|
|
@@ -8623,8 +8744,17 @@
|
|
|
8623
8744
|
},
|
|
8624
8745
|
"Listing": {
|
|
8625
8746
|
"type": "object",
|
|
8626
|
-
"description": "A vacation rental listing in your Repull workspace.\n\nAn **inactive** listing appears only in `GET /v1/listings`, and only when `?status=` asks for it. Such a row carries identity fields only \u2014 `id`, `name`, `status`, `channels` \u2014 so
|
|
8747
|
+
"description": "A vacation rental listing in your Repull workspace.\n\nAn **inactive** listing appears only in `GET /v1/listings`, and only when `?status=` asks for it. Such a row carries identity fields only \u2014 `id`, `name`, `status`, `inactiveReason`, `address.city`, `channels` \u2014 so the street, `content`, `details`, `createdAt` and `updatedAt` are absent until the listing is activated. `inactiveReason` is `plan_limit` (held back by the plan; activating needs a free slot or an upgrade), `unlisted_on_airbnb`, or `deactivated` (switched off by you). `GET /v1/listings/{id}` and every other listing endpoint answer `403 listing_inactive` for it. The one field you can add back is `thumbnailUrl`, by passing `?include=thumbnail` \u2014 enough to render an activate/deactivate picker with pictures from a single request.",
|
|
8627
8748
|
"properties": {
|
|
8749
|
+
"capabilities": {
|
|
8750
|
+
"type": "object",
|
|
8751
|
+
"description": "`GET /v1/listings/{id}` only. What the API can do with this listing.",
|
|
8752
|
+
"properties": {
|
|
8753
|
+
"reservations": {
|
|
8754
|
+
"$ref": "#/components/schemas/ReservationCapabilities"
|
|
8755
|
+
}
|
|
8756
|
+
}
|
|
8757
|
+
},
|
|
8628
8758
|
"units": {
|
|
8629
8759
|
"type": "array",
|
|
8630
8760
|
"description": "`GET /v1/listings/{id}` only. The physical rooms under a hotel-model listing (a Mews or Cloudbeds room type); empty for a single home. Same items as `GET /v1/listings/{id}/units`.",
|
|
@@ -8664,8 +8794,15 @@
|
|
|
8664
8794
|
},
|
|
8665
8795
|
"name": {
|
|
8666
8796
|
"type": "string",
|
|
8797
|
+
"description": "The host's internal nickname for the listing.",
|
|
8667
8798
|
"example": "I - Stafford Apartment"
|
|
8668
8799
|
},
|
|
8800
|
+
"publicName": {
|
|
8801
|
+
"type": "string",
|
|
8802
|
+
"nullable": true,
|
|
8803
|
+
"description": "The title guests see on the channel (e.g. the Airbnb listing title). `name` is the host's internal nickname for the listing; show `publicName` in anything a guest or end user reads. Present on inactive rows too.",
|
|
8804
|
+
"example": "Centre Oxford bright single room D"
|
|
8805
|
+
},
|
|
8669
8806
|
"address": {
|
|
8670
8807
|
"type": "object",
|
|
8671
8808
|
"properties": {
|
|
@@ -10318,7 +10455,64 @@
|
|
|
10318
10455
|
"type": "object",
|
|
10319
10456
|
"additionalProperties": true,
|
|
10320
10457
|
"nullable": true,
|
|
10321
|
-
"description": "Required for `type: \"standard\" | \"rate-plan\"
|
|
10458
|
+
"description": "Required for `type: \"standard\" | \"rate-plan\"` \u2014 the pricing-settings object to PUT. With `type: \"fees\"` it is the raw alternative to `fees`: `{\"standard_fees\": [...]}` **replaces every fee** on the listing (Airbnb does not merge), so send the complete list. Prefer `fees`."
|
|
10459
|
+
},
|
|
10460
|
+
"fees": {
|
|
10461
|
+
"type": "array",
|
|
10462
|
+
"minItems": 1,
|
|
10463
|
+
"nullable": true,
|
|
10464
|
+
"description": "With `type: \"fees\"` \u2014 the fee changes to apply. **Merged by `fee_type`**: fees you do not mention are kept, the ones you send are set, and `amount: null` removes that fee. (Airbnb itself replaces the whole fee list on every write, so Repull reads the listing's current fees, applies your changes and writes the full set.) The response is the listing's fees as Airbnb holds them afterwards.\n\n**Units \u2014 the same as `GET \u2026/pricing` returns:** a `flat` fee is the amount in the listing currency \u00d7 1,000,000 (`160000000` = 160.00); a `percent` fee is a whole percent of the rent (`10` = 10%).\n\nExample \u2014 add a 10% management fee and keep everything else: `{\"type\":\"fees\",\"fees\":[{\"fee_type\":\"PASS_THROUGH_MANAGEMENT_FEE\",\"amount\":10,\"amount_type\":\"percent\"}]}`. Remove the pet fee: `{\"type\":\"fees\",\"fees\":[{\"fee_type\":\"PASS_THROUGH_PET_FEE\",\"amount\":null}]}`.",
|
|
10465
|
+
"items": {
|
|
10466
|
+
"type": "object",
|
|
10467
|
+
"required": [
|
|
10468
|
+
"fee_type",
|
|
10469
|
+
"amount"
|
|
10470
|
+
],
|
|
10471
|
+
"additionalProperties": false,
|
|
10472
|
+
"properties": {
|
|
10473
|
+
"fee_type": {
|
|
10474
|
+
"type": "string",
|
|
10475
|
+
"description": "Airbnb fee type: `PASS_THROUGH_CLEANING_FEE`, `PASS_THROUGH_SHORT_TERM_CLEANING_FEE`, `PASS_THROUGH_PET_FEE`, `PASS_THROUGH_SECURITY_DEPOSIT`, `PASS_THROUGH_MANAGEMENT_FEE`, `PASS_THROUGH_RESORT_FEE`, `PASS_THROUGH_COMMUNITY_FEE`, `PASS_THROUGH_LINEN_FEE`.",
|
|
10476
|
+
"example": "PASS_THROUGH_MANAGEMENT_FEE"
|
|
10477
|
+
},
|
|
10478
|
+
"amount": {
|
|
10479
|
+
"type": "number",
|
|
10480
|
+
"nullable": true,
|
|
10481
|
+
"minimum": 0,
|
|
10482
|
+
"description": "`null` removes the fee. Flat: currency \u00d7 1,000,000. Percent: whole percent.",
|
|
10483
|
+
"example": 10
|
|
10484
|
+
},
|
|
10485
|
+
"amount_type": {
|
|
10486
|
+
"type": "string",
|
|
10487
|
+
"enum": [
|
|
10488
|
+
"flat",
|
|
10489
|
+
"percent"
|
|
10490
|
+
],
|
|
10491
|
+
"description": "Defaults to the existing fee's, else `flat`. Percent is accepted for management and resort fees."
|
|
10492
|
+
},
|
|
10493
|
+
"charge_type": {
|
|
10494
|
+
"type": "string",
|
|
10495
|
+
"enum": [
|
|
10496
|
+
"PER_GROUP",
|
|
10497
|
+
"PER_PERSON",
|
|
10498
|
+
"PER_PET"
|
|
10499
|
+
],
|
|
10500
|
+
"description": "Who it is charged per. Defaults to the existing fee's, else `PER_GROUP`."
|
|
10501
|
+
},
|
|
10502
|
+
"charge_period": {
|
|
10503
|
+
"type": "string",
|
|
10504
|
+
"enum": [
|
|
10505
|
+
"PER_BOOKING",
|
|
10506
|
+
"PER_NIGHT"
|
|
10507
|
+
],
|
|
10508
|
+
"description": "Once per booking or per night. Defaults to the existing fee's, else `PER_BOOKING`."
|
|
10509
|
+
},
|
|
10510
|
+
"offline": {
|
|
10511
|
+
"type": "boolean",
|
|
10512
|
+
"description": "Collected offline by the host rather than through Airbnb. Default `false`."
|
|
10513
|
+
}
|
|
10514
|
+
}
|
|
10515
|
+
}
|
|
10322
10516
|
},
|
|
10323
10517
|
"records": {
|
|
10324
10518
|
"type": "array",
|
|
@@ -11043,6 +11237,8 @@
|
|
|
11043
11237
|
"checkOut",
|
|
11044
11238
|
"guest"
|
|
11045
11239
|
],
|
|
11240
|
+
"additionalProperties": false,
|
|
11241
|
+
"description": "Which fields a listing takes depends on whether it is managed in a PMS \u2014 see the operation description and `GET /v1/listings/{id}` \u2192 `capabilities.reservations`. A field the listing cannot take is refused by name (`422 unsupported_field`), never dropped.",
|
|
11046
11242
|
"properties": {
|
|
11047
11243
|
"listingId": {
|
|
11048
11244
|
"type": "integer",
|
|
@@ -11066,37 +11262,73 @@
|
|
|
11066
11262
|
"platform": {
|
|
11067
11263
|
"type": "string",
|
|
11068
11264
|
"default": "direct",
|
|
11069
|
-
"description": "OTA platforms are deliberately absent \u2014 those reservations are owned by the channel and arrive through sync."
|
|
11265
|
+
"description": "OTA platforms are deliberately absent \u2014 those reservations are owned by the channel and arrive through sync. `owner` is refused on a PMS listing (block owner stays in the PMS)."
|
|
11070
11266
|
},
|
|
11071
11267
|
"status": {
|
|
11072
11268
|
"type": "string",
|
|
11073
|
-
"default": "
|
|
11074
|
-
"description": "
|
|
11269
|
+
"default": "confirmed",
|
|
11270
|
+
"description": "`confirmed` (default) or `tentative` (an optional hold, where the PMS has one). On a listing not managed in a PMS the value is passed to the reservation pipeline as before."
|
|
11271
|
+
},
|
|
11272
|
+
"adults": {
|
|
11273
|
+
"type": "integer",
|
|
11274
|
+
"minimum": 1,
|
|
11275
|
+
"example": 2
|
|
11276
|
+
},
|
|
11277
|
+
"children": {
|
|
11278
|
+
"type": "integer",
|
|
11279
|
+
"minimum": 0,
|
|
11280
|
+
"example": 1
|
|
11281
|
+
},
|
|
11282
|
+
"guestCount": {
|
|
11283
|
+
"type": "integer",
|
|
11284
|
+
"minimum": 1,
|
|
11285
|
+
"description": "Total guests. On a PMS listing without `adults`, used as the adult count.",
|
|
11286
|
+
"example": 3
|
|
11287
|
+
},
|
|
11288
|
+
"totalPrice": {
|
|
11289
|
+
"type": "number",
|
|
11290
|
+
"minimum": 0,
|
|
11291
|
+
"description": "PMS listings only: the total for the whole stay, in the listing's currency. Honoured where `capabilities.reservations.customPrice` is true; omit it and the PMS prices the stay (from its quote where it has one). Refused on a listing not managed in a PMS, whose rate engine prices the stay.",
|
|
11292
|
+
"example": 880
|
|
11293
|
+
},
|
|
11294
|
+
"notes": {
|
|
11295
|
+
"type": "string",
|
|
11296
|
+
"maxLength": 5000,
|
|
11297
|
+
"description": "PMS listings only: booking notes stored in the PMS.",
|
|
11298
|
+
"example": "Late arrival, around 22:00."
|
|
11299
|
+
},
|
|
11300
|
+
"unitId": {
|
|
11301
|
+
"type": "string",
|
|
11302
|
+
"description": "PMS listings only: book this unit (`GET /v1/listings/{id}` \u2192 `units[].id`). Refused by PMSs that cannot target a unit.",
|
|
11303
|
+
"example": "3f1c9a20"
|
|
11304
|
+
},
|
|
11305
|
+
"sendConfirmationEmail": {
|
|
11306
|
+
"type": "boolean",
|
|
11307
|
+
"description": "PMS listings only: ask the PMS to email the guest its own confirmation, where the PMS supports it.",
|
|
11308
|
+
"example": false
|
|
11075
11309
|
},
|
|
11076
11310
|
"checkInTime": {
|
|
11077
11311
|
"type": "string",
|
|
11078
11312
|
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
|
|
11313
|
+
"description": "Listings not managed in a PMS only.",
|
|
11079
11314
|
"example": "16:00"
|
|
11080
11315
|
},
|
|
11081
11316
|
"checkOutTime": {
|
|
11082
11317
|
"type": "string",
|
|
11083
11318
|
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
|
|
11319
|
+
"description": "Listings not managed in a PMS only.",
|
|
11084
11320
|
"example": "10:00"
|
|
11085
11321
|
},
|
|
11086
11322
|
"guestId": {
|
|
11087
11323
|
"type": "integer",
|
|
11088
|
-
"description": "
|
|
11324
|
+
"description": "Listings not managed in a PMS only: attach an existing guest instead of matching/creating one. Must belong to this workspace.",
|
|
11089
11325
|
"example": 91234
|
|
11090
11326
|
},
|
|
11091
|
-
"guestCount": {
|
|
11092
|
-
"type": "integer",
|
|
11093
|
-
"minimum": 1,
|
|
11094
|
-
"example": 2
|
|
11095
|
-
},
|
|
11096
11327
|
"currency": {
|
|
11097
11328
|
"type": "string",
|
|
11098
11329
|
"minLength": 3,
|
|
11099
11330
|
"maxLength": 3,
|
|
11331
|
+
"description": "Listings not managed in a PMS only (a PMS books in the property's currency).",
|
|
11100
11332
|
"example": "USD"
|
|
11101
11333
|
}
|
|
11102
11334
|
}
|
|
@@ -11105,17 +11337,17 @@
|
|
|
11105
11337
|
"type": "object",
|
|
11106
11338
|
"properties": {
|
|
11107
11339
|
"id": {
|
|
11108
|
-
"type": "
|
|
11109
|
-
"description": "Pass to `GET /v1/reservations/{id}` for the full record.",
|
|
11110
|
-
"example": 215708
|
|
11340
|
+
"type": "string",
|
|
11341
|
+
"description": "Pass to `GET /v1/reservations/{id}` for the full record. A string, like every id in API responses.",
|
|
11342
|
+
"example": "215708"
|
|
11111
11343
|
},
|
|
11112
11344
|
"confirmationCode": {
|
|
11113
11345
|
"type": "string",
|
|
11114
11346
|
"example": "DIR-8H2K4N"
|
|
11115
11347
|
},
|
|
11116
11348
|
"listingId": {
|
|
11117
|
-
"type": "
|
|
11118
|
-
"example": 4118
|
|
11349
|
+
"type": "string",
|
|
11350
|
+
"example": "4118"
|
|
11119
11351
|
},
|
|
11120
11352
|
"platform": {
|
|
11121
11353
|
"type": "string",
|
|
@@ -11135,13 +11367,14 @@
|
|
|
11135
11367
|
"format": "date"
|
|
11136
11368
|
},
|
|
11137
11369
|
"guestId": {
|
|
11138
|
-
"type": "
|
|
11139
|
-
"nullable": true
|
|
11370
|
+
"type": "string",
|
|
11371
|
+
"nullable": true,
|
|
11372
|
+
"example": "91234"
|
|
11140
11373
|
},
|
|
11141
11374
|
"totalPrice": {
|
|
11142
11375
|
"type": "number",
|
|
11143
11376
|
"nullable": true,
|
|
11144
|
-
"description": "The
|
|
11377
|
+
"description": "The total the booking was recorded at. On a PMS listing: the PMS's total (your `totalPrice` where the PMS honours one, else the PMS's own price). On any other listing: the price the rate engine derived (`0` when the listing has no rates for the range)."
|
|
11145
11378
|
},
|
|
11146
11379
|
"currency": {
|
|
11147
11380
|
"type": "string",
|
|
@@ -11150,7 +11383,7 @@
|
|
|
11150
11383
|
"unit": {
|
|
11151
11384
|
"type": "object",
|
|
11152
11385
|
"nullable": true,
|
|
11153
|
-
"description": "
|
|
11386
|
+
"description": "PMS listings: the unit the PMS assigned (hotel-model PMSs), or null. Absent for direct bookings.",
|
|
11154
11387
|
"properties": {
|
|
11155
11388
|
"id": {
|
|
11156
11389
|
"type": "string"
|
|
@@ -11162,41 +11395,7 @@
|
|
|
11162
11395
|
}
|
|
11163
11396
|
},
|
|
11164
11397
|
"pms": {
|
|
11165
|
-
"
|
|
11166
|
-
"description": "Mews or Cloudbeds listings only: the booking was made in the PMS first, and this is what it applied.",
|
|
11167
|
-
"properties": {
|
|
11168
|
-
"provider": {
|
|
11169
|
-
"type": "string",
|
|
11170
|
-
"example": "mews"
|
|
11171
|
-
},
|
|
11172
|
-
"reservationId": {
|
|
11173
|
-
"type": "string",
|
|
11174
|
-
"description": "The PMS's own id for the booking."
|
|
11175
|
-
},
|
|
11176
|
-
"applied": {
|
|
11177
|
-
"type": "array",
|
|
11178
|
-
"items": {
|
|
11179
|
-
"type": "string"
|
|
11180
|
-
}
|
|
11181
|
-
},
|
|
11182
|
-
"errors": {
|
|
11183
|
-
"type": "array",
|
|
11184
|
-
"items": {
|
|
11185
|
-
"type": "object",
|
|
11186
|
-
"properties": {
|
|
11187
|
-
"section": {
|
|
11188
|
-
"type": "string"
|
|
11189
|
-
},
|
|
11190
|
-
"message": {
|
|
11191
|
-
"type": "string"
|
|
11192
|
-
},
|
|
11193
|
-
"code": {
|
|
11194
|
-
"type": "string"
|
|
11195
|
-
}
|
|
11196
|
-
}
|
|
11197
|
-
}
|
|
11198
|
-
}
|
|
11199
|
-
}
|
|
11398
|
+
"$ref": "#/components/schemas/ReservationPmsOutcome"
|
|
11200
11399
|
}
|
|
11201
11400
|
}
|
|
11202
11401
|
},
|
|
@@ -11241,14 +11440,15 @@
|
|
|
11241
11440
|
"type": "object",
|
|
11242
11441
|
"properties": {
|
|
11243
11442
|
"id": {
|
|
11244
|
-
"type": "
|
|
11443
|
+
"type": "string",
|
|
11444
|
+
"description": "A string, like every id in API responses."
|
|
11245
11445
|
},
|
|
11246
11446
|
"confirmationCode": {
|
|
11247
11447
|
"type": "string",
|
|
11248
11448
|
"nullable": true
|
|
11249
11449
|
},
|
|
11250
11450
|
"listingId": {
|
|
11251
|
-
"type": "
|
|
11451
|
+
"type": "string",
|
|
11252
11452
|
"nullable": true
|
|
11253
11453
|
},
|
|
11254
11454
|
"checkIn": {
|
|
@@ -11288,6 +11488,240 @@
|
|
|
11288
11488
|
"example": [
|
|
11289
11489
|
"checkOut"
|
|
11290
11490
|
]
|
|
11491
|
+
},
|
|
11492
|
+
"pms": {
|
|
11493
|
+
"$ref": "#/components/schemas/ReservationPmsOutcome"
|
|
11494
|
+
}
|
|
11495
|
+
}
|
|
11496
|
+
},
|
|
11497
|
+
"ReservationPmsOutcome": {
|
|
11498
|
+
"type": "object",
|
|
11499
|
+
"description": "Present when the write was made in a PMS: what the PMS applied. `partial: true` means the booking exists in the PMS but the steps in `failedSections` (e.g. notes, a tentative state) did not apply \u2014 do not create it again.",
|
|
11500
|
+
"properties": {
|
|
11501
|
+
"provider": {
|
|
11502
|
+
"type": "string",
|
|
11503
|
+
"example": "hostaway"
|
|
11504
|
+
},
|
|
11505
|
+
"reservationId": {
|
|
11506
|
+
"type": "string",
|
|
11507
|
+
"nullable": true,
|
|
11508
|
+
"description": "The PMS's own id for the booking.",
|
|
11509
|
+
"example": "4471923"
|
|
11510
|
+
},
|
|
11511
|
+
"applied": {
|
|
11512
|
+
"type": "array",
|
|
11513
|
+
"items": {
|
|
11514
|
+
"type": "string"
|
|
11515
|
+
},
|
|
11516
|
+
"example": [
|
|
11517
|
+
"reservation"
|
|
11518
|
+
]
|
|
11519
|
+
},
|
|
11520
|
+
"errors": {
|
|
11521
|
+
"type": "array",
|
|
11522
|
+
"items": {
|
|
11523
|
+
"$ref": "#/components/schemas/ReservationPmsSectionError"
|
|
11524
|
+
}
|
|
11525
|
+
},
|
|
11526
|
+
"partial": {
|
|
11527
|
+
"type": "boolean",
|
|
11528
|
+
"example": false
|
|
11529
|
+
},
|
|
11530
|
+
"failedSections": {
|
|
11531
|
+
"type": "array",
|
|
11532
|
+
"items": {
|
|
11533
|
+
"$ref": "#/components/schemas/ReservationPmsSectionError"
|
|
11534
|
+
}
|
|
11535
|
+
},
|
|
11536
|
+
"quote": {
|
|
11537
|
+
"type": "object",
|
|
11538
|
+
"description": "Create only: the PMS quote the booking was priced from, when no `totalPrice` was sent.",
|
|
11539
|
+
"properties": {
|
|
11540
|
+
"available": {
|
|
11541
|
+
"type": "boolean"
|
|
11542
|
+
},
|
|
11543
|
+
"total": {
|
|
11544
|
+
"type": "number",
|
|
11545
|
+
"nullable": true
|
|
11546
|
+
},
|
|
11547
|
+
"currency": {
|
|
11548
|
+
"type": "string",
|
|
11549
|
+
"nullable": true
|
|
11550
|
+
}
|
|
11551
|
+
}
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11554
|
+
},
|
|
11555
|
+
"ReservationPmsSectionError": {
|
|
11556
|
+
"type": "object",
|
|
11557
|
+
"properties": {
|
|
11558
|
+
"section": {
|
|
11559
|
+
"type": "string",
|
|
11560
|
+
"example": "notes"
|
|
11561
|
+
},
|
|
11562
|
+
"code": {
|
|
11563
|
+
"type": "string",
|
|
11564
|
+
"example": "rejected"
|
|
11565
|
+
},
|
|
11566
|
+
"message": {
|
|
11567
|
+
"type": "string",
|
|
11568
|
+
"example": "Smoobu: notice too long"
|
|
11569
|
+
}
|
|
11570
|
+
}
|
|
11571
|
+
},
|
|
11572
|
+
"ReservationQuoteRequest": {
|
|
11573
|
+
"type": "object",
|
|
11574
|
+
"required": [
|
|
11575
|
+
"listingId",
|
|
11576
|
+
"checkIn",
|
|
11577
|
+
"checkOut"
|
|
11578
|
+
],
|
|
11579
|
+
"additionalProperties": false,
|
|
11580
|
+
"properties": {
|
|
11581
|
+
"listingId": {
|
|
11582
|
+
"type": "integer",
|
|
11583
|
+
"example": 4118
|
|
11584
|
+
},
|
|
11585
|
+
"checkIn": {
|
|
11586
|
+
"type": "string",
|
|
11587
|
+
"format": "date",
|
|
11588
|
+
"example": "2026-10-01"
|
|
11589
|
+
},
|
|
11590
|
+
"checkOut": {
|
|
11591
|
+
"type": "string",
|
|
11592
|
+
"format": "date",
|
|
11593
|
+
"description": "Must be after `checkIn`.",
|
|
11594
|
+
"example": "2026-10-05"
|
|
11595
|
+
},
|
|
11596
|
+
"adults": {
|
|
11597
|
+
"type": "integer",
|
|
11598
|
+
"minimum": 1,
|
|
11599
|
+
"example": 2
|
|
11600
|
+
},
|
|
11601
|
+
"children": {
|
|
11602
|
+
"type": "integer",
|
|
11603
|
+
"minimum": 0,
|
|
11604
|
+
"example": 1
|
|
11605
|
+
},
|
|
11606
|
+
"guestCount": {
|
|
11607
|
+
"type": "integer",
|
|
11608
|
+
"minimum": 1,
|
|
11609
|
+
"description": "Total guests, when you do not split adults and children.",
|
|
11610
|
+
"example": 3
|
|
11611
|
+
},
|
|
11612
|
+
"unitId": {
|
|
11613
|
+
"type": "string",
|
|
11614
|
+
"description": "Quote one unit (`GET /v1/listings/{id}` \u2192 `units[].id`).",
|
|
11615
|
+
"example": "3f1c9a20"
|
|
11616
|
+
}
|
|
11617
|
+
}
|
|
11618
|
+
},
|
|
11619
|
+
"ReservationQuoteResponse": {
|
|
11620
|
+
"type": "object",
|
|
11621
|
+
"properties": {
|
|
11622
|
+
"listingId": {
|
|
11623
|
+
"type": "string",
|
|
11624
|
+
"example": "4118"
|
|
11625
|
+
},
|
|
11626
|
+
"provider": {
|
|
11627
|
+
"type": "string",
|
|
11628
|
+
"description": "The PMS that priced it.",
|
|
11629
|
+
"example": "hostaway"
|
|
11630
|
+
},
|
|
11631
|
+
"checkIn": {
|
|
11632
|
+
"type": "string",
|
|
11633
|
+
"format": "date"
|
|
11634
|
+
},
|
|
11635
|
+
"checkOut": {
|
|
11636
|
+
"type": "string",
|
|
11637
|
+
"format": "date"
|
|
11638
|
+
},
|
|
11639
|
+
"available": {
|
|
11640
|
+
"type": "boolean",
|
|
11641
|
+
"description": "Whether the PMS would take the booking as asked.",
|
|
11642
|
+
"example": true
|
|
11643
|
+
},
|
|
11644
|
+
"total": {
|
|
11645
|
+
"type": "number",
|
|
11646
|
+
"nullable": true,
|
|
11647
|
+
"description": "Total for the stay, in `currency`. Null when the PMS gave no price (e.g. not available).",
|
|
11648
|
+
"example": 880
|
|
11649
|
+
},
|
|
11650
|
+
"currency": {
|
|
11651
|
+
"type": "string",
|
|
11652
|
+
"nullable": true,
|
|
11653
|
+
"example": "USD"
|
|
11654
|
+
},
|
|
11655
|
+
"breakdown": {
|
|
11656
|
+
"type": "object",
|
|
11657
|
+
"nullable": true,
|
|
11658
|
+
"description": "The parts the PMS itemized; absent parts were not itemized.",
|
|
11659
|
+
"properties": {
|
|
11660
|
+
"accommodation": {
|
|
11661
|
+
"type": "number"
|
|
11662
|
+
},
|
|
11663
|
+
"cleaningFee": {
|
|
11664
|
+
"type": "number"
|
|
11665
|
+
},
|
|
11666
|
+
"taxes": {
|
|
11667
|
+
"type": "number"
|
|
11668
|
+
},
|
|
11669
|
+
"fees": {
|
|
11670
|
+
"type": "number"
|
|
11671
|
+
}
|
|
11672
|
+
}
|
|
11673
|
+
},
|
|
11674
|
+
"restrictions": {
|
|
11675
|
+
"type": "array",
|
|
11676
|
+
"items": {
|
|
11677
|
+
"type": "string"
|
|
11678
|
+
},
|
|
11679
|
+
"description": "The PMS's reasons, verbatim, when `available` is false.",
|
|
11680
|
+
"example": []
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11683
|
+
},
|
|
11684
|
+
"ReservationCapabilities": {
|
|
11685
|
+
"type": "object",
|
|
11686
|
+
"description": "Which reservation writes the API performs for this listing (or, on `GET /v1/connect/{provider}`, for any listing of that connection). Derived from the PMS connector, the connection, and its write policy \u2014 a flag is true only when all three allow it.",
|
|
11687
|
+
"properties": {
|
|
11688
|
+
"managedBy": {
|
|
11689
|
+
"type": "string",
|
|
11690
|
+
"description": "`pms` \u2014 booked in the connected PMS; `repull` \u2014 a direct booking made in Repull."
|
|
11691
|
+
},
|
|
11692
|
+
"provider": {
|
|
11693
|
+
"type": "string",
|
|
11694
|
+
"nullable": true,
|
|
11695
|
+
"example": "hostaway"
|
|
11696
|
+
},
|
|
11697
|
+
"create": {
|
|
11698
|
+
"type": "boolean",
|
|
11699
|
+
"description": "`POST /v1/reservations`."
|
|
11700
|
+
},
|
|
11701
|
+
"modify": {
|
|
11702
|
+
"type": "boolean",
|
|
11703
|
+
"description": "`PATCH /v1/reservations/{id}`."
|
|
11704
|
+
},
|
|
11705
|
+
"cancel": {
|
|
11706
|
+
"type": "boolean",
|
|
11707
|
+
"description": "`POST /v1/reservations/{id}/cancel`."
|
|
11708
|
+
},
|
|
11709
|
+
"quote": {
|
|
11710
|
+
"type": "boolean",
|
|
11711
|
+
"description": "`POST /v1/reservations/quote`."
|
|
11712
|
+
},
|
|
11713
|
+
"customPrice": {
|
|
11714
|
+
"type": "boolean",
|
|
11715
|
+
"description": "`totalPrice` on create is honoured; otherwise the PMS (or the rate engine) prices the stay."
|
|
11716
|
+
},
|
|
11717
|
+
"notes": {
|
|
11718
|
+
"type": "string",
|
|
11719
|
+
"description": "What the flags do not say: limits, required access, and why something is off."
|
|
11720
|
+
},
|
|
11721
|
+
"verifiedAgainst": {
|
|
11722
|
+
"type": "string",
|
|
11723
|
+
"nullable": true,
|
|
11724
|
+
"description": "`sandbox` \u2014 run end to end on the vendor sandbox (Mews, Cloudbeds); `vendor_docs` \u2014 verified against the vendor's API documentation only. Null for direct bookings."
|
|
11291
11725
|
}
|
|
11292
11726
|
}
|
|
11293
11727
|
},
|
|
@@ -12747,31 +13181,13 @@
|
|
|
12747
13181
|
"answers": {
|
|
12748
13182
|
"type": "object",
|
|
12749
13183
|
"minProperties": 1,
|
|
12750
|
-
"description": "Keyed by each question's `answer_key`. Each value carries exactly one field
|
|
13184
|
+
"description": "Keyed by each question's `answer_key`. Each value carries exactly one `<type>_value` field named after the question's `type` (lower-case): `text_value`, `attestation_value` (boolean), `radio_value`, `dropdown_value`, `email_value`, `future_date_value` (YYYY-MM-DD) and `file_upload_value` (object with the base64 file) are the ones Airbnb returns in production; other question types follow the same pattern. Airbnb validates the value against its question. Example: `{\"email\": {\"email_value\": \"host@example.com\"}, \"expiration_date\": {\"future_date_value\": \"2029-02-04\"}, \"attestation\": {\"attestation_value\": true}}`.",
|
|
12751
13185
|
"additionalProperties": {
|
|
12752
13186
|
"type": "object",
|
|
12753
|
-
"
|
|
12754
|
-
"
|
|
12755
|
-
|
|
12756
|
-
|
|
12757
|
-
},
|
|
12758
|
-
"attestation_value": {
|
|
12759
|
-
"type": "boolean"
|
|
12760
|
-
},
|
|
12761
|
-
"radio_value": {
|
|
12762
|
-
"type": "string"
|
|
12763
|
-
},
|
|
12764
|
-
"date_value": {
|
|
12765
|
-
"type": "string",
|
|
12766
|
-
"description": "ISO date, YYYY-MM-DD."
|
|
12767
|
-
},
|
|
12768
|
-
"selected_options_value": {
|
|
12769
|
-
"type": "array",
|
|
12770
|
-
"items": {
|
|
12771
|
-
"type": "string"
|
|
12772
|
-
}
|
|
12773
|
-
}
|
|
12774
|
-
}
|
|
13187
|
+
"minProperties": 1,
|
|
13188
|
+
"maxProperties": 1,
|
|
13189
|
+
"description": "Exactly one `<type>_value` field.",
|
|
13190
|
+
"additionalProperties": true
|
|
12775
13191
|
}
|
|
12776
13192
|
}
|
|
12777
13193
|
}
|
|
@@ -12855,7 +13271,7 @@
|
|
|
12855
13271
|
},
|
|
12856
13272
|
"AirbnbListingDetailsWriteRequest": {
|
|
12857
13273
|
"type": "object",
|
|
12858
|
-
"description": "Update what kind of property this is, when the quiet hours are,
|
|
13274
|
+
"description": "Update what kind of property this is, when the quiet hours are, how the guest gets in, the house manual, directions or Wi-Fi details. At least one field required. These are among the attributes Airbnb locks on established listings \u2014 see `blockedFields` on the response.",
|
|
12859
13275
|
"additionalProperties": false,
|
|
12860
13276
|
"properties": {
|
|
12861
13277
|
"property_type_group": {
|
|
@@ -12899,7 +13315,7 @@
|
|
|
12899
13315
|
"category"
|
|
12900
13316
|
],
|
|
12901
13317
|
"additionalProperties": false,
|
|
12902
|
-
"description": "How the guest lets themselves in \u2014 Airbnb's `check_in_option`.",
|
|
13318
|
+
"description": "How the guest lets themselves in \u2014 Airbnb's `check_in_option`. `instruction` is the arrival instructions the guest sees.",
|
|
12903
13319
|
"properties": {
|
|
12904
13320
|
"category": {
|
|
12905
13321
|
"type": "string",
|
|
@@ -12918,6 +13334,30 @@
|
|
|
12918
13334
|
"maxLength": 2000
|
|
12919
13335
|
}
|
|
12920
13336
|
}
|
|
13337
|
+
},
|
|
13338
|
+
"house_manual": {
|
|
13339
|
+
"type": "string",
|
|
13340
|
+
"nullable": true,
|
|
13341
|
+
"maxLength": 10000,
|
|
13342
|
+
"description": "The house manual guests see after booking."
|
|
13343
|
+
},
|
|
13344
|
+
"directions": {
|
|
13345
|
+
"type": "string",
|
|
13346
|
+
"nullable": true,
|
|
13347
|
+
"maxLength": 5000,
|
|
13348
|
+
"description": "Directions to the property, shown to booked guests."
|
|
13349
|
+
},
|
|
13350
|
+
"wifi_network": {
|
|
13351
|
+
"type": "string",
|
|
13352
|
+
"nullable": true,
|
|
13353
|
+
"maxLength": 255,
|
|
13354
|
+
"description": "Wi-Fi network name."
|
|
13355
|
+
},
|
|
13356
|
+
"wifi_password": {
|
|
13357
|
+
"type": "string",
|
|
13358
|
+
"nullable": true,
|
|
13359
|
+
"maxLength": 255,
|
|
13360
|
+
"description": "Wi-Fi password."
|
|
12921
13361
|
}
|
|
12922
13362
|
}
|
|
12923
13363
|
},
|
|
@@ -13197,6 +13637,12 @@
|
|
|
13197
13637
|
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
13198
13638
|
"listing_ids": [
|
|
13199
13639
|
"4118"
|
|
13640
|
+
],
|
|
13641
|
+
"listings": [
|
|
13642
|
+
{
|
|
13643
|
+
"id": "4118",
|
|
13644
|
+
"name": "R-Sable 1302"
|
|
13645
|
+
}
|
|
13200
13646
|
]
|
|
13201
13647
|
}
|
|
13202
13648
|
}
|
|
@@ -13736,7 +14182,7 @@
|
|
|
13736
14182
|
],
|
|
13737
14183
|
"default": "active"
|
|
13738
14184
|
},
|
|
13739
|
-
"description": "Filter by status. Default returns active only; pass `inactive` to invert or `all` to include both. Inactive properties carry identity fields only \u2014 `id`, `name`, `status`, `lifecycleStatus`, `channels` and `updatedAt` \u2014 never `address
|
|
14185
|
+
"description": "Filter by status. Default returns active only; pass `inactive` to invert or `all` to include both. Inactive properties carry identity fields only \u2014 `id`, `name`, `city`, `status`, `inactiveReason` (`plan_limit`, `unlisted_on_airbnb` or `deactivated`), `lifecycleStatus`, `channels` and `updatedAt` \u2014 never `address` or `currency`."
|
|
13740
14186
|
},
|
|
13741
14187
|
{
|
|
13742
14188
|
"name": "lifecycle_status",
|
|
@@ -14157,13 +14603,23 @@
|
|
|
14157
14603
|
"post": {
|
|
14158
14604
|
"operationId": "create_reservation",
|
|
14159
14605
|
"summary": "Create a reservation",
|
|
14160
|
-
"description": "Creates a reservation and everything that hangs off one
|
|
14606
|
+
"description": "Creates a reservation \u2014 in the listing's PMS when it has one, otherwise as a direct booking in Repull.\n\n### Where the booking is made\n\n- **A listing managed in a connected PMS** (Mews, Cloudbeds, Hostaway, Guesty, Beds24, BookingSync, Lodgify, Smoobu, Hospitable, iGMS, OwnerRez): the booking is created **in the PMS first**, then recorded in Repull from the PMS's own record, so the next sync lands on the same confirmation code and nothing is duplicated. A booking is **never** created only in Repull for such a listing \u2014 the PMS would keep selling the dates. What the PMS cannot do is refused (`422 pms_write_unsupported`), never faked. The PMS checks availability: taken dates answer `409 pms_unavailable`.\n- **Any other listing**: a direct booking made in Repull, with everything that hangs off one \u2014 the guest, the conversation, the calendar block and the `reservation.created` fan-out that issues the door code and starts the messaging automations. Priced by the listing's own rates; **availability is NOT checked** (call `GET /v1/availability/{propertyId}` first if that matters).\n\n`GET /v1/listings/{id}` \u2192 `capabilities.reservations` says which applies to a listing and exactly what it supports (`create`, `modify`, `cancel`, `quote`, `customPrice`, plus `notes`).\n\n### Fields by listing kind\n\n| Field | PMS listing | Direct-booking listing |\n|---|---|---|\n| `listingId`, `checkIn`, `checkOut`, `guest`, `guestCount`, `adults`, `children` | \u2713 | \u2713 |\n| `status` | `confirmed` (default) or `tentative` | \u2713 |\n| `totalPrice` | \u2713 where `capabilities.reservations.customPrice`; otherwise the PMS prices the stay | `422 unsupported_field` (priced from the listing's rates) |\n| `notes`, `unitId`, `sendConfirmationEmail` | \u2713 | `422 unsupported_field` |\n| `checkInTime`, `checkOutTime`, `currency`, `guestId` | `422 unsupported_field` (the PMS's own settings apply) | \u2713 |\n| `platform` | `direct` or `website` (`owner` \u2192 `422 pms_write_unsupported`; block owner stays in the PMS) | `direct`, `website` or `owner` |\n\nA field a listing cannot take is refused by name, never silently dropped. `platform` never accepts `airbnb` / `booking` / `vrbo`: those reservations are owned by the channel and arrive through sync.\n\n### Per-PMS limits\n\n| PMS | create | change | cancel | quote | `totalPrice` | Limits |\n|---|---|---|---|---|---|---|\n| Mews | \u2713 | \u2713 | \u2713 | \u2013 | \u2713 | \u2014 |\n| Cloudbeds | \u2713 | \u2713 | \u2713 | \u2013 | \u2013 | Books at the rate plan's price; group bookings supported. |\n| Hostaway | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Direct-channel bookings only; a specific unit is refused; a date change keeps the booked total. |\n| Guesty | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Cancels direct and Vrbo bookings; other channel bookings are cancelled on the channel. |\n| Beds24 | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Needs the `write:bookings` scope; a multi-room property needs `unitId`. |\n| BookingSync | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Needs `bookings_write`; fees and taxes are not itemized; no guest email. |\n| Lodgify | \u2713 | \u2713 | \u2713 (declines) | \u2713 | \u2713 | Cancel declines the booking; single-room bookings. |\n| Smoobu | \u2713 | \u2713 (no dates) | \u2713 | \u2713 | \u2713 | Dates cannot be changed through Smoobu's API \u2014 cancel and rebook, or change them in Smoobu. |\n| Hospitable | \u2713 | \u2713 | \u2713 | \u2713 (Direct plan) | \u2713 | Manual reservations only; needs `reservation:write`; adds no fees or taxes. |\n| iGMS | \u2713 | \u2713 | \u2713 | \u2013 | \u2713 (required) | iGMS direct bookings only; a price is required; no tentative holds. |\n| OwnerRez | \u2713 | \u2713 | \u2013 | \u2713 | \u2013 | No cancel through OwnerRez's API; priced by the property's own rates; needs the `full` scope. |\n\nEvery PMS except Cloudbeds refuses group bookings, and every vacation-rental PMS refuses to change or cancel a booking that came from a channel (Airbnb, Booking.com, Vrbo\u2026) \u2014 that is done on the channel.\n\n**Verification.** Mews and Cloudbeds were run end to end on their vendors' sandboxes. Every other PMS is **verified against the vendor's API documentation only** \u2014 no live account has been written to yet. `capabilities.reservations.verifiedAgainst` says which.\n\n### Idempotency\n\n**Send `Idempotency-Key`.** A network timeout here is exactly the case it exists for. The key is also sent to the PMS as the booking's reference, so even a retry that reaches the PMS again finds the booking instead of making a second one (`409 pms_duplicate` with `existing`, or the existing booking returned). A completed answer is replayed with `Idempotency-Status: cached` and the PMS is not called again. `502 pms_error` (the PMS could not be reached) is NOT stored \u2014 retry with the same key. `502 reservation_created_in_pms_only` IS stored, unlike every other 5xx: the booking exists in the PMS and arrives with the next sync, so a retry replays that answer rather than booking twice.\n\n### Partial success\n\nWhen the PMS created the booking but a follow-up step did not apply (for example the notes, or a tentative state), the response is still `201`, with `pms.partial: true` and the steps in `pms.failedSections`. The booking exists \u2014 do not create it again.\n\n`X-Account-Id` restricts the listing to one connected account. Returns `403 listing_inactive` when the listing is inactive.",
|
|
14161
14607
|
"tags": [
|
|
14162
14608
|
"Reservations"
|
|
14163
14609
|
],
|
|
14164
14610
|
"parameters": [
|
|
14165
14611
|
{
|
|
14166
14612
|
"$ref": "#/components/parameters/IdempotencyKey"
|
|
14613
|
+
},
|
|
14614
|
+
{
|
|
14615
|
+
"name": "X-Account-Id",
|
|
14616
|
+
"in": "header",
|
|
14617
|
+
"required": false,
|
|
14618
|
+
"schema": {
|
|
14619
|
+
"type": "string",
|
|
14620
|
+
"example": "126"
|
|
14621
|
+
},
|
|
14622
|
+
"description": "Restrict the request to one connected account (a Repull connection id, `GET /v1/connect` \u2192 `id`). A listing or reservation outside that account answers `404 not_found`. Omit it to act workspace-wide."
|
|
14167
14623
|
}
|
|
14168
14624
|
],
|
|
14169
14625
|
"requestBody": {
|
|
@@ -14172,17 +14628,159 @@
|
|
|
14172
14628
|
"application/json": {
|
|
14173
14629
|
"schema": {
|
|
14174
14630
|
"$ref": "#/components/schemas/ReservationCreateRequest"
|
|
14631
|
+
},
|
|
14632
|
+
"examples": {
|
|
14633
|
+
"pms": {
|
|
14634
|
+
"summary": "A listing managed in a PMS, at a set price",
|
|
14635
|
+
"value": {
|
|
14636
|
+
"listingId": 4118,
|
|
14637
|
+
"checkIn": "2026-10-01",
|
|
14638
|
+
"checkOut": "2026-10-05",
|
|
14639
|
+
"guest": {
|
|
14640
|
+
"firstName": "Ada",
|
|
14641
|
+
"lastName": "Lovelace",
|
|
14642
|
+
"email": "ada@example.com",
|
|
14643
|
+
"phone": "+14035551234"
|
|
14644
|
+
},
|
|
14645
|
+
"adults": 2,
|
|
14646
|
+
"children": 1,
|
|
14647
|
+
"totalPrice": 880,
|
|
14648
|
+
"notes": "Late arrival, around 22:00.",
|
|
14649
|
+
"status": "confirmed",
|
|
14650
|
+
"sendConfirmationEmail": false
|
|
14651
|
+
}
|
|
14652
|
+
},
|
|
14653
|
+
"direct": {
|
|
14654
|
+
"summary": "A direct booking on a listing no PMS manages",
|
|
14655
|
+
"value": {
|
|
14656
|
+
"listingId": 4119,
|
|
14657
|
+
"checkIn": "2026-10-01",
|
|
14658
|
+
"checkOut": "2026-10-05",
|
|
14659
|
+
"guest": {
|
|
14660
|
+
"firstName": "Ada",
|
|
14661
|
+
"email": "ada@example.com"
|
|
14662
|
+
},
|
|
14663
|
+
"guestCount": 2,
|
|
14664
|
+
"checkInTime": "16:00",
|
|
14665
|
+
"currency": "USD"
|
|
14666
|
+
}
|
|
14667
|
+
}
|
|
14175
14668
|
}
|
|
14176
14669
|
}
|
|
14177
14670
|
}
|
|
14178
14671
|
},
|
|
14179
14672
|
"responses": {
|
|
14180
14673
|
"201": {
|
|
14181
|
-
"description": "Reservation created",
|
|
14674
|
+
"description": "Reservation created. `pms` is present when it was booked in a PMS; `pms.partial: true` means the booking exists but `pms.failedSections` did not apply.",
|
|
14182
14675
|
"content": {
|
|
14183
14676
|
"application/json": {
|
|
14184
14677
|
"schema": {
|
|
14185
14678
|
"$ref": "#/components/schemas/ReservationCreateResponse"
|
|
14679
|
+
},
|
|
14680
|
+
"examples": {
|
|
14681
|
+
"pms": {
|
|
14682
|
+
"summary": "Booked in Hostaway",
|
|
14683
|
+
"value": {
|
|
14684
|
+
"id": "215708",
|
|
14685
|
+
"confirmationCode": "HA-4471923",
|
|
14686
|
+
"listingId": "4118",
|
|
14687
|
+
"platform": "direct",
|
|
14688
|
+
"status": "confirmed",
|
|
14689
|
+
"checkIn": "2026-10-01",
|
|
14690
|
+
"checkOut": "2026-10-05",
|
|
14691
|
+
"guestId": "91234",
|
|
14692
|
+
"totalPrice": 880,
|
|
14693
|
+
"currency": "USD",
|
|
14694
|
+
"unit": null,
|
|
14695
|
+
"pms": {
|
|
14696
|
+
"provider": "hostaway",
|
|
14697
|
+
"reservationId": "4471923",
|
|
14698
|
+
"applied": [
|
|
14699
|
+
"reservation"
|
|
14700
|
+
],
|
|
14701
|
+
"errors": [],
|
|
14702
|
+
"partial": false,
|
|
14703
|
+
"failedSections": []
|
|
14704
|
+
}
|
|
14705
|
+
}
|
|
14706
|
+
},
|
|
14707
|
+
"partial": {
|
|
14708
|
+
"summary": "Booked in Smoobu; the notes did not apply",
|
|
14709
|
+
"value": {
|
|
14710
|
+
"id": "215709",
|
|
14711
|
+
"confirmationCode": "SM-1290031",
|
|
14712
|
+
"listingId": "4120",
|
|
14713
|
+
"platform": "direct",
|
|
14714
|
+
"status": "confirmed",
|
|
14715
|
+
"checkIn": "2026-10-01",
|
|
14716
|
+
"checkOut": "2026-10-05",
|
|
14717
|
+
"guestId": "91235",
|
|
14718
|
+
"totalPrice": 640,
|
|
14719
|
+
"currency": "EUR",
|
|
14720
|
+
"unit": null,
|
|
14721
|
+
"pms": {
|
|
14722
|
+
"provider": "smoobu",
|
|
14723
|
+
"reservationId": "1290031",
|
|
14724
|
+
"applied": [
|
|
14725
|
+
"reservation"
|
|
14726
|
+
],
|
|
14727
|
+
"errors": [
|
|
14728
|
+
{
|
|
14729
|
+
"section": "notes",
|
|
14730
|
+
"code": "rejected",
|
|
14731
|
+
"message": "Smoobu: notice too long"
|
|
14732
|
+
}
|
|
14733
|
+
],
|
|
14734
|
+
"partial": true,
|
|
14735
|
+
"failedSections": [
|
|
14736
|
+
{
|
|
14737
|
+
"section": "notes",
|
|
14738
|
+
"code": "rejected",
|
|
14739
|
+
"message": "Smoobu: notice too long"
|
|
14740
|
+
}
|
|
14741
|
+
]
|
|
14742
|
+
}
|
|
14743
|
+
}
|
|
14744
|
+
},
|
|
14745
|
+
"direct": {
|
|
14746
|
+
"summary": "A direct booking",
|
|
14747
|
+
"value": {
|
|
14748
|
+
"id": "215710",
|
|
14749
|
+
"confirmationCode": "DIR-8H2K4N",
|
|
14750
|
+
"listingId": "4119",
|
|
14751
|
+
"platform": "direct",
|
|
14752
|
+
"status": "confirmed",
|
|
14753
|
+
"checkIn": "2026-10-01",
|
|
14754
|
+
"checkOut": "2026-10-05",
|
|
14755
|
+
"guestId": "91236",
|
|
14756
|
+
"totalPrice": 0,
|
|
14757
|
+
"currency": "USD"
|
|
14758
|
+
}
|
|
14759
|
+
}
|
|
14760
|
+
}
|
|
14761
|
+
}
|
|
14762
|
+
}
|
|
14763
|
+
},
|
|
14764
|
+
"400": {
|
|
14765
|
+
"description": "The body is not JSON.",
|
|
14766
|
+
"content": {
|
|
14767
|
+
"application/json": {
|
|
14768
|
+
"schema": {
|
|
14769
|
+
"$ref": "#/components/schemas/Error"
|
|
14770
|
+
},
|
|
14771
|
+
"examples": {
|
|
14772
|
+
"invalidJson": {
|
|
14773
|
+
"summary": "The body is not JSON",
|
|
14774
|
+
"value": {
|
|
14775
|
+
"error": {
|
|
14776
|
+
"code": "invalid_json",
|
|
14777
|
+
"message": "Request body is not valid JSON: Unexpected token",
|
|
14778
|
+
"fix": "Send a JSON object with `Content-Type: application/json`.",
|
|
14779
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_json",
|
|
14780
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
14781
|
+
}
|
|
14782
|
+
}
|
|
14783
|
+
}
|
|
14186
14784
|
}
|
|
14187
14785
|
}
|
|
14188
14786
|
}
|
|
@@ -14191,23 +14789,295 @@
|
|
|
14191
14789
|
"$ref": "#/components/responses/Unauthorized"
|
|
14192
14790
|
},
|
|
14193
14791
|
"403": {
|
|
14194
|
-
"
|
|
14792
|
+
"description": "The listing is inactive (`listing_inactive`), or the PMS connection lacks write access to bookings (`connection_reauth_required` \u2014 reconnect, then retry with the same key).",
|
|
14793
|
+
"content": {
|
|
14794
|
+
"application/json": {
|
|
14795
|
+
"schema": {
|
|
14796
|
+
"$ref": "#/components/schemas/Error"
|
|
14797
|
+
},
|
|
14798
|
+
"examples": {
|
|
14799
|
+
"reauthRequired": {
|
|
14800
|
+
"summary": "The grant lacks write access to bookings",
|
|
14801
|
+
"value": {
|
|
14802
|
+
"error": {
|
|
14803
|
+
"code": "connection_reauth_required",
|
|
14804
|
+
"message": "Beds24 needs to be reconnected before Repull can create reservations there: Beds24: token lacks scope write:bookings",
|
|
14805
|
+
"fix": "Reconnect Beds24 with an invite code that grants `write:bookings` (plus `bookings-personal` and `bookings-financial`), then retry.",
|
|
14806
|
+
"docs_url": "https://repull.dev/docs/errors/connection_reauth_required",
|
|
14807
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14808
|
+
"provider": "beds24",
|
|
14809
|
+
"reconnect": {
|
|
14810
|
+
"method": "POST",
|
|
14811
|
+
"path": "/v1/connect/beds24"
|
|
14812
|
+
}
|
|
14813
|
+
}
|
|
14814
|
+
}
|
|
14815
|
+
},
|
|
14816
|
+
"listingInactive": {
|
|
14817
|
+
"summary": "The listing is inactive",
|
|
14818
|
+
"value": {
|
|
14819
|
+
"error": {
|
|
14820
|
+
"code": "listing_inactive",
|
|
14821
|
+
"message": "Listing 4118 is inactive.",
|
|
14822
|
+
"fix": "Activate it with `PATCH /v1/listings/{id}` `{\"active\": true}`, then retry.",
|
|
14823
|
+
"docs_url": "https://repull.dev/docs/errors/listing_inactive",
|
|
14824
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
14825
|
+
}
|
|
14826
|
+
}
|
|
14827
|
+
}
|
|
14828
|
+
}
|
|
14829
|
+
}
|
|
14830
|
+
}
|
|
14195
14831
|
},
|
|
14196
14832
|
"404": {
|
|
14197
|
-
"description": "The
|
|
14833
|
+
"description": "The listing (or `guestId`) is not in this workspace, or not in the `X-Account-Id` account.",
|
|
14198
14834
|
"content": {
|
|
14199
14835
|
"application/json": {
|
|
14200
14836
|
"schema": {
|
|
14201
14837
|
"$ref": "#/components/schemas/Error"
|
|
14838
|
+
},
|
|
14839
|
+
"examples": {
|
|
14840
|
+
"notFound": {
|
|
14841
|
+
"summary": "Not in this workspace (or not in the X-Account-Id account)",
|
|
14842
|
+
"value": {
|
|
14843
|
+
"error": {
|
|
14844
|
+
"code": "not_found",
|
|
14845
|
+
"message": "Listing 4118 not found in this workspace.",
|
|
14846
|
+
"fix": "Verify the listing exists in this workspace (`GET /v1/listings`) and, with `X-Account-Id`, that it belongs to that account.",
|
|
14847
|
+
"docs_url": "https://repull.dev/docs/errors/not_found",
|
|
14848
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
14849
|
+
}
|
|
14850
|
+
}
|
|
14851
|
+
}
|
|
14852
|
+
}
|
|
14853
|
+
}
|
|
14854
|
+
}
|
|
14855
|
+
},
|
|
14856
|
+
"409": {
|
|
14857
|
+
"description": "The PMS will not take it as asked: writes off for the API (`pms_writes_off`), no connection (`no_connection`), dates taken (`pms_unavailable`), or already booked with this key (`pms_duplicate`, with `existing`).",
|
|
14858
|
+
"content": {
|
|
14859
|
+
"application/json": {
|
|
14860
|
+
"schema": {
|
|
14861
|
+
"$ref": "#/components/schemas/Error"
|
|
14862
|
+
},
|
|
14863
|
+
"examples": {
|
|
14864
|
+
"pmsWritesOff": {
|
|
14865
|
+
"summary": "The connection is set not to change bookings through the API",
|
|
14866
|
+
"value": {
|
|
14867
|
+
"error": {
|
|
14868
|
+
"code": "pms_writes_off",
|
|
14869
|
+
"message": "Bookings for this property are managed in its PMS, and this connection is set not to change them through the API. Nothing was sent to the PMS.",
|
|
14870
|
+
"fix": "Turn on `reservations.api` with `PATCH /v1/connect/{provider}/write-policy`, or make the change in the PMS.",
|
|
14871
|
+
"docs_url": "https://repull.dev/docs/connect/write-policy",
|
|
14872
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
14873
|
+
}
|
|
14874
|
+
}
|
|
14875
|
+
},
|
|
14876
|
+
"noConnection": {
|
|
14877
|
+
"summary": "The listing is managed in a PMS this workspace is no longer connected to",
|
|
14878
|
+
"value": {
|
|
14879
|
+
"error": {
|
|
14880
|
+
"code": "no_connection",
|
|
14881
|
+
"message": "Listing 4118 is managed in Hostaway, but there is no Hostaway connection.",
|
|
14882
|
+
"fix": "The listing is managed in Hostaway, but this workspace has no connection to it. Reconnect with `POST /v1/connect/hostaway`, then retry. Nothing was sent to the PMS.",
|
|
14883
|
+
"docs_url": "https://repull.dev/docs/errors/no_connection",
|
|
14884
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14885
|
+
"provider": "hostaway",
|
|
14886
|
+
"reconnect": {
|
|
14887
|
+
"method": "POST",
|
|
14888
|
+
"path": "/v1/connect/hostaway"
|
|
14889
|
+
}
|
|
14890
|
+
}
|
|
14891
|
+
}
|
|
14892
|
+
},
|
|
14893
|
+
"pmsUnavailable": {
|
|
14894
|
+
"summary": "The dates are taken in the PMS",
|
|
14895
|
+
"value": {
|
|
14896
|
+
"error": {
|
|
14897
|
+
"code": "pms_unavailable",
|
|
14898
|
+
"message": "Guesty cannot take it: Guesty: listing is not available for the requested dates",
|
|
14899
|
+
"fix": "Guesty says the dates (or the unit) are not available. Pick other dates, or check them first with `POST /v1/reservations/quote`. Nothing was booked.",
|
|
14900
|
+
"docs_url": "https://repull.dev/docs/errors/pms_unavailable",
|
|
14901
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14902
|
+
"provider": "guesty",
|
|
14903
|
+
"pms": {
|
|
14904
|
+
"applied": [],
|
|
14905
|
+
"errors": [
|
|
14906
|
+
{
|
|
14907
|
+
"section": "reservation",
|
|
14908
|
+
"code": "unavailable",
|
|
14909
|
+
"message": "Guesty: listing is not available for the requested dates"
|
|
14910
|
+
}
|
|
14911
|
+
]
|
|
14912
|
+
}
|
|
14913
|
+
}
|
|
14914
|
+
}
|
|
14915
|
+
},
|
|
14916
|
+
"pmsDuplicate": {
|
|
14917
|
+
"summary": "The PMS already holds a booking with this Idempotency-Key",
|
|
14918
|
+
"value": {
|
|
14919
|
+
"error": {
|
|
14920
|
+
"code": "pms_duplicate",
|
|
14921
|
+
"message": "Guesty already holds this booking: Guesty: a reservation with this originId already exists",
|
|
14922
|
+
"fix": "This request was already made with the same idempotency key; look the booking up instead of creating it again.",
|
|
14923
|
+
"docs_url": "https://repull.dev/docs/errors/pms_duplicate",
|
|
14924
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14925
|
+
"provider": "guesty",
|
|
14926
|
+
"existing": {
|
|
14927
|
+
"externalId": "64f1c2a9e1b2",
|
|
14928
|
+
"confirmationCode": "GY-a1B2c3D4"
|
|
14929
|
+
}
|
|
14930
|
+
}
|
|
14931
|
+
}
|
|
14932
|
+
}
|
|
14202
14933
|
}
|
|
14203
14934
|
}
|
|
14204
14935
|
}
|
|
14205
14936
|
},
|
|
14206
14937
|
"422": {
|
|
14207
|
-
"
|
|
14938
|
+
"description": "Invalid field (`invalid_params`), a field this listing cannot take (`unsupported_field`), something the PMS's API cannot do (`pms_write_unsupported`), the PMS refused the content (`pms_rejected`), or the pipeline declined (`reservation_not_created`).",
|
|
14939
|
+
"content": {
|
|
14940
|
+
"application/json": {
|
|
14941
|
+
"schema": {
|
|
14942
|
+
"$ref": "#/components/schemas/Error"
|
|
14943
|
+
},
|
|
14944
|
+
"examples": {
|
|
14945
|
+
"invalidParams": {
|
|
14946
|
+
"summary": "A field failed validation",
|
|
14947
|
+
"value": {
|
|
14948
|
+
"error": {
|
|
14949
|
+
"code": "invalid_params",
|
|
14950
|
+
"message": "`checkOut` is invalid: `checkOut` (2026-10-01) must be after `checkIn` (2026-10-05) \u2014 a stay spans at least one night.",
|
|
14951
|
+
"fix": "Send the departure date as `YYYY-MM-DD`, after `checkIn`, e.g. `\"checkOut\": \"2026-10-05\"`.",
|
|
14952
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_params",
|
|
14953
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14954
|
+
"field": "checkOut",
|
|
14955
|
+
"value_received": "2026-10-01"
|
|
14956
|
+
}
|
|
14957
|
+
}
|
|
14958
|
+
},
|
|
14959
|
+
"unsupportedFieldPms": {
|
|
14960
|
+
"summary": "A direct-booking-only field on a PMS listing",
|
|
14961
|
+
"value": {
|
|
14962
|
+
"error": {
|
|
14963
|
+
"code": "unsupported_field",
|
|
14964
|
+
"message": "`checkInTime` cannot be set on a booking made in Hostaway: arrival and departure times come from the property's settings in the PMS.",
|
|
14965
|
+
"fix": "`GET /v1/listings/{id}` \u2192 `capabilities.reservations` shows what the listing supports. `totalPrice`, `notes`, `unitId` and `sendConfirmationEmail` are honoured only on listings managed in a PMS; `checkInTime`, `checkOutTime`, `currency` and `guestId` only on listings that are not.",
|
|
14966
|
+
"docs_url": "https://repull.dev/docs/errors/unsupported_field",
|
|
14967
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14968
|
+
"provider": "hostaway",
|
|
14969
|
+
"fields": [
|
|
14970
|
+
"checkInTime"
|
|
14971
|
+
]
|
|
14972
|
+
}
|
|
14973
|
+
}
|
|
14974
|
+
},
|
|
14975
|
+
"unsupportedFieldDirect": {
|
|
14976
|
+
"summary": "A PMS-only field on a direct-booking listing",
|
|
14977
|
+
"value": {
|
|
14978
|
+
"error": {
|
|
14979
|
+
"code": "unsupported_field",
|
|
14980
|
+
"message": "`totalPrice` is only honoured for listings managed in a PMS. This listing is not.",
|
|
14981
|
+
"fix": "`GET /v1/listings/{id}` \u2192 `capabilities.reservations` shows what the listing supports. `totalPrice`, `notes`, `unitId` and `sendConfirmationEmail` are honoured only on listings managed in a PMS; `checkInTime`, `checkOutTime`, `currency` and `guestId` only on listings that are not.",
|
|
14982
|
+
"docs_url": "https://repull.dev/docs/errors/unsupported_field",
|
|
14983
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14984
|
+
"fields": [
|
|
14985
|
+
"totalPrice"
|
|
14986
|
+
]
|
|
14987
|
+
}
|
|
14988
|
+
}
|
|
14989
|
+
},
|
|
14990
|
+
"pmsWriteUnsupported": {
|
|
14991
|
+
"summary": "Owner stays on a PMS listing",
|
|
14992
|
+
"value": {
|
|
14993
|
+
"error": {
|
|
14994
|
+
"code": "pms_write_unsupported",
|
|
14995
|
+
"message": "Bookings for this listing are managed in Hostaway. Block owner stays in Hostaway; the block reaches Repull with the next sync.",
|
|
14996
|
+
"fix": "Hostaway's API cannot create this booking. Do it in Hostaway; the change reaches Repull with the next sync.",
|
|
14997
|
+
"docs_url": "https://repull.dev/docs/errors/pms_write_unsupported",
|
|
14998
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
14999
|
+
"provider": "hostaway"
|
|
15000
|
+
}
|
|
15001
|
+
}
|
|
15002
|
+
},
|
|
15003
|
+
"pmsRejected": {
|
|
15004
|
+
"summary": "The PMS refused the content",
|
|
15005
|
+
"value": {
|
|
15006
|
+
"error": {
|
|
15007
|
+
"code": "pms_rejected",
|
|
15008
|
+
"message": "Hostaway refused to create the reservation: Hostaway: guestEmail is invalid",
|
|
15009
|
+
"fix": "Hostaway refused it; its reason is in `message`. Nothing was changed. Correct the request and retry with a NEW `Idempotency-Key`.",
|
|
15010
|
+
"docs_url": "https://repull.dev/docs/errors/pms_rejected",
|
|
15011
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15012
|
+
"provider": "hostaway",
|
|
15013
|
+
"pms": {
|
|
15014
|
+
"applied": [],
|
|
15015
|
+
"errors": [
|
|
15016
|
+
{
|
|
15017
|
+
"section": "reservation",
|
|
15018
|
+
"code": "rejected",
|
|
15019
|
+
"message": "Hostaway: guestEmail is invalid"
|
|
15020
|
+
}
|
|
15021
|
+
]
|
|
15022
|
+
}
|
|
15023
|
+
}
|
|
15024
|
+
}
|
|
15025
|
+
}
|
|
15026
|
+
}
|
|
15027
|
+
}
|
|
15028
|
+
}
|
|
14208
15029
|
},
|
|
14209
15030
|
"500": {
|
|
14210
15031
|
"$ref": "#/components/responses/InternalError"
|
|
15032
|
+
},
|
|
15033
|
+
"502": {
|
|
15034
|
+
"description": "The PMS could not be reached (`pms_error` \u2014 nothing confirmed; retry with the same key), or the booking was created in the PMS but not yet recorded in Repull (`reservation_created_in_pms_only` \u2014 do NOT retry; replayed for the same key).",
|
|
15035
|
+
"content": {
|
|
15036
|
+
"application/json": {
|
|
15037
|
+
"schema": {
|
|
15038
|
+
"$ref": "#/components/schemas/Error"
|
|
15039
|
+
},
|
|
15040
|
+
"examples": {
|
|
15041
|
+
"pmsError": {
|
|
15042
|
+
"summary": "The PMS could not be reached \u2014 retry with the same key",
|
|
15043
|
+
"value": {
|
|
15044
|
+
"error": {
|
|
15045
|
+
"code": "pms_error",
|
|
15046
|
+
"message": "Smoobu could not be reached to create the reservation: timeout of 30000ms exceeded",
|
|
15047
|
+
"fix": "Nothing was changed. Retry with the same idempotency key.",
|
|
15048
|
+
"docs_url": "https://repull.dev/docs/errors/pms_error",
|
|
15049
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15050
|
+
"provider": "smoobu"
|
|
15051
|
+
}
|
|
15052
|
+
}
|
|
15053
|
+
},
|
|
15054
|
+
"createdInPmsOnly": {
|
|
15055
|
+
"summary": "Created in the PMS, not yet recorded in Repull \u2014 do NOT retry",
|
|
15056
|
+
"value": {
|
|
15057
|
+
"error": {
|
|
15058
|
+
"code": "reservation_created_in_pms_only",
|
|
15059
|
+
"message": "Created in Lodgify (LG-88231) but its record could not be read here. It will arrive with the next sync.",
|
|
15060
|
+
"fix": "Do NOT retry. The booking exists in Lodgify (see `existing`) but could not be recorded in Repull yet; it arrives with the next sync and `reservation.created` fires then. Retrying with the same `Idempotency-Key` replays this answer.",
|
|
15061
|
+
"docs_url": "https://repull.dev/docs/errors/reservation_created_in_pms_only",
|
|
15062
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15063
|
+
"provider": "lodgify",
|
|
15064
|
+
"existing": {
|
|
15065
|
+
"externalId": "88231",
|
|
15066
|
+
"confirmationCode": "LG-88231"
|
|
15067
|
+
},
|
|
15068
|
+
"pms": {
|
|
15069
|
+
"applied": [
|
|
15070
|
+
"reservation"
|
|
15071
|
+
],
|
|
15072
|
+
"errors": []
|
|
15073
|
+
},
|
|
15074
|
+
"retryable": false
|
|
15075
|
+
}
|
|
15076
|
+
}
|
|
15077
|
+
}
|
|
15078
|
+
}
|
|
15079
|
+
}
|
|
15080
|
+
}
|
|
14211
15081
|
}
|
|
14212
15082
|
}
|
|
14213
15083
|
}
|
|
@@ -14265,7 +15135,7 @@
|
|
|
14265
15135
|
"patch": {
|
|
14266
15136
|
"operationId": "update_reservation",
|
|
14267
15137
|
"summary": "Update a reservation",
|
|
14268
|
-
"description": "Changes the dates, the occupancy, or the unit.
|
|
15138
|
+
"description": "Changes the dates, the occupancy, or the unit.\n\n**A stay managed in a connected PMS is changed in the PMS first**, then Repull's copy is refreshed from the PMS's record \u2014 changing only Repull's copy would be reverted by the next sync. On such a stay, `checkIn`, `checkOut` and `guestCount` are changed; moving it to another listing and changing its check-in/check-out times are done in the PMS (`422 pms_write_unsupported`), as is anything the PMS's API cannot change (for example dates on Smoobu). A channel booking that came in through the PMS (Airbnb, Booking.com, \u2026) is changed on the channel (`409 reservation_owned_by_channel`). The PMS checks availability: taken dates answer `409 pms_unavailable`. The PMS's outcome comes back as `pms`. `GET /v1/listings/{id}` \u2192 `capabilities.reservations.modify` says whether a listing's PMS supports changes.\n\n**Any other stay** drives the same command path the dashboard does, so the side effects come with it: the change audit is appended, bound task due dates re-sync, the old calendar dates unblock and the new ones block, the conversation's cached listing is invalidated, and `reservation.updated` fires \u2014 which is what revokes and re-issues the door code.\n\nSupply at least one field; an empty body returns 422 rather than a 200 that changed nothing.\n\n**Moving and re-dating in one call is one operation.** Send `listingId` together with `checkIn`/`checkOut` and it is applied as a single move, so the access code is re-issued once rather than twice.\n\n### Fields this endpoint deliberately does NOT accept\n\nEach is rejected by name with the reason, never accepted and ignored:\n\n| Field | Why |\n|---|---|\n| `guest` / `guestDetails` | Guest name, email and phone live on the guest record. The underlying command has no branch for them, so accepting them would return a success that changed nothing. |\n| `pricing` / `totalPrice` / `currency` | Repricing writes the price breakdown, the pricing row and a pricing-history entry. It belongs to its own endpoint. |\n| `status` | Not a field. Cancelling, confirming and checking out are separate operations with materially different side effects \u2014 cancellation issues a credit refund and revokes access codes. |\n| `platform` | Immutable: it records where the booking actually originated. |\n| `notes` | `internal_notes` is an append-only audit trail the system writes on every change. |\n\n**Availability is NOT checked for stays outside a PMS.** A date change that overlaps another booking will be written. Call `GET /v1/availability/{propertyId}` first if that matters.\n\n`X-Account-Id` restricts the reservation (and a listing it is moved to) to one connected account. Returns `403 listing_inactive` when the reservation is on an inactive listing, or when a `listingId` move targets one; nothing is changed.",
|
|
14269
15139
|
"tags": [
|
|
14270
15140
|
"Reservations"
|
|
14271
15141
|
],
|
|
@@ -14281,6 +15151,16 @@
|
|
|
14281
15151
|
},
|
|
14282
15152
|
{
|
|
14283
15153
|
"$ref": "#/components/parameters/IdempotencyKey"
|
|
15154
|
+
},
|
|
15155
|
+
{
|
|
15156
|
+
"name": "X-Account-Id",
|
|
15157
|
+
"in": "header",
|
|
15158
|
+
"required": false,
|
|
15159
|
+
"schema": {
|
|
15160
|
+
"type": "string",
|
|
15161
|
+
"example": "126"
|
|
15162
|
+
},
|
|
15163
|
+
"description": "Restrict the request to one connected account (a Repull connection id, `GET /v1/connect` \u2192 `id`). A listing or reservation outside that account answers `404 not_found`. Omit it to act workspace-wide."
|
|
14284
15164
|
}
|
|
14285
15165
|
],
|
|
14286
15166
|
"requestBody": {
|
|
@@ -14308,7 +15188,33 @@
|
|
|
14308
15188
|
"$ref": "#/components/responses/Unauthorized"
|
|
14309
15189
|
},
|
|
14310
15190
|
"403": {
|
|
14311
|
-
"
|
|
15191
|
+
"description": "The reservation (or the listing it is moved to) is inactive (`listing_inactive`), or the PMS connection lacks write access to bookings (`connection_reauth_required`).",
|
|
15192
|
+
"content": {
|
|
15193
|
+
"application/json": {
|
|
15194
|
+
"schema": {
|
|
15195
|
+
"$ref": "#/components/schemas/Error"
|
|
15196
|
+
},
|
|
15197
|
+
"examples": {
|
|
15198
|
+
"reauthRequired": {
|
|
15199
|
+
"summary": "The grant lacks write access to bookings",
|
|
15200
|
+
"value": {
|
|
15201
|
+
"error": {
|
|
15202
|
+
"code": "connection_reauth_required",
|
|
15203
|
+
"message": "Beds24 needs to be reconnected before Repull can create reservations there: Beds24: token lacks scope write:bookings",
|
|
15204
|
+
"fix": "Reconnect Beds24 with an invite code that grants `write:bookings` (plus `bookings-personal` and `bookings-financial`), then retry.",
|
|
15205
|
+
"docs_url": "https://repull.dev/docs/errors/connection_reauth_required",
|
|
15206
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15207
|
+
"provider": "beds24",
|
|
15208
|
+
"reconnect": {
|
|
15209
|
+
"method": "POST",
|
|
15210
|
+
"path": "/v1/connect/beds24"
|
|
15211
|
+
}
|
|
15212
|
+
}
|
|
15213
|
+
}
|
|
15214
|
+
}
|
|
15215
|
+
}
|
|
15216
|
+
}
|
|
15217
|
+
}
|
|
14312
15218
|
},
|
|
14313
15219
|
"404": {
|
|
14314
15220
|
"description": "The reservation, or the destination property when moving, does not exist in this workspace.",
|
|
@@ -14320,11 +15226,186 @@
|
|
|
14320
15226
|
}
|
|
14321
15227
|
}
|
|
14322
15228
|
},
|
|
15229
|
+
"409": {
|
|
15230
|
+
"description": "The PMS will not change it: writes off for the API (`pms_writes_off`), no connection (`no_connection`), dates taken (`pms_unavailable`), a channel booking (`reservation_owned_by_channel`), or part of a group booking (`pms_group_booking`).",
|
|
15231
|
+
"content": {
|
|
15232
|
+
"application/json": {
|
|
15233
|
+
"schema": {
|
|
15234
|
+
"$ref": "#/components/schemas/Error"
|
|
15235
|
+
},
|
|
15236
|
+
"examples": {
|
|
15237
|
+
"pmsWritesOff": {
|
|
15238
|
+
"summary": "The connection is set not to change bookings through the API",
|
|
15239
|
+
"value": {
|
|
15240
|
+
"error": {
|
|
15241
|
+
"code": "pms_writes_off",
|
|
15242
|
+
"message": "Bookings for this property are managed in its PMS, and this connection is set not to change them through the API. Nothing was sent to the PMS.",
|
|
15243
|
+
"fix": "Turn on `reservations.api` with `PATCH /v1/connect/{provider}/write-policy`, or make the change in the PMS.",
|
|
15244
|
+
"docs_url": "https://repull.dev/docs/connect/write-policy",
|
|
15245
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
15246
|
+
}
|
|
15247
|
+
}
|
|
15248
|
+
},
|
|
15249
|
+
"noConnection": {
|
|
15250
|
+
"summary": "The listing is managed in a PMS this workspace is no longer connected to",
|
|
15251
|
+
"value": {
|
|
15252
|
+
"error": {
|
|
15253
|
+
"code": "no_connection",
|
|
15254
|
+
"message": "Listing 4118 is managed in Hostaway, but there is no Hostaway connection.",
|
|
15255
|
+
"fix": "The listing is managed in Hostaway, but this workspace has no connection to it. Reconnect with `POST /v1/connect/hostaway`, then retry. Nothing was sent to the PMS.",
|
|
15256
|
+
"docs_url": "https://repull.dev/docs/errors/no_connection",
|
|
15257
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15258
|
+
"provider": "hostaway",
|
|
15259
|
+
"reconnect": {
|
|
15260
|
+
"method": "POST",
|
|
15261
|
+
"path": "/v1/connect/hostaway"
|
|
15262
|
+
}
|
|
15263
|
+
}
|
|
15264
|
+
}
|
|
15265
|
+
},
|
|
15266
|
+
"pmsUnavailable": {
|
|
15267
|
+
"summary": "The dates are taken in the PMS",
|
|
15268
|
+
"value": {
|
|
15269
|
+
"error": {
|
|
15270
|
+
"code": "pms_unavailable",
|
|
15271
|
+
"message": "Guesty cannot take it: Guesty: listing is not available for the requested dates",
|
|
15272
|
+
"fix": "Guesty says the dates (or the unit) are not available. Pick other dates, or check them first with `POST /v1/reservations/quote`. Nothing was booked.",
|
|
15273
|
+
"docs_url": "https://repull.dev/docs/errors/pms_unavailable",
|
|
15274
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15275
|
+
"provider": "guesty",
|
|
15276
|
+
"pms": {
|
|
15277
|
+
"applied": [],
|
|
15278
|
+
"errors": [
|
|
15279
|
+
{
|
|
15280
|
+
"section": "reservation",
|
|
15281
|
+
"code": "unavailable",
|
|
15282
|
+
"message": "Guesty: listing is not available for the requested dates"
|
|
15283
|
+
}
|
|
15284
|
+
]
|
|
15285
|
+
}
|
|
15286
|
+
}
|
|
15287
|
+
}
|
|
15288
|
+
},
|
|
15289
|
+
"ownedByChannel": {
|
|
15290
|
+
"summary": "A channel booking (incl. one relayed through a PMS)",
|
|
15291
|
+
"value": {
|
|
15292
|
+
"error": {
|
|
15293
|
+
"code": "reservation_owned_by_channel",
|
|
15294
|
+
"message": "This reservation came from airbnb through Hostaway. Change it on airbnb; the change reaches this workspace with the next sync.",
|
|
15295
|
+
"fix": "This booking belongs to a channel (Airbnb, Booking.com, Vrbo, \u2026). Change it on the channel; the change reaches Repull with the next sync and the matching webhook fires.",
|
|
15296
|
+
"docs_url": "https://repull.dev/docs/errors/reservation_owned_by_channel",
|
|
15297
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15298
|
+
"provider": "hostaway",
|
|
15299
|
+
"owner": "airbnb"
|
|
15300
|
+
}
|
|
15301
|
+
}
|
|
15302
|
+
},
|
|
15303
|
+
"groupBooking": {
|
|
15304
|
+
"summary": "Part of a group booking in the PMS",
|
|
15305
|
+
"value": {
|
|
15306
|
+
"error": {
|
|
15307
|
+
"code": "pms_group_booking",
|
|
15308
|
+
"message": "This stay is one room of a Cloudbeds group booking; change it in Cloudbeds.",
|
|
15309
|
+
"fix": "This stay is part of a group booking in Cloudbeds. Change or cancel group bookings in Cloudbeds; the change reaches Repull with the next sync.",
|
|
15310
|
+
"docs_url": "https://repull.dev/docs/errors/pms_group_booking",
|
|
15311
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15312
|
+
"provider": "cloudbeds"
|
|
15313
|
+
}
|
|
15314
|
+
}
|
|
15315
|
+
}
|
|
15316
|
+
}
|
|
15317
|
+
}
|
|
15318
|
+
}
|
|
15319
|
+
},
|
|
14323
15320
|
"422": {
|
|
14324
|
-
"
|
|
15321
|
+
"description": "Invalid or refused field (`invalid_params`, `unsupported_field`), something the PMS's API cannot change (`pms_write_unsupported`), the PMS refused the change (`pms_rejected`), or the command declined (`reservation_not_modified`).",
|
|
15322
|
+
"content": {
|
|
15323
|
+
"application/json": {
|
|
15324
|
+
"schema": {
|
|
15325
|
+
"$ref": "#/components/schemas/Error"
|
|
15326
|
+
},
|
|
15327
|
+
"examples": {
|
|
15328
|
+
"invalidParams": {
|
|
15329
|
+
"summary": "A field failed validation",
|
|
15330
|
+
"value": {
|
|
15331
|
+
"error": {
|
|
15332
|
+
"code": "invalid_params",
|
|
15333
|
+
"message": "`checkOut` is invalid: `checkOut` (2026-10-01) must be after `checkIn` (2026-10-05) \u2014 a stay spans at least one night.",
|
|
15334
|
+
"fix": "Send the departure date as `YYYY-MM-DD`, after `checkIn`, e.g. `\"checkOut\": \"2026-10-05\"`.",
|
|
15335
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_params",
|
|
15336
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15337
|
+
"field": "checkOut",
|
|
15338
|
+
"value_received": "2026-10-01"
|
|
15339
|
+
}
|
|
15340
|
+
}
|
|
15341
|
+
},
|
|
15342
|
+
"pmsWriteUnsupported": {
|
|
15343
|
+
"summary": "Dates on Smoobu",
|
|
15344
|
+
"value": {
|
|
15345
|
+
"error": {
|
|
15346
|
+
"code": "pms_write_unsupported",
|
|
15347
|
+
"message": "Smoobu cannot change the dates of a booking through its API. Cancel and rebook, or change them in Smoobu.",
|
|
15348
|
+
"fix": "Smoobu's API cannot change this booking. Do it in Smoobu; the change reaches Repull with the next sync.",
|
|
15349
|
+
"docs_url": "https://repull.dev/docs/errors/pms_write_unsupported",
|
|
15350
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15351
|
+
"provider": "smoobu"
|
|
15352
|
+
}
|
|
15353
|
+
}
|
|
15354
|
+
},
|
|
15355
|
+
"pmsRejected": {
|
|
15356
|
+
"summary": "The PMS refused the content",
|
|
15357
|
+
"value": {
|
|
15358
|
+
"error": {
|
|
15359
|
+
"code": "pms_rejected",
|
|
15360
|
+
"message": "Hostaway refused to create the reservation: Hostaway: guestEmail is invalid",
|
|
15361
|
+
"fix": "Hostaway refused it; its reason is in `message`. Nothing was changed. Correct the request and retry with a NEW `Idempotency-Key`.",
|
|
15362
|
+
"docs_url": "https://repull.dev/docs/errors/pms_rejected",
|
|
15363
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15364
|
+
"provider": "hostaway",
|
|
15365
|
+
"pms": {
|
|
15366
|
+
"applied": [],
|
|
15367
|
+
"errors": [
|
|
15368
|
+
{
|
|
15369
|
+
"section": "reservation",
|
|
15370
|
+
"code": "rejected",
|
|
15371
|
+
"message": "Hostaway: guestEmail is invalid"
|
|
15372
|
+
}
|
|
15373
|
+
]
|
|
15374
|
+
}
|
|
15375
|
+
}
|
|
15376
|
+
}
|
|
15377
|
+
}
|
|
15378
|
+
}
|
|
15379
|
+
}
|
|
15380
|
+
}
|
|
14325
15381
|
},
|
|
14326
15382
|
"500": {
|
|
14327
15383
|
"$ref": "#/components/responses/InternalError"
|
|
15384
|
+
},
|
|
15385
|
+
"502": {
|
|
15386
|
+
"description": "The PMS could not be reached (`pms_error`) \u2014 nothing was changed; retry.",
|
|
15387
|
+
"content": {
|
|
15388
|
+
"application/json": {
|
|
15389
|
+
"schema": {
|
|
15390
|
+
"$ref": "#/components/schemas/Error"
|
|
15391
|
+
},
|
|
15392
|
+
"examples": {
|
|
15393
|
+
"pmsError": {
|
|
15394
|
+
"summary": "The PMS could not be reached \u2014 retry with the same key",
|
|
15395
|
+
"value": {
|
|
15396
|
+
"error": {
|
|
15397
|
+
"code": "pms_error",
|
|
15398
|
+
"message": "Smoobu could not be reached to create the reservation: timeout of 30000ms exceeded",
|
|
15399
|
+
"fix": "Nothing was changed. Retry with the same idempotency key.",
|
|
15400
|
+
"docs_url": "https://repull.dev/docs/errors/pms_error",
|
|
15401
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
15402
|
+
"provider": "smoobu"
|
|
15403
|
+
}
|
|
15404
|
+
}
|
|
15405
|
+
}
|
|
15406
|
+
}
|
|
15407
|
+
}
|
|
15408
|
+
}
|
|
14328
15409
|
}
|
|
14329
15410
|
}
|
|
14330
15411
|
}
|
|
@@ -15444,7 +16525,7 @@
|
|
|
15444
16525
|
"get": {
|
|
15445
16526
|
"operationId": "list_connections",
|
|
15446
16527
|
"summary": "List PMS/OTA connections",
|
|
15447
|
-
"description": "Returns
|
|
16528
|
+
"description": "Returns every PMS and OTA connection in the workspace, each with its `status`.\n\n**Spot connections that need attention.** A connection whose `status` is not `active` may need the host to do something before it works \u2014 most commonly a Booking.com Extranet connection where the invited user was granted only partial access (`status: \"needs_permissions\"`). A Smoobu connection still on a legacy single API key carries `action.reason: \"reauth_required\"` while its `status` is `active`: Smoobu stops accepting those keys on October 31, 2026, and `fixUrl` opens the form for a new API key + secret (the connection id stays the same). These connections carry two extra fields:\n\n- `action` \u2014 `{ required: true, reason, message }`. `reason` is a stable machine code (e.g. `needs_permissions`); `message` is a host-facing one-liner describing what to do.\n- `fixUrl` \u2014 a durable link that reopens the hosted Connect flow **bound to that account, on the fix screen** (e.g. \"grant full access\" + a Re-check button). It is safe to store and show in your own dashboard.\n\n**Self-serve repair:** when `action.required` is true, surface a \"Fix\" button that opens `fixUrl` in a new tab (or embed it). The host resolves the issue (e.g. grants the user full access in Booking.com) and clicks Re-check; the import finishes on its own and the connection flips back to `active` \u2014 no re-invite, no support ticket. Poll this endpoint (or read it after the host returns) to confirm `action` has cleared.",
|
|
15448
16529
|
"tags": [
|
|
15449
16530
|
"Connect"
|
|
15450
16531
|
],
|
|
@@ -15488,6 +16569,13 @@
|
|
|
15488
16569
|
"nullable": true,
|
|
15489
16570
|
"description": "Your own correlation token, e.g. your user id (at most 500 characters). Echoed in this response, on the redirect back (`&state=`), in the popup message, and in the `connect.session.completed` webhook."
|
|
15490
16571
|
},
|
|
16572
|
+
"reservationHistoryMonths": {
|
|
16573
|
+
"type": "integer",
|
|
16574
|
+
"minimum": 1,
|
|
16575
|
+
"maximum": 60,
|
|
16576
|
+
"example": 24,
|
|
16577
|
+
"description": "Airbnb \u2014 how many months of past reservations the first import pulls (1\u201360). Omit it for the default window. Upcoming stays are always imported. A wider window takes longer to import, because every extra month is more stays to fetch."
|
|
16578
|
+
},
|
|
15491
16579
|
"accessType": {
|
|
15492
16580
|
"type": "string",
|
|
15493
16581
|
"enum": [
|
|
@@ -15740,6 +16828,13 @@
|
|
|
15740
16828
|
"default": "full_access",
|
|
15741
16829
|
"description": "Airbnb only \u2014 selects the OAuth scope set. 'read_only' grants read-only scopes; 'messaging' grants read scopes plus message read/send but NOT property management, so it can coexist with another app (e.g. an existing PMS) that already holds property management on the same Airbnb account; 'full_access' (default) grants full host scopes including the exclusive property management (only one app per Airbnb account can hold it). The hosted consent screen normally lets the host pick a tier; passing `accessType` explicitly fixes the tier and hides that choice, so the host can only continue with the tier you requested. Omit it to let the host choose."
|
|
15742
16830
|
},
|
|
16831
|
+
"reservationHistoryMonths": {
|
|
16832
|
+
"type": "integer",
|
|
16833
|
+
"minimum": 1,
|
|
16834
|
+
"maximum": 60,
|
|
16835
|
+
"example": 24,
|
|
16836
|
+
"description": "Airbnb \u2014 how many months of past reservations the first import pulls (1\u201360). Omit it for the default window. Upcoming stays are always imported. A wider window takes longer to import, because every extra month is more stays to fetch."
|
|
16837
|
+
},
|
|
15743
16838
|
"state": {
|
|
15744
16839
|
"type": "string",
|
|
15745
16840
|
"description": "Airbnb + Booking.com \u2014 your own correlation token, e.g. your user id (at most 500 characters). Echoed on the redirect back (`&state=`) and in the `connect.session.completed` webhook."
|
|
@@ -16399,7 +17494,7 @@
|
|
|
16399
17494
|
"post": {
|
|
16400
17495
|
"operationId": "submitSmoobuCredentials",
|
|
16401
17496
|
"summary": "Submit Smoobu credentials for a Connect session",
|
|
16402
|
-
"description": "Completes a credentials-pattern connection for Smoobu
|
|
17497
|
+
"description": "Completes a credentials-pattern connection for Smoobu with an HMAC API key + API secret, created in Smoobu \u2192 Settings \u2192 Advanced \u2192 API Keys (Create API Key, then Generate Secret \u2014 the secret is shown only once). Smoobu retires single legacy API keys on October 31, 2026, so `apiSecret` is required; a request with only `apiKey` returns `invalid_params`.\n\nReconnecting replaces the stored credentials on the workspace's existing Smoobu connection \u2014 the `pmsConnectionId` stays the same.\n\nThe credentials are validated against Smoobu before anything is persisted, so an invalid pair returns `invalid_credentials` rather than creating a dead connection. On success the `pms_connections` row is written and the Connect session moves to its terminal state.\n\nNo API key required when called with a `sessionId` \u2014 the session is the capability token.",
|
|
16403
17498
|
"tags": [
|
|
16404
17499
|
"Connect"
|
|
16405
17500
|
],
|
|
@@ -16417,7 +17512,21 @@
|
|
|
16417
17512
|
"credentials": {
|
|
16418
17513
|
"type": "object",
|
|
16419
17514
|
"additionalProperties": true,
|
|
16420
|
-
"description": "API key from Smoobu \u2192 Settings \u2192
|
|
17515
|
+
"description": "HMAC API key + secret from Smoobu \u2192 Settings \u2192 Advanced \u2192 API Keys.",
|
|
17516
|
+
"properties": {
|
|
17517
|
+
"apiKey": {
|
|
17518
|
+
"type": "string",
|
|
17519
|
+
"description": "Smoobu API key."
|
|
17520
|
+
},
|
|
17521
|
+
"apiSecret": {
|
|
17522
|
+
"type": "string",
|
|
17523
|
+
"description": "Smoobu API secret (shown once when generated)."
|
|
17524
|
+
}
|
|
17525
|
+
},
|
|
17526
|
+
"required": [
|
|
17527
|
+
"apiKey",
|
|
17528
|
+
"apiSecret"
|
|
17529
|
+
]
|
|
16421
17530
|
}
|
|
16422
17531
|
},
|
|
16423
17532
|
"required": [
|
|
@@ -17755,11 +18864,26 @@
|
|
|
17755
18864
|
"get": {
|
|
17756
18865
|
"operationId": "list_airbnb_listings",
|
|
17757
18866
|
"summary": "List Airbnb listings",
|
|
17758
|
-
"description": "List every Airbnb listing this workspace has access to via the connected Airbnb account. **Pure DB read \u2014 never calls Airbnb upstream.** The connect flow is what populates the local cache; the API serves what's already there. Customers with a disconnected host still see their last-synced data, with the top-level `dataFreshness` envelope flagging the staleness and pointing at the reconnect URL.\n\nPass `?include=amenities` to enrich each connection with its locally-cached amenity set. Returns `null` per connection when the cache is empty.\n\nPass `?include=thumbnail` to add `thumbnailUrl` to each listing \u2014 one extra column on the query that already runs, so a selection screen renders from a single request instead of one call per listing. `null` when the listing has no thumbnail stored. Combine comma-separated, e.g. `?include=amenities,thumbnail`.\n\n**Can this listing be written to?** Every connection carries `syncCategory` \u2014 Airbnb's own per-listing API sync decision (`sync_all`, `sync_rates_and_availability`, or `none`) \u2014 and `writable`, which is `false` exactly when that category is `none`. Airbnb authorises sync one listing at a time, so a connected account can still hold listings Airbnb refuses every write to; a write to one of those returns `403 listing_not_api_connected` before anything is sent, and reconnecting the account does not change it (the host must switch the listing on in Airbnb). Check `writable` here before a portfolio-wide push instead of discovering it one 403 at a time.\n\nInactive listings are left out; they
|
|
18867
|
+
"description": "List every Airbnb listing this workspace has access to via the connected Airbnb account. **Pure DB read \u2014 never calls Airbnb upstream.** The connect flow is what populates the local cache; the API serves what's already there. Customers with a disconnected host still see their last-synced data, with the top-level `dataFreshness` envelope flagging the staleness and pointing at the reconnect URL.\n\nPass `?include=amenities` to enrich each connection with its locally-cached amenity set. Returns `null` per connection when the cache is empty.\n\nPass `?include=thumbnail` to add `thumbnailUrl` to each listing \u2014 one extra column on the query that already runs, so a selection screen renders from a single request instead of one call per listing. `null` when the listing has no thumbnail stored. Combine comma-separated, e.g. `?include=amenities,thumbnail`.\n\n**Can this listing be written to?** Every connection carries `syncCategory` \u2014 Airbnb's own per-listing API sync decision (`sync_all`, `sync_rates_and_availability`, or `none`) \u2014 and `writable`, which is `false` exactly when that category is `none`. Airbnb authorises sync one listing at a time, so a connected account can still hold listings Airbnb refuses every write to; a write to one of those returns `403 listing_not_api_connected` before anything is sent, and reconnecting the account does not change it (the host must switch the listing on in Airbnb). Check `writable` here before a portfolio-wide push instead of discovering it one 403 at a time.\n\nInactive listings are left out unless `?status=inactive|all` asks for them; they then come back with identity fields only \u2014 `listingId`, `name`, `city`, `status`, `inactiveReason` (`plan_limit`, `unlisted_on_airbnb` or `deactivated`) and each connection's ids and account \u2014 so you can show the user what to activate. They keep syncing and are complete again once activated.\n\n**Several Airbnb accounts?** A workspace can connect more than one. By default this returns every connected account's rows; pass `?account_id=<airbnb host id>` to scope to one. Every row carries `accountId` + `accountName` either way, and `dataFreshness.accounts[]` reports each account's freshness separately, so one disconnected host no longer marks the whole response stale.",
|
|
17759
18868
|
"tags": [
|
|
17760
18869
|
"Airbnb"
|
|
17761
18870
|
],
|
|
17762
18871
|
"parameters": [
|
|
18872
|
+
{
|
|
18873
|
+
"name": "status",
|
|
18874
|
+
"in": "query",
|
|
18875
|
+
"required": false,
|
|
18876
|
+
"schema": {
|
|
18877
|
+
"type": "string",
|
|
18878
|
+
"enum": [
|
|
18879
|
+
"active",
|
|
18880
|
+
"inactive",
|
|
18881
|
+
"all"
|
|
18882
|
+
],
|
|
18883
|
+
"default": "active"
|
|
18884
|
+
},
|
|
18885
|
+
"description": "`active` (default) leaves inactive listings out. `inactive` returns only them and `all` returns both. An inactive listing comes back with identity fields only (ids, `name`, `city`, `status`, `inactiveReason`, its account), which is enough to show what can be activated. Every row carries `status`."
|
|
18886
|
+
},
|
|
17763
18887
|
{
|
|
17764
18888
|
"$ref": "#/components/parameters/AirbnbAccountId"
|
|
17765
18889
|
},
|
|
@@ -20212,9 +21336,230 @@
|
|
|
20212
21336
|
}
|
|
20213
21337
|
},
|
|
20214
21338
|
"put": {
|
|
20215
|
-
"operationId": "update_airbnb_listing_amenities",
|
|
20216
|
-
"summary": "Update Airbnb amenities",
|
|
20217
|
-
"description": "Set amenities on an Airbnb listing. **Write-side** \u2014 calls Airbnb upstream.\n\n**Partial by design**: only the amenities you name change, so turning one off is a one-line body and nothing else on the listing moves. Ids are the `id` values `GET /amenities` returns (e.g. `wireless_internet`, `ac`, `kitchen`); case is ignored. Airbnb refuses ids outside its vocabulary \u2014 that comes back as `422 airbnb_rejected` carrying Airbnb's own message.\n\n`accessibility_amenities` go to Airbnb's separate accessibility resource, which has **no read side at all** \u2014 Airbnb offers no endpoint to fetch them back, and the combined amenities GET 404s on production listings. What you can read back is our own copy: this endpoint updates it on success, and `GET /amenities` returns it under `accessibilityAmenities`. Airbnb may also hold an accessibility claim for review until photo evidence is attached; pass `photo_ids` to supply it.\n\nOur Airbnb copy is updated on success so a read straight after this write returns the new values. The platform-neutral copy behind `GET /v1/listings/{id}?include=amenities` uses a different amenity vocabulary and is refreshed by the next sync, except where an id happens to be identical in both.\n\nReturns `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.",
|
|
21339
|
+
"operationId": "update_airbnb_listing_amenities",
|
|
21340
|
+
"summary": "Update Airbnb amenities",
|
|
21341
|
+
"description": "Set amenities on an Airbnb listing. **Write-side** \u2014 calls Airbnb upstream.\n\n**Partial by design**: only the amenities you name change, so turning one off is a one-line body and nothing else on the listing moves. Ids are the `id` values `GET /amenities` returns (e.g. `wireless_internet`, `ac`, `kitchen`); case is ignored. Airbnb refuses ids outside its vocabulary \u2014 that comes back as `422 airbnb_rejected` carrying Airbnb's own message.\n\n`accessibility_amenities` go to Airbnb's separate accessibility resource, which has **no read side at all** \u2014 Airbnb offers no endpoint to fetch them back, and the combined amenities GET 404s on production listings. What you can read back is our own copy: this endpoint updates it on success, and `GET /amenities` returns it under `accessibilityAmenities`. Airbnb may also hold an accessibility claim for review until photo evidence is attached; pass `photo_ids` to supply it.\n\nOur Airbnb copy is updated on success so a read straight after this write returns the new values. The platform-neutral copy behind `GET /v1/listings/{id}?include=amenities` uses a different amenity vocabulary and is refreshed by the next sync, except where an id happens to be identical in both.\n\nReturns `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.",
|
|
21342
|
+
"tags": [
|
|
21343
|
+
"Airbnb"
|
|
21344
|
+
],
|
|
21345
|
+
"parameters": [
|
|
21346
|
+
{
|
|
21347
|
+
"name": "id",
|
|
21348
|
+
"in": "path",
|
|
21349
|
+
"required": true,
|
|
21350
|
+
"schema": {
|
|
21351
|
+
"type": "string"
|
|
21352
|
+
},
|
|
21353
|
+
"description": "Repull listing id (numeric string)."
|
|
21354
|
+
}
|
|
21355
|
+
],
|
|
21356
|
+
"requestBody": {
|
|
21357
|
+
"required": true,
|
|
21358
|
+
"content": {
|
|
21359
|
+
"application/json": {
|
|
21360
|
+
"schema": {
|
|
21361
|
+
"type": "object",
|
|
21362
|
+
"additionalProperties": false,
|
|
21363
|
+
"description": "At least one amenity across `amenities` and `accessibility_amenities`. A body that changes nothing is refused rather than reported as a successful write.",
|
|
21364
|
+
"properties": {
|
|
21365
|
+
"amenities": {
|
|
21366
|
+
"type": "array",
|
|
21367
|
+
"maxItems": 200,
|
|
21368
|
+
"items": {
|
|
21369
|
+
"type": "object",
|
|
21370
|
+
"required": [
|
|
21371
|
+
"id",
|
|
21372
|
+
"is_present"
|
|
21373
|
+
],
|
|
21374
|
+
"additionalProperties": false,
|
|
21375
|
+
"properties": {
|
|
21376
|
+
"id": {
|
|
21377
|
+
"type": "string",
|
|
21378
|
+
"description": "Airbnb amenity id, e.g. `wireless_internet`. Case is ignored."
|
|
21379
|
+
},
|
|
21380
|
+
"is_present": {
|
|
21381
|
+
"type": "boolean",
|
|
21382
|
+
"description": "`true` claims the amenity, `false` removes it. Required \u2014 an amenity with no `is_present` would be a silent no-op."
|
|
21383
|
+
},
|
|
21384
|
+
"instruction": {
|
|
21385
|
+
"type": "string",
|
|
21386
|
+
"nullable": true,
|
|
21387
|
+
"maxLength": 500,
|
|
21388
|
+
"description": "Optional host note shown with the amenity."
|
|
21389
|
+
}
|
|
21390
|
+
}
|
|
21391
|
+
}
|
|
21392
|
+
},
|
|
21393
|
+
"accessibility_amenities": {
|
|
21394
|
+
"type": "array",
|
|
21395
|
+
"maxItems": 100,
|
|
21396
|
+
"items": {
|
|
21397
|
+
"type": "object",
|
|
21398
|
+
"required": [
|
|
21399
|
+
"id",
|
|
21400
|
+
"is_present"
|
|
21401
|
+
],
|
|
21402
|
+
"additionalProperties": false,
|
|
21403
|
+
"properties": {
|
|
21404
|
+
"id": {
|
|
21405
|
+
"type": "string"
|
|
21406
|
+
},
|
|
21407
|
+
"is_present": {
|
|
21408
|
+
"type": "boolean"
|
|
21409
|
+
},
|
|
21410
|
+
"instruction": {
|
|
21411
|
+
"type": "string",
|
|
21412
|
+
"nullable": true,
|
|
21413
|
+
"maxLength": 500
|
|
21414
|
+
},
|
|
21415
|
+
"photo_ids": {
|
|
21416
|
+
"type": "array",
|
|
21417
|
+
"maxItems": 20,
|
|
21418
|
+
"items": {
|
|
21419
|
+
"type": "string"
|
|
21420
|
+
},
|
|
21421
|
+
"description": "Airbnb photo ids evidencing the accessibility claim (`photoAirbnbId` from `GET /photos`)."
|
|
21422
|
+
}
|
|
21423
|
+
}
|
|
21424
|
+
}
|
|
21425
|
+
}
|
|
21426
|
+
}
|
|
21427
|
+
}
|
|
21428
|
+
}
|
|
21429
|
+
}
|
|
21430
|
+
},
|
|
21431
|
+
"responses": {
|
|
21432
|
+
"200": {
|
|
21433
|
+
"description": "Amenities updated",
|
|
21434
|
+
"content": {
|
|
21435
|
+
"application/json": {
|
|
21436
|
+
"schema": {
|
|
21437
|
+
"type": "object",
|
|
21438
|
+
"required": [
|
|
21439
|
+
"data",
|
|
21440
|
+
"stored"
|
|
21441
|
+
],
|
|
21442
|
+
"properties": {
|
|
21443
|
+
"data": {
|
|
21444
|
+
"type": "object",
|
|
21445
|
+
"properties": {
|
|
21446
|
+
"amenities": {
|
|
21447
|
+
"type": "integer",
|
|
21448
|
+
"description": "How many regular amenities were written."
|
|
21449
|
+
},
|
|
21450
|
+
"accessibilityAmenities": {
|
|
21451
|
+
"type": "integer",
|
|
21452
|
+
"description": "How many accessibility amenities were written."
|
|
21453
|
+
}
|
|
21454
|
+
}
|
|
21455
|
+
},
|
|
21456
|
+
"stored": {
|
|
21457
|
+
"type": "boolean",
|
|
21458
|
+
"description": "Whether our own copy was brought in line with the change."
|
|
21459
|
+
}
|
|
21460
|
+
}
|
|
21461
|
+
}
|
|
21462
|
+
}
|
|
21463
|
+
}
|
|
21464
|
+
},
|
|
21465
|
+
"401": {
|
|
21466
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
21467
|
+
},
|
|
21468
|
+
"403": {
|
|
21469
|
+
"$ref": "#/components/responses/ListingInactive"
|
|
21470
|
+
},
|
|
21471
|
+
"404": {
|
|
21472
|
+
"$ref": "#/components/responses/NotFound"
|
|
21473
|
+
},
|
|
21474
|
+
"422": {
|
|
21475
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
21476
|
+
},
|
|
21477
|
+
"429": {
|
|
21478
|
+
"$ref": "#/components/responses/AirbnbRateLimited"
|
|
21479
|
+
},
|
|
21480
|
+
"500": {
|
|
21481
|
+
"$ref": "#/components/responses/InternalError"
|
|
21482
|
+
}
|
|
21483
|
+
}
|
|
21484
|
+
}
|
|
21485
|
+
},
|
|
21486
|
+
"/v1/channels/airbnb/listings/{id}/checkin-guide": {
|
|
21487
|
+
"get": {
|
|
21488
|
+
"operationId": "get_airbnb_checkin_guide",
|
|
21489
|
+
"summary": "Get Airbnb check-in guide",
|
|
21490
|
+
"description": "Return every published locale variant of an Airbnb listing's check-in guide. **Pure DB read** from `listings_airbnb_check_in_guides`. Pass `?locale=en` to filter to one locale (prefix match). Returns `404` when the listing has no Airbnb connection in this workspace.\n\nReturns `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.",
|
|
21491
|
+
"tags": [
|
|
21492
|
+
"Airbnb"
|
|
21493
|
+
],
|
|
21494
|
+
"parameters": [
|
|
21495
|
+
{
|
|
21496
|
+
"name": "id",
|
|
21497
|
+
"in": "path",
|
|
21498
|
+
"required": true,
|
|
21499
|
+
"schema": {
|
|
21500
|
+
"type": "string"
|
|
21501
|
+
},
|
|
21502
|
+
"description": "Repull listing id (numeric string)."
|
|
21503
|
+
},
|
|
21504
|
+
{
|
|
21505
|
+
"name": "locale",
|
|
21506
|
+
"in": "query",
|
|
21507
|
+
"required": false,
|
|
21508
|
+
"schema": {
|
|
21509
|
+
"type": "string",
|
|
21510
|
+
"example": "en"
|
|
21511
|
+
},
|
|
21512
|
+
"description": "Filter to a single locale (prefix match, case-insensitive)."
|
|
21513
|
+
}
|
|
21514
|
+
],
|
|
21515
|
+
"responses": {
|
|
21516
|
+
"200": {
|
|
21517
|
+
"description": "Check-in guides",
|
|
21518
|
+
"content": {
|
|
21519
|
+
"application/json": {
|
|
21520
|
+
"schema": {
|
|
21521
|
+
"type": "object",
|
|
21522
|
+
"required": [
|
|
21523
|
+
"data",
|
|
21524
|
+
"dataFreshness"
|
|
21525
|
+
],
|
|
21526
|
+
"properties": {
|
|
21527
|
+
"data": {
|
|
21528
|
+
"type": "array",
|
|
21529
|
+
"items": {
|
|
21530
|
+
"type": "object",
|
|
21531
|
+
"additionalProperties": true
|
|
21532
|
+
}
|
|
21533
|
+
},
|
|
21534
|
+
"dataFreshness": {
|
|
21535
|
+
"$ref": "#/components/schemas/AirbnbDataFreshness"
|
|
21536
|
+
}
|
|
21537
|
+
}
|
|
21538
|
+
}
|
|
21539
|
+
}
|
|
21540
|
+
}
|
|
21541
|
+
},
|
|
21542
|
+
"401": {
|
|
21543
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
21544
|
+
},
|
|
21545
|
+
"403": {
|
|
21546
|
+
"$ref": "#/components/responses/ListingInactive"
|
|
21547
|
+
},
|
|
21548
|
+
"404": {
|
|
21549
|
+
"$ref": "#/components/responses/NotFound"
|
|
21550
|
+
},
|
|
21551
|
+
"422": {
|
|
21552
|
+
"$ref": "#/components/responses/UnprocessableEntity"
|
|
21553
|
+
},
|
|
21554
|
+
"500": {
|
|
21555
|
+
"$ref": "#/components/responses/InternalError"
|
|
21556
|
+
}
|
|
21557
|
+
}
|
|
21558
|
+
},
|
|
21559
|
+
"put": {
|
|
21560
|
+
"operationId": "update_airbnb_checkin_guide",
|
|
21561
|
+
"summary": "Replace the steps of an Airbnb check-in guide",
|
|
21562
|
+
"description": "Write the check-in guide guests see before arrival: an ordered list of text steps. **Replaces** every existing step, so send the whole guide; `{\"steps\": []}` removes them all. The response is the guide re-read from Airbnb after the write.\n\nIf the listing has no guide yet, one is created in `locale` (default: the existing guide's, else `en`).\n\nSafe on failure: the new steps are created before the old ones are removed, and if a create fails the steps this call added are removed again, so the guide is never left emptier than it was.\n\nText steps only. Steps with photos need Airbnb's media upload and are not supported here yet. For the other arrival details use `PUT /v1/channels/airbnb/listings/{id}/details`: `check_in_option.instruction` (arrival instructions), `house_manual`, `directions`, `wifi_network`, `wifi_password`.\n\nReturns `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.",
|
|
20218
21563
|
"tags": [
|
|
20219
21564
|
"Airbnb"
|
|
20220
21565
|
],
|
|
@@ -20235,103 +21580,91 @@
|
|
|
20235
21580
|
"application/json": {
|
|
20236
21581
|
"schema": {
|
|
20237
21582
|
"type": "object",
|
|
21583
|
+
"required": [
|
|
21584
|
+
"steps"
|
|
21585
|
+
],
|
|
20238
21586
|
"additionalProperties": false,
|
|
20239
|
-
"description": "At least one amenity across `amenities` and `accessibility_amenities`. A body that changes nothing is refused rather than reported as a successful write.",
|
|
20240
21587
|
"properties": {
|
|
20241
|
-
"
|
|
20242
|
-
"type": "
|
|
20243
|
-
"
|
|
20244
|
-
"
|
|
20245
|
-
"type": "object",
|
|
20246
|
-
"required": [
|
|
20247
|
-
"id",
|
|
20248
|
-
"is_present"
|
|
20249
|
-
],
|
|
20250
|
-
"additionalProperties": false,
|
|
20251
|
-
"properties": {
|
|
20252
|
-
"id": {
|
|
20253
|
-
"type": "string",
|
|
20254
|
-
"description": "Airbnb amenity id, e.g. `wireless_internet`. Case is ignored."
|
|
20255
|
-
},
|
|
20256
|
-
"is_present": {
|
|
20257
|
-
"type": "boolean",
|
|
20258
|
-
"description": "`true` claims the amenity, `false` removes it. Required \u2014 an amenity with no `is_present` would be a silent no-op."
|
|
20259
|
-
},
|
|
20260
|
-
"instruction": {
|
|
20261
|
-
"type": "string",
|
|
20262
|
-
"nullable": true,
|
|
20263
|
-
"maxLength": 500,
|
|
20264
|
-
"description": "Optional host note shown with the amenity."
|
|
20265
|
-
}
|
|
20266
|
-
}
|
|
20267
|
-
}
|
|
21588
|
+
"locale": {
|
|
21589
|
+
"type": "string",
|
|
21590
|
+
"example": "en",
|
|
21591
|
+
"description": "Language of the guide when one has to be created. Ignored when the listing already has a guide."
|
|
20268
21592
|
},
|
|
20269
|
-
"
|
|
21593
|
+
"steps": {
|
|
20270
21594
|
"type": "array",
|
|
20271
|
-
"maxItems":
|
|
21595
|
+
"maxItems": 30,
|
|
21596
|
+
"description": "The guide's steps, in the order guests see them.",
|
|
20272
21597
|
"items": {
|
|
20273
21598
|
"type": "object",
|
|
20274
21599
|
"required": [
|
|
20275
|
-
"
|
|
20276
|
-
"is_present"
|
|
21600
|
+
"notes"
|
|
20277
21601
|
],
|
|
20278
21602
|
"additionalProperties": false,
|
|
20279
21603
|
"properties": {
|
|
20280
|
-
"
|
|
20281
|
-
"type": "string"
|
|
20282
|
-
},
|
|
20283
|
-
"is_present": {
|
|
20284
|
-
"type": "boolean"
|
|
20285
|
-
},
|
|
20286
|
-
"instruction": {
|
|
21604
|
+
"notes": {
|
|
20287
21605
|
"type": "string",
|
|
20288
|
-
"
|
|
20289
|
-
"maxLength":
|
|
20290
|
-
},
|
|
20291
|
-
"photo_ids": {
|
|
20292
|
-
"type": "array",
|
|
20293
|
-
"maxItems": 20,
|
|
20294
|
-
"items": {
|
|
20295
|
-
"type": "string"
|
|
20296
|
-
},
|
|
20297
|
-
"description": "Airbnb photo ids evidencing the accessibility claim (`photoAirbnbId` from `GET /photos`)."
|
|
21606
|
+
"minLength": 1,
|
|
21607
|
+
"maxLength": 5000
|
|
20298
21608
|
}
|
|
20299
21609
|
}
|
|
20300
21610
|
}
|
|
20301
21611
|
}
|
|
20302
21612
|
}
|
|
21613
|
+
},
|
|
21614
|
+
"example": {
|
|
21615
|
+
"steps": [
|
|
21616
|
+
{
|
|
21617
|
+
"notes": "The lockbox is to the right of the front door. Your code arrives by message on the day of arrival."
|
|
21618
|
+
},
|
|
21619
|
+
{
|
|
21620
|
+
"notes": "Parking: use spot 12 in the garage under the building."
|
|
21621
|
+
}
|
|
21622
|
+
]
|
|
20303
21623
|
}
|
|
20304
21624
|
}
|
|
20305
21625
|
}
|
|
20306
21626
|
},
|
|
20307
21627
|
"responses": {
|
|
20308
21628
|
"200": {
|
|
20309
|
-
"description": "
|
|
21629
|
+
"description": "The guide as Airbnb holds it after the write",
|
|
20310
21630
|
"content": {
|
|
20311
21631
|
"application/json": {
|
|
20312
21632
|
"schema": {
|
|
20313
21633
|
"type": "object",
|
|
20314
|
-
"required": [
|
|
20315
|
-
"data",
|
|
20316
|
-
"stored"
|
|
20317
|
-
],
|
|
20318
21634
|
"properties": {
|
|
20319
21635
|
"data": {
|
|
20320
21636
|
"type": "object",
|
|
20321
21637
|
"properties": {
|
|
20322
|
-
"
|
|
20323
|
-
"type": "integer"
|
|
20324
|
-
"description": "How many regular amenities were written."
|
|
21638
|
+
"listingId": {
|
|
21639
|
+
"type": "integer"
|
|
20325
21640
|
},
|
|
20326
|
-
"
|
|
20327
|
-
"type": "
|
|
20328
|
-
|
|
21641
|
+
"locale": {
|
|
21642
|
+
"type": "string"
|
|
21643
|
+
},
|
|
21644
|
+
"published": {
|
|
21645
|
+
"type": "boolean",
|
|
21646
|
+
"nullable": true
|
|
21647
|
+
},
|
|
21648
|
+
"steps": {
|
|
21649
|
+
"type": "array",
|
|
21650
|
+
"items": {
|
|
21651
|
+
"type": "object",
|
|
21652
|
+
"properties": {
|
|
21653
|
+
"id": {
|
|
21654
|
+
"type": "integer"
|
|
21655
|
+
},
|
|
21656
|
+
"notes": {
|
|
21657
|
+
"type": "string",
|
|
21658
|
+
"nullable": true
|
|
21659
|
+
},
|
|
21660
|
+
"mediaUrl": {
|
|
21661
|
+
"type": "string",
|
|
21662
|
+
"nullable": true
|
|
21663
|
+
}
|
|
21664
|
+
}
|
|
21665
|
+
}
|
|
20329
21666
|
}
|
|
20330
21667
|
}
|
|
20331
|
-
},
|
|
20332
|
-
"stored": {
|
|
20333
|
-
"type": "boolean",
|
|
20334
|
-
"description": "Whether our own copy was brought in line with the change."
|
|
20335
21668
|
}
|
|
20336
21669
|
}
|
|
20337
21670
|
}
|
|
@@ -20341,132 +21674,6 @@
|
|
|
20341
21674
|
"401": {
|
|
20342
21675
|
"$ref": "#/components/responses/Unauthorized"
|
|
20343
21676
|
},
|
|
20344
|
-
"403": {
|
|
20345
|
-
"$ref": "#/components/responses/ListingInactive"
|
|
20346
|
-
},
|
|
20347
|
-
"404": {
|
|
20348
|
-
"$ref": "#/components/responses/NotFound"
|
|
20349
|
-
},
|
|
20350
|
-
"422": {
|
|
20351
|
-
"$ref": "#/components/responses/UnprocessableEntity"
|
|
20352
|
-
},
|
|
20353
|
-
"429": {
|
|
20354
|
-
"$ref": "#/components/responses/AirbnbRateLimited"
|
|
20355
|
-
},
|
|
20356
|
-
"500": {
|
|
20357
|
-
"$ref": "#/components/responses/InternalError"
|
|
20358
|
-
}
|
|
20359
|
-
}
|
|
20360
|
-
}
|
|
20361
|
-
},
|
|
20362
|
-
"/v1/channels/airbnb/listings/{id}/checkin-guide": {
|
|
20363
|
-
"get": {
|
|
20364
|
-
"operationId": "get_airbnb_checkin_guide",
|
|
20365
|
-
"summary": "Get Airbnb check-in guide",
|
|
20366
|
-
"description": "Return every published locale variant of an Airbnb listing's check-in guide. **Pure DB read** from `listings_airbnb_check_in_guides`. Pass `?locale=en` to filter to one locale (prefix match). Returns `404` when the listing has no Airbnb connection in this workspace.\n\nReturns `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.",
|
|
20367
|
-
"tags": [
|
|
20368
|
-
"Airbnb"
|
|
20369
|
-
],
|
|
20370
|
-
"parameters": [
|
|
20371
|
-
{
|
|
20372
|
-
"name": "id",
|
|
20373
|
-
"in": "path",
|
|
20374
|
-
"required": true,
|
|
20375
|
-
"schema": {
|
|
20376
|
-
"type": "string"
|
|
20377
|
-
},
|
|
20378
|
-
"description": "Repull listing id (numeric string)."
|
|
20379
|
-
},
|
|
20380
|
-
{
|
|
20381
|
-
"name": "locale",
|
|
20382
|
-
"in": "query",
|
|
20383
|
-
"required": false,
|
|
20384
|
-
"schema": {
|
|
20385
|
-
"type": "string",
|
|
20386
|
-
"example": "en"
|
|
20387
|
-
},
|
|
20388
|
-
"description": "Filter to a single locale (prefix match, case-insensitive)."
|
|
20389
|
-
}
|
|
20390
|
-
],
|
|
20391
|
-
"responses": {
|
|
20392
|
-
"200": {
|
|
20393
|
-
"description": "Check-in guides",
|
|
20394
|
-
"content": {
|
|
20395
|
-
"application/json": {
|
|
20396
|
-
"schema": {
|
|
20397
|
-
"type": "object",
|
|
20398
|
-
"required": [
|
|
20399
|
-
"data",
|
|
20400
|
-
"dataFreshness"
|
|
20401
|
-
],
|
|
20402
|
-
"properties": {
|
|
20403
|
-
"data": {
|
|
20404
|
-
"type": "array",
|
|
20405
|
-
"items": {
|
|
20406
|
-
"type": "object",
|
|
20407
|
-
"additionalProperties": true
|
|
20408
|
-
}
|
|
20409
|
-
},
|
|
20410
|
-
"dataFreshness": {
|
|
20411
|
-
"$ref": "#/components/schemas/AirbnbDataFreshness"
|
|
20412
|
-
}
|
|
20413
|
-
}
|
|
20414
|
-
}
|
|
20415
|
-
}
|
|
20416
|
-
}
|
|
20417
|
-
},
|
|
20418
|
-
"401": {
|
|
20419
|
-
"$ref": "#/components/responses/Unauthorized"
|
|
20420
|
-
},
|
|
20421
|
-
"403": {
|
|
20422
|
-
"$ref": "#/components/responses/ListingInactive"
|
|
20423
|
-
},
|
|
20424
|
-
"404": {
|
|
20425
|
-
"$ref": "#/components/responses/NotFound"
|
|
20426
|
-
},
|
|
20427
|
-
"422": {
|
|
20428
|
-
"$ref": "#/components/responses/UnprocessableEntity"
|
|
20429
|
-
},
|
|
20430
|
-
"500": {
|
|
20431
|
-
"$ref": "#/components/responses/InternalError"
|
|
20432
|
-
}
|
|
20433
|
-
}
|
|
20434
|
-
},
|
|
20435
|
-
"put": {
|
|
20436
|
-
"operationId": "update_airbnb_checkin_guide",
|
|
20437
|
-
"summary": "Upsert Airbnb check-in guide",
|
|
20438
|
-
"description": "Upsert the check-in guide for one locale on an Airbnb listing. **Write-side** \u2014 calls Airbnb upstream; the DB mirror is reconciled by the sync worker once the upstream call returns. Target the locale with `?locale=en` (defaults to `en`). Requires a connected Airbnb host, else `404 no_connection`.\n\nReturns `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.",
|
|
20439
|
-
"tags": [
|
|
20440
|
-
"Airbnb"
|
|
20441
|
-
],
|
|
20442
|
-
"parameters": [
|
|
20443
|
-
{
|
|
20444
|
-
"name": "id",
|
|
20445
|
-
"in": "path",
|
|
20446
|
-
"required": true,
|
|
20447
|
-
"schema": {
|
|
20448
|
-
"type": "string"
|
|
20449
|
-
},
|
|
20450
|
-
"description": "Repull listing id (numeric string)."
|
|
20451
|
-
},
|
|
20452
|
-
{
|
|
20453
|
-
"name": "locale",
|
|
20454
|
-
"in": "query",
|
|
20455
|
-
"required": false,
|
|
20456
|
-
"schema": {
|
|
20457
|
-
"type": "string",
|
|
20458
|
-
"default": "en"
|
|
20459
|
-
},
|
|
20460
|
-
"description": "Locale to upsert. Defaults to `en`."
|
|
20461
|
-
}
|
|
20462
|
-
],
|
|
20463
|
-
"responses": {
|
|
20464
|
-
"200": {
|
|
20465
|
-
"description": "Check-in guide upserted"
|
|
20466
|
-
},
|
|
20467
|
-
"401": {
|
|
20468
|
-
"$ref": "#/components/responses/Unauthorized"
|
|
20469
|
-
},
|
|
20470
21677
|
"403": {
|
|
20471
21678
|
"$ref": "#/components/responses/AirbnbWriteForbidden"
|
|
20472
21679
|
},
|
|
@@ -22257,7 +23464,7 @@
|
|
|
22257
23464
|
],
|
|
22258
23465
|
"default": "active"
|
|
22259
23466
|
},
|
|
22260
|
-
"description": "Filter by listing status. Defaults to `active`. Pass `inactive` to list the listings you can activate, `archived` for archived ones, or `all` for every status. Inactive listings are returned with identity fields only \u2014 `id`, `name`, `status` and `channels` \u2014 and never with
|
|
23467
|
+
"description": "Filter by listing status. Defaults to `active`. Pass `inactive` to list the listings you can activate, `archived` for archived ones, or `all` for every status. Inactive listings are returned with identity fields only \u2014 `id`, `name`, `status`, `inactiveReason` (`plan_limit`, `unlisted_on_airbnb` or `deactivated`), `address.city` and `channels` \u2014 and never with the street, `content` or `details`; activate one to see the rest. The only field you can add to an inactive row is `thumbnailUrl`, via `?include=thumbnail`."
|
|
22261
23468
|
},
|
|
22262
23469
|
{
|
|
22263
23470
|
"name": "channel",
|
|
@@ -22936,10 +24143,27 @@
|
|
|
22936
24143
|
"get": {
|
|
22937
24144
|
"operationId": "list_booking_properties",
|
|
22938
24145
|
"summary": "List Booking.com properties",
|
|
22939
|
-
"description": "List every Booking.com property this workspace holds. Each property is returned ONCE, with the Repull listings mapped under it.\n\nA Booking.com property is a building; its rooms are what guests book, and each room is mapped to one Repull listing \u2014 so one property routinely carries many listings. `listings[].roomBookingId` is the Booking.com room id an ARI write takes.\n\nA property whose rooms are not mapped yet is still listed, with `mappingStatus: \"unmapped\"` and an empty `listings` array. That is a real mid-onboarding state, not an error: finish `POST /v1/connect/booking/map-rooms` and the listings appear. Such a property used to be dropped silently, which made a mapped-but-unreadable workspace indistinguishable from one with no Booking connection at all.\n\nInactive listings are left out of `listings
|
|
24146
|
+
"description": "List every Booking.com property this workspace holds. Each property is returned ONCE, with the Repull listings mapped under it.\n\nA Booking.com property is a building; its rooms are what guests book, and each room is mapped to one Repull listing \u2014 so one property routinely carries many listings. `listings[].roomBookingId` is the Booking.com room id an ARI write takes.\n\nA property whose rooms are not mapped yet is still listed, with `mappingStatus: \"unmapped\"` and an empty `listings` array. That is a real mid-onboarding state, not an error: finish `POST /v1/connect/booking/map-rooms` and the listings appear. Such a property used to be dropped silently, which made a mapped-but-unreadable workspace indistinguishable from one with no Booking connection at all.\n\nInactive listings are left out of `listings` unless `?status=inactive|all` asks for them; they then appear with identity fields only (`listingId`, `name`, `city`, `status`, `inactiveReason`, room). `mappingStatus` counts every mapped listing, inactive ones included, so a property whose listings are all inactive is still `mapped`.",
|
|
22940
24147
|
"tags": [
|
|
22941
24148
|
"Booking.com"
|
|
22942
24149
|
],
|
|
24150
|
+
"parameters": [
|
|
24151
|
+
{
|
|
24152
|
+
"name": "status",
|
|
24153
|
+
"in": "query",
|
|
24154
|
+
"required": false,
|
|
24155
|
+
"schema": {
|
|
24156
|
+
"type": "string",
|
|
24157
|
+
"enum": [
|
|
24158
|
+
"active",
|
|
24159
|
+
"inactive",
|
|
24160
|
+
"all"
|
|
24161
|
+
],
|
|
24162
|
+
"default": "active"
|
|
24163
|
+
},
|
|
24164
|
+
"description": "`active` (default) leaves inactive listings out. `inactive` returns only them and `all` returns both. An inactive listing comes back with identity fields only (ids, `name`, `city`, `status`, `inactiveReason`, its account), which is enough to show what can be activated. Every row carries `status`."
|
|
24165
|
+
}
|
|
24166
|
+
],
|
|
22943
24167
|
"responses": {
|
|
22944
24168
|
"200": {
|
|
22945
24169
|
"description": "Properties",
|
|
@@ -24338,10 +25562,27 @@
|
|
|
24338
25562
|
"get": {
|
|
24339
25563
|
"operationId": "list_vrbo_listings",
|
|
24340
25564
|
"summary": "List VRBO listings",
|
|
24341
|
-
"description": "List the Vrbo units linked to this workspace's listings, from the host's connected Vrbo account (host sign-in, beta).\n\nInactive listings are left out; they
|
|
25565
|
+
"description": "List the Vrbo units linked to this workspace's listings, from the host's connected Vrbo account (host sign-in, beta).\n\nInactive listings are left out unless `?status=inactive|all` asks for them; they then come back with identity fields only (ids, `listingName`, `listingCity`, `status`, `inactiveReason`). They keep syncing and are complete again once activated.",
|
|
24342
25566
|
"tags": [
|
|
24343
25567
|
"VRBO"
|
|
24344
25568
|
],
|
|
25569
|
+
"parameters": [
|
|
25570
|
+
{
|
|
25571
|
+
"name": "status",
|
|
25572
|
+
"in": "query",
|
|
25573
|
+
"required": false,
|
|
25574
|
+
"schema": {
|
|
25575
|
+
"type": "string",
|
|
25576
|
+
"enum": [
|
|
25577
|
+
"active",
|
|
25578
|
+
"inactive",
|
|
25579
|
+
"all"
|
|
25580
|
+
],
|
|
25581
|
+
"default": "active"
|
|
25582
|
+
},
|
|
25583
|
+
"description": "`active` (default) leaves inactive listings out. `inactive` returns only them and `all` returns both. An inactive listing comes back with identity fields only (ids, `name`, `city`, `status`, `inactiveReason`, its account), which is enough to show what can be activated. Every row carries `status`."
|
|
25584
|
+
}
|
|
25585
|
+
],
|
|
24345
25586
|
"responses": {
|
|
24346
25587
|
"200": {
|
|
24347
25588
|
"description": "Listings",
|
|
@@ -27582,8 +28823,8 @@
|
|
|
27582
28823
|
},
|
|
27583
28824
|
"put": {
|
|
27584
28825
|
"operationId": "updateAirbnbListingDetails",
|
|
27585
|
-
"summary": "Update property type,
|
|
27586
|
-
"description": "Change what kind of property the Airbnb listing is, when its quiet hours are,
|
|
28826
|
+
"summary": "Update property type, quiet hours, check-in method, house manual, directions or Wi-Fi",
|
|
28827
|
+
"description": "Change what kind of property the Airbnb listing is, when its quiet hours are, how the guest gets in (`check_in_option.instruction` is the arrival instructions), the house manual, directions to the property, or the Wi-Fi network and password. Partial: only the fields you send are written. At least one required; an unknown field is refused by name rather than dropped.\n\nThis is the UPDATE path for fields that previously had none. `POST /v1/listings` accepts a `propertyType` when a listing is CREATED and nothing could change it afterwards, so a listing mis-typed at import stayed mis-typed; the check-in method was mirrored and never exposed at all.\n\n**A 200 does not by itself mean the change was applied.** `property_type_category`, `property_type_group`, `check_in_option`, `house_manual`, `directions`, `wifi_network` and `wifi_password` are among the attributes Airbnb locks on some listings: the write returns 200, and Airbnb applies nothing for the locked ones. The response reports `blockedFields` \u2014 the fields YOU sent that Airbnb dropped \u2014 and `blockedFields: []` is what a landed write looks like. `GET \u2026/details` reports the same list as `lockedFields` so you can check first.\n\nCanonical property type (the value Repull keeps and republishes) is set with `PUT /v1/listings/{id}/content` under `details`; this endpoint writes straight to Airbnb.\n\nSend `Idempotency-Key` to make a retry safe.",
|
|
27587
28828
|
"tags": [
|
|
27588
28829
|
"Airbnb"
|
|
27589
28830
|
],
|
|
@@ -27725,7 +28966,7 @@
|
|
|
27725
28966
|
"put": {
|
|
27726
28967
|
"operationId": "updateAirbnbListingPermits",
|
|
27727
28968
|
"summary": "Answer Airbnb permit questions",
|
|
27728
|
-
"description": "Answer the regulatory permit questions for a listing \u2014 the licence or registration number a city requires to keep the listing up.\n\nRead the questions first with `GET \u2026/permits?source=live`. For each permit, pick one of its `flows[]` and send its `slug` as `flow_slug`; key every answer by the question's `answer_key`, and let the question's `type` decide the value field
|
|
28969
|
+
"description": "Answer the regulatory permit questions for a listing \u2014 the licence or registration number a city requires to keep the listing up.\n\nRead the questions first with `GET \u2026/permits?source=live`. For each permit, pick one of its `flows[]` and send its `slug` as `flow_slug`; key every answer by the question's `answer_key`, and let the question's `type` decide the value field \u2014 `<type>_value`: `text_value`, `attestation_value`, `radio_value`, `dropdown_value`, `email_value`, `future_date_value`, `file_upload_value`, and so on. Answers are forwarded verbatim \u2014 nothing is defaulted or inferred, because a wrong licence number can take a listing down in a regulated city.\n\nSend `Idempotency-Key`: a timeout here leaves you unable to tell \"never arrived\" from \"arrived, response lost\", and this is a compliance filing.\n\nAirbnb refusing the answers (an unknown `answer_key`, a malformed licence number) is `422 airbnb_rejected` carrying Airbnb's own reason. An expired or revoked Airbnb connection is `403 connection_reauth_required`.\n\n**Changing and removing answers.** Airbnb has no call that deletes or withdraws a submitted registration, so neither does Repull, and at least one permit is required. To change an answer Airbnb marks `answer_editable`, submit the flow again with the new answers \u2014 the latest submission replaces the previous one. When a submission fails with status `failed_recoverable`, fix it and submit again; `failed` cannot be resubmitted. Hosts can also manage this at airbnb.com/verify-listing/{listing_id}.",
|
|
27729
28970
|
"tags": [
|
|
27730
28971
|
"Airbnb"
|
|
27731
28972
|
],
|
|
@@ -32173,7 +33414,8 @@
|
|
|
32173
33414
|
"type": "object",
|
|
32174
33415
|
"properties": {
|
|
32175
33416
|
"listingId": {
|
|
32176
|
-
"type": "
|
|
33417
|
+
"type": "string",
|
|
33418
|
+
"description": "Repull listing id (numeric string, like every `*Id` on the wire)."
|
|
32177
33419
|
},
|
|
32178
33420
|
"total": {
|
|
32179
33421
|
"type": "integer"
|
|
@@ -32236,7 +33478,7 @@
|
|
|
32236
33478
|
"post": {
|
|
32237
33479
|
"operationId": "cancel_reservation",
|
|
32238
33480
|
"summary": "Cancel a reservation",
|
|
32239
|
-
"description": "Cancels a reservation where it lives.\n\n- **
|
|
33481
|
+
"description": "Cancels a reservation where it lives.\n\n- **A booking managed in a connected PMS** (Mews, Cloudbeds, Hostaway, Guesty, Beds24, BookingSync, Lodgify, Smoobu, Hospitable, iGMS): cancelled in the PMS, then read back, so Repull and the PMS agree. No cancellation fee is charged. Lodgify *declines* the booking rather than deleting it. **OwnerRez's API cannot cancel** \u2014 `422 pms_write_unsupported`; cancel it in OwnerRez. `GET /v1/listings/{id}` \u2192 `capabilities.reservations.cancel` says which applies.\n- **Direct, website or owner bookings**: cancelled in Repull \u2014 the nights are released and `reservation.cancelled` fires.\n- **A channel booking** (Airbnb, Booking.com, VRBO), including one that came in through a PMS: `409 reservation_owned_by_channel`. Cancel it on the channel; the cancellation reaches Repull with the next sync.\n\nCancelling an already-cancelled reservation is not an error: the response carries `alreadyCancelled: true`.\n\nPMS integrations other than Mews and Cloudbeds are verified against the vendor's API documentation only.\n\n`X-Account-Id` restricts the reservation to one connected account. Returns `403 listing_inactive` when the listing is inactive.",
|
|
32240
33482
|
"tags": [
|
|
32241
33483
|
"Reservations"
|
|
32242
33484
|
],
|
|
@@ -32249,6 +33491,19 @@
|
|
|
32249
33491
|
"type": "integer"
|
|
32250
33492
|
},
|
|
32251
33493
|
"description": "Reservation id."
|
|
33494
|
+
},
|
|
33495
|
+
{
|
|
33496
|
+
"$ref": "#/components/parameters/IdempotencyKey"
|
|
33497
|
+
},
|
|
33498
|
+
{
|
|
33499
|
+
"name": "X-Account-Id",
|
|
33500
|
+
"in": "header",
|
|
33501
|
+
"required": false,
|
|
33502
|
+
"schema": {
|
|
33503
|
+
"type": "string",
|
|
33504
|
+
"example": "126"
|
|
33505
|
+
},
|
|
33506
|
+
"description": "Restrict the request to one connected account (a Repull connection id, `GET /v1/connect` \u2192 `id`). A listing or reservation outside that account answers `404 not_found`. Omit it to act workspace-wide."
|
|
32252
33507
|
}
|
|
32253
33508
|
],
|
|
32254
33509
|
"requestBody": {
|
|
@@ -32311,36 +33566,30 @@
|
|
|
32311
33566
|
"description": "Present and true when the reservation was already cancelled."
|
|
32312
33567
|
},
|
|
32313
33568
|
"pms": {
|
|
32314
|
-
"
|
|
32315
|
-
|
|
32316
|
-
|
|
32317
|
-
|
|
32318
|
-
|
|
32319
|
-
|
|
32320
|
-
|
|
32321
|
-
|
|
32322
|
-
|
|
32323
|
-
|
|
32324
|
-
|
|
32325
|
-
|
|
32326
|
-
|
|
32327
|
-
|
|
32328
|
-
|
|
32329
|
-
|
|
32330
|
-
|
|
32331
|
-
|
|
32332
|
-
|
|
32333
|
-
|
|
32334
|
-
|
|
32335
|
-
|
|
32336
|
-
|
|
32337
|
-
|
|
32338
|
-
"code": {
|
|
32339
|
-
"type": "string"
|
|
32340
|
-
}
|
|
32341
|
-
}
|
|
32342
|
-
}
|
|
32343
|
-
}
|
|
33569
|
+
"$ref": "#/components/schemas/ReservationPmsOutcome"
|
|
33570
|
+
}
|
|
33571
|
+
}
|
|
33572
|
+
}
|
|
33573
|
+
}
|
|
33574
|
+
}
|
|
33575
|
+
},
|
|
33576
|
+
"400": {
|
|
33577
|
+
"description": "The body is not JSON.",
|
|
33578
|
+
"content": {
|
|
33579
|
+
"application/json": {
|
|
33580
|
+
"schema": {
|
|
33581
|
+
"$ref": "#/components/schemas/Error"
|
|
33582
|
+
},
|
|
33583
|
+
"examples": {
|
|
33584
|
+
"invalidJson": {
|
|
33585
|
+
"summary": "The body is not JSON",
|
|
33586
|
+
"value": {
|
|
33587
|
+
"error": {
|
|
33588
|
+
"code": "invalid_json",
|
|
33589
|
+
"message": "Request body is not valid JSON: Unexpected token",
|
|
33590
|
+
"fix": "Send a JSON object with `Content-Type: application/json`.",
|
|
33591
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_json",
|
|
33592
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
32344
33593
|
}
|
|
32345
33594
|
}
|
|
32346
33595
|
}
|
|
@@ -32351,14 +33600,199 @@
|
|
|
32351
33600
|
"401": {
|
|
32352
33601
|
"$ref": "#/components/responses/Unauthorized"
|
|
32353
33602
|
},
|
|
33603
|
+
"403": {
|
|
33604
|
+
"description": "The listing is inactive (`listing_inactive`), or the PMS connection lacks write access to bookings (`connection_reauth_required`).",
|
|
33605
|
+
"content": {
|
|
33606
|
+
"application/json": {
|
|
33607
|
+
"schema": {
|
|
33608
|
+
"$ref": "#/components/schemas/Error"
|
|
33609
|
+
},
|
|
33610
|
+
"examples": {
|
|
33611
|
+
"reauthRequired": {
|
|
33612
|
+
"summary": "The grant lacks write access to bookings",
|
|
33613
|
+
"value": {
|
|
33614
|
+
"error": {
|
|
33615
|
+
"code": "connection_reauth_required",
|
|
33616
|
+
"message": "Beds24 needs to be reconnected before Repull can create reservations there: Beds24: token lacks scope write:bookings",
|
|
33617
|
+
"fix": "Reconnect Beds24 with an invite code that grants `write:bookings` (plus `bookings-personal` and `bookings-financial`), then retry.",
|
|
33618
|
+
"docs_url": "https://repull.dev/docs/errors/connection_reauth_required",
|
|
33619
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33620
|
+
"provider": "beds24",
|
|
33621
|
+
"reconnect": {
|
|
33622
|
+
"method": "POST",
|
|
33623
|
+
"path": "/v1/connect/beds24"
|
|
33624
|
+
}
|
|
33625
|
+
}
|
|
33626
|
+
}
|
|
33627
|
+
}
|
|
33628
|
+
}
|
|
33629
|
+
}
|
|
33630
|
+
}
|
|
33631
|
+
},
|
|
32354
33632
|
"404": {
|
|
32355
|
-
"
|
|
33633
|
+
"description": "The reservation is not in this workspace (or not in the `X-Account-Id` account), or the PMS no longer has it.",
|
|
33634
|
+
"content": {
|
|
33635
|
+
"application/json": {
|
|
33636
|
+
"schema": {
|
|
33637
|
+
"$ref": "#/components/schemas/Error"
|
|
33638
|
+
},
|
|
33639
|
+
"examples": {
|
|
33640
|
+
"notFound": {
|
|
33641
|
+
"summary": "Not in this workspace (or not in the X-Account-Id account)",
|
|
33642
|
+
"value": {
|
|
33643
|
+
"error": {
|
|
33644
|
+
"code": "not_found",
|
|
33645
|
+
"message": "Reservation 215708 not found in this workspace.",
|
|
33646
|
+
"fix": "Verify the reservation exists in this workspace (`GET /v1/reservations`) and, with `X-Account-Id`, that its listing belongs to that account.",
|
|
33647
|
+
"docs_url": "https://repull.dev/docs/errors/not_found",
|
|
33648
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
33649
|
+
}
|
|
33650
|
+
}
|
|
33651
|
+
}
|
|
33652
|
+
}
|
|
33653
|
+
}
|
|
33654
|
+
}
|
|
32356
33655
|
},
|
|
32357
33656
|
"409": {
|
|
32358
|
-
"description": "
|
|
33657
|
+
"description": "Cancel it elsewhere: a channel booking (`reservation_owned_by_channel`), part of a PMS group booking (`pms_group_booking`), writes off for the API (`pms_writes_off`), or no PMS connection (`no_connection`).",
|
|
33658
|
+
"content": {
|
|
33659
|
+
"application/json": {
|
|
33660
|
+
"schema": {
|
|
33661
|
+
"$ref": "#/components/schemas/Error"
|
|
33662
|
+
},
|
|
33663
|
+
"examples": {
|
|
33664
|
+
"ownedByChannel": {
|
|
33665
|
+
"summary": "A channel booking (incl. one relayed through a PMS)",
|
|
33666
|
+
"value": {
|
|
33667
|
+
"error": {
|
|
33668
|
+
"code": "reservation_owned_by_channel",
|
|
33669
|
+
"message": "This reservation came from airbnb through Hostaway. Change it on airbnb; the change reaches this workspace with the next sync.",
|
|
33670
|
+
"fix": "This booking belongs to a channel (Airbnb, Booking.com, Vrbo, \u2026). Change it on the channel; the change reaches Repull with the next sync and the matching webhook fires.",
|
|
33671
|
+
"docs_url": "https://repull.dev/docs/errors/reservation_owned_by_channel",
|
|
33672
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33673
|
+
"provider": "hostaway",
|
|
33674
|
+
"owner": "airbnb"
|
|
33675
|
+
}
|
|
33676
|
+
}
|
|
33677
|
+
},
|
|
33678
|
+
"groupBooking": {
|
|
33679
|
+
"summary": "Part of a group booking in the PMS",
|
|
33680
|
+
"value": {
|
|
33681
|
+
"error": {
|
|
33682
|
+
"code": "pms_group_booking",
|
|
33683
|
+
"message": "This stay is one room of a Cloudbeds group booking; change it in Cloudbeds.",
|
|
33684
|
+
"fix": "This stay is part of a group booking in Cloudbeds. Change or cancel group bookings in Cloudbeds; the change reaches Repull with the next sync.",
|
|
33685
|
+
"docs_url": "https://repull.dev/docs/errors/pms_group_booking",
|
|
33686
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33687
|
+
"provider": "cloudbeds"
|
|
33688
|
+
}
|
|
33689
|
+
}
|
|
33690
|
+
},
|
|
33691
|
+
"pmsWritesOff": {
|
|
33692
|
+
"summary": "The connection is set not to change bookings through the API",
|
|
33693
|
+
"value": {
|
|
33694
|
+
"error": {
|
|
33695
|
+
"code": "pms_writes_off",
|
|
33696
|
+
"message": "Bookings for this property are managed in its PMS, and this connection is set not to change them through the API. Nothing was sent to the PMS.",
|
|
33697
|
+
"fix": "Turn on `reservations.api` with `PATCH /v1/connect/{provider}/write-policy`, or make the change in the PMS.",
|
|
33698
|
+
"docs_url": "https://repull.dev/docs/connect/write-policy",
|
|
33699
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
33700
|
+
}
|
|
33701
|
+
}
|
|
33702
|
+
},
|
|
33703
|
+
"noConnection": {
|
|
33704
|
+
"summary": "The listing is managed in a PMS this workspace is no longer connected to",
|
|
33705
|
+
"value": {
|
|
33706
|
+
"error": {
|
|
33707
|
+
"code": "no_connection",
|
|
33708
|
+
"message": "Listing 4118 is managed in Hostaway, but there is no Hostaway connection.",
|
|
33709
|
+
"fix": "The listing is managed in Hostaway, but this workspace has no connection to it. Reconnect with `POST /v1/connect/hostaway`, then retry. Nothing was sent to the PMS.",
|
|
33710
|
+
"docs_url": "https://repull.dev/docs/errors/no_connection",
|
|
33711
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33712
|
+
"provider": "hostaway",
|
|
33713
|
+
"reconnect": {
|
|
33714
|
+
"method": "POST",
|
|
33715
|
+
"path": "/v1/connect/hostaway"
|
|
33716
|
+
}
|
|
33717
|
+
}
|
|
33718
|
+
}
|
|
33719
|
+
}
|
|
33720
|
+
}
|
|
33721
|
+
}
|
|
33722
|
+
}
|
|
32359
33723
|
},
|
|
32360
33724
|
"422": {
|
|
32361
|
-
"
|
|
33725
|
+
"description": "The PMS's API cannot cancel it (`pms_write_unsupported` \u2014 OwnerRez), the PMS refused (`pms_rejected`), or the body is invalid (`invalid_params`).",
|
|
33726
|
+
"content": {
|
|
33727
|
+
"application/json": {
|
|
33728
|
+
"schema": {
|
|
33729
|
+
"$ref": "#/components/schemas/Error"
|
|
33730
|
+
},
|
|
33731
|
+
"examples": {
|
|
33732
|
+
"pmsWriteUnsupported": {
|
|
33733
|
+
"summary": "The PMS API cannot do this (here: OwnerRez has no cancel)",
|
|
33734
|
+
"value": {
|
|
33735
|
+
"error": {
|
|
33736
|
+
"code": "pms_write_unsupported",
|
|
33737
|
+
"message": "OwnerRez cannot cancel a booking through its API. Cancel it in OwnerRez.",
|
|
33738
|
+
"fix": "Cancel it in OwnerRez; the cancellation reaches Repull with the next sync.",
|
|
33739
|
+
"docs_url": "https://repull.dev/docs/errors/pms_write_unsupported",
|
|
33740
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33741
|
+
"provider": "ownerrez"
|
|
33742
|
+
}
|
|
33743
|
+
}
|
|
33744
|
+
},
|
|
33745
|
+
"pmsRejected": {
|
|
33746
|
+
"summary": "The PMS refused the content",
|
|
33747
|
+
"value": {
|
|
33748
|
+
"error": {
|
|
33749
|
+
"code": "pms_rejected",
|
|
33750
|
+
"message": "Hostaway refused to create the reservation: Hostaway: guestEmail is invalid",
|
|
33751
|
+
"fix": "Hostaway refused it; its reason is in `message`. Nothing was changed. Correct the request and retry with a NEW `Idempotency-Key`.",
|
|
33752
|
+
"docs_url": "https://repull.dev/docs/errors/pms_rejected",
|
|
33753
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33754
|
+
"provider": "hostaway",
|
|
33755
|
+
"pms": {
|
|
33756
|
+
"applied": [],
|
|
33757
|
+
"errors": [
|
|
33758
|
+
{
|
|
33759
|
+
"section": "reservation",
|
|
33760
|
+
"code": "rejected",
|
|
33761
|
+
"message": "Hostaway: guestEmail is invalid"
|
|
33762
|
+
}
|
|
33763
|
+
]
|
|
33764
|
+
}
|
|
33765
|
+
}
|
|
33766
|
+
}
|
|
33767
|
+
}
|
|
33768
|
+
}
|
|
33769
|
+
}
|
|
33770
|
+
}
|
|
33771
|
+
},
|
|
33772
|
+
"502": {
|
|
33773
|
+
"description": "The PMS could not be reached (`pms_error`) \u2014 nothing was cancelled; retry.",
|
|
33774
|
+
"content": {
|
|
33775
|
+
"application/json": {
|
|
33776
|
+
"schema": {
|
|
33777
|
+
"$ref": "#/components/schemas/Error"
|
|
33778
|
+
},
|
|
33779
|
+
"examples": {
|
|
33780
|
+
"pmsError": {
|
|
33781
|
+
"summary": "The PMS could not be reached \u2014 retry with the same key",
|
|
33782
|
+
"value": {
|
|
33783
|
+
"error": {
|
|
33784
|
+
"code": "pms_error",
|
|
33785
|
+
"message": "Smoobu could not be reached to create the reservation: timeout of 30000ms exceeded",
|
|
33786
|
+
"fix": "Nothing was changed. Retry with the same idempotency key.",
|
|
33787
|
+
"docs_url": "https://repull.dev/docs/errors/pms_error",
|
|
33788
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
33789
|
+
"provider": "smoobu"
|
|
33790
|
+
}
|
|
33791
|
+
}
|
|
33792
|
+
}
|
|
33793
|
+
}
|
|
33794
|
+
}
|
|
33795
|
+
}
|
|
32362
33796
|
}
|
|
32363
33797
|
}
|
|
32364
33798
|
}
|
|
@@ -33424,7 +34858,8 @@
|
|
|
33424
34858
|
"type": "object",
|
|
33425
34859
|
"properties": {
|
|
33426
34860
|
"accountId": {
|
|
33427
|
-
"type": "
|
|
34861
|
+
"type": "string",
|
|
34862
|
+
"description": "The connection id (numeric string, like every `*Id` on the wire)."
|
|
33428
34863
|
},
|
|
33429
34864
|
"status": {
|
|
33430
34865
|
"type": "string"
|
|
@@ -34216,6 +35651,295 @@
|
|
|
34216
35651
|
}
|
|
34217
35652
|
}
|
|
34218
35653
|
}
|
|
35654
|
+
},
|
|
35655
|
+
"/v1/reservations/quote": {
|
|
35656
|
+
"post": {
|
|
35657
|
+
"operationId": "quote_reservation",
|
|
35658
|
+
"summary": "Quote a reservation in the PMS",
|
|
35659
|
+
"description": "Prices a stay and checks its availability **in the PMS that manages the listing**, without booking anything. It is the same check `POST /v1/reservations` makes before booking when no `totalPrice` is sent, so `available: true` with a `total` is what that create would be priced at (dates can still be taken in between).\n\n`available: false` is an answer, not an error: the PMS's reasons are in `restrictions` (minimum stay, closed to arrival, taken dates\u2026).\n\n- A listing **not managed in a PMS** answers `422 pms_not_linked`. Book it directly with `POST /v1/reservations`, or price it with `GET /v1/quotes`.\n- A PMS **without a quote API** (Mews, Cloudbeds, iGMS) answers `422 pms_write_unsupported`. You can still create the booking; on iGMS a `totalPrice` is required.\n\n`GET /v1/listings/{id}` \u2192 `capabilities.reservations.quote` says whether a listing can be quoted.\n\n### Per-PMS limits\n\n| PMS | create | change | cancel | quote | `totalPrice` | Limits |\n|---|---|---|---|---|---|---|\n| Mews | \u2713 | \u2713 | \u2713 | \u2013 | \u2713 | \u2014 |\n| Cloudbeds | \u2713 | \u2713 | \u2713 | \u2013 | \u2013 | Books at the rate plan's price; group bookings supported. |\n| Hostaway | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Direct-channel bookings only; a specific unit is refused; a date change keeps the booked total. |\n| Guesty | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Cancels direct and Vrbo bookings; other channel bookings are cancelled on the channel. |\n| Beds24 | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Needs the `write:bookings` scope; a multi-room property needs `unitId`. |\n| BookingSync | \u2713 | \u2713 | \u2713 | \u2713 | \u2713 | Needs `bookings_write`; fees and taxes are not itemized; no guest email. |\n| Lodgify | \u2713 | \u2713 | \u2713 (declines) | \u2713 | \u2713 | Cancel declines the booking; single-room bookings. |\n| Smoobu | \u2713 | \u2713 (no dates) | \u2713 | \u2713 | \u2713 | Dates cannot be changed through Smoobu's API \u2014 cancel and rebook, or change them in Smoobu. |\n| Hospitable | \u2713 | \u2713 | \u2713 | \u2713 (Direct plan) | \u2713 | Manual reservations only; needs `reservation:write`; adds no fees or taxes. |\n| iGMS | \u2713 | \u2713 | \u2713 | \u2013 | \u2713 (required) | iGMS direct bookings only; a price is required; no tentative holds. |\n| OwnerRez | \u2713 | \u2713 | \u2013 | \u2713 | \u2013 | No cancel through OwnerRez's API; priced by the property's own rates; needs the `full` scope. |\n\nEvery PMS except Cloudbeds refuses group bookings, and every vacation-rental PMS refuses to change or cancel a booking that came from a channel (Airbnb, Booking.com, Vrbo\u2026) \u2014 that is done on the channel.\n\n**Verification.** Mews and Cloudbeds were run end to end on their vendors' sandboxes. Every other PMS is **verified against the vendor's API documentation only** \u2014 no live account has been written to yet. `capabilities.reservations.verifiedAgainst` says which.\n\n`X-Account-Id` restricts the listing to one connected account. Returns `403 listing_inactive` when the listing is inactive.",
|
|
35660
|
+
"tags": [
|
|
35661
|
+
"Reservations"
|
|
35662
|
+
],
|
|
35663
|
+
"parameters": [
|
|
35664
|
+
{
|
|
35665
|
+
"name": "X-Account-Id",
|
|
35666
|
+
"in": "header",
|
|
35667
|
+
"required": false,
|
|
35668
|
+
"schema": {
|
|
35669
|
+
"type": "string",
|
|
35670
|
+
"example": "126"
|
|
35671
|
+
},
|
|
35672
|
+
"description": "Restrict the request to one connected account (a Repull connection id, `GET /v1/connect` \u2192 `id`). A listing or reservation outside that account answers `404 not_found`. Omit it to act workspace-wide."
|
|
35673
|
+
}
|
|
35674
|
+
],
|
|
35675
|
+
"requestBody": {
|
|
35676
|
+
"required": true,
|
|
35677
|
+
"content": {
|
|
35678
|
+
"application/json": {
|
|
35679
|
+
"schema": {
|
|
35680
|
+
"$ref": "#/components/schemas/ReservationQuoteRequest"
|
|
35681
|
+
},
|
|
35682
|
+
"examples": {
|
|
35683
|
+
"quote": {
|
|
35684
|
+
"summary": "Two adults and a child",
|
|
35685
|
+
"value": {
|
|
35686
|
+
"listingId": 4118,
|
|
35687
|
+
"checkIn": "2026-10-01",
|
|
35688
|
+
"checkOut": "2026-10-05",
|
|
35689
|
+
"adults": 2,
|
|
35690
|
+
"children": 1
|
|
35691
|
+
}
|
|
35692
|
+
}
|
|
35693
|
+
}
|
|
35694
|
+
}
|
|
35695
|
+
}
|
|
35696
|
+
},
|
|
35697
|
+
"responses": {
|
|
35698
|
+
"200": {
|
|
35699
|
+
"description": "The PMS's price and availability for the stay.",
|
|
35700
|
+
"content": {
|
|
35701
|
+
"application/json": {
|
|
35702
|
+
"schema": {
|
|
35703
|
+
"$ref": "#/components/schemas/ReservationQuoteResponse"
|
|
35704
|
+
},
|
|
35705
|
+
"examples": {
|
|
35706
|
+
"available": {
|
|
35707
|
+
"summary": "Bookable",
|
|
35708
|
+
"value": {
|
|
35709
|
+
"listingId": "4118",
|
|
35710
|
+
"provider": "hostaway",
|
|
35711
|
+
"checkIn": "2026-10-01",
|
|
35712
|
+
"checkOut": "2026-10-05",
|
|
35713
|
+
"available": true,
|
|
35714
|
+
"total": 880,
|
|
35715
|
+
"currency": "USD",
|
|
35716
|
+
"breakdown": {
|
|
35717
|
+
"accommodation": 720,
|
|
35718
|
+
"cleaningFee": 100,
|
|
35719
|
+
"taxes": 60
|
|
35720
|
+
},
|
|
35721
|
+
"restrictions": []
|
|
35722
|
+
}
|
|
35723
|
+
},
|
|
35724
|
+
"unavailable": {
|
|
35725
|
+
"summary": "Not bookable as asked",
|
|
35726
|
+
"value": {
|
|
35727
|
+
"listingId": "4118",
|
|
35728
|
+
"provider": "guesty",
|
|
35729
|
+
"checkIn": "2026-10-01",
|
|
35730
|
+
"checkOut": "2026-10-02",
|
|
35731
|
+
"available": false,
|
|
35732
|
+
"total": null,
|
|
35733
|
+
"currency": null,
|
|
35734
|
+
"breakdown": null,
|
|
35735
|
+
"restrictions": [
|
|
35736
|
+
"Guesty: minimum stay is 3 nights"
|
|
35737
|
+
]
|
|
35738
|
+
}
|
|
35739
|
+
}
|
|
35740
|
+
}
|
|
35741
|
+
}
|
|
35742
|
+
}
|
|
35743
|
+
},
|
|
35744
|
+
"400": {
|
|
35745
|
+
"description": "The body is not JSON.",
|
|
35746
|
+
"content": {
|
|
35747
|
+
"application/json": {
|
|
35748
|
+
"schema": {
|
|
35749
|
+
"$ref": "#/components/schemas/Error"
|
|
35750
|
+
},
|
|
35751
|
+
"examples": {
|
|
35752
|
+
"invalidJson": {
|
|
35753
|
+
"summary": "The body is not JSON",
|
|
35754
|
+
"value": {
|
|
35755
|
+
"error": {
|
|
35756
|
+
"code": "invalid_json",
|
|
35757
|
+
"message": "Request body is not valid JSON: Unexpected token",
|
|
35758
|
+
"fix": "Send a JSON object with `Content-Type: application/json`.",
|
|
35759
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_json",
|
|
35760
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
35761
|
+
}
|
|
35762
|
+
}
|
|
35763
|
+
}
|
|
35764
|
+
}
|
|
35765
|
+
}
|
|
35766
|
+
}
|
|
35767
|
+
},
|
|
35768
|
+
"401": {
|
|
35769
|
+
"$ref": "#/components/responses/Unauthorized"
|
|
35770
|
+
},
|
|
35771
|
+
"403": {
|
|
35772
|
+
"description": "The listing is inactive (`listing_inactive`), or the PMS connection must be reconnected (`connection_reauth_required`).",
|
|
35773
|
+
"content": {
|
|
35774
|
+
"application/json": {
|
|
35775
|
+
"schema": {
|
|
35776
|
+
"$ref": "#/components/schemas/Error"
|
|
35777
|
+
},
|
|
35778
|
+
"examples": {
|
|
35779
|
+
"reauthRequired": {
|
|
35780
|
+
"summary": "The grant lacks write access to bookings",
|
|
35781
|
+
"value": {
|
|
35782
|
+
"error": {
|
|
35783
|
+
"code": "connection_reauth_required",
|
|
35784
|
+
"message": "Beds24 needs to be reconnected before Repull can create reservations there: Beds24: token lacks scope write:bookings",
|
|
35785
|
+
"fix": "Reconnect Beds24 with an invite code that grants `write:bookings` (plus `bookings-personal` and `bookings-financial`), then retry.",
|
|
35786
|
+
"docs_url": "https://repull.dev/docs/errors/connection_reauth_required",
|
|
35787
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
35788
|
+
"provider": "beds24",
|
|
35789
|
+
"reconnect": {
|
|
35790
|
+
"method": "POST",
|
|
35791
|
+
"path": "/v1/connect/beds24"
|
|
35792
|
+
}
|
|
35793
|
+
}
|
|
35794
|
+
}
|
|
35795
|
+
}
|
|
35796
|
+
}
|
|
35797
|
+
}
|
|
35798
|
+
}
|
|
35799
|
+
},
|
|
35800
|
+
"404": {
|
|
35801
|
+
"description": "The listing is not in this workspace, or not in the `X-Account-Id` account.",
|
|
35802
|
+
"content": {
|
|
35803
|
+
"application/json": {
|
|
35804
|
+
"schema": {
|
|
35805
|
+
"$ref": "#/components/schemas/Error"
|
|
35806
|
+
},
|
|
35807
|
+
"examples": {
|
|
35808
|
+
"notFound": {
|
|
35809
|
+
"summary": "Not in this workspace (or not in the X-Account-Id account)",
|
|
35810
|
+
"value": {
|
|
35811
|
+
"error": {
|
|
35812
|
+
"code": "not_found",
|
|
35813
|
+
"message": "Listing 4118 not found in this workspace.",
|
|
35814
|
+
"fix": "Verify the listing exists in this workspace (`GET /v1/listings`) and, with `X-Account-Id`, that it belongs to that account.",
|
|
35815
|
+
"docs_url": "https://repull.dev/docs/errors/not_found",
|
|
35816
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
35817
|
+
}
|
|
35818
|
+
}
|
|
35819
|
+
}
|
|
35820
|
+
}
|
|
35821
|
+
}
|
|
35822
|
+
}
|
|
35823
|
+
},
|
|
35824
|
+
"409": {
|
|
35825
|
+
"description": "No PMS connection (`no_connection`), or writes are off for the API (`pms_writes_off`).",
|
|
35826
|
+
"content": {
|
|
35827
|
+
"application/json": {
|
|
35828
|
+
"schema": {
|
|
35829
|
+
"$ref": "#/components/schemas/Error"
|
|
35830
|
+
},
|
|
35831
|
+
"examples": {
|
|
35832
|
+
"noConnection": {
|
|
35833
|
+
"summary": "The listing is managed in a PMS this workspace is no longer connected to",
|
|
35834
|
+
"value": {
|
|
35835
|
+
"error": {
|
|
35836
|
+
"code": "no_connection",
|
|
35837
|
+
"message": "Listing 4118 is managed in Hostaway, but there is no Hostaway connection.",
|
|
35838
|
+
"fix": "The listing is managed in Hostaway, but this workspace has no connection to it. Reconnect with `POST /v1/connect/hostaway`, then retry. Nothing was sent to the PMS.",
|
|
35839
|
+
"docs_url": "https://repull.dev/docs/errors/no_connection",
|
|
35840
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
35841
|
+
"provider": "hostaway",
|
|
35842
|
+
"reconnect": {
|
|
35843
|
+
"method": "POST",
|
|
35844
|
+
"path": "/v1/connect/hostaway"
|
|
35845
|
+
}
|
|
35846
|
+
}
|
|
35847
|
+
}
|
|
35848
|
+
},
|
|
35849
|
+
"pmsWritesOff": {
|
|
35850
|
+
"summary": "The connection is set not to change bookings through the API",
|
|
35851
|
+
"value": {
|
|
35852
|
+
"error": {
|
|
35853
|
+
"code": "pms_writes_off",
|
|
35854
|
+
"message": "Bookings for this property are managed in its PMS, and this connection is set not to change them through the API. Nothing was sent to the PMS.",
|
|
35855
|
+
"fix": "Turn on `reservations.api` with `PATCH /v1/connect/{provider}/write-policy`, or make the change in the PMS.",
|
|
35856
|
+
"docs_url": "https://repull.dev/docs/connect/write-policy",
|
|
35857
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
35858
|
+
}
|
|
35859
|
+
}
|
|
35860
|
+
}
|
|
35861
|
+
}
|
|
35862
|
+
}
|
|
35863
|
+
}
|
|
35864
|
+
},
|
|
35865
|
+
"422": {
|
|
35866
|
+
"description": "Not managed in a PMS (`pms_not_linked`), a PMS without a quote API (`pms_write_unsupported`), or an invalid field (`invalid_params`).",
|
|
35867
|
+
"content": {
|
|
35868
|
+
"application/json": {
|
|
35869
|
+
"schema": {
|
|
35870
|
+
"$ref": "#/components/schemas/Error"
|
|
35871
|
+
},
|
|
35872
|
+
"examples": {
|
|
35873
|
+
"pmsNotLinked": {
|
|
35874
|
+
"summary": "The listing is not managed in a PMS",
|
|
35875
|
+
"value": {
|
|
35876
|
+
"error": {
|
|
35877
|
+
"code": "pms_not_linked",
|
|
35878
|
+
"message": "Listing 4118 is not managed in a PMS, so there is no PMS quote for it.",
|
|
35879
|
+
"fix": "Create the reservation directly; Repull prices it from the listing's own rates.",
|
|
35880
|
+
"docs_url": "https://repull.dev/docs/errors/pms_not_linked",
|
|
35881
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678"
|
|
35882
|
+
}
|
|
35883
|
+
}
|
|
35884
|
+
},
|
|
35885
|
+
"quoteUnsupported": {
|
|
35886
|
+
"summary": "The PMS has no quote API (Mews, Cloudbeds, iGMS)",
|
|
35887
|
+
"value": {
|
|
35888
|
+
"error": {
|
|
35889
|
+
"code": "pms_write_unsupported",
|
|
35890
|
+
"message": "iGMS cannot quote reservations through its API. Create the booking with a `totalPrice`.",
|
|
35891
|
+
"fix": "iGMS's API cannot quote this booking. Create it with a `totalPrice` instead.",
|
|
35892
|
+
"docs_url": "https://repull.dev/docs/errors/pms_write_unsupported",
|
|
35893
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
35894
|
+
"provider": "igms"
|
|
35895
|
+
}
|
|
35896
|
+
}
|
|
35897
|
+
},
|
|
35898
|
+
"invalidParams": {
|
|
35899
|
+
"summary": "A field failed validation",
|
|
35900
|
+
"value": {
|
|
35901
|
+
"error": {
|
|
35902
|
+
"code": "invalid_params",
|
|
35903
|
+
"message": "`checkOut` is invalid: `checkOut` (2026-10-01) must be after `checkIn` (2026-10-05) \u2014 a stay spans at least one night.",
|
|
35904
|
+
"fix": "Send the departure date as `YYYY-MM-DD`, after `checkIn`, e.g. `\"checkOut\": \"2026-10-05\"`.",
|
|
35905
|
+
"docs_url": "https://repull.dev/docs/errors/invalid_params",
|
|
35906
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
35907
|
+
"field": "checkOut",
|
|
35908
|
+
"value_received": "2026-10-01"
|
|
35909
|
+
}
|
|
35910
|
+
}
|
|
35911
|
+
}
|
|
35912
|
+
}
|
|
35913
|
+
}
|
|
35914
|
+
}
|
|
35915
|
+
},
|
|
35916
|
+
"502": {
|
|
35917
|
+
"description": "The PMS could not be reached (`pms_error`) \u2014 retry.",
|
|
35918
|
+
"content": {
|
|
35919
|
+
"application/json": {
|
|
35920
|
+
"schema": {
|
|
35921
|
+
"$ref": "#/components/schemas/Error"
|
|
35922
|
+
},
|
|
35923
|
+
"examples": {
|
|
35924
|
+
"pmsError": {
|
|
35925
|
+
"summary": "The PMS could not be reached \u2014 retry with the same key",
|
|
35926
|
+
"value": {
|
|
35927
|
+
"error": {
|
|
35928
|
+
"code": "pms_error",
|
|
35929
|
+
"message": "Smoobu could not be reached to create the reservation: timeout of 30000ms exceeded",
|
|
35930
|
+
"fix": "Nothing was changed. Retry with the same idempotency key.",
|
|
35931
|
+
"docs_url": "https://repull.dev/docs/errors/pms_error",
|
|
35932
|
+
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
|
|
35933
|
+
"provider": "smoobu"
|
|
35934
|
+
}
|
|
35935
|
+
}
|
|
35936
|
+
}
|
|
35937
|
+
}
|
|
35938
|
+
}
|
|
35939
|
+
}
|
|
35940
|
+
}
|
|
35941
|
+
}
|
|
35942
|
+
}
|
|
34219
35943
|
}
|
|
34220
35944
|
}
|
|
34221
35945
|
}
|