Case Study 05 · Marketing Operations

Webinar SMS Automation

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.

Industry

EdTech / Marketing Operations

Build and Deploy Time

Under one working day

Platform Cost

$0 (Cloudflare free tier)

Stack

Cloudflare Worker + D1 · Calendly API · Airtable API · SMS Gateway · Cron

3 External platforms orchestrated live
2 Clicks per SMS burst
T-15 Reminder fires automatically
$0 Monthly platform cost

Manual SMS bursts at the worst possible moment.

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

  • Multi-step export, cross-check, clean-up, upload pipeline per burst
  • Calendly's native SMS requires a second phone number field, adding friction that risks fewer sign-ups
  • Targeting list stale within hours during a live campaign
  • Families who already completed still received completion nudges
  • T-15 reminder depended on a person being available on a Sunday
  • No shared record of who received which message or when
  • Double-texting risk on every repeated run
  • Message length and link correctness depended on care under pressure

After

  • Phone number collected once on the booking form, no extra opt-in step, registration stays frictionless
  • 2 clicks with a live preview in between, per burst
  • CRM status checked live at send time, always current
  • Idempotent log makes double-sends structurally impossible
  • T-15 reminder fires automatically via cron, no human needed
  • Full audit trail on every message sent, visible on the dashboard
  • 130-character guard enforces single-segment SMS on every save
  • Kill switch cancels the reminder without sending anything

Three flows. Every edge case handled.

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.

01

Completion nudge (on demand)

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.

02

Automatic T-15 reminder (cron-driven)

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.

03

Reminder kill switch

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.

The worst cases are boring by design.

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.

Any team member. Any browser. No terminal.

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.

Live event overview

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.

Registrant table

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.

Message copy editor with live character counter

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.

Preview before every send

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.

Recent sends log

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.

Dashboard showing live registrant counts, webinar tracking keyword input, Zoom link override, and SMS copy editor with 130-character counters

Live stats, event tracking, Zoom link override, and SMS copy editor with real-time character count

Actions panel showing auto T-15 toggle, preview and send buttons for nudge and reminder, test SMS field, and kill switch controls

Actions panel: auto T-15 toggle, preview before every send, test to one number, and the reversible kill switch

Built for the next event on day one.

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.

One file. No framework. Deployable in seconds.

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

From a multi-step pipeline to two clicks with a preview.

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.