Your app needs to know what's on a property's calendar and keep up as it changes. The short version:
- Use webhooks. Load the calendar once, then let OwnerRez tell you what changed.
- Decide with
typeandstatus. Never with the title. - Wait for pending entries to settle. Don't schedule a cleaning or message a guest for something that isn't confirmed yet.
- Handle all three actions. Create, update, and delete, including an update that turns a block into a booking.
- Catch up after a gap. If you miss events, ask about everything that has changed since you were last in sync.
- Getting changes
- What's on the calendar
- Status: active, pending, canceled
- What to act on, by use case
- Related
Getting changes
Load once, then listen
When a host connects your app:
- Pull their calendar with
GET /v2/bookings, usingproperty_idsandfrom/tofor the properties and dates you care about. The list includes blocks, holds and canceled entries unless you filter bystatus. - Subscribe to booking webhooks.
- From then on, apply each webhook to your copy.
Send only the parameters the API reference lists for an endpoint. An unrecognized parameter is ignored today. Send property_id instead of property_ids and the first load fails with "Either 'property_ids' or 'since_utc' is required", while a since_utc request returns every property. From January 5, 2027, an unrecognized parameter returns 400. To page, request next_page_url exactly as returned until there isn't one, and start again from the first page if you change a filter. Don't page with offset: it's deprecated on every list, and from January 5, 2027, offset above 0 returns 400.
Don't poll on a timer. Webhooks arrive within seconds; polling every few minutes still lags.
Subscribe one way, not two
You can subscribe in either of two places:
- App settings (the Webhooks section of your OAuth app on the Developer/API settings page): one URL for every account that connects to your app.
- Per connection (
POST /v2/webhooksubscriptions): a URL per connected account, per action, optionally narrowed to a category.
Pick one. If both cover the same entity, you get every event twice.
Which webhooks you need
| You're building | Subscribe to |
| Anything that tracks stays (cleaning, tasks, guest apps, owner portals) | booking: create, update, and delete. Booking webhooks cover every calendar entry: guest bookings, owner stays, blocks and holds. |
| Dynamic pricing, booking websites | property with the availability, rates and rules categories. availability fires when a booking, block or hold changes a property's open dates, or a stay rule changes; rates when rates or seasons change; rules when check-in times, policies or guest limits change. Then read GET /v2/calendar for that property. The calendar is recalculated after the change, so it can lag the webhook by a short time. |
| Guest details | guest, if you show or sync guest contact info |
| Quote flows | quote, if you create quotes and follow them to a booking |
What a webhook carries
Every webhook is a POST with a JSON body like this (the booking is trimmed here):
{
"id": "3f6e2a8c-1b7d-4c55-9e0a-6d2f8b1c4a70",
"user_id": 56789,
"action": "entity_update",
"entity_type": "booking",
"entity_id": 12345,
"categories": ["dates"],
"entity": {
"id": 12345,
"type": "booking",
"status": "active",
"property_id": 678,
"arrival": "2026-12-10",
"departure": "2026-12-15",
"check_in": "16:00",
"check_out": "10:00",
"updated_utc": "2026-10-09T18:22:41Z"
}
}
ididentifies this delivery. A retry of the same delivery has the sameid. It is not the booking's id; that'sentity_id.entityis the whole booking, the same objectGET /v2/bookings/{id}returns, as it was when the change happened. A retry resends that same copy, so a retried webhook can be hours old.arrivalanddepartureare dates in the property's time zone.check_inandcheck_outare times in the property's time zone, filled from the property's defaults when the booking has none.
A delete carries no entity:
{
"id": "9b0c4d12-7e3a-4f1b-8a26-5c9d0e7f3b14",
"user_id": 56789,
"action": "entity_delete",
"entity_type": "booking",
"entity_id": 12346
}
Look entity_id up in your own copy. If you don't have it, ignore the delete. That's normal, for example, when a block was created and deleted before you heard about it.
Delivery rules
- Answer first, work later. Return a 2xx within 4 seconds. Save the payload (a queue or a table), answer, then process it in the background. A handler that calls our API or another service before answering will miss the 4 seconds when a host's bulk edit sends hundreds of webhooks at once, and those failures can pause your webhooks (below).
- Store our ids. Keep OwnerRez's ids on every record you build from our data: the booking
id, itsproperty_id, and theuser_idof the account that connected your app. Every webhook names the account (user_id) and the record (entity_type,entity_id), so with those stored you can match any webhook, including a delete, without calling us. - Expect retries. A failed delivery is retried 10 more times over the course of about 4 hours. Skip any delivery whose
idyou've already processed. - Expect out-of-order delivery. A retry can arrive after a newer event for the same booking. Keep the copy with the later
updated_utc, or fetch the booking by id when the order matters to you. A deleted booking doesn't come back, so after a delete, ignore later webhooks for thatentity_id. - Expect your own changes back. A booking your app creates or updates comes back to you as a webhook. Apply it like any other; it can include changes the host made at the same time.
- Expect merged changes. Several changes made close together can be delivered as a single update. A booking created and deleted before we send anything arrives only as the delete.
- Ignore what you don't recognize. We add fields, categories, and
typevalues over time. Ignore fields you don't use, and treat atypeyou don't know as closed dates with no stay: don't schedule work or message anyone for it. - Keep your error rate down. If you get 100 or more deliveries in about an hour and a half or more of them fail, OwnerRez pauses webhooks for your whole app, per-connection subscriptions included. It emails the account that owns your app, with a copy to the app's Additional Contact Email. They stay paused until you click Re-enable Webhooks on your app's page. Nothing from the paused period is sent later, including retries that were waiting. If webhooks stop arriving, check your app's page. See Failure Policy.
Catching up after a gap
If your endpoint was down or your webhooks were paused:
-
If they were paused, re-enable them first, so nothing new slips past while you catch up.
-
Pick your starting point:
- Endpoint down: when the last webhook you processed arrived, minus 5 minutes.
- Webhooks paused: 5 hours before the pause began, because retries that were waiting when it began were dropped too.
-
Ask for everything changed since then:
GET /v2/bookings?since_utc=<starting point>Don't add
status, date or property filters. Filters apply to the booking as it is now, so a booking that was canceled or moved out of your filter would be missing instead of showing you the change. Filter on your side. -
Follow
next_page_urlexactly as returned until there isn't one. Apply each booking as you would an update webhook.
since_utc returns bookings created, updated or canceled since then, with two gaps. It does not return deletes; a deleted booking just isn't in the list. And it skips a booking when only its door codes, tags, custom fields or agreements changed. Both reach you as webhooks, and during a gap shorter than about 4 hours, as retries. After a longer gap or a pause, also re-list every property and date range you track (property_ids and from/to, no since_utc, no status, plus whichever of include_door_codes, include_tags, include_fields and include_agreements you use). Drop the ids you hold that aren't returned, and take door codes, tags, fields and agreements from the fresh list.
What's on the calendar
Every entry on the calendar comes back from /v2/bookings. The type field tells you what it is.
type |
What it is | Where it comes from |
booking |
A guest's stay | Channels, the booking website, the host, your API calls, iCal imports set to bookings |
owner |
The owner staying, or owner guests | The host, the owner portal, the API |
block |
Dates closed with no stay attached: maintenance, owner-portal blocks, iCal imports set to blocks, and channel holds (see Pending) | The host, the owner portal, imports, channels |
quote_hold |
Dates held for an open quote, including Airbnb requests to book | Quotes with dates held; Airbnb request to book |
linked_availability |
Dates closed because a linked property is booked | OwnerRez, automatically |
is_block is deprecated; it's true for the last three. Use type.
The title is for people. Don't use it to decide anything. A block titled "Hold for HM5ABC1234" is a channel hold, not an owner stay, and a host can title a block anything they like.
Status: active, pending, canceled
status |
Meaning | Dates closed? |
active |
Confirmed | Yes |
pending |
OwnerRez is holding the dates, but the stay isn't confirmed | Yes |
canceled |
No longer happening | No |
What makes something pending
- A channel reservation being processed. Airbnb, Vrbo, Booking.com and Marriott send a reservation in steps. OwnerRez closes the dates with a hold, a pending
block, while it waits. About 6 in 10 channel bookings start this way. Airbnb holds are titled "Hold for" plus the confirmation code. Nearly all holds become the booking within 10 minutes, typically within a minute or two. - A request to book. The guest asked, and the host hasn't accepted yet. Airbnb requests show as
quote_hold. - A direct booking waiting on payment or approval.
- A guest changing dates on Airbnb. OwnerRez holds the new dates with a temporary block until the change is processed.
pending_until_utc is a deadline, and what happens when it passes depends on the source. A pending guest booking is canceled within about an hour if the host's rules say to cancel, or stays pending if the host chose to be notified instead. Channel holds and quote holds ignore the deadline and resolve when the channel or quote does. Every status change arrives as an update; don't work out expiry from pending_until_utc yourself.
How pending resolves
| What happens | What you receive |
|---|---|
| The reservation is confirmed | An update to the same id: status becomes active, type changes from block or quote_hold to booking, and a guest appears. It is not a create. |
| The request is declined or expires | An update: status becomes canceled |
| A pending guest booking passes its deadline unpaid | An update: status becomes canceled, unless the host chose to be notified instead |
| An Airbnb date change is processed | An update to the booking with its new dates, and a delete for the temporary block, usually within 10 minutes |
| Airbnb sends the same reservation twice | The first hold is deleted and the reservation continues on a new id. This is rare. |
Any booking's date change or move to another property arrives as an update to the same id.
Canceled can come back
A canceled entry can return to active or pending. A quote hold comes back if the quote is reopened. An Airbnb hold released while Airbnb was slow to confirm comes back when the confirmation arrives. Treat status as current state, not history.
Deletes
Hosts can delete blocks, and OwnerRez deletes temporary blocks when they're no longer needed: Airbnb date-change blocks, linked_availability blocks when the linked booking moves or ends, and a quote hold when its quote is deleted or its hold is turned off. You get a delete with no cancel before it.
What to act on, by use case
| You're building | Act on | Wait or ignore |
| Availability (calendars, channel tools, booking websites, search) | Every entry that isn't canceled, pending included. All of them close dates. | canceled |
| Dynamic pricing | Property webhooks, then GET /v2/calendar |
Individual bookings |
| Cleaning and tasks | booking and owner with status active. block only if you offer hosts a setting for it (default off), and only when active. |
pending until it settles; quote_hold; linked_availability; canceled |
| Guest apps, guidebooks, WiFi, door codes | booking with status active; owner too if owners use your app |
pending until it settles; blocks; quote_hold; linked_availability; canceled |
| Guest messaging and AI assistants | booking with status active. A quote_hold is a request to book waiting for the host, if you help answer those. |
Pending block entries, including "Hold for" channel holds; linked_availability; canceled |
| Guest screening and damage protection | booking with status active. A date change arrives as an update; a cancellation as an update with status canceled. |
pending until it settles, unless you screen requests to book (quote_hold); owner; blocks |
| Occupancy and noise monitoring | booking and owner with status active: someone is staying |
Blocks; quote_hold; linked_availability; canceled |
| Owner portals and owner reporting | booking and owner with status active; block too if your portal shows closed dates |
pending until it settles; quote_hold; linked_availability; canceled |
| Analytics | Every entry, canceled included. Use type to separate stays from closed dates. |
Nothing |
For cleaning apps in particular: schedule when an entry becomes active, not when it first appears. If you want to show a tentative cleaning for a pending stay, show it as tentative and don't notify anyone. Key each task on the booking id: when a hold becomes a booking, the update has the same id, so update the task instead of creating a second one. When the booking is canceled or deleted, remove the task; if a canceled booking returns to active, schedule it again.
Related
- Webhooks: payload format, categories, security, failure policy
- Creating Bookings, Blocks and Quotes
- Pagination & Conditional Requests (ETags)
- Owner Bookings