A calendar is not a view of the bookings, it is the record everyone reads.
The hard part of an appointment calendar app is not the booking form. It is that one appointment gets read by four parties who each want something different out of it: the customer wants a time in their own zone, the person doing the work wants a day laid out in order, the front desk wants the gaps, and the reminder job wants a timestamp it can subtract a day from. Design the record for all four or you will write it twice. If you are still choosing an approach rather than a design, start with the booking and scheduling app builder overview.
This page covers repeating appointments and the exceptions that break them, time zones and where the day starts, whether to sync with an external calendar and what the permission actually costs, and why reminders are the one part worth building first.
See what happens when you move one occurrenceThe short version
A repeating appointment is a rule, and every exception is a second record.
Two decisions shape everything else. Whether a repeating appointment is stored as a rule that generates occurrences or as a thousand rows written out in advance, and whether a time is stored as an instant, as a local wall clock time, or as both. Get either one wrong and the bugs turn up months later, when the clocks change or somebody moves a single Tuesday.
The iCalendar standard settled both of these long ago, and it is worth following even if you never export a single file. A series is a rule, occurrences are generated from it, and individual occurrences can carry exceptions. That model is the reason every calendar app you have ever used asks whether you meant this event or the whole series.
Moving one Tuesday is not the same as moving Tuesdays
RFC 5545 defines a recurrence rule with a frequency, an interval, an end given either as a count or as an until date, and selectors such as BYDAY, BYMONTHDAY and BYSETPOS. Every second Thursday, the last Friday of the month, weekdays only: all of that is one short rule rather than a table of dates. Storing the rule instead of the rows is what lets somebody change the time of a standing appointment without you rewriting a year of history.
The consequence is that occurrences are generated rather than stored, and that is where the design gets interesting. An occurrence that has been changed has to be recorded as an exception to the series: either the date is excluded outright, or a separate record overrides that one occurrence and points back at the series it belongs to. Cancelling one week is an exclusion. Moving one week is an override. They are different writes and they behave differently later.
This is why the dialogue asking whether you meant this event, this and all following, or the whole series is not a piece of interface politeness. Those three answers store three different things. This event writes an override. This and all following ends the current rule and begins a new one. The whole series edits the rule itself and leaves every override hanging off it exactly where it was, which is the case that produces the angry phone call.
Model all of this on day one even if you launch without repeating appointments, because retrofitting it means migrating rows that were written as though they were independent of each other.
Do not expand a series into rows to make the query easy
The temptation is real. Generating two years of rows makes the day view a plain date range query, and nothing else in the codebase has to understand recurrence at all. It also means one change to the rule has to find and rewrite every generated row, and any row somebody already edited by hand is either clobbered or orphaned. Generate occurrences at read time for the window you are showing, and store only the series and its exceptions.
RFC 5545, Internet Calendaring and Scheduling Core Object Specification
Try it
What the app writes when you edit one Tuesday
The series is every Tuesday at 09:00, twelve times over. You are standing on the fifth one. Pick what you meant by the edit.
Apply the change to
The change itself
One override, and the rule is untouched
A separate record is written for that single date at the new time, pointing back at the series it came from. The rule still says every Tuesday at 09:00, and every other occurrence is still generated from it.
Whose day is it
A booking at 09:00 is not a fact until you say where. Store the instant together with the zone identifier it was made in, something like Europe/Riga, rather than an offset. An offset is a property of a date rather than of a place, and it changes twice a year, so an offset you store today will be wrong in March.
The distinction that catches people out is between appointments pinned to a wall clock and appointments pinned to a moment. A class at 09:00 every Monday stays at 09:00 through a daylight saving change, because the rule is about local time. A call between two countries is one moment that shows a different clock time on each side. An app that treats both the same will move one of them by an hour twice a year, and it will be the one your customers are standing outside for.
Then there is the day boundary, which quietly breaks reporting. The phrase today's appointments depends on whose today you mean: the customer's, the practitioner's or the server's. Pick one, usually the place where the work physically happens, write the choice down, and apply it everywhere. Two different answers to what counts as today is how a day view and a daily summary email end up disagreeing in front of a customer.
A fixed weekly timetable is the simplest version of all this and worth separating out if it is all you need, which is a class schedule app: the same slot every week, the same room, and no per-customer booking to reconcile against anything.
Syncing with an external calendar costs more than it looks
Everyone asks for two-way sync with Google Calendar and almost nobody needs both directions. Be specific about what you actually want. Reading a staff calendar so you do not book over a dentist appointment is one feature. Writing your bookings into that calendar so they appear next to everything else is a second. Keeping the two in step forever, including edits, deletions and conflicts, is a third and considerably larger one.
Access is granted per user by that user, not by you. The Google Calendar API defines sixteen scopes and they are not interchangeable. The broad calendar scope is full read and write across their calendars. The calendar.readonly scope is view only. The calendar.events scope modifies events. The calendar.freebusy scope returns availability without event details.
That last one is the interesting option and it is routinely skipped over. If the requirement is do not double book, freebusy answers it without your app ever reading what the other appointments are, which is a smaller consent screen and a smaller thing to go wrong. Google makes the point directly in its own documentation: users more readily grant access to limited, clearly described scopes.
Newly does not ship a Google Calendar connection. We checked the current documentation, and the integrations listed for v2 apps are Newly Backend, push notifications through OneSignal and purchases through RevenueCat, with other services handled by asking the agent to add them. So a calendar sync means your own Google Cloud project, your own OAuth client and your own consent screen, and you should confirm with Google what review the scopes you pick require before you plan a launch date around them.
The option worth weighing against all of that is no sync at all plus a read-only feed. Staff subscribe to it in whatever calendar they already use, they see the bookings, and nobody is ever asked to grant write access to a personal calendar. It gets most of the benefit for a fraction of the work, and it fails in ways people can understand.
Reminders are the only intervention that moves the no-show rate
Everything else in a calendar app is bookkeeping. The reminder is the only part that changes what happens in the world, because it is the only part that reaches a person who has forgotten. We have not verified a figure for how far reminders cut no-shows and we are not going to quote one, but the mechanism is not in doubt: somebody who is reminded the evening before either turns up or cancels in time for the slot to be filled, and both of those beat silence.
So design the cancel path into the reminder rather than sending the reminder alone. A message that says you have an appointment tomorrow at ten and offers no way to move it converts a would-be reschedule into a no-show. One tap to confirm and one tap to move is the whole feature, and it is worth more than any other screen you will build.
Timing is a policy and it belongs in a setting rather than in the code. The evening before, twenty four hours ahead, two hours ahead: all three are defensible, and which one works depends on the kind of appointment and how far ahead people book. What is not defensible is hard-coding it, because you will want to change it within the first month of real use.
If reminders are the entire problem you are solving and the calendar itself belongs to somebody else, that is an appointment reminder app, and it is a far shorter build than the one described here.
What each way of running the book gives you
| Option | Repeats as a rule | Change one occurrence only | Customer sees their own time zone | Reminders go out on their own |
|---|---|---|---|---|
| Paper day book | No | Yes | No | No |
| A shared spreadsheet | No | Yes | No | No |
| A shared Google Calendar | Yes | Yes | if they open it themselves | device alerts only |
| Off-the-shelf booking platform | Yes | Yes | Yes | Yes |
| A calendar app you build | Yes | if you model exceptions | Yes | Yes |
Building one around your own week
Booking platforms are built around a generic appointment and priced per seat per month. What they resist is exactly what makes a business specific: a gap that has to follow a particular treatment, a first appointment that runs longer than the rest, a Saturday only one person works, a customer who may book three months out while everybody else gets two weeks. Those are not edge cases to be worked around. They are the business.
Newly is an AI app builder. You describe the app you want, including the rules your week genuinely runs on, and it builds and ships a real mobile app you own. Plans start at $25 a month, there is no free plan, and iOS builds ship through App Store Connect. It is not a booking product and it does not process card payments, so deposits or prepayment mean a payment provider under your own account.
If you build one thing before anything else, build the reminder that stops no-shows, because a calendar nobody is prompted by is a tidier version of the paper book you already have.
Questions people ask about appointment calendar apps
Store the rule, not the generated dates. A recurrence rule holds a frequency, an interval, an end given as a count or an until date, and selectors such as the last Friday of the month. Occurrences are generated from that rule at read time, and any occurrence somebody has changed is recorded separately as an exception to the series.
Describe the week your business actually runs
Write down the rules that make your book different, including the gaps and the appointments that repeat, and build the calendar around those instead of around a grid.
Start building