Docs/Channels/Airbnb

Airbnb Alterations

Propose a change to a confirmed Airbnb reservation — new dates, a new guest count, a new total, or a move to a different listing — and accept, decline or withdraw the ones already in flight.

POST/v1/channels/airbnb/alterations

Parameters

confirmation_codestringRequired

The Airbnb confirmation code of the reservation to change. GET /v1/channels/airbnb/reservations lists them.

check_instring (YYYY-MM-DD)

New check-in date, YYYY-MM-DD.

check_outstring (YYYY-MM-DD)

New check-out date. Must be after check_in when you send both.

number_of_guestsinteger

New guest count for the stay. A whole number, 1 or more.

total_pricenumber

New total for the whole stay, in the listing currency.

listing_idinteger

Move the reservation to this listing. Send the Repull listing id from GET /v1/properties — the same id every other endpoint takes. See Moving a reservation to another listing, below.

account_idstring

Scope the response to ONE connected Airbnb account. The value is the Airbnb host id — the same accounts[].externalAccountId that GET /v1/connect/airbnb returns and DELETE /v1/connect/airbnb?accountId= accepts. Omit it to read every connected account (the default). An id that is not connected to this workspace returns 404 not_found with field: "account_id" and your own ids in valid_values. This is not the X-Account-Id header, which carries a connection id and cannot tell two Airbnb hosts apart.

What to send

confirmation_code names the reservation, and at least one of check_in, check_out, number_of_guests, total_price or listing_id says what to change. Send only the fields you are changing; everything you leave out stays as it is on the reservation. Creating an alteration proposes the change — it stays pending until the guest accepts it.

  • A body with nothing but confirmation_code is refused with 422 invalid_params: an alteration has to change something, and one that changes nothing is not a request Airbnb can act on.
  • Unknown fields are refused rather than ignored. checkIn returns 422 invalid_params naming the field, so a misspelling can never look like a successful change.
  • Dates are checked before anything is sent: a date that is not YYYY-MM-DD, or a check_out that is not after check_in, is a 422 naming the field.
  • Read the result back with GET /v1/channels/airbnb/alterations, or wait for the webhook.
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "confirmation_code": "HMEXAMPLE1",
    "check_in": "2026-08-02",
    "check_out": "2026-08-06",
    "number_of_guests": 3
  }'

# Nothing to change -> 422, before anything reaches Airbnb
# {
#   "error": {
#     "code": "invalid_params",
#     "message": "An alteration has to change something. Send at least one of
#                 check_in, check_out, number_of_guests, total_price or
#                 listing_id alongside confirmation_code."
#   }
# }

Moving a reservation to another listing

Send listing_id to ask Airbnb to move the reservation to a different listing — usually because the unit it is on has become unavailable and a comparable one has not. listing_id is the Repull listing id from GET /v1/properties, not the Airbnb one: Repull checks the listing belongs to your workspace, is active and is connected to Airbnb, then translates it before the request goes out. If all you hold is the Airbnb listing id, send it as airbnb_listing_id instead.

  • Airbnb decides. Repull sends the move and reports the answer — whether Airbnb honours a listing change depends on the reservation, the two listings and Airbnb own rules. A refusal comes back as 422 airbnb_rejected with Airbnb reason in message.
  • A listing id that is not yours, or not connected to Airbnb, is 404 not_found naming listing_id. Nothing is sent upstream.
  • An inactive listing is 403 listing_inactive — for the listing you are moving to as well as the one the reservation is on.
  • A move can be combined with a date or guest change in the same request.
  • On success the response echoes the destination as newListingId (Repull) and newAirbnbListingId (Airbnb), and the same two fields appear on every later read of the alteration.
# Move the reservation to listing 4118, keeping its dates
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirmation_code": "HMEXAMPLE1", "listing_id": 4118}'

# Move it AND shorten the stay
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirmation_code": "HMEXAMPLE1", "listing_id": 4118, "check_out": "2026-08-05"}'

Accepting, declining and withdrawing

An alteration the guest proposed is answered with accept or decline. One you proposed yourself is withdrawn with cancel — use it when you sent the wrong dates or the wrong listing, so the request stops being pending instead of waiting to be accepted. All three take no body and name the alteration by its alterationId.

  • POST .../{id}/accept — approve the proposed change. The reservation is updated on Airbnb.
  • POST .../{id}/decline — reject it. The reservation stays as it is.
  • POST .../{id}/cancel — withdraw an alteration you proposed. Airbnb decides whether it can still be withdrawn; one that has already been answered is refused, with Airbnb reason in message.
  • An id that does not belong to a reservation in your workspace returns 404 not_found.
# Accept the change the guest asked for
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/accept \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Decline it
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/decline \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Withdraw one you proposed yourself
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/cancel \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

Reading alterations back

GET /v1/channels/airbnb/alterations returns the pending ones by default; pass ?type=all for the full history, or ?reservation_code= for a single reservation. Each row pairs the reservation as it stands today (original*) with what is being proposed (new*), so you can render the diff without a second call.

  • newCheckIn, newCheckOut, newGuestCount, newTotalPrice — the proposed dates, guest count and total.
  • newListingId and newAirbnbListingId — the listing the reservation would move to. Both are null when the alteration does not move it, which is the usual case.
  • alterationId is the id you pass to accept, decline and cancel. id is Repull own row id and is not interchangeable with it.
  • Alterations on inactive listings are left out of the list.
# Pending alterations across every connected account
curl 'https://api.repull.dev/v1/channels/airbnb/alterations' \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Everything ever proposed on one reservation
curl 'https://api.repull.dev/v1/channels/airbnb/alterations?type=all&reservation_code=HMEXAMPLE1' \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

What can come back

  • 422 invalid_params — the body failed validation before anything was sent to Airbnb. field names the field, value_received echoes it and fix says what to send.
  • 422 airbnb_rejected — Airbnb refused the change; message carries its own reason. Correct the request, because sending the same body again is refused again.
  • 403 connection_reauth_required — Airbnb no longer accepts the connection for this listing: the authorization expired or was revoked, or the listing was not selected when the host connected. Retrying will not help. Reconnect Airbnb at https://repull.dev/dashboard/connections, make sure the listing is selected, then retry.
  • 403 listing_inactive — the listing is inactive. Activate it with PATCH /v1/listings/{id} and {"active": true}, then retry.
  • 404 not_found — no reservation, alteration or listing with that id in your workspace. 404 no_connection — the workspace has no Airbnb connection at all.
  • 429 airbnb_rate_limited — Airbnb is rate-limiting writes for this host. Wait retry_after seconds when present, otherwise back off exponentially.
  • 502 airbnb_error — Airbnb had an outage or timed out. Nothing about the request needs to change: retry with backoff.

Several Airbnb accounts

A workspace can connect more than one Airbnb account. By default this route returns every connected account's rows; pass ?account_id=<airbnb host id> to scope to one. Every row carries accountId + accountName either way (the older hostId / hostName remain as aliases), so you can group without a second call. Airbnb host ids exceed 2^53 — keep them as strings and never parse them as numbers: Number("1000000000000000002") is 1772489413932732200, a different account, and returns 404. A two-account walkthrough lives on the Account scope page (/docs/scoping).

# Every connected account — the default, unchanged
curl 'https://api.repull.dev/v1/channels/airbnb/alterations' \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Just one account
curl 'https://api.repull.dev/v1/channels/airbnb/alterations?account_id=1000000000000000002' \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# An id this workspace has not connected — 404, with your own ids to copy
# {
#   "error": {
#     "code": "not_found",
#     "message": "No connected Airbnb account `1772489413932732200` in this workspace.",
#     "field": "account_id",
#     "value_received": "1772489413932732200",
#     "valid_values": ["1000000000000000002", "80000001"]
#   }
# }

Data freshness

Airbnb reads are served from Repull's local mirror and never call Airbnb upstream, so every response carries a dataFreshness envelope telling you whether to trust it. lastSyncedAt is the last import that actually landed data — a run that failed or was rate-limited never moves it. accounts[] gives the verdict per connected account; it is omitted when the workspace has no Airbnb account to attribute.

  • Top-level stale: true means every connected account is stale — nothing in the response is current. For the single-account workspace this is exactly the old behaviour.
  • Top-level stale: false with reason: "partial_account_staleness" means some accounts are fine and some are not. The response is usable; read accounts[] to see which rows to distrust. The reason is emitted deliberately even though stale is false, so a consumer reading only the aggregate is never told everything is fine while one account is down.
  • Top-level stale: false with no reason means every account is current.
  • With ?account_id=, accounts[] holds exactly that account and the top-level fields mirror it.
  • reason is one of host_disconnected_since_<iso>, host_disconnected, host_not_activated, sync_lag_>_24h, never_synced, or partial_account_staleness. fixUrl is the dashboard screen that resolves it.
{
  "dataFreshness": {
    "lastSyncedAt": "2026-09-18T04:12:09.000Z",
    "stale": false,
    "reason": "partial_account_staleness",
    "fixUrl": "https://repull.dev/dashboard/connections",
    "accounts": [
      {
        "accountId": "1000000000000000002",
        "accountName": "Seaside Stays",
        "lastSyncedAt": "2026-09-18T04:12:09.000Z",
        "stale": false
      },
      {
        "accountId": "80000001",
        "accountName": "Casey",
        "lastSyncedAt": null,
        "stale": true,
        "reason": "host_disconnected",
        "fixUrl": "https://repull.dev/dashboard/connections"
      }
    ]
  }
}

Example

# List pending alterations
curl https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Propose new dates and a new guest count
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -d '{"confirmation_code": "HMEXAMPLE1", "check_in": "2026-08-02", "check_out": "2026-08-06", "number_of_guests": 3}'

# Move the reservation to another listing (Repull listing id)
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations \
  -H "Authorization: Bearer sk_live_YOUR_KEY" \
  -d '{"confirmation_code": "HMEXAMPLE1", "listing_id": 4118}'

# Accept an alteration
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/accept \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Decline an alteration
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/decline \
  -H "Authorization: Bearer sk_live_YOUR_KEY"

# Withdraw one you proposed
curl -X POST https://api.repull.dev/v1/channels/airbnb/alterations/ALT_123/cancel \
  -H "Authorization: Bearer sk_live_YOUR_KEY"
AI