---
title: "Example: Booking System · RUAL Documentation"
description: "Resources, time slots, conflict checks and reminders."
canonical: https://docs.rual.nl/examples/booking-system
language: en
---

# Example: Booking System

Availability, reservations, reminders, and no double-bookings. A complete scheduling app with the flow that makes conflicts impossible.

Booking looks simple until two people grab the same slot at once. This example shows the model, the conflict-proof reservation flow, and the reminder machinery.

### Data Model

- **resources**: what gets booked (rooms, tables, staff): name, capacity, opening hours, timezone.

- **bookings**. One document per reservation:

```
{
  "_meta": {
    "cms": 1782166695442,
    "created": 1782166695,
    "entity": 1,
    "expiry": -1,
    "guid": "e67919283b4e37c9758326446605ab863bf452cda26f91c2aef74ba31f9a3214",
    "removed": 0,
    "ums": 1782170295118,
    "update_hash": "7b25e0af9163c4d8052ae7f31964bc0d8a5f3c62",
    "updated": 1782170295
  },
  "customer_guid": "b9ee6a631b24e78d1aa48aef0dc067fff7bfbacd24a56ef4ed1f08e537d48b6bd02",
  "customer_name": "sami b",
  "end": 1782364800,
  "reminder_sent": false,
  "resource_guid": "e01ff8643394371a8544d2c9f8a1b3e5d70892c4f6a0b8d1e3f5a7c9b2d4e6f8a0b2",
  "resource_name": "meeting room 2",
  "start": 1782361200,
  "status": "confirmed"
}
```

Resource and customer are denormalized onto the booking: calendar views show names without joins (see [Storage Examples](https://docs.rual.nl/blueprints/storage-examples)).

### Checking Availability

- Query the resource's bookings overlapping the requested window: `start requested_start`. Two range filters on the same query via [`query_bool_range_field`](https://docs.rual.nl/block-types/query/query_bool_range_field).

- No overlaps → slot is free. Overlaps → return the conflicting bookings so the UI can show why.

- Generate the day's available slots from opening hours minus confirmed bookings, in the function: not in the page.

### No Double-Booking

- The reservation flow first re-checks overlap (never trust the page's earlier check).

- Then it creates the booking with a status transition inside one mutation set: mutations process sequentially per document, so the last check and the write can't interleave (see [mutations](https://docs.rual.nl/blueprints/storage#mutations)).

- On conflict: reply 409 with the overlapping booking. See [error conventions](https://docs.rual.nl/cluster/api-guide#errors).

- Cancellation is a status change to `cancelled`. Never a delete. The audit trail and no-show stats survive.

### Reminders

- A [`schedule_repeating_event`](https://docs.rual.nl/block-types/events/schedule_repeating_event) runs hourly: query confirmed bookings starting within the next 24h where `reminder_sent = false`.

- Send each with the email pattern from [Email System Setup](https://docs.rual.nl/tutorials/email-system), then set `reminder_sent = true`: idempotent even if the run repeats.

- Customers manage their own bookings with `user_current` → term query on `customer_guid`.

### Next Steps
