repull 0.2.28 → 0.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/repull/api/conversations_api.rb +2 -2
  3. data/lib/repull/api/guests_api.rb +83 -2
  4. data/lib/repull/api/listings_api.rb +2 -2
  5. data/lib/repull/api/reservations_api.rb +2 -2
  6. data/lib/repull/api/reviews_api.rb +2 -2
  7. data/lib/repull/models/connect_status_capabilities.rb +14 -5
  8. data/lib/repull/models/guest_create_request.rb +14 -4
  9. data/lib/repull/models/guest_create_response.rb +15 -5
  10. data/lib/repull/models/guest_create_response_pms.rb +158 -0
  11. data/lib/repull/models/guest_update_request.rb +205 -0
  12. data/lib/repull/models/guest_update_response.rb +209 -0
  13. data/lib/repull/models/{guest_create_response_contacts_inner.rb → guest_update_response_contacts_inner.rb} +3 -3
  14. data/lib/repull/models/guest_update_response_pms_inner.rb +158 -0
  15. data/lib/repull/models/listing_capabilities.rb +14 -5
  16. data/lib/repull/models/listing_content_update_response.rb +15 -5
  17. data/lib/repull/models/listing_content_update_response_pms.rb +172 -0
  18. data/lib/repull/models/listing_content_update_response_pms_errors_inner.rb +199 -0
  19. data/lib/repull/models/pms_capabilities.rb +242 -0
  20. data/lib/repull/models/pms_capabilities_calendar.rb +148 -0
  21. data/lib/repull/models/pms_capabilities_conversations.rb +167 -0
  22. data/lib/repull/models/pms_capabilities_guests.rb +158 -0
  23. data/lib/repull/models/pms_capabilities_listings.rb +238 -0
  24. data/lib/repull/models/pms_capabilities_payments.rb +148 -0
  25. data/lib/repull/models/pms_capabilities_reservations.rb +158 -0
  26. data/lib/repull/models/pms_capabilities_reviews.rb +158 -0
  27. data/lib/repull/models/pms_capabilities_tasks.rb +156 -0
  28. data/lib/repull/models/reply_to_review201_response.rb +12 -1
  29. data/lib/repull/models/review.rb +12 -1
  30. data/lib/repull/models/send_message_request.rb +2 -2
  31. data/lib/repull/version.rb +1 -1
  32. data/lib/repull.rb +16 -1
  33. data/openapi/v1.json +392 -24
  34. data/scripts/regen.sh +1 -1
  35. metadata +17 -2
data/openapi/v1.json CHANGED
@@ -1368,6 +1368,12 @@
1368
1368
  "type": "string",
1369
1369
  "nullable": true
1370
1370
  },
1371
+ "pms": {
1372
+ "type": "string",
1373
+ "nullable": true,
1374
+ "example": "guesty",
1375
+ "description": "The PMS this review was read from (`guesty`, `hostaway`, \u2026) when it came through one \u2014 `platform` is still the channel the guest wrote it on. A reply (`POST /v1/reviews/{id}/reply`) goes through this PMS; `GET /v1/connect/{provider}` \u2192 `capabilities.pms.reviews.reply` says whether it can. `null` for a review from a directly connected channel."
1376
+ },
1371
1377
  "listingId": {
1372
1378
  "type": "string",
1373
1379
  "nullable": true,
@@ -2171,10 +2177,13 @@
2171
2177
  },
2172
2178
  "capabilities": {
2173
2179
  "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.",
2180
+ "description": "PMS providers only. `reservations`: which reservation writes the API performs on this connection's listings \u2014 the connector's support combined with `writePolicy`. `pms`: everything else the API does through this PMS (review replies, request answers, listing content, guests, message channel/attachments, calendar). When `connected` is false, what the connector supports once connected.",
2175
2181
  "properties": {
2176
2182
  "reservations": {
2177
2183
  "$ref": "#/components/schemas/ReservationCapabilities"
2184
+ },
2185
+ "pms": {
2186
+ "$ref": "#/components/schemas/PmsCapabilities"
2178
2187
  }
2179
2188
  }
2180
2189
  },
@@ -8061,7 +8070,51 @@
8061
8070
  "items": {
8062
8071
  "type": "string"
8063
8072
  },
8064
- "description": "Provided-but-not-applied fields \u2014 e.g. \"photos\" when a non-empty photos array carried no valid http(s) URL."
8073
+ "description": "Provided-but-not-applied fields \u2014 e.g. \"photos\" when a non-empty photos array carried no valid http(s) URL. On a listing a PMS manages, also the content sections the PMS refused (`title`, `descriptions`, `times`, `capacity`, `amenities`, `houseRules`, `address`, `photos`), which are then not written here either."
8074
+ },
8075
+ "pms": {
8076
+ "type": "object",
8077
+ "nullable": true,
8078
+ "description": "Present when the listing is managed in a PMS: the PMS-owned fields were written there first, and this is its per-section outcome.",
8079
+ "properties": {
8080
+ "provider": {
8081
+ "type": "string",
8082
+ "example": "guesty"
8083
+ },
8084
+ "applied": {
8085
+ "type": "array",
8086
+ "items": {
8087
+ "type": "string"
8088
+ },
8089
+ "description": "Sections the PMS applied."
8090
+ },
8091
+ "errors": {
8092
+ "type": "array",
8093
+ "items": {
8094
+ "type": "object",
8095
+ "properties": {
8096
+ "section": {
8097
+ "type": "string"
8098
+ },
8099
+ "code": {
8100
+ "type": "string",
8101
+ "enum": [
8102
+ "rejected",
8103
+ "unavailable",
8104
+ "unsupported",
8105
+ "not_found",
8106
+ "reauth_required",
8107
+ "duplicate"
8108
+ ]
8109
+ },
8110
+ "message": {
8111
+ "type": "string"
8112
+ }
8113
+ }
8114
+ },
8115
+ "description": "Sections the PMS refused, with its reason."
8116
+ }
8117
+ }
8065
8118
  }
8066
8119
  }
8067
8120
  },
@@ -8748,10 +8801,13 @@
8748
8801
  "properties": {
8749
8802
  "capabilities": {
8750
8803
  "type": "object",
8751
- "description": "`GET /v1/listings/{id}` only. What the API can do with this listing.",
8804
+ "description": "`GET /v1/listings/{id}` only. What the API can do with this listing. `pms` is present when a connected PMS manages it.",
8752
8805
  "properties": {
8753
8806
  "reservations": {
8754
8807
  "$ref": "#/components/schemas/ReservationCapabilities"
8808
+ },
8809
+ "pms": {
8810
+ "$ref": "#/components/schemas/PmsCapabilities"
8755
8811
  }
8756
8812
  }
8757
8813
  },
@@ -11725,6 +11781,150 @@
11725
11781
  }
11726
11782
  }
11727
11783
  },
11784
+ "PmsCapabilities": {
11785
+ "type": "object",
11786
+ "description": "What the API does through a connected PMS beyond reservation writes, read from the same connector table the router uses \u2014 a `false` flag is a `422 pms_write_unsupported` naming the PMS.",
11787
+ "properties": {
11788
+ "provider": {
11789
+ "type": "string",
11790
+ "example": "guesty"
11791
+ },
11792
+ "connected": {
11793
+ "type": "boolean",
11794
+ "description": "`false`: what the connector supports once connected (on a listing: a dead link \u2014 every write answers `409 no_connection`)."
11795
+ },
11796
+ "reservations": {
11797
+ "type": "object",
11798
+ "properties": {
11799
+ "respond": {
11800
+ "type": "boolean",
11801
+ "description": "`POST /v1/reservations/{id}/accept|decline` on requests this PMS relays."
11802
+ },
11803
+ "preapprove": {
11804
+ "type": "boolean",
11805
+ "description": "`POST /v1/conversations/{id}/pre-approval` on inquiries this PMS relays."
11806
+ }
11807
+ }
11808
+ },
11809
+ "reviews": {
11810
+ "type": "object",
11811
+ "properties": {
11812
+ "read": {
11813
+ "type": "boolean",
11814
+ "description": "Its reviews appear in `GET /v1/reviews` (with `pms` set)."
11815
+ },
11816
+ "reply": {
11817
+ "type": "boolean",
11818
+ "description": "`POST /v1/reviews/{id}/reply`."
11819
+ }
11820
+ }
11821
+ },
11822
+ "listings": {
11823
+ "type": "object",
11824
+ "description": "Sections `PUT /v1/listings/{id}/content` writes to the PMS.",
11825
+ "properties": {
11826
+ "title": {
11827
+ "type": "boolean"
11828
+ },
11829
+ "descriptions": {
11830
+ "type": "boolean"
11831
+ },
11832
+ "times": {
11833
+ "type": "boolean"
11834
+ },
11835
+ "capacity": {
11836
+ "type": "boolean"
11837
+ },
11838
+ "amenities": {
11839
+ "type": "boolean"
11840
+ },
11841
+ "houseRules": {
11842
+ "type": "boolean"
11843
+ },
11844
+ "address": {
11845
+ "type": "boolean"
11846
+ },
11847
+ "photosAdd": {
11848
+ "type": "boolean"
11849
+ },
11850
+ "photosDelete": {
11851
+ "type": "boolean"
11852
+ },
11853
+ "photosReorder": {
11854
+ "type": "boolean"
11855
+ },
11856
+ "photoCaptions": {
11857
+ "type": "boolean"
11858
+ }
11859
+ }
11860
+ },
11861
+ "guests": {
11862
+ "type": "object",
11863
+ "properties": {
11864
+ "create": {
11865
+ "type": "boolean",
11866
+ "description": "`POST /v1/guests` with `provider`."
11867
+ },
11868
+ "update": {
11869
+ "type": "boolean",
11870
+ "description": "`PATCH /v1/guests/{id}` on a guest linked to this PMS."
11871
+ }
11872
+ }
11873
+ },
11874
+ "conversations": {
11875
+ "type": "object",
11876
+ "properties": {
11877
+ "send": {
11878
+ "type": "boolean"
11879
+ },
11880
+ "attachments": {
11881
+ "type": "boolean",
11882
+ "description": "`attachments` on `POST /v1/conversations/{id}/messages`."
11883
+ },
11884
+ "channelSelect": {
11885
+ "type": "boolean",
11886
+ "description": "`channel` on `POST /v1/conversations/{id}/messages`."
11887
+ }
11888
+ }
11889
+ },
11890
+ "calendar": {
11891
+ "type": "object",
11892
+ "properties": {
11893
+ "write": {
11894
+ "type": "boolean",
11895
+ "description": "`PUT /v1/availability/{propertyId}` reaches the PMS."
11896
+ }
11897
+ }
11898
+ },
11899
+ "payments": {
11900
+ "type": "object",
11901
+ "properties": {
11902
+ "read": {
11903
+ "type": "boolean",
11904
+ "description": "Payments recorded in the PMS are imported onto the reservation."
11905
+ }
11906
+ }
11907
+ },
11908
+ "tasks": {
11909
+ "type": "object",
11910
+ "properties": {
11911
+ "read": {
11912
+ "type": "boolean"
11913
+ },
11914
+ "write": {
11915
+ "type": "boolean"
11916
+ }
11917
+ }
11918
+ },
11919
+ "notes": {
11920
+ "type": "object",
11921
+ "additionalProperties": {
11922
+ "type": "string"
11923
+ },
11924
+ "description": "The connector's own notes per family (limits, required access)."
11925
+ }
11926
+ }
11927
+ },
11728
11928
  "GuestCreateRequest": {
11729
11929
  "type": "object",
11730
11930
  "required": [
@@ -11763,6 +11963,100 @@
11763
11963
  "isBusinessTraveler": {
11764
11964
  "type": "boolean",
11765
11965
  "default": false
11966
+ },
11967
+ "provider": {
11968
+ "type": "string",
11969
+ "example": "guesty",
11970
+ "description": "A connected PMS to create the guest in as well. The guest is created there FIRST; a PMS that cannot create guest profiles returns `422 pms_write_unsupported` and nothing is created. The PMS's guest id comes back as `pms.externalId`, and later `PATCH /v1/guests/{id}` changes reach it."
11971
+ }
11972
+ }
11973
+ },
11974
+ "GuestUpdateRequest": {
11975
+ "type": "object",
11976
+ "minProperties": 1,
11977
+ "properties": {
11978
+ "firstName": {
11979
+ "type": "string",
11980
+ "minLength": 1
11981
+ },
11982
+ "lastName": {
11983
+ "type": "string"
11984
+ },
11985
+ "email": {
11986
+ "type": "string",
11987
+ "format": "email",
11988
+ "description": "Added as the guest's newest email; earlier ones are kept."
11989
+ },
11990
+ "phone": {
11991
+ "type": "string",
11992
+ "description": "E.164 preferred. Added as the guest's newest phone; earlier ones are kept."
11993
+ },
11994
+ "language": {
11995
+ "type": "string",
11996
+ "description": "BCP-47 tag."
11997
+ }
11998
+ }
11999
+ },
12000
+ "GuestUpdateResponse": {
12001
+ "type": "object",
12002
+ "properties": {
12003
+ "id": {
12004
+ "type": "integer"
12005
+ },
12006
+ "firstName": {
12007
+ "type": "string"
12008
+ },
12009
+ "lastName": {
12010
+ "type": "string",
12011
+ "nullable": true
12012
+ },
12013
+ "language": {
12014
+ "type": "string",
12015
+ "nullable": true
12016
+ },
12017
+ "contacts": {
12018
+ "type": "array",
12019
+ "items": {
12020
+ "type": "object",
12021
+ "properties": {
12022
+ "type": {
12023
+ "type": "string",
12024
+ "enum": [
12025
+ "email",
12026
+ "phone"
12027
+ ]
12028
+ },
12029
+ "value": {
12030
+ "type": "string"
12031
+ },
12032
+ "isPrimary": {
12033
+ "type": "boolean"
12034
+ }
12035
+ }
12036
+ }
12037
+ },
12038
+ "updatedAt": {
12039
+ "type": "string",
12040
+ "format": "date-time",
12041
+ "nullable": true
12042
+ },
12043
+ "pms": {
12044
+ "type": "array",
12045
+ "description": "Each PMS the change was written to first (the guest's linked PMSs), with the sections it applied.",
12046
+ "items": {
12047
+ "type": "object",
12048
+ "properties": {
12049
+ "provider": {
12050
+ "type": "string"
12051
+ },
12052
+ "applied": {
12053
+ "type": "array",
12054
+ "items": {
12055
+ "type": "string"
12056
+ }
12057
+ }
12058
+ }
12059
+ }
11766
12060
  }
11767
12061
  }
11768
12062
  },
@@ -11821,6 +12115,20 @@
11821
12115
  "createdAt": {
11822
12116
  "type": "string",
11823
12117
  "format": "date-time"
12118
+ },
12119
+ "pms": {
12120
+ "type": "object",
12121
+ "nullable": true,
12122
+ "description": "Set when `provider` was sent: the PMS the guest was also created in, and its id there.",
12123
+ "properties": {
12124
+ "provider": {
12125
+ "type": "string"
12126
+ },
12127
+ "externalId": {
12128
+ "type": "string",
12129
+ "nullable": true
12130
+ }
12131
+ }
11824
12132
  }
11825
12133
  }
11826
12134
  },
@@ -11865,7 +12173,8 @@
11865
12173
  },
11866
12174
  "channel": {
11867
12175
  "type": "string",
11868
- "description": "Force a channel. Omit to send on whichever channel the conversation already uses, which is the right default."
12176
+ "example": "email",
12177
+ "description": "Force a channel. Omit to send on whichever channel the conversation already uses, which is the right default. One of `airbnb`, `booking`, `vrbo`, `sms`, `email`, `website` \u2014 except on a conversation a connected PMS relays (Guesty, Hostaway, \u2026), where the message is sent through the PMS and `channel` is passed to it: the PMS's own channel/module name (Guesty `airbnb2`, `bookingCom`, `email`, `sms`, \u2026) or one of Repull's names, which the PMS maps. A PMS that cannot choose a channel returns `422 pms_write_unsupported`; `GET /v1/connect/{provider}` \u2192 `capabilities.pms.conversations.channelSelect` says so beforehand."
11869
12178
  },
11870
12179
  "attachments": {
11871
12180
  "type": "array",
@@ -11874,7 +12183,7 @@
11874
12183
  "items": {
11875
12184
  "$ref": "#/components/schemas/SendMessageAttachment"
11876
12185
  },
11877
- "description": "Files to send. See the per-channel table above."
12186
+ "description": "Files to send. See the per-channel table above. On a conversation a connected PMS relays, files go through the PMS \u2014 `422 pms_write_unsupported` when its API cannot send them (`capabilities.pms.conversations.attachments` on `GET /v1/connect/{provider}`)."
11878
12187
  }
11879
12188
  }
11880
12189
  },
@@ -15524,7 +15833,7 @@
15524
15833
  "post": {
15525
15834
  "operationId": "createGuest",
15526
15835
  "summary": "Create a guest",
15527
- "description": "Creates a guest in the workspace, with contact normalisation applied (phone digits, email lowercased) and each contact stored as its own record.\n\n**This is find-or-create, and the response tells you which happened.** A guest already on file matching on email \u2014 then phone \u2014 AND name is returned instead of a duplicate being created. Read the `created` flag rather than inferring from the status: `201` with `created: true` means a new record was written, `200` with `created: false` means an existing guest matched. Quietly handing back an existing record as though it were new is exactly the ambiguity this flag removes.\n\nField names are camelCase, and an unrecognised field is rejected by name rather than silently dropped.\n\nSend `Idempotency-Key` to make a retry safe.",
15836
+ "description": "Creates a guest in the workspace, with contact normalisation applied (phone digits, email lowercased) and each contact stored as its own record.\n\n**This is find-or-create, and the response tells you which happened.** A guest already on file matching on email \u2014 then phone \u2014 AND name is returned instead of a duplicate being created. Read the `created` flag rather than inferring from the status: `201` with `created: true` means a new record was written, `200` with `created: false` means an existing guest matched. Quietly handing back an existing record as though it were new is exactly the ambiguity this flag removes.\n\nField names are camelCase, and an unrecognised field is rejected by name rather than silently dropped.\n\n**Creating the guest in a connected PMS too:** send `provider` (e.g. `guesty`). The guest is created in the PMS first and its id there comes back as `pms.externalId`; a PMS whose API cannot create guest profiles returns `422 pms_write_unsupported` naming it (Hostaway today) and nothing is created. `GET /v1/connect/{provider}` \u2192 `capabilities.pms.guests.create` says so beforehand.\n\nSend `Idempotency-Key` to make a retry safe.",
15528
15837
  "tags": [
15529
15838
  "Guests"
15530
15839
  ],
@@ -15648,6 +15957,61 @@
15648
15957
  "$ref": "#/components/responses/InternalError"
15649
15958
  }
15650
15959
  }
15960
+ },
15961
+ "patch": {
15962
+ "operationId": "updateGuest",
15963
+ "summary": "Update a guest",
15964
+ "description": "Change a guest's name, email, phone or language. Email and phone are added as the guest's newest contact; earlier ones are kept.\n\n**Guests linked to a connected PMS** (created with `provider`, or imported from one) are changed in that PMS first. A PMS whose API cannot change guest profiles returns `422 pms_write_unsupported` naming it (Hostaway today) and nothing is written; `GET /v1/connect/{provider}` \u2192 `capabilities.pms.guests.update` says so beforehand. A revoked PMS connection is `403 connection_reauth_required`.\n\nSend `Idempotency-Key` to make a retry safe.",
15965
+ "tags": [
15966
+ "Guests"
15967
+ ],
15968
+ "parameters": [
15969
+ {
15970
+ "name": "id",
15971
+ "in": "path",
15972
+ "required": true,
15973
+ "schema": {
15974
+ "type": "integer"
15975
+ }
15976
+ },
15977
+ {
15978
+ "$ref": "#/components/parameters/IdempotencyKey"
15979
+ }
15980
+ ],
15981
+ "requestBody": {
15982
+ "required": true,
15983
+ "content": {
15984
+ "application/json": {
15985
+ "schema": {
15986
+ "$ref": "#/components/schemas/GuestUpdateRequest"
15987
+ }
15988
+ }
15989
+ }
15990
+ },
15991
+ "responses": {
15992
+ "200": {
15993
+ "description": "Guest updated (and in its PMS, when linked to one).",
15994
+ "content": {
15995
+ "application/json": {
15996
+ "schema": {
15997
+ "$ref": "#/components/schemas/GuestUpdateResponse"
15998
+ }
15999
+ }
16000
+ }
16001
+ },
16002
+ "401": {
16003
+ "$ref": "#/components/responses/Unauthorized"
16004
+ },
16005
+ "403": {
16006
+ "$ref": "#/components/responses/ListingInactive"
16007
+ },
16008
+ "404": {
16009
+ "$ref": "#/components/responses/NotFound"
16010
+ },
16011
+ "422": {
16012
+ "$ref": "#/components/responses/UnprocessableEntity"
16013
+ }
16014
+ }
15651
16015
  }
15652
16016
  },
15653
16017
  "/v1/conversations": {
@@ -17956,7 +18320,7 @@
17956
18320
  "post": {
17957
18321
  "operationId": "replyToReview",
17958
18322
  "summary": "Reply to a review on any channel",
17959
- "description": "Resolves the review, reads its channel and dispatches the reply. Channel-neutral: you do not need to know where the review came from.\n\nReplies work on Airbnb, Booking.com and VRBO. Each channel accepts one reply per review (VRBO: a second is `409 already_replied`; a review VRBO no longer takes a response to is `409 reply_not_allowed`). On VRBO the response is signed with a name \u2014 the connected account's host name, or `name` if you send it. A review from a channel without a reply API returns `422 unsupported_channel` naming the channels that do work.\n\nTo review a guest (Airbnb only), use `POST /v1/reviews/{id}/guest-review`.\n\n**Inactive listings:** a review of an inactive listing returns `403 listing_inactive` and no reply reaches the channel. Activate the listing first.",
18323
+ "description": "Resolves the review, reads its channel and dispatches the reply. Channel-neutral: you do not need to know where the review came from.\n\nReplies work on Airbnb, Booking.com and VRBO. Each channel accepts one reply per review (VRBO: a second is `409 already_replied`; a review VRBO no longer takes a response to is `409 reply_not_allowed`). On VRBO the response is signed with a name \u2014 the connected account's host name, or `name` if you send it. A review from a channel without a reply API returns `422 unsupported_channel` naming the channels that do work.\n\n**Reviews read from a PMS** (`pms` set on the review \u2014 Guesty, Hostaway, \u2026) are answered through that PMS. A PMS whose API has no reply returns `422 pms_write_unsupported` naming it (Hostaway today); `GET /v1/connect/{provider}` \u2192 `capabilities.pms.reviews.reply` says so beforehand. A revoked PMS connection is `403 connection_reauth_required`.\n\nTo review a guest (Airbnb only), use `POST /v1/reviews/{id}/guest-review`.\n\n**Inactive listings:** a review of an inactive listing returns `403 listing_inactive` and no reply reaches the channel. Activate the listing first.",
17960
18324
  "tags": [
17961
18325
  "Reviews"
17962
18326
  ],
@@ -18009,6 +18373,11 @@
18009
18373
  "platform": {
18010
18374
  "type": "string"
18011
18375
  },
18376
+ "pms": {
18377
+ "type": "string",
18378
+ "nullable": true,
18379
+ "description": "The PMS the reply went through, when the review came from one."
18380
+ },
18012
18381
  "response": {
18013
18382
  "type": "string"
18014
18383
  }
@@ -23697,7 +24066,7 @@
23697
24066
  "put": {
23698
24067
  "operationId": "updateListingContent",
23699
24068
  "summary": "Update canonical listing content",
23700
- "description": "Write your PMS's canonical listing content \u2014 title, description, amenities, address, occupancy, and policies \u2014 into a Repull listing, making it the source of truth. This is the flagship \"the PMS owns listing content, Repull distributes it\" enabler.\n\n**Partial update:** every field is optional. Only the fields you send are written; absent fields are left untouched. `amenities` is a FULL replacement of the amenity set (omit to leave untouched, send `[]` to clear).\n\n**Multilingual:** send `locale` to say which language this copy is in (`it`, `pt-BR`, \u2026). Canonical content is stored per locale, so each language keeps its own row instead of overwriting the English one. Omit it for English.\n\n**Local write only \u2014 NOT a channel publish.** This mutates Repull's own copy of the content. It does NOT push to Airbnb / Booking.com; it marks the channels dirty so a later publish knows what changed. Distribution stays a separate explicit step.\n\n**Photos are deferred:** a provided `photos` array is echoed back in the `deferred` field and NOT persisted (media ingestion is a follow-up).\n\nCross-tenant access (a listing that belongs to a different workspace) returns 404 \u2014 never 403. This endpoint is served even when the account is over the plan-listings cap, since editing content on a listing you already own never grows the portfolio.\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.",
24069
+ "description": "Write your PMS's canonical listing content \u2014 title, description, amenities, address, occupancy, and policies \u2014 into a Repull listing, making it the source of truth. This is the flagship \"the PMS owns listing content, Repull distributes it\" enabler.\n\n**Listings managed in a connected PMS** (Guesty, Hostaway, \u2026): the PMS owns their content, so title, descriptions, check-in/out times, capacity, amenities, house rules, address and added photos are written to the PMS first; Repull keeps only what it accepted (refused sections are in `deferred`, its per-section outcome in `pms`). A section that PMS cannot write returns `422 pms_write_unsupported` naming it when nothing was applied (or is listed in `deferred` when other sections were); `GET /v1/listings/{id}` \u2192 `capabilities.pms.listings` says which sections it takes. A PMS whose connector writes no listing content at all (Mews, Cloudbeds, Lodgify, \u2026) keeps today's behaviour: the content is written to Repull only. Send photos with `photosMode: \"append\"` \u2014 replacing a PMS listing's photo set needs the PMS's own photo ids. A revoked PMS connection is `403 connection_reauth_required`.\n\n**Partial update:** every field is optional. Only the fields you send are written; absent fields are left untouched. `amenities` is a FULL replacement of the amenity set (omit to leave untouched, send `[]` to clear).\n\n**Multilingual:** send `locale` to say which language this copy is in (`it`, `pt-BR`, \u2026). Canonical content is stored per locale, so each language keeps its own row instead of overwriting the English one. Omit it for English.\n\n**Local write only \u2014 NOT a channel publish.** This mutates Repull's own copy of the content. It does NOT push to Airbnb / Booking.com; it marks the channels dirty so a later publish knows what changed. Distribution stays a separate explicit step.\n\n**Photos are deferred:** a provided `photos` array is echoed back in the `deferred` field and NOT persisted (media ingestion is a follow-up).\n\nCross-tenant access (a listing that belongs to a different workspace) returns 404 \u2014 never 403. This endpoint is served even when the account is over the plan-listings cap, since editing content on a listing you already own never grows the portfolio.\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.",
23701
24070
  "tags": [
23702
24071
  "Listings"
23703
24072
  ],
@@ -29194,7 +29563,7 @@
29194
29563
  "post": {
29195
29564
  "operationId": "preapprove_conversation",
29196
29565
  "summary": "Pre-approve an inquiry (Airbnb, VRBO)",
29197
- "description": "Pre-approve the inquiry on this conversation: the guest who asked about dates may now book them at the listed price, without waiting on you. To change the dates, guests or price, send a special offer instead (`POST /v1/conversations/{id}/special-offers`).\n\nFind inquiries that need an answer with `GET /v1/inquiries` (default `status=open`); each carries the `conversationId` to use here.\n\nOne endpoint for every channel with pre-approvals: **Airbnb** (listings connected directly) and **VRBO**. A Booking.com or direct-booking conversation, or an Airbnb one relayed through a PMS (Hostaway, Guesty), returns `422 channel_not_supported` and nothing is sent \u2014 `GET /v1/conversations/{id}` \u2192 `capabilities.canPreApprove` says where it works.\n\n`blockInstantBooking` is Airbnb only (VRBO has no such switch: `422 invalid_params`). `message` is sent to the guest with a VRBO pre-approval (a friendly default otherwise).\n\nThe inquiry is marked `pre_approved` everywhere, the same as pre-approving on the channel. Withdraw it with `DELETE /v1/conversations/{id}/pre-approval` (VRBO).\n\nA channel\u2019s refusal is never reported as a success: an inquiry that already moved on is `409 inquiry_no_longer_open`, an expired one `409 inquiry_expired`, a conversation that already has a booking `409 conversation_already_booked`.\n\nSend `Idempotency-Key`: a repeat with the same key replays the first response instead of acting twice (a `409 idempotency_key_in_use` while the first is still running). A 5xx, a `429 airbnb_rate_limited` or a `403 connection_reauth_required` is not stored \u2014 nothing was done \u2014 so retrying with the same key reaches Airbnb again.",
29566
+ "description": "Pre-approve the inquiry on this conversation: the guest who asked about dates may now book them at the listed price, without waiting on you. To change the dates, guests or price, send a special offer instead (`POST /v1/conversations/{id}/special-offers`).\n\nFind inquiries that need an answer with `GET /v1/inquiries` (default `status=open`); each carries the `conversationId` to use here.\n\nOne endpoint for every channel with pre-approvals: **Airbnb** (listings connected directly) and **VRBO**. A Booking.com or direct-booking conversation returns `422 channel_not_supported` and nothing is sent \u2014 `GET /v1/conversations/{id}` \u2192 `capabilities.canPreApprove` says where it works.\n\n**An inquiry relayed by a PMS** (Guesty, Hostaway, \u2026) is pre-approved in that PMS. A PMS whose API cannot returns `422 pms_write_unsupported` naming it (Hostaway today); `GET /v1/connect/{provider}` \u2192 `capabilities.pms.reservations.preapprove` says so beforehand. The response then carries `pms`.\n\n`blockInstantBooking` is Airbnb only (VRBO has no such switch: `422 invalid_params`). `message` is sent to the guest with a VRBO pre-approval (a friendly default otherwise).\n\nThe inquiry is marked `pre_approved` everywhere, the same as pre-approving on the channel. Withdraw it with `DELETE /v1/conversations/{id}/pre-approval` (VRBO).\n\nA channel\u2019s refusal is never reported as a success: an inquiry that already moved on is `409 inquiry_no_longer_open`, an expired one `409 inquiry_expired`, a conversation that already has a booking `409 conversation_already_booked`.\n\nSend `Idempotency-Key`: a repeat with the same key replays the first response instead of acting twice (a `409 idempotency_key_in_use` while the first is still running). A 5xx, a `429 airbnb_rate_limited` or a `403 connection_reauth_required` is not stored \u2014 nothing was done \u2014 so retrying with the same key reaches Airbnb again.",
29198
29567
  "tags": [
29199
29568
  "Conversations"
29200
29569
  ],
@@ -29450,7 +29819,7 @@
29450
29819
  "error": {
29451
29820
  "code": "channel_not_supported",
29452
29821
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
29453
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
29822
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
29454
29823
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
29455
29824
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
29456
29825
  "channel": "booking"
@@ -29694,7 +30063,7 @@
29694
30063
  "error": {
29695
30064
  "code": "channel_not_supported",
29696
30065
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
29697
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
30066
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
29698
30067
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
29699
30068
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
29700
30069
  "channel": "booking"
@@ -30403,7 +30772,7 @@
30403
30772
  "error": {
30404
30773
  "code": "channel_not_supported",
30405
30774
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
30406
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
30775
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
30407
30776
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
30408
30777
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
30409
30778
  "channel": "booking"
@@ -30829,7 +31198,7 @@
30829
31198
  "error": {
30830
31199
  "code": "channel_not_supported",
30831
31200
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
30832
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
31201
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
30833
31202
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
30834
31203
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
30835
31204
  "channel": "booking"
@@ -31114,7 +31483,7 @@
31114
31483
  "error": {
31115
31484
  "code": "channel_not_supported",
31116
31485
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
31117
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
31486
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
31118
31487
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
31119
31488
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
31120
31489
  "channel": "booking"
@@ -31453,7 +31822,7 @@
31453
31822
  "post": {
31454
31823
  "operationId": "accept_reservation_request",
31455
31824
  "summary": "Accept a booking request",
31456
- "description": "Accept a pending Airbnb booking request \u2014 a reservation with status `pending`, made on a listing without Instant Book. Find them with `GET /v1/reservations?status=pending`. Airbnb expires a request the host has not answered within 24 hours.\n\nAirbnb confirms asynchronously: the reservation\u2019s status moves to confirmed, and a `reservation.updated` webhook fires, when Airbnb\u2019s notification lands (usually within seconds). The response reports what Airbnb was asked to do.\n\n**Airbnb only**, and only for listings connected to Airbnb directly: other channels have no request step (`422 channel_not_supported`). A reservation that is not pending is refused before Airbnb is contacted (`409 reservation_not_pending`); one Airbnb says already moved on is `409 request_no_longer_pending`. Neither is worth retrying.\n\nTakes no body.\n\nSend `Idempotency-Key`: a repeat with the same key replays the first response instead of acting twice (a `409 idempotency_key_in_use` while the first is still running). A 5xx, a `429 airbnb_rate_limited` or a `403 connection_reauth_required` is not stored \u2014 nothing was done \u2014 so retrying with the same key reaches Airbnb again.",
31825
+ "description": "Accept a pending Airbnb booking request \u2014 a reservation with status `pending`, made on a listing without Instant Book. Find them with `GET /v1/reservations?status=pending`. Airbnb expires a request the host has not answered within 24 hours.\n\nAirbnb confirms asynchronously: the reservation\u2019s status moves to confirmed, and a `reservation.updated` webhook fires, when Airbnb\u2019s notification lands (usually within seconds). The response reports what Airbnb was asked to do.\n\n**Airbnb**, for listings connected to Airbnb directly; other channels have no request step (`422 channel_not_supported`). **A request relayed by a PMS** (Guesty, Hostaway, \u2026) is answered in that PMS, whatever channel it came from; a PMS whose API cannot answer requests returns `422 pms_write_unsupported` naming it (Hostaway today), and `GET /v1/connect/{provider}` \u2192 `capabilities.pms.reservations.respond` says so beforehand. The response then carries `pms`. A reservation that is not pending is refused before Airbnb is contacted (`409 reservation_not_pending`); one Airbnb says already moved on is `409 request_no_longer_pending`. Neither is worth retrying.\n\nTakes no body.\n\nSend `Idempotency-Key`: a repeat with the same key replays the first response instead of acting twice (a `409 idempotency_key_in_use` while the first is still running). A 5xx, a `429 airbnb_rate_limited` or a `403 connection_reauth_required` is not stored \u2014 nothing was done \u2014 so retrying with the same key reaches Airbnb again.",
31457
31826
  "tags": [
31458
31827
  "Reservations"
31459
31828
  ],
@@ -31684,7 +32053,7 @@
31684
32053
  "error": {
31685
32054
  "code": "channel_not_supported",
31686
32055
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
31687
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
32056
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
31688
32057
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
31689
32058
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
31690
32059
  "channel": "booking"
@@ -31692,16 +32061,15 @@
31692
32061
  }
31693
32062
  },
31694
32063
  "viaPms": {
31695
- "summary": "An Airbnb booking relayed through a PMS",
32064
+ "summary": "A booking relayed by a PMS whose API cannot answer requests",
31696
32065
  "value": {
31697
32066
  "error": {
31698
- "code": "channel_not_supported",
31699
- "message": "Reservation 233530 reaches Airbnb through guesty, not a direct Airbnb connection. Accept it in guesty.",
31700
- "fix": "Pre-approvals, special offers and accepting/declining requests exist only on Airbnb, and only for listings connected to Airbnb directly (not through a PMS such as Hostaway or Guesty).",
32067
+ "code": "pms_write_unsupported",
32068
+ "message": "Hostaway's API cannot accept or decline booking requests.",
32069
+ "fix": "Do it in Hostaway; the change reaches Repull with the next sync. `GET /v1/connect/hostaway` \u2192 `capabilities.pms` lists what Hostaway supports.",
31701
32070
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
31702
32071
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
31703
- "channel": "airbnb",
31704
- "pms": "guesty"
32072
+ "provider": "hostaway"
31705
32073
  }
31706
32074
  }
31707
32075
  },
@@ -32120,7 +32488,7 @@
32120
32488
  "error": {
32121
32489
  "code": "channel_not_supported",
32122
32490
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
32123
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
32491
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
32124
32492
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
32125
32493
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
32126
32494
  "channel": "booking"
@@ -34308,7 +34676,7 @@
34308
34676
  "error": {
34309
34677
  "code": "channel_not_supported",
34310
34678
  "message": "Reservation 233530 came from Booking.com. Booking requests can only be accepted or declined on Airbnb \u2014 other channels confirm instantly or have no request step.",
34311
- "fix": "Pre-approvals and special offers work on Airbnb (connected directly, not through a PMS such as Hostaway or Guesty) and VRBO; accepting/declining booking requests on Airbnb. `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
34679
+ "fix": "Pre-approvals and special offers work on Airbnb (connected directly) and VRBO; accepting/declining booking requests on Airbnb. Requests and inquiries relayed by a PMS are answered in that PMS (`pms_write_unsupported` when its API cannot). `GET /v1/conversations/{id}` \u2192 `capabilities` says what works on a conversation. Booking.com and direct bookings confirm without a request step.",
34312
34680
  "docs_url": "https://repull.dev/docs/channels/airbnb/inquiries-and-requests",
34313
34681
  "request_id": "req_01J5X7Y8Z9ABCDEF12345678",
34314
34682
  "channel": "booking"
data/scripts/regen.sh CHANGED
@@ -73,7 +73,7 @@ npx --yes @openapitools/openapi-generator-cli@latest generate \
73
73
  -i "$SPEC_PATH" \
74
74
  -g ruby \
75
75
  -o "$GEN_OUT" \
76
- --additional-properties=gemName=repull,moduleName=Repull,gemVersion=0.2.28
76
+ --additional-properties=gemName=repull,moduleName=Repull,gemVersion=0.2.29
77
77
 
78
78
  echo "==> syncing lib/ + docs/"
79
79
  rm -rf "$ROOT/lib/repull" "$ROOT/lib/repull.rb" "$ROOT/docs"