Case Study 06 · Marketing Operations

Free Trial Class Lead Automation

Three linked automation systems that route, screen, and track free trial class leads end to end. Built entirely from Meta Lead Ads, Activepieces, Airtable, and Discord, with no new tools, no database, and no code steps outside what those four platforms already offer.

Industry

EdTech / Marketing Operations

Build Window

May to August 2026, iterative

Extra Software Spend

$0, code steps only

Stack

Meta Lead Ads · Activepieces · Airtable API · Discord Bot

4 Tools orchestrated, zero new spend
3 Linked automation systems
1 Alert per completion, guaranteed
0 Manual triage steps required

Every lead needed a human to notice it.

Free trial classes are a tutoring company's highest-volume acquisition offer: a parent sees a Meta ad, fills an instant form, and a sales caller phones to confirm the booking. Before automation, a human had to notice the lead, decide who should call it, check whether the family had already been through a trial before, and log everything by hand. Four specific failure modes kept recurring.

Before

  • All leads defaulted to one caller, adding a second required manual per-lead decisions
  • No way to know if a lead was a repeat tester before dialling
  • Free classes delivered twice to the same family, wasted coach time and seats
  • Airtable's edit trigger re-announced old completions on any record touch
  • Operational table and master contact base drifted apart over time
  • Phone number formatting mismatches made one parent look like two people

After

  • Strict caller alternation, zero human triage, expandable in two lines
  • Every lead's history classified and posted before the caller ever dials
  • Repeat testers flagged only when a class was genuinely delivered
  • Bot-managed flag guarantees exactly one alert per real completion
  • Cross-base sync keeps operational table and master CRM in step
  • Phone numbers matched on normalised last 8 digits, formatting-proof

Form submission to caller's phone, in seconds.

The constraint on any solution was strict: the team runs on Activepieces, Airtable, and Discord, with no appetite for new tools, new subscriptions, or a database. Everything below is built from flow triggers, router branches, and small JavaScript code steps calling the Airtable and Discord APIs directly.

01

Capture

02

Screen

03

Assign

04

Notify

05

Record

06

Complete

Round-robin assignment without a database.

Leads should alternate between callers automatically, so each new signup is owned within seconds, workload splits fairly, and every CRM record carries an accountable owner. The hard part: automation platforms run each flow execution in isolation. A code step has no memory of previous runs, so it cannot know whose turn it is. The textbook answer is external state, a database or key-value store, exactly the kind of extra infrastructure the team wanted to avoid.

The solution uses Airtable itself as the shared state. A small config table holds one row per lead funnel, and each row has a plain Number field called Counter. The rotation step reads the counter, picks the caller by modulo arithmetic, and increments the counter for the next run.

// pick the caller by modulo, then PATCH Counter = currentCounter + 1
const callers = ["Caller A", "Caller B"];
const s1Caller = callers[currentCounter % callers.length];

Result: strict alternation with zero human triage, verified end to end through both branches. Because the counter only ever increments, it doubles as a free lifetime tally per funnel. Adding a third caller is one entry in the callers array, one entry in the Discord ID map, and a publish. Known trade-off: two leads arriving in the same instant could draw the same caller. At current volumes this is rare and harmless, so the simplicity was kept over adding locking.

The Discord ping, with repeat screening built in.

A free class is a real cost: a coach's time, a seat, admin work creating logins. Some families sign up repeatedly to collect multiple free classes, while others signed up months ago and never attended. Both facts change how a caller should handle the conversation, and neither was visible at the moment of dialling.

The hard part was not finding an old record, it was deciding what an old record means. A previous signup is not proof a family received a class: plenty sign up and never attend, and some are mid-cycle, added to a class group with the class not yet run. Flag those as repeat testers and the system accuses innocent families, and callers stop trusting the warnings.

A screening step sits on the flow's trunk, immediately after the lead's record is created. It searches for earlier records matching the lead's contact details and classifies history against one strict rule: a previous trial only counts as delivered when that record's Remove from group box is ticked, meaning the full cycle demonstrably happened. Mid-cycle records and signups that never reached a class deliberately do not qualify.

History found What the caller sees How it is used
No earlier record Message posts clean, nothing added Normal first-contact call
Earlier signup, never delivered Context line: signed up before but never got the class Caller frames the call as a follow-through, not a cold pitch
Full cycle completed Red warning: repeat tester, already received a free class Caller steers to paid enrolment instead of a second freebie
Discord message showing a clean Free Trial Class Lead assignment with no prior history, tagging the assigned caller with name, phone, email, year level and requested class

No history found: clean assignment, normal first-contact call

Discord message with a blue context line reading signed up before but never got the class, above the Free Trial Class Lead assignment

Earlier signup, never delivered: context line frames the follow-through

Discord message with a red warning reading REPEAT TESTER already received one free class, listing prior signup history, above the Free Trial Class Lead assignment

Full cycle completed: red warning steers the caller to paid enrolment

Every assignment message is now also a briefing, at the cost of one lookup per lead and zero effort to consult. The strict delivered-only rule keeps the red flag trustworthy: when it appears, the class genuinely ran.

Completion alerts that fire exactly once.

When a caller finishes with a lead and ticks Remove from group, the team needs one notification saying the lead is complete, and the master contact base needs to learn this family has used their free trial. Without that write-back, the repeat screening in System 2 has no history to read.

Two separate difficulties made this hard. First, Airtable's automation trigger watches Last Modified, which updates on any field edit, and cannot watch for a specific field changing to a specific value. A naive flow fires the completion alert every time anyone fixes a typo on an already-completed record. Second, matching the lead back to the master base by phone number fails constantly in naive form, because the same number gets stored in different formats across bases.

// dedupe gate: proceed only when both are true
Remove from group = TRUE AND Discord Notified = FALSE

// cross-base match on normalised phone, last 8 digits
FIND(last8, SUBSTITUTE({Number} & "", " ", ""))

Dedupe is handled with a second, bot-managed checkbox. The flow proceeds only when both conditions above are true, and its final step ticks Discord Notified on the record so a fresh completion is never re-announced. Cross-base matching strips spaces and compares on the last eight digits, which survives country-code and formatting differences entirely.

One alert per completion, no matter how many times the record is edited afterwards, and a master base that stays truthful about who has used their free class. This is the system that closes the loop: completion sync writes the history, repeat screening reads it, rotation decides who acts on it. Each system alone is small. Chained, they run the funnel.

Reliability baked into every step.

Beyond the three systems, several practices run through every code step in the flow, keeping the whole pipeline trustworthy at volume rather than merely functional on a good day.

Guarded inputs

Required inputs are checked at the top of every code step, so a missing mapping returns a plain-English error naming the input, instead of a cryptic API failure buried in a log.

Safe auto-create writes

Airtable writes use typecast so a new caller or a new form option auto-creates its select option instead of rejecting the record. Safe here because every value originates from a controlled source.

One canonical version per step

Each code step exists as one canonical version pasted identically into every branch of the flow, so a fix made in one place is a fix made everywhere it runs.

Rules that made three small systems compound.

Shared state can be one record

A counter field on a config row gives isolated automation runs a memory, no database required, and doubles as a lead tally per funnel.

Screen before anyone dials

A history lookup costs one query per lead and turns every assignment message into a briefing, catching repeat freebie seekers at zero marginal effort.

Define ground truth precisely

Received a class means the remove-from-group cycle completed, nothing less. A loose definition would flag no-shows as repeat testers and destroy trust in the warning.

Put information where eyes already are

The warning rides on the message the caller was going to read anyway. A separate repeat-tester report would have been technically identical and behaviourally useless.

Edit-triggered automations need a fired flag

A bot-managed checkbox is the difference between an alert that fires once and one that fires on every future edit of the record.

Match people by normalised phone

Comparing the last eight digits with spaces stripped means formatting and country-code variants never split one family into two records.

Four tools already in use, zero new spend.

Component Technology Purpose Cost
Lead capture Meta Lead Ads instant form Native conversion event: name, phone, email, year level, requested class Existing
Automation engine Activepieces flows, router branches, JS code steps Screening, rotation, record creation, notification, completion sync Existing
Operational CRM Airtable REST API Free Trial Class working table: contact details, caller, group status Existing
Master contact base Airtable REST API, separate base Lifetime contact history across every funnel, source of truth Existing
Team notification layer Discord Bot API Lead assignment pings, history warnings, completion alerts Existing

Zero manual triage between form and phone.

Rotation is live with two callers, verified end to end through both branches, expandable with a two-line edit. Every lead posts with its history classified before the caller dials, with the red warning reserved for genuinely delivered trials. Completion alerts fire exactly once, and the master base stays current on who has used their free class, which in turn powers the screening for the next signup from that family.

The one-line version: a parent taps a Meta ad, and seconds later the right caller is tagged in Discord with the lead's details and its full history, the CRM already knows who owns it, and when the class is delivered the system remembers, so the next signup from that family arrives pre-screened.