Provider Capability Matrix
Exactly what each channel supports today, capability by capability, with a real request for every supported operation. Honest cells: supported means shipped and callable, partial means it works with caveats, unsupported means the API has no operation for it on that channel, and n/a means the channel itself has no such thing (Booking.com has no inquiries, and hosts there do not review guests).
Every cell names the endpoints behind it, and the grades are checked against the API's own OpenAPI description before this page can ship: a capability marked unsupported here cannot be one the API is able to do. Where a cell reads partial, the section below it says exactly which part.
Vrbo is in beta: the host signs in with their Vrbo account and Repull reads and answers for it. The Booking.com column is the connectivity-partner connection; the Extranet login covers reservations, messages and reviews and leaves listings, rates and availability to the host's own channel manager — see Works alongside an existing PMS.
| Capability | Airbnb | Booking.com | Vrbo |
|---|---|---|---|
| Connect (hosted flow) | Supported | Supported | Supported Sign in with the host account (beta) |
| Works alongside an existing PMS | Supported Messaging access — the PMS keeps listing management | Supported Extranet login (beta) — reservations, messages, reviews | Supported Messaging access — the calendar is never pushed |
| Listing discovery (read) | Supported | Supported | Supported |
| Create a new channel listing | Supported Publish with hostId | Supported setup → create-property | Unsupported Link listings the host already has |
| Link an existing channel listing | Supported POST …/listings/map | Supported POST …/booking/listings/map · or map rooms during Connect | Partial Map units on the hosted Connect page |
| Canonical content write | Supported PUT …/listings/{id}/content — one write for every channel; to edit Airbnb alone, see Granular content write | Supported Same write, every channel; to edit Booking.com alone, see Granular content write | Unsupported Content stays on Vrbo |
| Publish / push listing content | Supported Pushes the content Repull holds | Supported Pushes the content Repull holds: description, facilities, rooms and beds, photos, policies, cancellation, check-in, fees | Unsupported |
| Granular content read | Supported Every field group, plus settings · quality · check-out guide | Supported photos · facilities · description · settings · policies · licences · check-in · contacts, read straight from Booking.com | Unsupported |
| Granular content write | Supported 11 field groups — amenities · descriptions · details · rooms · photos (+ cover, order) · check-in guide · booking settings · permits · safety disclosures | Supported photos · facilities · description · settings · policies · licences · check-in · contacts, property and room level | Unsupported |
| Listing state action | Supported push · publish · unlist · relist · delete | Supported unlist · relist (closes and reopens availability) | Unsupported |
| Availability write (per-channel) | Supported | Supported | Unsupported Use the generic ARI write |
| Pricing write (per-channel) | Supported | Supported | Unsupported Use the generic ARI write |
| Channel markup | Supported Per listing · prices re-sent on change | Supported Per property, shared by its listings · prices re-sent on change | Unsupported Airbnb and Booking.com only |
| Calendar read | Supported The listing's calendar, before markup — the same on every channel | Supported The listing's calendar, before markup — the same on every channel | Supported The listing's calendar, before markup — the same on every channel |
| Generic ARI write | Supported Pushed on write | Supported Pushed on write | Partial Pushed on write with full access · prices, availability, minimum stay |
| Reservations (read) | Supported | Supported | Supported |
| Accept · decline · cancel a reservation | Supported By confirmation code, or by Repull reservation id | Unsupported Bookings arrive confirmed | Unsupported Manage bookings on Vrbo |
| Reservation alterations | Supported List · read · create · accept · decline · cancel | Unsupported | Unsupported |
| Messaging | Supported Threads · send · edit/unsend/react/mark read | Supported Conversations · send · attachments | Supported Conversations · send (text) |
| Inquiry: special offers & pre-approvals | Supported Create · read · withdraw | n/a Booking.com has no inquiries | Supported Pre-approve · edit the quote · withdraw |
| Guest reviews | Supported List · reply · review a guest | Supported List · reply | Supported List · reply |
| Host reviews | Supported Review the guest · edit it | n/a Hosts do not review guests on Booking.com | Unsupported Airbnb only |
| Financials | Supported Payout ledger: every payout and its lines, stable ids, refresh on demand | Supported Charges read + replace | Unsupported |
| Webhooks | Supported | Supported | Supported |
| Usage & quota | Supported Account-level — all channels | Supported Account-level — all channels | Supported Account-level — all channels |
Specialty channels
One more provider is live with a narrower surface. Plumguide exposes reads plus availability/pricing writes and a webhook config. It has no content or mapping write, and the generic calendar write does not reach it — push its calendar with PUT /v1/channels/plumguide/availability.
| Capability | Plumguide |
|---|---|
| Connect (hosted flow) | Supported |
| Listing discovery (read) | Supported GET …/plumguide/listings |
| Reservations / bookings (read) | Supported GET …/plumguide/bookings |
| Availability write | Supported PUT …/plumguide/availability |
| Pricing write | Supported PUT …/plumguide/pricing |
| Content / mapping write | Unsupported |
| Webhooks | Supported GET · PUT (replace) · DELETE |
One id everywhere
4118). Channel-specific ids (Airbnb airbnbId/hostId, Booking hotelId) live under it — see IDs & External IDs.Connect
Both providers connect through the same hosted flow. Start a session, redirect the user to the returned connect.repull.dev URL, then confirm with the status endpoint.
POST /v1/connect/airbnb # or /v1/connect/booking
{ "redirectUrl": "https://your-app.com/done", "accessType": "full_access" }
# →
{
"url": "https://connect.repull.dev/cs_abc123",
"provider": "airbnb",
"sessionId": "cs_abc123",
"expiresAt": "2026-07-30T12:00:00.000Z"
}
# Confirm
GET /v1/connect/airbnb
{ "connected": true, "provider": "airbnb", "id": 12, "status": "active", "externalAccountId": "112233" }Works alongside an existing PMS
On all three channels Repull can connect next to the PMS or channel manager the host already uses, without taking over its connection. The PMS keeps listings, rates and availability; Repull brings you the host's reservations, guest messages and reviews, and sends replies.
- Airbnb — connect with
accessType: "messaging". - Booking.com — connect with the Extranet login.
- Vrbo — connect with
accessType: "messaging"; the calendar is never pushed.
The full picture, channel by channel: Use Repull alongside a PMS.
Listing discovery (read)
Read the listings each connection exposes. Both are pure reads — no live provider call.
Why Airbnb has listings and Booking.com has properties
/v1/channels/airbnb/listings, /v1/channels/booking/properties) the channel keeps its own words, because the ids and shapes are the channel's. An Airbnb listing is one bookable place. A Booking.com property is a building whose roomsare what guests book — one property often holds several of your listings, one per room. Your own listings, the same on every channel, are /v1/listings; /v1/properties is an older name for the same thing and keeps working.Airbnb — supported
GET /v1/channels/airbnb/listings
{
"data": [
{
"listingId": 4118,
"name": "Lakeview Loft",
"connections": [
{
"id": 55,
"airbnbId": "12345678",
"accountId": "112233", // which connected Airbnb account (hostId is an alias)
"accountName": "Seaside Stays",
"active": true,
"primary": true
}
]
}
],
"pagination": { "nextCursor": null, "hasMore": false, "total": 1 },
"dataFreshness": {
"lastSyncedAt": "2026-09-18T04:12:09.000Z",
"stale": false,
"accounts": [
{ "accountId": "112233", "accountName": "Seaside Stays", "lastSyncedAt": "2026-09-18T04:12:09.000Z", "stale": false }
]
}
}Every Airbnb collection route — listings, reservations, messaging, reviews, transactions, alterations — accepts ?account_id=<airbnb host id> to scope the response to one connected account, stamps each row with accountId / accountName, and reports freshness per account in dataFreshness.accounts[]. See Account scope → Several Airbnb accounts.
Booking.com — supported
Returns a bare array (no data wrapper).
GET /v1/channels/booking/properties
[
{
"listingId": 4118,
"name": "Lakeview Loft",
"connections": [
{ "id": 12, "hotelId": "998877", "active": true, "syncCategory": "full" }
]
}
]Create vs link a channel listing
Two different operations, often confused. Create makes a brand-new listing on the channel from a canonical Repull property. Link points an existing channel listing (one the host already has on Airbnb or Booking.com) at a canonical Repull property so the two are kept in sync. Pick by which side the listing already exists on.
| You want to… | Airbnb | Booking.com |
|---|---|---|
| Create a new channel listing from a Repull property | POST /v1/listings/{id}/publish/airbnb with hostId | POST /v1/channels/booking/setup with create-property, then POST /v1/listings/{id}/publish/booking |
| Link an existing channel listing to a Repull property | POST /v1/channels/airbnb/listings/map | POST /v1/channels/booking/listings/map, or map rooms during Connect |
Create — Airbnb supported
Publish in create mode — pass a hostId (no airbnbConnectionId) and Repull creates a brand-new Airbnb listing under that host, pushing the content it already holds for the property. Pass exactly one of the two: with hostId it creates, with airbnbConnectionId it updates an existing listing (see Publish below).
POST /v1/listings/4118/publish/airbnb
{ "hostId": "112233" } // create — omit airbnbConnectionId
# →
{ "listingId": 4118, "channel": "airbnb", "result": { /* push result */ } }Create — Booking.com supported
One call opens a brand-new property from a Repull listing: the property, its first room with the listing's beds, a rate plan and the product that makes that room sellable, the contact and invoice details, the listing's facilities, cancellation policy and prices, then Booking.com's readiness check. You do not supply a legal entity — the one your workspace already contracts under is reused, and a second is never registered. Send a contact (name, email, phone in international form) for Booking.com to reach about the property; without one, the workspace owner is used.
POST /v1/channels/booking/setup
{ "action": "create-property", "listing_id": 4118,
"contact": { "name": "Ana Silva", "email": "ana@example.com", "phone": "+14155550142" } }
# →
{ "action": "create-property", "listingId": "4118",
"propertyId": "17325248", "roomId": "1732524801",
"legalEntity": { "id": "59731", "source": "workspace" },
"status": "being_built", "sellable": false,
"warnings": ["not yet bookable — Booking.com's readiness check says: Property (17325248): License required, but not found"],
"nextSteps": [ /* ... */ ] }
# see what still blocks opening, then check-and-open in one step:
POST /v1/channels/booking/setup { "action": "check-readiness", "property_id": "17325248" }
# → { "ready": false, "blockers": ["Property (17325248): License required, but not found"] }
POST /v1/channels/booking/setup { "action": "advance", "property_id": "17325248" }A new property is not sellable the moment it exists.Booking.com opens it only once its readiness check passes, and the check names each thing still missing — a main photo that is still processing, no availability, or a licence the region requires. check-readiness returns that list as blockers; advance checks and, when nothing is left, opens the property. Give the listing a price before you create it: that is what writes the calendar Booking.com needs.
Link — Airbnb supported
The host already has this listing on Airbnb and you discovered it via GET /v1/channels/airbnb/listings (which surfaces airbnbId + hostId per connection). Link points that Airbnb listing at the canonical Repull listingId of your choice — the consolidation / de-dup case. API-key-scoped, and idempotent (re-linking to the same listing is a 200 no-op).
POST /v1/channels/airbnb/listings/map
{ "airbnbId": "12345678", "listingId": 4118 } // optional "hostId", "syncEnabled"
# →
{ "success": true, "alreadyMapped": false, "airbnbId": "12345678",
"listingId": 4118, "previousListingId": 9001, "platformLinkId": 77 }Link — Booking.com rooms supported
The host already has this property on Booking.com. Booking.com attaches at the room: a property is a building and its rooms are what a guest books, so you map each room to one Repull listing. Find the room ids with GET /v1/channels/booking/properties/{id}/rooms, then map them with an API key — the counterpart of the Airbnb link above. Set listingId: null to unmap a room.
POST /v1/channels/booking/listings/map
{ "roomBookingId": "1557551502", "listingId": 3867 }
# →
{ "success": true, "alreadyMapped": false,
"roomBookingId": "1557551502", "listingId": "3867", "previousListingId": null,
"hotelId": "15575515", "roomId": "968", "roomName": "Room 02804",
"platformLinkId": "3988", "reservationsImported": 1 }The property's reservations come in with the mapping. Once the room is mapped, every active reservation Booking.com holds for the property is imported onto its listing, and reservationsImportedsays how many were processed. You make no follow-up call — and there is no other moment they would arrive, because the regular sync only picks up new changes on rooms that are already mapped. A reservation already present is left as it is, so re-sending the same mapping never duplicates; it retries the import instead. A property with a long history can take tens of seconds. If the import cannot run, the mapping still stands and the field is null.
The hosted Connect flow maps rooms the same way when a host claims their property there, and imports the same way. One listing can carry several Booking.com rooms — that is a normal arrangement and is not refused.
Listing content
Content is written once, to the canonical listing, and distributed by a separate publish. PUT /v1/listings/{id}/content is that write: one channel-agnostic call for title, description, summary, amenities, address, occupancy, property details and policies. Every field is optional and only the fields you send are written, so a partial update is just a smaller body — with the exception of amenities, which is a full replacement of the amenity set. Send locale to say which language this copy is in; each locale keeps its own row rather than overwriting the English one.
PUT /v1/listings/4118/content
{
"title": "Sable 1302 — mountain-view suite",
"summary": "Two bedrooms, hot-spring views, ski shuttle at the door.",
"amenities": ["wifi", "kitchen", "free_parking"],
"occupancy": { "maxGuests": 4, "bedrooms": 2, "beds": 3, "bathrooms": 1.5 }
}
# → what was written, and anything that was accepted but not applied
{ "id": "4118", "changed": ["title", "summary", "amenities", "occupancy"], "deferred": [] }Writing content is not publishing it
photos array comes back in deferred rather than being stored. Upload photos with POST /v1/listings/{id}/photos/upload-url.PATCH /v1/listings/{id} is a different call with a narrow job: it toggles the active flag and writes no content.
PATCH /v1/listings/4118
{ "active": false }
# →
{ "id": 4118, "active": false }Airbnb publish — supported
Publishing pushes content Repull already holds for the listing to Airbnb. The request carries no content — only which connection (or host) to publish under. Provide exactly one of airbnbConnectionId (update an existing listing) or hostId (create a new one).
POST /v1/listings/4118/publish/airbnb
{ "airbnbConnectionId": 55 } // or { "hostId": "112233" }, optional "force": true
# →
{ "listingId": 4118, "channel": "airbnb", "result": { /* push result */ } }Two ways to push, and what a publish leaves out
POST /v1/channels/airbnb/listings/{id} with {"action": "push"} or "publish" does the same push as the call above, alongside unlist, relist and delete — see Listing state actions. A publish also does not carry every field: which sections it pushes, which fields Airbnb locks, and how to read back what took effect are on the Airbnb publication contract.Booking.com publish — supported
Publishing pushes the content Repull holds for the listing to the Booking.com property it is on. The listing needs a Booking.com property first — one you created or linked. If the listing sits on more than one property, name it with hotelId; without it the call is refused with 409 ambiguous_booking_mapping rather than guessing.
POST /v1/listings/4118/publish/booking
{ "hotelId": "15575515" } # optional when the listing is on one property
# →
{
"listingId": 4118,
"channel": "booking",
"result": {
"published": true,
"hotelId": "15575515",
"sections": ["photos", "policies", "charges"],
"errors": []
}
}published is true only when every section that had something to send was accepted. A refused section is named in errorswith Booking.com's own reason, and the same text stays readable afterwards on GET /v1/listings/{id}/publish-status.
What Booking.com does with what you send
- Description— Booking.com rewrites it into its own copy, in every language, and publishes that rather than your text. Allow about three hours.
- Amenitiesbecome Booking.com facilities. Most common ones have a direct match; a facility Booking.com needs more detail for than a listing holds (a pool's type and season, for instance) is left out rather than guessed.
- Cancellation policybecomes the nearest Booking.com policy — Booking.com has no grace window, so a strict policy maps to “free until 14 days before arrival, then 50%”.
- Room nameson Booking.com are its own standard names (“Two-Bedroom Apartment”); the listing's title is kept as the operator-side reference.
Granular Airbnb content — supported
Airbnb content is addressable field-group by field-group, and every group that can be read can also be written except the three that Airbnb itself keeps read-only — settings, quality and the check-out guide. Eleven groups take a write. Reads are served from your synced copy and never wait on Airbnb; writes call Airbnb and are reflected back once it answers. All target the canonical Repull listingId, not the Airbnb listing id. Use these when you want one section changed on the live listing without republishing the whole thing.
# Reads (served from your synced copy — never wait on Airbnb) GET /v1/channels/airbnb/listings/4118/amenities # regular + accessibility amenities GET /v1/channels/airbnb/listings/4118/descriptions GET /v1/channels/airbnb/listings/4118/details GET /v1/channels/airbnb/listings/4118/rooms GET /v1/channels/airbnb/listings/4118/photos GET /v1/channels/airbnb/listings/4118/checkin-guide # ?locale=en GET /v1/channels/airbnb/listings/4118/booking-settings GET /v1/channels/airbnb/listings/4118/permits # ?source=live for Airbnb's questions GET /v1/channels/airbnb/listings/4118/safety-disclosures GET /v1/channels/airbnb/listings/4118/settings # read-only GET /v1/channels/airbnb/listings/4118/quality # read-only, ?type=all|standards|issues|stats GET /v1/channels/airbnb/listings/4118/checkout-guide # read-only # Writes (call Airbnb) PUT /v1/channels/airbnb/listings/4118/amenities PUT /v1/channels/airbnb/listings/4118/descriptions # one locale per call PUT /v1/channels/airbnb/listings/4118/details # property type, room type, quiet hours, check-in method PUT /v1/channels/airbnb/listings/4118/booking-settings PUT /v1/channels/airbnb/listings/4118/permits # answer Airbnb's permit questions PUT /v1/channels/airbnb/listings/4118/safety-disclosures PUT /v1/channels/airbnb/listings/4118/checkin-guide # ?locale=en POST /v1/channels/airbnb/listings/4118/rooms # body = room object (minus room_id) PUT /v1/channels/airbnb/listings/4118/rooms DELETE /v1/channels/airbnb/listings/4118/rooms?roomId=456 POST /v1/channels/airbnb/listings/4118/photos # upload PATCH /v1/channels/airbnb/listings/4118/photos # caption, room assignment DELETE /v1/channels/airbnb/listings/4118/photos?photoId=789 PUT /v1/channels/airbnb/listings/4118/photos/cover PUT /v1/channels/airbnb/listings/4118/photos/order
Per-field-group writes go straight to Airbnb
PUT /v1/listings/{id}/content when the change belongs to the property on every channel, and use these when it is Airbnb-specific. Some of them are the only way to set a field — a publish deliberately does not carry permits, safety disclosures, the check-in method, or descriptions in a second language. The publication contract lists what a publish does and does not push.Granular Booking.com content — supported
One endpoint, GET/POST /v1/channels/booking/content, typed by type, reads and writes one kind of content on a Booking.com property without touching the listing or Airbnb: photos, facilities, description, settings, policies, licences, checkin_methods and contacts. Add room_id for a room's facilities, photos or licence. Reads come straight from Booking.com, and what a read returns can be sent back as it is.
# the rules for this region, then the licence
GET /v1/channels/booking/content?property_id=17325248&type=licences
POST /v1/channels/booking/content
{ "type": "licences", "property_id": "17325248", "variantId": 58,
"contentData": [{ "name": "business_tax_receipt", "value": "BTR123456-01-2026" },
{ "name": "certificate_number", "value": "1234567" }] }
# one room's facilities
POST /v1/channels/booking/content
{ "type": "facilities", "property_id": "17325248", "room_id": "1732524801",
"facilities": [{ "room_facility_id": 8, "state": "PRESENT" }] }A write Booking.com refuses answers 422 booking_rejectedwith its reason — including the refusals Booking.com returns inside an HTTP 200. A licence value that does not match the region's format is refused before it is sent, naming the field.
Listing state actions (Airbnb)
One endpoint carries the five state changes, selected by action. The path id is the canonical Repull listing id. push and publish are the same operation as POST /v1/listings/{id}/publish/airbnb above.
POST /v1/channels/airbnb/listings/4118
{ "action": "unlist", "airbnbConnectionId": 77 }
# action:
# push | publish push the content Repull holds to Airbnb
# unlist take the live Airbnb listing down
# relist put it back up
# delete deactivate the Repull record only — Airbnb is untouchedUnlisting and deactivating are different operations
delete deactivates the Repull record and keeps it: the Airbnb listing stays live and keeps taking bookings. unlist takes the guest-facing listing down, and it is verified rather than assumed — Airbnb accepting the call is not the same as the listing coming down, so it is read back and you get 422 airbnb_rejected if it is still up. unlist and relist require airbnbConnectionId, because one Repull listing can be connected to more than one Airbnb listing. Side by side on the publication contract.Booking.com has no per-listing action route. To exclude a Booking.com-connected property from Repull, use the channel-agnostic PATCH /v1/listings/{id} with {"active": false}. Opening a property for sale is a setup action.
Availability & pricing (ARI)
Calendar read — supported
Every listing has one calendar in Repull, whatever channels it is on: availability, the listing's own nightly price and minimum stay, day by day. It is the source each channel is priced from, so it is the same answer for Airbnb and Booking.com. from and to (ISO YYYY-MM-DD) are required. The price here is before any channel markup; to see what a channel holds right now, read it from the channel (GET /v1/channels/booking/availability).
GET /v1/availability/27826?from=2026-08-01&to=2026-08-07
{
"propertyId": "27826",
"currency": "BRL",
"days": [
{ "date": "2026-08-01", "available": true, "price": 245, "minNights": 2 }
],
"coverage": {
"requestedDays": 7,
"coveredDays": 1,
"missingDates": ["2026-08-02", "2026-08-03", "2026-08-04",
"2026-08-05", "2026-08-06", "2026-08-07"]
}
}A missing date is unknown, never bookable
days carries only the dates backed by a real calendar row. Anything else in the window is listed in coverage.missingDates — availability there is unknown. The endpoint deliberately does not synthesise a default, because a fabricated open date can be double-booked. A property with no calendar still returns a real 200 with days: []; a 404 means the property id does not exist in this workspace.Generic ARI write — supported
This is the default for calendar values. One write covers the channels Repull can write a calendar to: it sets the Repull calendar and pushes the result to Airbnb and Booking.com in the same step. For Plumguide use PUT /v1/channels/plumguide/availability. PATCH /v1/availability/batch applies the same settings across up to 500 properties. The body applies one settings object to a list of dates, and at least one of available, price, minNights, maxNights is required — a body with only dates returns 422. Reach for the per-channel routes below only when you need a restriction the generic shape does not carry, such as closed-to-arrival.
PUT /v1/availability/27826
{
"dates": ["2026-08-01", "2026-08-02"],
"available": false,
"price": 320
}
{
"listingIds": ["27826"],
"dates": 2,
"synced": { "attempted": 2, "succeeded": 2, "failed": 0, "authErrors": 0 }
}Airbnb — supported
A calendar operation sets availability, nightly price, and stay limits together. 4118 is the Repull listing id, not the Airbnb listing id. Note the real field names: availability is a tri-state enum (not a boolean), price is daily_price, and stay limits are min_nights/max_nights (1–1125). Airbnb also supports closed_to_arrival/closed_to_departure. A blocked date ("availability": "unavailable") is sent with busy_subtype: "BLOCKED_BY_HOST" unless you pass "OUTSIDE_RESERVATION" for a date held by a booking on another channel. Unknown fields are refused with 422 invalid_params.
PUT /v1/channels/airbnb/listings/4118/availability
{
"type": "calendar",
"operations": [
{
"start_date": "2026-08-01",
"end_date": "2026-08-07",
"availability": "available", // "available" | "unavailable" | "default"
"daily_price": 245,
"min_nights": 2,
"closed_to_arrival": false
}
]
}Pricing has its own typed endpoint (base price, length-of-stay, rate plans, fees):
PUT /v1/channels/airbnb/listings/4118/pricing
{ "type": "standard", "settings": { "default_daily_price": 245 } }
# Currency is its own sub-resource
{ "type": "currency", "currency": "USD" }Errors: 422 invalid_params (a field failed validation, named in field), 422 airbnb_rejected (Airbnb refused the change; message carries its reason), 403 connection_reauth_required (reconnect at repull.dev/dashboard/connections; retrying will not help), 403 listing_not_api_connected (Airbnb API sync is off for that one listing — the host turns it on inside Airbnb; reconnecting the account does not help), 403 listing_inactive, 404 not_found, 429 airbnb_rate_limited, and 502 airbnb_error (Airbnb outage — retry). Every type and field is on Update Airbnb Pricing.
Booking.com — supported
Booking's per-channel schema is the only one that carries maxStay and closed-to-arrival / closed-to-departure restrictions. Updates target the property_id (hotel id) and its rooms.
Two things are particular to Booking.com. A rate amount is priced at an occupancy — a party size the rate plan sells at — and a write that names one the rate plan does not price at is accepted and then silently ignored, so the response reports whether the price is really live rather than that it was sent. And dateRange is inclusive at both ends: 2026-08-01 → 2026-08-07 is seven nights. Both are covered on Update Booking.com Pricing.
Where roomId and rateId come from
Every Booking.com update below needs a roomId and a rateId. Read them from GET /v1/channels/booking/properties/{listingId}/rooms, which takes a Repull listing id and returns each room on the property Booking.com publishes that listing under, with every rate plan attached to it.
GET /v1/channels/booking/properties/28028/rooms
{
"hotelId": "5432505",
"listingId": "28028",
"otherHotelIds": [],
"source": "booking",
"mirrorReason": null,
"rooms": [
{
"roomId": "543250501",
"roomName": "Deluxe Villa",
"maxAdults": 4,
"rates": [
{ "rateId": "15436997", "rateName": "Standard Rate", "policy": "General", "policyId": "195090900", "maxPersons": 4, "pricingType": "RLO", "isChildRate": null },
{ "rateId": "47885754", "rateName": "No reembolsable", "policy": "Non Refundable", "policyId": "195090903", "maxPersons": 4, "pricingType": "Standard", "isChildRate": null }
]
}
]
}maxPersons is the party size that rate plan prices, and it is the occupancy a rate amount has to be written at — see the occupancy note below. maxAdultsis Booking.com's capacity for the room itself, which is what a write falls back to when a rate plan states no maxPersons.
source tells you how fresh the ids are
source: "booking" means the rooms were read from Booking.com just now. If Booking.com returns nothing usable for the property, the rooms and rate plans recorded at your last import are returned instead, source is "mirror", and mirrorReasonsays what went wrong — the ids are still Booking.com's own and can be written against, but they may be stale and maxPersons, policy, policyId, pricingType and isChildRate come back null because only the live feed states them. An empty rooms array means the property genuinely has no rooms — a read that failed is an error, never an empty list.PUT /v1/channels/booking/availability
{
"type": "availability",
"property_id": "998877",
"updates": [
{
"roomId": "456",
"rateId": "789",
"dateRange": { "start": "2026-08-01", "end": "2026-08-07" },
"availableRooms": 3,
"closed": false,
"restrictions": {
"minStay": 2,
"maxStay": 14,
"closedToArrival": false,
"closedToDeparture": false
}
}
]
}PUT /v1/channels/booking/listings/4118/pricing
{
"updates": [
{ "roomId": "456", "rateId": "789", "dateRange": { "start": "2026-08-01", "end": "2026-08-07" }, "price": 245, "currency": "USD", "occupancy": 4 }
]
}
# →
{
"hotelId": "998877",
"listingId": "4118",
"requested": 1,
"occupancy": [ { "index": 0, "roomId": "456", "rateId": "789", "value": 4, "source": "rate_plan" } ],
"applied": "verified",
"verification": { "ran": true, "matched": 7, "mismatched": 0, "rows": [ /* one row per night */ ] },
"booking": { /* Booking.com's answer, verbatim */ },
"errors": []
}A rate amount is written at an occupancy
Booking.com stores a nightly amount against the party size the rate planprices, not against the room's capacity. Sent above that number the price is declined in silence and the night keeps its old amount; sent below it the write returns 400. Send occupancyon the update, or leave it out and Repull resolves it from Booking.com's own numbers and echoes the value it used — and where it came from — back in occupancy[]. If it cannot be resolved the write is refused with 422 rather than sent at a guess. The rate plan's number is the maxPersons on the rooms read above.
applied says what is actually known about the nights, because Booking.com acknowledges the request without any per-date status. The nights are read back: verified — every night carries the amount that was sent; mismatch — some do not, and verification.rows names them; rejected — Booking.com named errors; unverified — no read-back ran (you sent verify: false), which is an unknown, not a success.
Field support differs by channel
closed_to_arrival/closed_to_departure per Airbnb calendar operation, and closedToArrival/closedToDeparture in Booking.com restrictions. Booking.com's dedicated stop-sell flag is closed.Channel markup — supported
A channel can sell a listing for more than its own price: the channel's markup is added when a price is sent there. A $200 night with a 35% Airbnb markup goes to Airbnb as $270; the calendar keeps $200. On Airbnb the markup is per listing. On Booking.com it belongs to the property, so every listing priced through that property shares it. Changing a markup re-sends the affected prices to that channel straight away.
GET /v1/listings/30198/markups
{ "id": "30198",
"airbnb": [{ "airbnbId": "1781522409437931174", "markupPercent": 35 }],
"booking": [{ "hotelId": "17325248", "markupPercent": 18, "listingIds": ["30198"] }] }
PUT /v1/listings/30198/markups
{ "channel": "booking", "markupPercent": 20 }
# → { "channel": "booking", "markupPercent": 20, "changed": true,
# "hotelId": "17325248", "affectedListingIds": ["30198"], "pricesResent": true }markupPercent is a percentage (15 for 15%); null removes it. A listing on more than one Booking.com property names which with hotelId— otherwise the request is refused with 409 ambiguous_booking_mapping listing them, rather than repricing a property it guessed.
Reservations (read)
Reservations from both channels read through the same normalized shape, and new ones are imported for you as they arrive — there is nothing to acknowledge. The Booking.com-shaped read is GET /v1/channels/booking/reservations, scoped to your workspace: without a hotel_id it covers only your own Booking.com properties, and a property id not connected to your workspace returns 404 not_found.
GET /v1/reservations
{
"data": [
{
"id": 214303,
"listingId": 4118,
"checkIn": "2026-08-01",
"checkOut": "2026-08-05",
"status": "confirmed",
"platform": "airbnb",
"confirmationCode": "HMABC123",
"totalPrice": "540.00",
"currency": "USD",
"primaryGuest": { "firstName": "Jane", "lastName": "Doe", "language": "en" },
"createdAt": "2026-01-01T00:00:00.000Z",
"bookedAt": "2026-01-01T00:00:05.000Z"
}
],
"pagination": { "next_cursor": null, "has_more": false }
}No updatedAt — reconcile carefully
createdAt and bookedAt, but no updatedAt or revision field. You cannot detect a change by polling a timestamp — reconcile against the reservation.updated webhook or re-fetch and diff against your last-known state.Accept, decline or cancel a reservation (Airbnb)
Act on a booking request or a confirmed stay. Every one of these calls Airbnb as the account that owns the booking. decline needs a reason from Airbnb's list and a message to the guest (500 characters at most; Airbnb sends it). cancel needs a host-cancellation reason, and host cancellations carry Airbnb's penalties. The body is validated before anything reaches Airbnb, and an unknown field is refused rather than dropped.
POST /v1/channels/airbnb/reservations/HMABC123
{ "action": "accept" }
{ "action": "decline", "reason": "dates_not_available", "message": "Sorry, those dates are no longer available." }
{ "action": "cancel", "reason": "calendar_conflict" }
# decline: dates_not_available | not_comfortable | listing_not_ready | different_dates_needed | spam | other
# cancel: calendar_conflict | maintenance_issue | unable_to_host | other
# The same by Repull reservation id
POST /v1/reservations/214303/accept
POST /v1/reservations/214303/declineThere is no pre-approve action here: a pre-approval answers an inquiry, which has no confirmation code yet — see Inquiry: special offers & pre-approvals. Booking.com reservations arrive confirmed, so there is nothing to accept or decline.
Reservation alterations (Airbnb)
List, read and create alteration requests (change dates, guest count, or price) on an Airbnb reservation, and respond to one the guest proposed: accept, decline, or cancel a request you made yourself. Every write calls Airbnb and needs a connected Airbnb host, else 404 no_connection; an alteration id that belongs to another workspace returns 404 not_found. Booking.com has no equivalent alteration route.
GET /v1/channels/airbnb/alterations # list
GET /v1/channels/airbnb/alterations/{id} # single
POST /v1/channels/airbnb/alterations
{ "reservationCode": "HMABC123", "startDate": "2026-08-02", "endDate": "2026-08-06", "guests": 3 }
# Respond to a pending alteration (no body required)
POST /v1/channels/airbnb/alterations/{id}/accept
POST /v1/channels/airbnb/alterations/{id}/decline
POST /v1/channels/airbnb/alterations/{id}/cancelMessaging
List threads, read one, send in it as the host, and act on a single message. Reads are served from your synced copy; sending and the message PATCH call Airbnb. The action discriminator selects edit / unsend / read / react. Booking.com conversations, including sending files, go through the channel-agnostic /v1/conversations— the same calls work for both channels. Editing, unsending and reacting are Airbnb features; Booking.com has no equivalent.
GET /v1/channels/airbnb/messaging # list threads
GET /v1/channels/airbnb/messaging/{threadId} # read a thread
GET /v1/channels/airbnb/messaging/{threadId}/messages # ?cursor= | ?all=true
POST /v1/channels/airbnb/messaging/{threadId}/messages
{ "message": "Your door code is ready." } # or "mediaUrl" for a photo or video
PATCH /v1/channels/airbnb/messaging/{threadId}/messages/{messageId}
{ "action": "edit", "message": "Updated reply text" } # or unsend | read | reactAirbnb rejects off-platform contact details and links
airbnb_error rather than being delivered. Media is a separate rule — one file per message, and no text on the same message.Inquiry: special offers & pre-approvals (Airbnb)
An inquiry is a guest's question before they book. Answer it with a special offer (your own terms) or a pre-approval, read an offer back, and withdraw one. Booking.com has no inquiries, so this is Airbnb only. All are write-side or live Airbnb calls, addressed by Airbnb ids. The type discriminator picks offer (custom terms) or preapproval. With Repull ids, use the conversation endpoints instead, and accept or decline booking requests by reservation id — see Inquiries & Booking Requests.
POST /v1/channels/airbnb/offers
{ "type": "offer", "thread_id": "2675957479", "listing_id": "955656266214757921",
"start_date": "2026-10-01", "nights": 4, "total_price": 880,
"guest_details": { "number_of_guests": 2 } }
# preapproval: { "type": "preapproval", "thread_id": "2675957479" }
GET /v1/channels/airbnb/offers?offerId=1459920384
DELETE /v1/channels/airbnb/offers?offerId=1459920384
# The same by Repull ids
POST /v1/conversations/{id}/pre-approval
POST /v1/conversations/{id}/special-offers
POST /v1/reservations/{id}/accept | POST /v1/reservations/{id}/declineReviews
Guest reviews— what guests write about a stay — read on both channels, with one public response per review. Host reviews— the host reviewing the guest — are an Airbnb feature: submitting publishes it, and it cannot be edited afterwards. Hosts do not review guests on Booking.com. The Reviews API does all of it with one review id, whatever the channel.
# Reviews API — every channel, one review id
GET /v1/reviews # ?platform=airbnb|booking, ?status=unanswered, ?reviewerRole=host
GET /v1/reviews/{id}
POST /v1/reviews/{id}/reply # public reply, Airbnb + Booking.com, one per review
POST /v1/reviews/{id}/guest-review # review a guest (Airbnb; publishes, final)
# Channel routes — same actions by channel ids
GET /v1/channels/airbnb/reviews # ?account_id= to scope to one connected account
PUT /v1/channels/airbnb/reviews/{id} # = /v1/reviews/{id}/guest-review
POST /v1/channels/airbnb/reviews/{id}/respond # deprecated: use /v1/reviews/{id}/reply
GET /v1/channels/booking/reviews?property_id=998877
POST /v1/channels/booking/reviews # reply by Booking.com property + review idA second response to the same review is refused, not silently ignored: Airbnb answers 409, Booking.com rejects it upstream. Reviews of inactive listings are left out of the reads and reappear once the listing is active again.
Financials
Airbnb host transactions (payouts, adjustments, resolutions) and Booking.com extra-charge sets (cleaning fee, resort fee, city tax…).
Airbnb transactions — supported
Every payout Airbnb sent the host, followed by the lines it paid, each with a stable id and a signed amount; a payout's lines add up to what it paid out. Reads come from Repull's stored copy and never call Airbnb; POST on the same path refreshes it. Filter with ?payout_id=, ?confirmation_code=, ?status= or a date range. See Airbnb Transactions.
GET /v1/channels/airbnb/transactions?payout_id=M-HQLLNSWKUWK7R
{
"data": [
{ "transactionId": "M-HQLLNSWKUWK7R", "type": "Payout", "isPayout": true, "amount": 2552.38 },
{ "transactionId": "M-HQLLNSWKUWK7R:adjustment:HMQYRTJ5D8:1", "type": "Adjustment", "amount": -681.53,
"payout": { "payoutId": "M-HQLLNSWKUWK7R", "lineIndex": 1 } },
{ "transactionId": "M-HQLLNSWKUWK7R:reservation:HMRQ8FC4YN:1", "type": "Reservation", "amount": 40.60,
"payout": { "payoutId": "M-HQLLNSWKUWK7R", "lineIndex": 2 } }
]
}Booking.com charges — supported
Read and replace the extra-charge set for a property. PUT is a full replacement — include every charge you want to keep. property_id is required.
GET /v1/channels/booking/charges?property_id=998877
PUT /v1/channels/booking/charges
{ "property_id": "998877", "charges": [ { "type": "cleaning_fee", "amount": 60, "currency": "USD" } ] }Booking.com property & setup
Read the Booking.com connection record(s) for a Repull listing, and drive property onboarding through the setup action-router (legal entity, readiness, contacts, policies, open for sale). Every action that takes a property_id needs a property connected to your workspace; any other id returns 404 not_found. check-legal-status always returns 404 — a legal entity cannot be tied to one workspace, so check its status in the Booking.com Extranet.
GET /v1/channels/booking/properties/{id} # connection record(s) for a Repull listing
POST /v1/channels/booking/setup
{ "action": "create-legal-entity", /* … */ } # returns 201 with the entity's status
POST /v1/channels/booking/setup
{ "action": "check-readiness", "property_id": "998877" }Webhooks
The canonical, cross-channel subscription is /v1/webhooks: both channels emit account.* and reservation.* events. Subscribe with the events array (not event_types). Deliveries are signed Stripe-style and retried up to 5 times over 7 days.
POST /v1/webhooks
{
"url": "https://your-app.com/webhooks",
"events": ["reservation.created", "reservation.updated", "account.disconnected"]
}Per-channel webhook config
Booking.com events for your properties arrive through /v1/webhooks above. GET/POST/DELETE /v1/channels/booking/webhooks is deprecated and always returns 403 forbidden: Booking.com notification subscriptions are shared by every workspace, so they cannot be read or changed through the API. Plumguide has its own config, managed directly:
# Plumguide webhook config (PUT is a full replace)
GET /v1/channels/plumguide/webhooks
PUT /v1/channels/plumguide/webhooks { /* full webhook config */ }
DELETE /v1/channels/plumguide/webhooksSee Event Types, Verify Signatures, and Retries & Replay.
Plumguide
Read listings and bookings, and push availability + pricing. Filter bookings to one listing with listing_id, or fetch a single booking with booking_code. Webhook config is covered under Webhooks.
GET /v1/channels/plumguide/listings GET /v1/channels/plumguide/bookings # ?listing_id=… | ?booking_code=… PUT /v1/channels/plumguide/availability PUT /v1/channels/plumguide/pricing
Usage & quota
Account-level, not channel-specific. A summary aggregates usage over a range (tier + plan limits, quota used/remaining, next reset, per-operation breakdown), a lightweight tier snapshot powers status badges and quota meters, and the raw request log is cursor-paginated. null limits mean unlimited on that dimension.
GET /v1/usage/summary # ?range=… — aggregated usage + per-operation breakdown GET /v1/usage/tier # current tier, limits, used, remaining, next reset GET /v1/usage/logs # cursor-paginated raw request log, newest first