Home › Help › Deposits, reminders, and reports
Deposits, reminders, and reports
ShootCal keeps three light "business" features layered on top of your Google Calendar. None of them store dollar amounts, and ShootCal never keeps a parallel mirror of your calendar event bodies. Deposit state is read straight off the Google event itself, so every surface agrees on which days are truly booked.
Deposit tracking is a simple paid / not-paid switch on a session. When you mark a session's deposit as received, the day turns green; mark it unpaid and it turns red. That flag is saved as an invisible note on the Google event itself, so your Mac, iPhone, the web app, and your public availability all agree on which days are truly booked. Your client never sees "paid" or "unpaid" on their copy of the invite.
Client reminder emails are optional and turned on per session type. When you switch reminders on for, say, "Family Session," ShootCal sends the client a friendly reminder a set number of days before the shoot. The email comes from your own Gmail, so to the client it just looks like you wrote it. A background job checks hourly and sends each booking at most one reminder, only during daytime hours.
Reports is a counts-only dashboard: how many bookings this month, this year, all-time, a breakdown by session type, a last-12-months trend, and how many deposits are collected versus still pending. The booking counts are tallied from your stored booking records (booking-page bookings plus sessions the AI scan found on your calendar), and the deposit counts are scanned live from your primary calendar. Because ShootCal never stores prices as numbers (they are free text), Reports counts things rather than adding up money. Every number is tappable to see the exact bookings or deposit events behind it.
The deposit flag: where it lives and how it is read
The deposit status is stored as a private property on the Google Calendar event itself (one of Google's "extended properties"), set to either paid or unpaid. Google keeps private extended properties invisible in its own UI and never copies them to attendees, so the client's invite never shows the deposit state.
Reading is deliberately identical across surfaces so web and native agree on which days are booked. The backend reader resolves in this order: (1) the deposit property, accepting several historical spellings of "paid" and "not paid" for older events; (2) if the flag is absent, the event's Google color (green = paid, red = not paid); otherwise not paid. The native app is now stricter: it resolves paid status SOLELY from the deposit property, and the old color fallback and an even older "Paid - " title-prefix reader were both removed after a one-time backfill stamped the modern flag onto every historical paid event. So today the flag is the single source of truth on native; the backend (which powers the web app and the public availability feed) still keeps the color as a secondary read for any flag-less legacy event.
Color coding: paid green, pending red, otherwise the Google color
A session is colored by deposit state ONLY when the flag is explicitly set. Both surfaces track a "has a deposit status" signal, meaning the deposit property is present (paid or unpaid). If it is present, the session shows green when paid and red when not; otherwise it falls back to the event's normal Google color (in Google's standard palette, Basil is green and Tomato is red). This "has a deposit status" distinction was added specifically so that flag-only paid events (backfilled with the flag but never given the green color) still render green instead of their original color.
Writing the deposit flag (the toggle)
All write paths update only the deposit property and leave everything else on the event untouched. On the web, marking a deposit sends a partial update that sets the private deposit property to paid or unpaid. Native uses a similar server call for the Reports drill-down, and a dedicated toggle for the calendar/agenda view. That toggle writes the same property, and ADDITIONALLY swaps the event's Google color between green and red only when the event already uses one of those two deposit-style colors, so the green/red flip is visible on Google's own clients; events with a custom color or no color keep their color so the toggle never overwrites a user's pick. Titles are never modified.
Client reminder emails: the hourly job and exactly what triggers a send
A scheduled job runs once an hour and reads each approved web user's settings. Reminders are OPT-IN PER SESSION TYPE, not a global switch: a type emails only if its reminders are turned on, and a user with no type turned on is skipped entirely. Each type may set its own "days before" lead time (clamped to 1 through 14 days), otherwise it inherits the Studio Default (1 day). You can optionally supply your own reminder template with merge fields like client name, session type, studio, date, time, and location; with no template a built-in friendly body is used.
The candidates are accepted bookings that have a real calendar event, have not already been reminded, and start within the next 14 days (the widest window; each booking is then checked against its own type's window). A send fires only when ALL guards pass: the booking's session type has reminders on; now is inside that type's "days before" window; the session is at least 2 hours away (a minimum lead time so a same-hour booking is never spammed); it is the photographer's local daytime, 8am to 8pm, using the primary calendar's timezone (no client timezone is stored); and the event still exists live on the calendar (a fresh fetch from Google confirms it; a failed fetch skips the whole user that run rather than risk a bad send). The email is sent FROM the photographer's own Gmail, as a combined plain-text and HTML message.
Reminder guards: once-only, drift, and "booked inside the window"
A per-booking "reminder sent" marker makes the job email each booking at most once. The job also marks a booking "handled" WITHOUT emailing in three suppression cases: the booking was confirmed INSIDE its own reminder window, so the confirmation email already served as the reminder; the calendar event no longer exists (drift from a deleted or cancelled event); or the client email is invalid. Importantly, if a type's reminders are simply OFF, the booking is left untouched (not marked sent) so turning the type on later while the session is still upcoming still lets the reminder go out. The job always exits cleanly so a single failure never spams the scheduler, and a send that throws or returns an error is left unmarked to retry next run.
Note: this client-facing email job is distinct from two other notification jobs, one that sends Web Push alerts to the photographer every few minutes, and one that sends the photographer a night-before email digest of their own day. Those notify YOU; only the client-reminder job emails the client.
Reports: counts only, two data sources, and the deposit scan
Reports is served to both the web app and the native apps from one shared summary builder. ShootCal stores no numeric money, prices are free text and deposits are a flag, so the summary is deliberately count-based. Booking counts combine two stored sources with the same shape (a session-type name plus a start time): accepted booking-page bookings, plus sessions discovered by the AI calendar scan. It produces this-month, this-year, and all-time totals, a by-type breakdown (each carrying the type's free-text price for DISPLAY only, never summed), and a last-12-months trend. Month boundaries use the primary calendar's timezone; the 12-month buckets are anchored to the first of the month before subtracting, to avoid end-of-month overflow.
Deposits in Reports are a separate count: a scan fetches your primary calendar over a window from 60 days back to 180 days ahead and keeps only events whose deposit flag is explicitly set, then counts paid vs pending (about a 240-day lookback in total). This needs a usable Google access token; if the fetch fails the summary reports the deposit count as unavailable and the UI hides the deposit stats rather than showing a misleading 0/0. The drill-downs list the exact rows behind each number, and crucially reuse the SAME deposit scan so the deposit count and the list can never disagree. The native Reports screen renders the identical stats and tappable drill-downs, and its deposit drill can mark a deposit paid in place.
FAQ
Does my client ever see whether I have marked their deposit as paid?
No. The deposit status lives in the Google event's private extended properties. Google keeps private extended properties invisible in its own Calendar UI and never propagates them to attendees, so the copy of the invite on your client's calendar shows no paid/unpaid marker. ShootCal also never writes the status into the event title, titles are left untouched on every toggle path.
If I toggle a deposit, does it change the event color on my regular Google Calendar apps too?
Only sometimes, and only intentionally. The native toggle swaps the event's Google color between green and red ONLY when the event already uses one of those two deposit-style colors, so the green/red flip is visible in Google's own clients. If you gave the event a custom color, or no color, the toggle leaves the color alone and just flips the invisible deposit flag, so ShootCal still shows green/red without overwriting your color choice. The web deposit action only writes the flag and does not touch the color.
Why do my reports show counts instead of total revenue?
By design. ShootCal stores no numeric money anywhere: session prices are free text and a deposit is just a paid/unpaid flag, never an amount. So Reports counts sessions and deposits instead of summing money. The per-type free-text price is shown next to the by-type count purely for display and is never added up. If you want the rows behind any number, every stat is tappable and the drill-down returns the actual bookings or deposit events.
What exactly has to be true for a client to get an automatic reminder email?
All of these: (1) the booking's session type has reminders turned on in your settings (it is opt-in per type, there is no global switch); (2) the booking is an accepted booking-page booking, has a real calendar event, and has not been reminded yet; (3) now is inside that type's window of the start time minus its days-before value (its own setting, else the Studio Default, clamped to 1 through 14 days); (4) the session is at least 2 hours away; (5) it is currently between 8am and 8pm in your primary calendar's timezone; and (6) the calendar event still exists live (a deleted event is suppressed, never emailed). The email is sent from your own Gmail.
Will a client get reminded twice, or get a reminder for a session I already cancelled?
No to both. A per-booking "reminder sent" marker makes the hourly job send each booking at most once. For a cancelled or deleted event, the job re-fetches the live calendar each run; if the event is gone it marks the booking handled and never emails. It also suppresses (marks handled, no email) bookings that were confirmed inside their own reminder window, since your confirmation email already acted as the reminder.
What time window do the deposit counts in Reports actually cover?
The deposit scan reads your primary calendar from 60 days ago through 180 days ahead, an overall lookback of about 240 days. It counts only events whose deposit flag is explicitly set (paid or pending) and ignores cancelled events. If the calendar fetch fails or there is no usable Google token, the summary reports the deposit count as unavailable and the UI hides the deposit stats entirely rather than showing a wrong 0 paid / 0 pending.
Do hand-created calendar sessions show up in my booking counts, or only booking-page bookings?
Both, but from two different stored sources. Accepted bookings made through your booking page are tracked as booking requests. Sessions you created by hand on the calendar are picked up by the AI calendar scan and stored separately; the summary merges both into the booking counts so Reports reflects real session volume, not just booking-page activity. The scan-imported sessions have no client email and carry no deposit status in the booking detail list.
Is the deposit-paid confirmation email the same thing as the client reminder email?
No, they are separate features on different surfaces. The client REMINDER is the server job driven off accepted booking-page bookings. The deposit-paid CONFIRMATION is a native-only feature: when you flip a deposit to paid in the Mac/iOS app and have opted in, it emails the client attendee on that event a confirmation from your Gmail. The backend has no server-side deposit-paid email, and that confirmation keys off the event's client attendee rather than a booking record.