---
title: Availability β three-layer system
description: Yatra's three layers of availability β Manual Availability Dates, Recurring Availability Rules, and the Trip Builder default β explained end-to-end. Setup, priority, and what the customer sees on the public booking page.
prev:
text: Create a trip
link: /trip-creation
next:
text: Departures
link: /departures
---
# Availability β three-layer system
Yatra has **three layers** that decide whether a trip is bookable on a given date and what the customer sees on the booking calendar:
| # | Layer | Priority | Where it's configured |
| - | --- | --- | --- |
| 1 | **Manual Availability Dates** | π₯ Highest | Yatra β Trips β [trip] β Availability β *Add Date* |
| 2 | **Recurring Availability Rules** | π₯ Middle | Yatra β Trips β [trip] β Availability β *Rules* tab |
| 3 | **Trip Builder default** | π₯ Lowest | Trip Builder β Availability & Booking section |
::: tip How the priority is enforced
A specific manual date **always** wins over any recurring rule that covers the same day. A recurring rule **always** wins over the trip default. This is what makes "sold out" badges, seat counts, and one-off price overrides work the way you expect β admin actions trump anything that was generated automatically.
:::
---
## What each layer is for
### Layer 3 β Trip Builder default (the baseline)
**When you'd use it:** simple flexible-booking trips ("Bookable any day from 2026-03-01 to 2026-12-31, capped at 10 travelers per booking") where you don't need per-date control.
**What it sets:**
| Field | Where it lives |
| --- | --- |
| **Available From / To** | Trip Builder β Availability & Booking β Availability Period |
| **Booking Window (days)** | Same section |
| **Minimum / Maximum Travelers** | Trip Builder β Availability & Booking β Capacity & Travelers |
| **Departure Time / Time Slots** | Same section, varies by Single-day vs Multi-day trip type |
The Trip Builder default is what fires when no Availability Date and no Recurring Rule cover the date the customer picks. It's the **"flexible booking"** mode β Yatra generates synthetic availability for every day in the trip's *Available From β Available To* window.
Field reference: [Create a trip β 1.4 Availability & Booking](/trip-creation#_1-4-availability-booking).
---
### Layer 2 β Recurring Availability Rules
**When you'd use it:** trips that run on a regular pattern ("Every Saturday and Sunday from May to September", "Daily, July through August", "First Monday of every month"). Rules generate dates automatically β you set the pattern once and Yatra fills the calendar for the next year.
**Form fields** (from `DepartureRecurringRuleForm.tsx`):
| Field | Type | Notes |
| --- | --- | --- |
| **Recurrence type** | enum: `daily` / `weekly` / `monthly` / `custom_days` | The cadence pattern. |
| **Weekdays** | multi-select (SunβSat) | Shown when *Recurrence type = weekly* or `custom_days`. 0 = Sunday β¦ 6 = Saturday. |
| **Start date** | date | First date the rule produces. |
| **End date** | date | Last date β leave blank for "indefinite". |
| **Max capacity** | number | Seats available **per generated date**. Default: same as trip max travelers. |
| **Base price** | number | Optional override per generated date. Falls back to the trip's pricing. |
| **Active** | toggle | Off = the rule stops generating new dates (existing manually-overridden rows survive). |
Behind the scenes, rules are stored in `wp_yatra_new_trip_availability_rules`. The resolver materialises them on-the-fly β there's no physical row in the *Availability Dates* table until somebody books or you click *Convert to manual date*.
---
### Layer 1 β Manual Availability Dates (the override)
**When you'd use it:** any time you need *per-date* control β block a specific Saturday for a private event, increase the seat count for a holiday surge, mark the trip *sold out* before all seats are booked, or apply a one-off price for a flash sale.
**Form fields** (from `AvailabilityForm.tsx`):
| Field | Type | Notes |
| --- | --- | --- |
| **Departure Date** | date | Required. The actual day the trip runs. |
| **Departure Time** | time (optional) | Required only for day-tour trips with multiple time slots (so you can override one slot but not the others). |
| **Arrival Date / Arrival Time** | date + time | Optional β used for multi-day trips when the end date isn't `start + duration`. |
| **Status** | select | `available` / `sold_out` / `blocked` (cancelled). *Sold out* hides the date from the customer's calendar. |
| **Max capacity** | number | Seat count for this specific date. Defaults to the trip's max travelers. |
| **Booked count** | number (read-only) | How many seats are already booked. Capacity β booked = seats remaining. |
| **Price override** | currency | Optional β bypasses the trip's regular price for this date. |
| **Per-traveler-category prices** | repeater | Optional β bypasses the trip's traveler-based pricing matrix for this date. |
| **Starting / Ending location** | location pickers | Optional β useful when a specific departure leaves from a different point. |
| **Notes** | textarea | Internal-only. |
A Manual Availability Date wins over any rule that covers the same day. This is how you say "for *this* specific Saturday, the price is $799 instead of $499, capacity is 6 instead of 10, and there's only one departure at 9 AM."
---
## The priority order β worked example
Let's take a single trip (*Sunrise Hike*) configured like this:
- **Trip Builder default:** Available 2026-01-01 β 2026-12-31, max travelers 10, default time 06:00.
- **Recurring Rule R1:** Daily, 2026-04-01 β 2026-10-31, max capacity 8, base price $89.
- **Recurring Rule R2:** Weekly Saturdays only, 2026-04-01 β 2026-10-31, max capacity 12, base price $119 (the "premium weekend" rule).
- **Manual Availability Dates:**
- 2026-06-21 (Sat) β status `sold_out`, capacity 12.
- 2026-07-04 (Sat) β capacity 20, price $149 (4th of July flash).
- 2026-12-25 (Fri) β status `blocked` (closed for Christmas).
What the customer sees when picking dates:
| Customer picks | Layer that wins | What the customer sees |
| --- | --- | --- |
| 2026-03-15 (Sun) | **Trip default** | Bookable, $89 (trip price), 10 seats. Falls outside both recurring rules β uses the trip default. |
| 2026-05-10 (Sun) | **Rule R1** (daily) | Bookable, $89, 8 seats. R1 covers this day; R2 only covers Saturdays. |
| 2026-05-09 (Sat) | **Rule R2** (Sat-only) | Bookable, $119, 12 seats. R2 is more specific than R1, so it wins. *(Within rules, the more-specific pattern wins.)* |
| 2026-06-21 (Sat) | **Manual Date** (sold_out) | Date is hidden / disabled on the calendar with a "Sold out" badge. |
| 2026-07-04 (Sat) | **Manual Date** (override) | Bookable, $149, 20 seats. Manual override beats R2 even though R2 has a Saturday rule for the same date. |
| 2026-12-25 (Fri) | **Manual Date** (blocked) | Hidden completely from the calendar (the date doesn't render at all). |
| 2027-01-15 | *Nothing* | Not bookable β falls outside the trip's Available From β Available To window. |
::: warning "Rule beats rule" is rare
Within the recurring rules layer, the resolver picks the *first* matching rule. If two rules overlap on the same date with no specific time, behaviour is undefined β clean up overlapping rules. Use **time slots** if you need multiple departures on the same date.
:::
---
## Step-by-step: setting up each layer
### Step 1 β Set the Trip Builder default
This is the always-present fallback.
1. Open Yatra β Trips β click your trip β **Trip Builder**.
2. Click **Availability & Booking** in the sidebar.
3. Set **Available From** and **Available To** to your booking window (e.g. `2026-04-01` β `2026-10-31`).
4. Set **Minimum / Maximum Travelers**.
5. For single-day trips, pick a **Default Departure Time** OR enable **Multiple Time Slots** for a repeater of `{ time, label }` rows.
6. Save.

At this point the trip is bookable on **every day** in your range. Layers 2 and 3 below let you carve it up.
### Step 2 β (Optional) Add Recurring Rules
1. Open Yatra β Trips β [your trip] β Availability tab.
2. Click the **Rules** sub-tab.
3. Click **+ Add Rule**.
4. Fill in:
- **Recurrence type** β start with *weekly* for "every Saturday and Sunday" trips.
- **Weekdays** β pick the days that match.
- **Start / End date** β bounding window. Leave End blank for "forever".
- **Max capacity** β seats per generated date. Often differs from the trip's general cap.
- **Base price** β optional per-rule override.
- **Active** β leave on.
5. Save. A preview calendar on the right shows you the next 90 days the rule will produce.

Repeat for multiple patterns ("daily MayβSep", "Saturdays only for premium weekends", etc.).
### Step 3 β (Optional) Add Manual Availability Dates
Use this for one-off overrides β block a date, change a single date's capacity / price, mark sold-out manually.
1. Same place: Yatra β Trips β [trip] β Availability, **Dates** sub-tab.
2. Click **+ Add Date**.
3. Fill in:
- **Departure date** (and time, for day tours).
- **Status** β `available`, `sold_out`, or `blocked`.
- **Max capacity** β defaults to the trip max, override here.
- **Price override** β optional.
- **Per-traveler-category prices** β optional, only when the trip uses traveler-based pricing.
- **Notes** β internal only.
4. Save.

The new date immediately overrides any rule that covered it.
::: tip "Convert rule date to manual"
On the Availability calendar, each rule-generated date has a *Convert to manual* action β it materialises the date as a manual row so you can override capacity / price / status individually without disabling the rule.
:::
---
## How it shows up to the customer

When a customer opens the trip page and clicks **Book Now**, Yatra calls `AvailabilityResolutionService::getAllAvailabilityDates()` for the next N months (configurable; default 12) and the result powers the date picker:
- **Available date** β clickable, shows "X seats left" subtitle, base price visible.
- **Sold-out date** β disabled with a "Sold out" badge.
- **Blocked date** β hidden completely (no chip rendered).
- **Date with a price override** β clickable, with the override price shown in place of the regular one.
After the customer picks a date, the **traveler-count step** uses *that date's* capacity number (manual override > rule capacity > trip max). And the **price summary** at the bottom uses *that date's* price (manual override > rule base price > trip price).

If a customer keeps the booking page open and an admin marks the date sold out, the **booking-create** API revalidates capacity at submit time and will fail with `availability_sold_out` if seats have run out β the customer sees a friendly "Sorry β this date just sold out" message and is prompted to pick another date.
---
## When to use which layer β a cheat sheet
| You want to⦠| Use this layer |
| --- | --- |
| Make a brand-new trip bookable for the whole season | Trip Builder default (Step 1) |
| Run a trip every weekend in summer | Recurring Rule (Step 2) |
| Run a trip every day in July + August | Recurring Rule (`daily` recurrence) |
| Sell out a specific Saturday manually before it fills | Manual Date with `status=sold_out` |
| Add 2 extra seats for a holiday surge | Manual Date with higher `max_capacity` |
| Run a flash sale on a single date | Manual Date with `price_override` |
| Block a specific date because your guide is unavailable | Manual Date with `status=blocked` |
| Same trip, different time slots on the same day | Time slots (Trip Builder default) + per-slot Manual Dates if you need per-time overrides |
| Different start location for one specific departure | Manual Date with `starting_location` filled |
---
## Where this is wired in code
If you're extending Yatra programmatically:
| Symbol | Purpose |
| --- | --- |
| `Yatra\Services\AvailabilityResolutionService::resolveAvailabilityForDate($tripId, $date, $time)` | Returns the resolved availability object for a single date. The priority-1/2/3 logic is here. |
| `Yatra\Services\AvailabilityResolutionService::getAllAvailabilityDates($tripId, $from, $to)` | Returns the merged calendar for a date range β what the customer's date picker calls. |
| `Yatra\Repositories\AvailabilityRepository` | Reads / writes the Manual Availability Dates table. |
| `Yatra\Services\RecurringAvailabilityService::generateDatesForTrip($tripId, $from, $to)` | Materialises Recurring Rules on-the-fly. |
| Database table `wp_yatra_trip_availability_dates` | Stores Manual Availability Dates. |
| Database table `wp_yatra_new_trip_availability_rules` | Stores Recurring Rules. |
Hooks the resolution flow fires:
- `yatra_resolved_availability` (filter) β mutate the resolved object before it goes to the frontend.
- `yatra_availability_calendar_dates` (filter) β mutate the full calendar array.
- `yatra_availability_date_created` / `_updated` / `_deleted` (actions) β fire on manual-date CRUD.
---
## Troubleshooting
**A date I added manually doesn't show on the calendar** β check its **status**. `blocked` hides the date completely; `sold_out` shows it disabled. Also confirm the date is within the trip's *Available From β Available To* window.
**A recurring rule isn't producing dates** β make sure *Active* is on, *Start date* is in the past or near future, and your *Weekdays* picks at least one day for weekly recurrence. The rule's preview calendar (right column) should show upcoming dates.
**The customer sees a different price than what I set** β the resolver follows priority 1 β 2 β 3. Make sure no rule with a higher-priority base price covers the date. The detail page shows which source won (`availability_date` / `recurring_rule` / `trip_default`).
**Two rules with overlapping patterns conflict** β clean them up. The resolver picks the first match deterministically but the UI doesn't currently warn about overlaps. Use one rule per "shape" (one daily, one weekly with weekdays, etc.).
**Capacity doesn't decrement when I confirm a booking** β Yatra updates `booked_count` only when the booking transitions to `confirmed`. Pending bookings reserve a soft hold (configurable in [Settings β Booking](/settings#_3-booking) β *Booking Expiry Hours*).
---
## Where to read more
- [Create a trip β 1.4 Availability & Booking](/trip-creation#_1-4-availability-booking) β the Trip Builder default fields.
- [Departures](/departures) β Departures are date-time events you publish for the public-facing departure list. They're consumed by the same resolver but managed under their own admin page.
- [Bookings & customers](/booking-settings) β how booked dates decrement capacity and how cancellations restore it.
- [Hooks & filters β Availability](/hooks-filters) β every action / filter the availability pipeline fires.