Case Study 05 · Marketing Operations
A serverless SMS operations platform built and deployed in under a day. It orchestrates a scheduling platform, a CRM, and an SMS gateway to send perfectly timed, CRM-targeted messages, controlled entirely from a browser-based dashboard with no exports, no spreadsheets, and no terminal required.
The Problem
Webinars are a core step in the enrolment funnel. The existing process for any SMS push was a pipeline of exports and hand-offs: download the registrant list from the scheduling platform, cross-check it against the CRM in a spreadsheet, clean up phone formats by hand, then upload the result to the SMS platform. Each burst consumed a meaningful block of staff time and had to be repeated several times during campaign week, exactly when the team was busiest.
The obvious alternative, enabling Calendly's built-in SMS reminders, introduces its own conversion problem. Calendly's native SMS feature requires registrants to enter their phone number as a separate step on the booking form, in addition to their name and email. That extra field adds friction at exactly the moment someone is deciding whether to commit. For a free webinar where the sign-up barrier should be as low as possible, asking for a phone number twice (once on the registration form, again for SMS opt-in) risks reducing sign-up volume. The custom solution collects the phone number once on the booking form and handles all SMS delivery centrally, keeping the registration flow short.
Two message types were critical. The first was a completion nudge: a registrant whose qualifying task was not yet done was one well-timed SMS away from becoming a full participant rather than a spectator. The second was a join-link reminder 15 minutes before the webinar started on a Sunday afternoon, where a person not available at exactly the right time meant hundreds of parents got nothing. Both failed the same way: they depended on a person being available, having the right file ready, and executing a multi-step process under pressure.
Before
After
How It Works
The platform runs three distinct message flows, each designed around the specific failure mode it replaces. All three read live state at the moment they run, which is what makes targeting self-correcting rather than stale.
The worker pulls every active registrant from Calendly and checks each one against the CRM in real time, matching on email with phone as a secondary check. Registrants whose CRM record carries the completion tag are excluded with an explicit reason. Registrants without a resolvable mobile are surfaced rather than silently dropped. Everyone else receives the nudge. Because the check runs live at each press, the audience shrinks itself between pushes: a family who completes on Thursday night is automatically out of Friday's send.
A 5-minute cron trigger wakes the worker continuously. When the tracked webinar falls within the 10 to 20 minute window, the reminder fires automatically to all active registrants carrying the Zoom link resolved at that exact moment: manual override first, then the link attached to the scheduling event, then a configured fallback. If no link can be resolved, the worker refuses to send rather than texting a blank placeholder. A manual send button remains available for teams who prefer to pull the trigger themselves.
Plans change close to broadcast time. Cancelling on the dashboard writes a per-event marker that blocks the reminder for that event only, automatic and manual alike. It sends nothing, it is fully reversible up to the moment the reminder would have fired, and it has no effect on any future webinar. The worst-case action is deliberately the most boring one possible.
Design principle borrowed from safety-critical systems: looking is always free. Every number on the dashboard, every preview, every registrant list can be inspected without risk, because nothing sends without an explicit, confirmed instruction. Dry run is the default state of every endpoint.
Safety Engineering
The most important property of a system that sends SMS to hundreds of parents is that its failure modes are dull. Every guard in this platform eliminates a class of error rather than reducing its frequency.
Idempotent send log
Every real send writes a key combining purpose, event, and phone number to the database before the next message is attempted. Double-clicks, overlapping cron ticks, and repeated runs all collapse into a single delivered message per person per purpose. Structural impossibility, not careful timing.
130-character guard
A hard limit blocks saving any message copy that would split into multiple SMS segments. The counter measures the final substituted message (with the real Zoom link and event name filled in) not the template, because a template's length is never the message's length.
Phone normalisation
Numbers arrive from parents in every imaginable format. A normaliser resolves local, spaced, bracketed, and internationally prefixed forms to one canonical mobile number. Landlines and overseas numbers are rejected outright (SMS spend is wasted on both) and every rejection surfaces the raw input so an operator can fix the record at the source.
Link resolution guard
If no Zoom link can be resolved for the reminder (no manual override, no link on the event, no fallback), the worker refuses to send rather than delivering a message with a placeholder. A broken reminder is better than a mass text telling parents to click nothing.
Test mode
Any message can be sent to a single test number using the exact live copy and the real substituted link, without touching the send log. An operator testing to their own phone is not thereby excluded from the real burst later. This caught a real defect before the first live send: a bare domain did not render as a tappable link on all devices.
Self-correcting targeting
State is checked live at send time, never cached from an export. A family who completes the qualifying task between two nudge runs is automatically excluded from the second send, with no manual list management required. Stale targeting is structurally impossible.
Operations Dashboard
The worker serves its own single-page dashboard, protected by a shared key. The entire SMS operation is drivable from a browser with no exports, no third-party logins, and no terminal access required. The page loads live state on every open.
The tracked event, its start time, and four headline counts: total registrations, completions, families still to act, and registrants without a usable mobile number. The operator knows the full picture before touching anything.
One row per family showing completion status as a badge, the mobile number with its source (booking form or recovered from CRM), and current student status read directly from the booking form. Families still to complete sort to the top because they are the ones worth acting on.
Both message templates are editable in place with a live counter measuring the final substituted length. The counter blocks saving anything over 130 characters. Previously customised copy is never overwritten by a deploy.
Every action has a preview step that renders the exact would-send list before anything fires. Mass sends require explicit confirmation. The preview and the real send use the same underlying endpoint, so what you see is exactly what goes out.
A full audit trail closes the page: every message sent, to whom, when, and for which purpose. This is the record the old process never had. It doubles as the answer to "did the reminder actually go out?" without opening a third-party platform.
Live stats, event tracking, Zoom link override, and SMS copy editor with real-time character count
Actions panel: auto T-15 toggle, preview before every send, test to one number, and the reversible kill switch
Reusability
Generality was a launch requirement, not an afterthought. The tracked webinar is selected by a name keyword edited on the dashboard. Message copy uses placeholders for the join link and event name. The automatic reminder default is event-generic. Campaign-specific pieces simply lie dormant when a webinar has no qualifying task component.
Launching the next event's SMS operation is a keyword edit and a copy line. The same deployment serves every future webinar with no code changes.
The pattern generalises well beyond webinars. Any workflow shaped like "read a schedule, check a CRM, message exactly the right people at exactly the right minute" can be assembled from the same parts. The second build will be faster than the first.
Technical Stack
| Component | Technology | Purpose | Cost |
|---|---|---|---|
| Backend and dashboard | Cloudflare Worker (single JS file, no framework) | All logic, HTTP API, and the operations dashboard served from the edge | Free |
| State and deduplication | Cloudflare D1 (SQLite) | Idempotent send log and operator settings (copy, link override, keyword) | Free |
| Automatic reminder | Cloudflare Cron Trigger (5-minute cadence) | Wakes the worker to check the event window and fire T-15 reminder | Free |
| Registrant list and event data | Calendly v2 API | Live registrant list, event start time, booking form answers, Zoom link | Existing |
| CRM targeting | Airtable REST API (read-only scope) | Completion status via record tags, fallback phone numbers | Existing |
| SMS delivery | SMS gateway API | Australian mobile delivery, billed per message by the provider | Per send |
Result
The operator-time saving is the visible win. An SMS burst went from a multi-step export, cross-check, clean-up, and upload pipeline to two clicks with a preview in between. Campaign weeks involve several bursts, so the saving compounds precisely when the team is busiest.
The deeper win is that whole classes of error were eliminated rather than reduced. Stale targeting cannot happen because state is checked live at send time. Double sends cannot happen because the log is consulted before the gateway. Malformed numbers, overlong messages, and missing links are each caught by a dedicated guard. And the T-15 reminder fires itself, making missed timing impossible regardless of what the team is doing on a Sunday afternoon.
The platform was designed, built, tested against production data, and deployed inside a single working day.
The build-time lesson: a system designed around eliminating error classes rather than reducing error rates takes the same time to build and never needs revisiting for the same reason twice. Every guard in this platform was designed so that the problem it addresses simply cannot happen, not just happens less often.