Airbnb Reservations
List, get, accept, decline, and cancel Airbnb reservations. Includes price breakdown and full guest details. Cursor-paginated like every other list endpoint — walk pages with ?cursor=<pagination.nextCursor> until pagination.hasMore is false.
/v1/channels/airbnb/reservationsParameters
account_idstringScope 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.
cursorstringOpaque cursor from the previous response's pagination.nextCursor. Omit for the first page.
limitintegerMax items per page. Default 50, max 100.
listing_idstringFilter to one Airbnb listing id.
statusstringFilter by status (pending, accepted, denied, cancelled, completed, failed_verification, request_voided). Omit to receive all statuses.
start_datestringISO 8601 (YYYY-MM-DD) lower bound on the date range filter.
end_datestringISO 8601 (YYYY-MM-DD) upper bound on the date range filter.
Accept, decline or cancel
POST /v1/channels/airbnb/reservations/{code} acts on a reservation by its Airbnb confirmation code, as the Airbnb account that owns it. If you have the Repull reservation id, POST /v1/reservations/{id}/accept and /decline do the same for booking requests — see Inquiries & Booking Requests (/docs/channels/airbnb/inquiries-and-requests).
{"action": "accept"}— accept a pending booking request. Takes no other field.{"action": "decline", "reason": "…", "message": "…"}— decline a pending booking request.reasonis one ofdates_not_available,not_comfortable,listing_not_ready,different_dates_needed,spam,other;message(1–500 characters) is sent to the guest.{"action": "cancel", "reason": "…"}— cancel a confirmed booking as the host.reasonis one ofcalendar_conflict,maintenance_issue,unable_to_host,other. Host cancellations carry Airbnb penalties.- There is no
pre-approveaction: a pre-approval answers an inquiry, which has no confirmation code yet. Sending it returns422 invalid_paramspointing atPOST /v1/conversations/{id}/pre-approval. - Errors:
422 invalid_params(names the field),409 request_no_longer_pendingor409 request_expired(do not retry),422 airbnb_rejected(Airbnb's reason inmessage),403 connection_reauth_required,429 airbnb_rate_limited,502 airbnb_error. SendIdempotency-Keyto make a retry safe.
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/reservations' \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# Just one account
curl 'https://api.repull.dev/v1/channels/airbnb/reservations?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: truemeans 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: falsewithreason: "partial_account_staleness"means some accounts are fine and some are not. The response is usable; readaccounts[]to see which rows to distrust. Thereasonis emitted deliberately even thoughstaleisfalse, so a consumer reading only the aggregate is never told everything is fine while one account is down. - Top-level
stale: falsewith noreasonmeans every account is current. - With
?account_id=,accounts[]holds exactly that account and the top-level fields mirror it. reasonis one ofhost_disconnected_since_<iso>,host_disconnected,host_not_activated,sync_lag_>_24h,never_synced, orpartial_account_staleness.fixUrlis 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
# Page 1 (default 50)
curl 'https://api.repull.dev/v1/channels/airbnb/reservations?limit=50' \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# Walk to page 2 with the cursor returned by page 1
curl 'https://api.repull.dev/v1/channels/airbnb/reservations?limit=50&cursor=eyJ1IjoiWDE5...' \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# Filter to one listing's reservations within a date window
curl 'https://api.repull.dev/v1/channels/airbnb/reservations?listing_id=18871326&start_date=2026-06-01&end_date=2026-08-31' \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# Accept a pending booking request
curl -X POST https://api.repull.dev/v1/channels/airbnb/reservations/HMA1234567 \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Idempotency-Key: accept-HMA1234567" \
-H "Content-Type: application/json" \
-d '{"action": "accept"}'
# Decline one
curl -X POST https://api.repull.dev/v1/channels/airbnb/reservations/HMA1234567 \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"action": "decline", "reason": "dates_not_available", "message": "Sorry, those dates are no longer available."}'Response
{
"data": [
{
"confirmationCode": "HMABC12345",
"statusType": "accept",
"accountId": "1000000000000000002",
"accountName": "Seaside Stays",
"listingId": "18871326",
"startDate": "2026-07-06",
"endDate": "2026-07-08",
"nights": 2,
"guestFirstName": "Nowf",
"guestLastName": "Abugauch",
"hostCurrency": "CAD",
"payoutAmount": "750.74"
}
],
"pagination": {
"nextCursor": "eyJ1IjoiWDE5MmFXRmtkV04wT21sa2VEb3oifQ",
"hasMore": true
},
"dataFreshness": {
"lastSyncedAt": "2026-09-18T04:12:09.000Z",
"stale": false,
"accounts": [
{
"accountId": "1000000000000000002",
"accountName": "Seaside Stays",
"lastSyncedAt": "2026-09-18T04:12:09.000Z",
"stale": false
}
]
}
}