# Calendar and booking

Real Agent gives you a calendar of your schedule and a public page where people can book time with you. The calendar and the booking page are two sides of the same data: a booking someone makes shows up on your calendar straight away.

## The admin calendar

Open `/calendar` on your Real Agent address (the same sign-in as the dashboard). It shows your schedule in four views:

- **Day** and **Week** show an hour grid with each timed entry as a block.
- **Month** shows the familiar month grid with a few entries per day.
- **Year** shows twelve small months; days with something on them carry a dot, and clicking a month opens it.

Use **Today** and the arrows to move around, and the view buttons to switch. The calendar reads everything time-bound in your graph: meetings imported from a calendar export or created by a booking, appointments Real Agent schedules for you in chat, and tasks that have a due date (shown on their day).

Click any entry to open its details: the full title, time, location, and who is attending, with each attendee on its own line (name, and email beneath it). For a booked meeting you can also edit its title, time, or location, or delete it, and the grid updates straight away. You can **Download** a booked meeting as a calendar file (`.ics`) to add it to Apple, Google, or Outlook calendars; when the meeting has attendees, Download asks whether to include them in the file before it saves, and a meeting with no attendees downloads straight away. The private note is never written into the downloaded file, since the file can travel outside your admin. In the edit view you can add an attendee (give a name, an email, or both) and remove one; adding someone who is already a contact reuses that contact rather than making a duplicate, and removing them takes them off the meeting but keeps the contact. The edit view also has a private note for that meeting, a place for your own reminders that only you and your team ever see; it never appears on the public booking page or anywhere outside your admin. Appointments Real Agent schedules and due-dated tasks are shown for detail only and are changed where they are created, not here. New meetings are still made by a booking or by asking Real Agent in chat, not from the calendar.

Everything on the calendar comes from records the rest of Real Agent already uses, scoped to your account.

## The public booking page

The booking page is a separate, public site on its own web address (for example `book.yourdomain.com`). A visitor sees only open time slots, never the titles or details of your existing meetings. They pick a slot, enter their name, email, and an optional note, and submit. That is the whole flow, like a simple Calendly.

The page is hosted on Cloudflare so it keeps working and keeps taking bookings even if your Real Agent device is briefly offline. Each submission is stored safely at the edge first, then turned into a meeting on your calendar shortly after.

## Putting the booking page live

Ask the admin to run the **calendar-site** skill ("put up a booking link" or "deploy the booking site"). It will ask for:

- your **time zone**,
- the **meeting length** (for example 30 minutes),
- a **buffer** between meetings (for example 10 minutes),
- your **weekly availability** (which days and hours you take bookings),
- the **web address** the booking page should use.

It writes those settings, builds the page, puts it live on that address, and checks that a test booking is captured. To change your availability later, run the same skill again with the new numbers.

## How a booking becomes a meeting

1. A visitor submits a booking on the public page.
2. The booking is saved immediately at the edge (so it is never lost).
3. Your Real Agent device picks it up on a short cycle, creates a `:Meeting` for it, and links the visitor as a contact.
4. You are notified by email (and by WhatsApp only if you already have a thread with the agent; it never starts a new WhatsApp conversation).
5. The new meeting appears on your `/calendar`.

This usually completes within a couple of minutes of the booking.

## If a booking does not show up

Bookings are reconciled on a cycle and the system audits itself each run. In the device logs:

- `[calendar-booking]` lines trace one booking through submit, write, notify, and done.
- `[calendar-reconcile]` lines report four health numbers per account: `eligibleExamined` (bookings the check looked at), `pendingUnreconciled` (bookings captured but not yet turned into meetings), `pendingOldestAgeMin` (how many minutes the longest-waiting of those has been waiting) and `orphanMeetings` (booking meetings with no matching saved booking). Entries that only block out a slot are not counted — they are not bookings and never become meetings.

A `pendingUnreconciled` number that stays above zero across several runs means those bookings are failing to turn into meetings, and the `attempt=` number on the matching `[calendar-booking]` lines says how many times each has been tried. A booking whose meeting is deleted is picked up again automatically on the next run. To read them: `journalctl --user -u <brand-service> | grep -E '\[calendar-booking\]|\[calendar-reconcile\]'`, then filter by one booking id for its full story. The edge half of a booking (before your device sees it) is in the Cloudflare Pages function logs.

A count on its own cannot tell a short lag from a long one: five bookings waiting two minutes and five waiting nine hours are both `pendingUnreconciled=5`. `pendingOldestAgeMin` is what separates them. It reads `0` when nothing is waiting, and also when something is waiting but for under a minute, so read it together with the count beside it.

A separate `op=liveness` line is printed every ten minutes by your device's own server, whether or not anything is wrong. `stale=false` means the booking check has run recently. `stale=true` means it has not run in over six minutes, which is the case nothing else can tell you about: a check that has stopped cannot report that it has stopped. `lastSkipReason` names the last account whose check finished without looking at any bookings, and reads `none` when every account it got to was checked. If you run more than one account, a check can both look at one account and skip another, so seeing a recent check time next to a skip reason is normal and means two different accounts.

## When your real calendar lives elsewhere

The booking page publishes open slots from the internal calendar, the one Real Agent keeps in its own graph.
If you also keep commitments on a connected calendar (Google Calendar or Microsoft 365 through a
claude.ai connector), those live in the admin session. So the booking page does not offer a time you are
busy on the connected calendar, the admin session reads the connector's free/busy every 30 minutes and
reflects those busy blocks into the availability the page publishes from. The reflection is read-only: it
holds the connector's busy times against the booking page and never writes anything back to your Google
or Microsoft calendar.

This runs from the admin session because it is the only seat that can see both your connected calendar and
the internal booking calendar at once. When you ask what you have on or whether you are free, the admin
reads the connected calendar directly and answers from the full picture. A specialist cannot do this: it
never sees the connected calendar, so it would answer from the internal calendar alone. If the sync ever
falls behind, the reconcile run reports it as a drift count so it is caught before a visitor books a slot
you are not really free for.

## What this does not do

One meeting type only (one length, one weekly window, one buffer). Your meetings in Real Agent are the source of truth for the booking page, unless your real calendar is a connected calendar (Google Calendar or Microsoft 365), in which case its busy times are reflected into the page as described above. iCloud and other calendars are not synced. No rescheduling or cancellation links, reminders, or visitor time-zone selection. The booking page shows open times from a snapshot your device publishes to the edge on a short timer, so the page keeps showing availability and taking bookings even while your device is offline — a slot that gets booked drops off at once, and times that have passed are never shown. If your device stays offline a long time the open times can go stale, but they never vanish; they refresh the moment it is back online.
