> For the complete documentation index, see [llms.txt](https://kb.craftformswp.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kb.craftformswp.com/documentation/ecommerce/catalog/calendar-sync.md).

# Calendar Sync

List a room, an apartment or a boat on several sites and keep one calendar. Calendar sync connects a booking item to Airbnb, Booking.com, Vrbo, Google Calendar or any other site that shares an iCal link. Bookings made on one site block the dates everywhere else, and CraftForms sits in the middle: each site only exchanges calendars with CraftForms, never with the other sites.

![The Calendar sync section of a catalog item with two calendars](https://97821998-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FM2yV1foUgoGHBC66VBDw%2Fuploads%2F6sLVc6DmHh7IJIs22oKn%2Fcalendar-sync--calendar-sync-section.jpg?alt=media)

### What you need

* **CraftForms PRO**, version 2.2 or later.
* A booking item in **CraftForms → Catalog**.
* For each site: its calendar link (an iCal or `.ics` address), and a place on that site to add an outside calendar. Airbnb, Booking.com, Vrbo, Google Calendar and most booking software have both.

### How it works

Each site you connect is a pair of links:

* **In:** the site's calendar link. CraftForms reads it and blocks the dates booked there.
* **Out:** a link CraftForms makes for that site. You paste it into the site, and the site reads it to block the dates booked elsewhere.

The link for a site carries:

* the bookings made with your CraftForms forms,
* the dates you closed on the item (blocked dates and closed periods),
* the bookings of **every other** connected site.

It never carries that site's own bookings. So Airbnb learns about Booking.com bookings through CraftForms, and the other way round, and no site gets its own bookings sent back to it.

### Connect a site

1. Go to **CraftForms → Catalog** and open your booking item.
2. Open the **Calendar sync** section and click **Add a calendar**.
3. Pick the site: **Airbnb**, **Booking.com**, **Vrbo**, **Google Calendar** or **Other**. If you paste a link first, CraftForms guesses the site from it. Optional: give it a **Name**, like "Airbnb, sea view listing".
4. Paste the site's calendar link into **From \[site]: its calendar link**.
5. Click **Save**. CraftForms reads the calendar straight away.
6. Copy the link under **To \[site]: paste this link there** (click **Copy**), and add it to the site as an imported calendar.
7. Repeat for each site.

Where to find the links:

| Site                | Its calendar link (In)                                                     | Where to paste yours (Out)                    |
| ------------------- | -------------------------------------------------------------------------- | --------------------------------------------- |
| **Airbnb**          | Calendar → Availability → Export calendar                                  | Calendar → Availability → Import calendar     |
| **Booking.com**     | Property → Calendar → Sync calendars → Export                              | Property → Calendar → Sync calendars → Import |
| **Vrbo**            | Calendar → Import/Export → Export                                          | Calendar → Import/Export → Import             |
| **Google Calendar** | Calendar settings → Integrate calendar → **Secret address in iCal format** | Other calendars → **+** → From URL            |

Menu names on these sites change from time to time. Look for "export calendar", "import calendar" or "iCal".

### The Calendar sync section

Each connected site shows:

| Part         | What it shows                                                                                                                                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Status       | **Synced 5 minutes ago · 12 bookings** when the last read worked. **Not answering (HTTP 404), first failed 3 hours ago** when the link can't be read. **Waiting for the first sync** or **Save to connect** for a new calendar. |
| **Sync now** | Reads this site's calendar now, without waiting for the next hourly read.                                                                                                                                                       |
| **Remove**   | Disconnects the site (after you save). Its bookings stop blocking your dates, and its link stops working.                                                                                                                       |
| **Copy**     | Copies your link for this site.                                                                                                                                                                                                 |

In the screenshot above, the second calendar points at an address that doesn't exist, to show the error state. When a calendar can't be read, CraftForms keeps the dates from its last good read, so they stay blocked. Check the link on the other site and click **Sync now**.

Under the list, a note says how often the calendars are read.

### How often calendars are read

CraftForms reads each site's calendar **every hour**, and right after you save a new link. The other sites read your link on their own schedule, often every few hours. Until they do, they don't know about a booking made elsewhere.

That gap is part of how iCal works on every platform. It's why a double booking can still happen now and then, and why CraftForms looks for them (see below).

The hourly read uses WordPress's scheduler (WP-Cron), which runs when someone visits your site. On a quiet site it can run late. For exact timing, use a real server cron (see [For developers](#for-developers)).

### Double bookings

Two guests can book the same night on two sites before either site hears of the other. After each read and each new booking, CraftForms compares your bookings and every site's bookings, from today on. When two of them claim the same night, the **Calendar sync** section shows a red message:

> Booked twice for 7 Nov – 8 Nov. Cancel or move one of the two bookings on the site where it was made.

Cancel or move one of the bookings on the site where it was made. The message goes away by itself at the next read, once only one booking is left.

Double bookings are checked for whole days (nights and full-day bookings). Items booked by the hour are protected by the normal time-slot checks.

### After a cancellation

When a CraftForms booking is cancelled or refunded, its dates are free again straight away. The other sites still show them as booked until they read your link again, and in the meantime their calendars may send the same dates back to CraftForms. CraftForms recognises these "echoes" of its own bookings for 48 hours and ignores them, so a cancelled booking doesn't keep your dates blocked.

### What gets imported

* All-day events block whole days. Events with times block only those times, so items booked by the hour keep the rest of the day open.
* Simple repeating events (daily, weekly, monthly, yearly) are expanded.
* Imported dates block the booking calendar on your forms, the [Catalog Search](/documentation/ecommerce/catalog/catalog-search.md), and the check when a form is sent.
* The other sites see your bookings as "Booked" and other dates as "Not available", with no guest names or details.

### Items that used one iCal link

Before 2.2, an item had one **Import — iCal Feed URL** and one **Export** link. Nothing needs to change: the old import link shows as one calendar in **Calendar sync**, and the old export link keeps working. It carries only your CraftForms bookings. To have that site get the other sites' bookings too, paste its new link from **Calendar sync** into it.

### Good to know

* Each link has a long random key, so it can't be guessed. Anyone who has the link can see which dates are busy, but not who booked them.
* A duplicated item gets new links. Paste them into the sites again for the copy.
* The links work only while the item is active.
* Calendar sync doesn't change prices or guest details on the other sites. It only blocks dates.
* iCal is never instant. For live updates between platforms you'd need a channel manager connected to each platform's own system.

### For developers

**Use a real cron.** Turn off the WP-Cron read and call the importer from your own scheduler. `sync_due()` reads only the calendars that are due:

```php
add_filter('craftforms_ical_cron_enabled', '__return_false');

// In your cron job (for example a WP-CLI command run every 5 minutes):
(new \CraftForms\ICal_Importer())->sync_due();
```

**Read more often, and check before each booking:**

```php
add_filter('craftforms_ical_sync_interval', fn() => 15 * MINUTE_IN_SECONDS); // minimum 5 minutes
add_filter('craftforms_ical_live_check_max_age', fn() => 120); // re-read calendars older than 2 minutes before accepting a booking
```

**Be told about double bookings.** `craftforms_ical_conflict` fires once for each new double booking:

```php
add_action('craftforms_ical_conflict', function ($item_id, $conflict) {
    wp_mail(get_option('admin_email'), 'Double booking',
        "Item $item_id: {$conflict['from']} to {$conflict['to']}");
}, 10, 2);
```

`$conflict` has `from` and `to` (`to` is the morning after the last night), and `a` and `b`, the two bookings: each has a `source` (`order:<id>` or `feed:<calendar id>`) and its `span`.

Other hooks: `craftforms_ical_sync_complete` (after an item's calendars were read), `craftforms_booking_released` (a booking's dates are free again), `craftforms_ical_max_feeds` (limit calendars per item) and `craftforms_ical_manual_sync_allowed` (return a `WP_Error` to limit **Sync now**).

**Add a whole-day booking by hand** (a phone or walk-in booking) with the REST API. It goes through the same checks as a booking on the form: closed dates, stay rules, the connected calendars and capacity. It then shows in every site's link.

```
POST /wp-json/craftforms/v1/orders/booking
{ "resource_id": 12, "checkin": "2026-11-07", "checkout": "2026-11-09",
  "name": "Anna Smith", "email": "anna@example.com", "status": "confirmed" }
```

`status` is `pending`, `confirmed` (the default) or `paid`. Optional: `phone`, `total`, `currency`. Items booked by the hour can't be added this way. The request needs an administrator (or a user with CraftForms access) and is refused with `409` when the nights are closed or taken.
