Skip to content

Work · Onboarding & training journey · Coach running weekly live trainings

Training & Onboarding Automation

A weekly live training run on hand-sent emails

Weekly live trainings ran on manual emails, with missed sessions and users stalling before first login. A full journey automation made the trainings run themselves end to end.

  • GoHighLevel
  • Zoom
  • Vimeo
  • Role · sole designer & builder
  • 6 min
weekly trainings automated
100%Measured
manual admin
ZeroMeasured
no-show recovery
AutoMeasured
System size
System flow · 6 stages · 32 components reconstructed from the build record

The situation

The coach ran a free live training every week, in the evening, on a fixed weekday pattern. It was the top of the whole business: people registered, attended, and some of them bought.

The machinery around it was manual. Someone sent the confirmation, someone the reminders, and someone worked out afterwards who had shown up. That last part was the worst of it, because the participant report and the registrant list are two different things, and reconciling them by eye is slow and wrong.

People who missed a session dropped out of the story. There was no reliable way to get them the recording and a seat the following week, so a no-show was a lost lead.

What was broken

  • Confirmations and reminders were hand-sent, so the ladder was as reliable as whoever was awake.
  • Attendance was reconciled by eye, and one person who dropped and rejoined looked like two attendees.
  • No-shows went unrecovered, because nothing linked the finished recording back to the people who missed it.
  • The confirmation email named a hard-coded day, which is wrong for anyone who registers after that week's last session.
  • Buyers were receiving the prospect invite, because the gate that used to suppress them had been removed at some point.

Constraints

  • The CRM cannot hold an expiring credential. The registrant endpoint needs OAuth with hourly expiry, and the CRM can neither refresh a token nor map the returned join link back onto a contact.
  • The recurring series had to be presented to the registrant as a single scheduled event, never as a recurrence.
  • A hand-enrolled blast workflow cannot use relative delays, because send times would then depend on the minute the operator clicked.
  • Every SMS had to be ASCII-only and single-segment, since smart quotes force the wide encoding and roughly triple the send cost.
  • Some steps could not be automated at all and had to be named as human operating steps instead.

FIG 01 · System Flow

How the system actually runs

Every component below is reconstructed from the project’s own build records. Nothing is added to make it look bigger.

components
32
components
stages
6
stages
decisions & guards
9
decisions & guards
AI
0
AI
human handoffs
1
human handoffs
platforms
5
platforms
  1. Stage 1, Registration & assignment: Form capture, webinar registration, join-link write-back, and which night this person belongs to.
    1. Free training form (Capture, GoHighLevel). The public registration form for the weekly evening training. Connects to Register & assign workflow when form submitted.
    2. Register & assign workflow (Workflow, GoHighLevel). A form-submitted trigger firing a custom webhook to the serverless function with email, name and contact id plus a shared-secret header, then tagging the contact as registered. Connects to Registration function when signed webhook.
    3. Registration function (API / service, Serverless function). POST-only, shared-secret verified, payload-validated glue that registers the contact and writes the results back; a header mismatch is rejected before anything else happens. Connects to Cached OAuth token when token missing or near expiry; Register all occurrences when payload valid; Session assignment maths when always.
    4. Cached OAuth token (Guard, Server-to-server OAuth). An account-credentials token fetched with basic auth and cached in module scope with its expiry, refreshed shortly before it lapses and reused across warm invocations. Connects to Register all occurrences when token valid.
    5. Register all occurrences (Calendar, Zoom). Registers the person on the recurring webinar without naming an occurrence, so the returned join link is valid for any session. Connects to Join-link write-back when join link returned.
    6. Join-link write-back (CRM record, GoHighLevel). The unique join link written into a dedicated contact field, which is itself the trigger for the confirmation workflow. Connects to Confirmation & reminders when join-link field not empty.
    7. Session assignment maths (Workflow, Serverless function). Server-side date maths in the event timezone: the assigned session is the next weekday the series runs, and a registration after the last session of the week rolls to the following week. Connects to Confirmation & reminders when assigned day and datetime set.
  2. Stage 2, Confirmation & reminders: An instant confirmation and a ladder that tightens from the day to the hour to the minute.
    1. Confirmation & reminders (Workflow, GoHighLevel). Triggered by the registered tag: an instant confirmation email and SMS carrying the join link and the assigned day, then reminders scheduled relative to the assigned session datetime. Connects to Morning-of email when morning of the assigned date; Purchase exit condition when purchase tag added.
    2. Morning-of email (Message, GoHighLevel email). The day-of reminder, sent on the assigned session date. Connects to One-hour SMS when one hour before.
    3. One-hour SMS (Message, GoHighLevel SMS). An SMS an hour before the session starts. Connects to Live-now SMS when at the start time.
    4. Live-now SMS (Message, GoHighLevel SMS). The start-time SMS carrying the join link. Connects to The live session when attendee joins.
    5. Purchase exit condition (Guard, GoHighLevel). The reminder workflow exits on the purchase tag, so a buyer stops receiving acquisition reminders the moment they buy. Connects to Buyer & suppression when acquisition messaging stops.
  3. Stage 3, The live session: The recurring training itself, and who is invited into which room.
    1. The live session (Calendar, Zoom). The recurring evening training, run on a fixed weekday pattern at a fixed hour. Connects to Session-ended webhook when session ends; Recording handler when cloud recording ready.
    2. Buyer / non-buyer split (Decision, GoHighLevel). Two segmented sessions on the same night — an earlier one for anyone who has bought, a later one for non-buyers — each with its own meeting link and its own workflow. Connects to Buyer suppression gate when non-buyer workflow.
    3. Buyer suppression gate (Guard, GoHighLevel). A head-of-workflow branch with one dead-ending branch per buyer tag and the entire pre-existing chain on the else branch. Connects to Same-night blast workflow when no buyer tag matched; Buyer & suppression when any buyer tag, dead end.
    4. Same-night blast workflow (Workflow, GoHighLevel). A triggerless, hand-enrolled workflow — an opening SMS, emails at one hour, thirty minutes and ten minutes before, then a we-are-live SMS — where every wait is an absolute date-time with a skip-if-passed policy. Connects to The live session when waits elapsed or skipped.
  4. Stage 4, Attendance reconciliation: A signed webhook turns a raw participant report into per-session records and tags.
    1. Session-ended webhook (Trigger, Zoom to serverless). A signed webhook on session end: raw-body capture, an endpoint validation challenge response, signature verification, a fast acknowledgement, then all the real work in the background. Connects to Participant reconciliation when signature verified and acked.
    2. Participant reconciliation (Workflow, Serverless function). Paginates the participant report and the approved registrant list, dedupes participants by lowercased email and sums their durations across rejoins, then computes no-shows as approved registrants absent from the attendee set. Connects to Per-session records when per person; Contact rollups when per person; Attended / no-show tags when attended or no-show.
    3. Per-session records (Data store, GoHighLevel custom object). A per-session attendance record associated to the contact, keyed by the session identifier and carrying minutes watched — zero for a no-show — written idempotently so a re-run rewrites rather than duplicates. Connects to Recording handler when records name the session.
    4. Contact rollups (CRM record, GoHighLevel custom fields). Contact-level rollups: last session date, last status, and a count of sessions attended. Connects to Attended, not bought when session counts available.
    5. Attended / no-show tags (CRM record, GoHighLevel tags). A tag pair added alongside the records, with the naming convention standardized deliberately across the whole build. Connects to No-show recovery when no-show tag; Attended, not bought when attended tag.
  5. Stage 5, Segmented follow-up: Four forks: no-show, attended mid-week, attended at week end, and bought.
    1. Recording handler (API / service, Serverless function). A recording-completed webhook, copying the signed-webhook pattern verbatim, that finds the session records, writes the replay URL onto every contact for that session, and tags the replay-ready signal on no-shows only. Connects to No-show recovery when replay-ready tag, no-shows only; End-of-week branch when replay URL populated.
    2. No-show recovery (Workflow, GoHighLevel). Triggered by the no-show tag, waits for the replay-ready signal, sends the replay by email and SMS, and invites the contact to the following week sessions. Connects to The live session when invited to next week; Seat filled or recovered when replay sent, seat re-offered.
    3. Attended, not bought (Decision, GoHighLevel). Triggered by the attended tag; waits until the morning after, exits if the purchase tag is present, and otherwise branches on the assigned session day. Connects to The live session when mid-week, same link reused; End-of-week branch when last session of the week; Buyer & suppression when purchase tag present, exit.
    4. End-of-week branch (Decision, GoHighLevel). Attendees who did not buy on the last session of the week get the replay over the weekend plus an invitation to the following week, rather than a separate weekend offer. Connects to The live session when next-week invite; Seat filled or recovered when replay carries the weekend.
    5. Buyer & suppression (Workflow, GoHighLevel). An order-form trigger filtered to the specific product tags the buyer, removes them from acquisition messaging, and hands off to the later ascension phase. Connects to Seat filled or recovered when bought and suppressed.
    6. Seat filled or recovered (Outcome, GoHighLevel). Every registrant ends in a known state — attended, recovered with the replay and a new seat, or bought and suppressed from acquisition.
  6. Stage 6, Operations & safeguards: The verification, collision and cost rules that keep the automation from fighting itself.
    1. Replay ownership rule (Guard, Design decision). Replay sending is owned by exactly two workflows; a third standalone replay-delivery workflow was explicitly deprecated and left unpublished. Connects to No-show recovery when sole replay owner.
    2. Frozen send copies (Guard, GoHighLevel). The published workflow email steps had template sync disabled, so each step holds a frozen send copy while the library master and the preview URL point elsewhere. Connects to Read-back verification when send copies proved by read-back.
    3. Read-back verification (Report, CRM API). Every edit verified by reading it back: step counts unchanged, zero occurrences of the dead link, the head step confirmed to be the gate, and each email template re-fetched and asserted old-gone and new-present. Connects to Same-night blast workflow when read-back before go-live.
    4. Human operating steps (Human, CRM console). Named human steps with reasons: enroll the list before the release-window cutoff, publish the draft workflow because drafts cannot enroll, and pause a colliding recurring workflow for the night. Connects to Same-night blast workflow when publish and enroll before cutoff.
    5. Single-segment SMS rule (Guard, GoHighLevel SMS). Every SMS written ASCII-only and single-segment with an opt-out line, because smart quotes force the wide encoding and cut the per-segment character limit. Connects to Same-night blast workflow when copy constraint.
0/15

Someone signs up for the free training after the last session of the week has already run.

Read this system as text
  1. Stage 01

    Registration & assignment

    Form capture, webinar registration, join-link write-back, and which night this person belongs to.

    • Free training form · Capture · GoHighLevel — The public registration form for the weekly evening training. → Register & assign workflow (form submitted)
    • Register & assign workflow · Workflow · GoHighLevel — A form-submitted trigger firing a custom webhook to the serverless function with email, name and contact id plus a shared-secret header, then tagging the contact as registered. → Registration function (signed webhook)
    • Registration function · API / service · Serverless function — POST-only, shared-secret verified, payload-validated glue that registers the contact and writes the results back; a header mismatch is rejected before anything else happens. → Cached OAuth token (token missing or near expiry), Register all occurrences (payload valid), Session assignment maths (always)
    • Cached OAuth token · Guard · Server-to-server OAuth — An account-credentials token fetched with basic auth and cached in module scope with its expiry, refreshed shortly before it lapses and reused across warm invocations. → Register all occurrences (token valid)
    • Register all occurrences · Calendar · Zoom — Registers the person on the recurring webinar without naming an occurrence, so the returned join link is valid for any session. → Join-link write-back (join link returned)
    • Join-link write-back · CRM record · GoHighLevel — The unique join link written into a dedicated contact field, which is itself the trigger for the confirmation workflow. → Confirmation & reminders (join-link field not empty)
    • Session assignment maths · Workflow · Serverless function — Server-side date maths in the event timezone: the assigned session is the next weekday the series runs, and a registration after the last session of the week rolls to the following week. → Confirmation & reminders (assigned day and datetime set)
  2. Stage 02

    Confirmation & reminders

    An instant confirmation and a ladder that tightens from the day to the hour to the minute.

    • Confirmation & reminders · Workflow · GoHighLevel — Triggered by the registered tag: an instant confirmation email and SMS carrying the join link and the assigned day, then reminders scheduled relative to the assigned session datetime. → Morning-of email (morning of the assigned date), Purchase exit condition (purchase tag added)
    • Morning-of email · Message · GoHighLevel email — The day-of reminder, sent on the assigned session date. → One-hour SMS (one hour before)
    • One-hour SMS · Message · GoHighLevel SMS — An SMS an hour before the session starts. → Live-now SMS (at the start time)
    • Live-now SMS · Message · GoHighLevel SMS — The start-time SMS carrying the join link. → The live session (attendee joins)
    • Purchase exit condition · Guard · GoHighLevel — The reminder workflow exits on the purchase tag, so a buyer stops receiving acquisition reminders the moment they buy. → Buyer & suppression (acquisition messaging stops)
  3. Stage 03

    The live session

    The recurring training itself, and who is invited into which room.

    • The live session · Calendar · Zoom — The recurring evening training, run on a fixed weekday pattern at a fixed hour. → Session-ended webhook (session ends), Recording handler (cloud recording ready)
    • Buyer / non-buyer split · Decision · GoHighLevel — Two segmented sessions on the same night — an earlier one for anyone who has bought, a later one for non-buyers — each with its own meeting link and its own workflow. → Buyer suppression gate (non-buyer workflow)
    • Buyer suppression gate · Guard · GoHighLevel — A head-of-workflow branch with one dead-ending branch per buyer tag and the entire pre-existing chain on the else branch. → Same-night blast workflow (no buyer tag matched), Buyer & suppression (any buyer tag, dead end)
    • Same-night blast workflow · Workflow · GoHighLevel — A triggerless, hand-enrolled workflow — an opening SMS, emails at one hour, thirty minutes and ten minutes before, then a we-are-live SMS — where every wait is an absolute date-time with a skip-if-passed policy. → The live session (waits elapsed or skipped)
  4. Stage 04

    Attendance reconciliation

    A signed webhook turns a raw participant report into per-session records and tags.

    • Session-ended webhook · Trigger · Zoom to serverless — A signed webhook on session end: raw-body capture, an endpoint validation challenge response, signature verification, a fast acknowledgement, then all the real work in the background. → Participant reconciliation (signature verified and acked)
    • Participant reconciliation · Workflow · Serverless function — Paginates the participant report and the approved registrant list, dedupes participants by lowercased email and sums their durations across rejoins, then computes no-shows as approved registrants absent from the attendee set. → Per-session records (per person), Contact rollups (per person), Attended / no-show tags (attended or no-show)
    • Per-session records · Data store · GoHighLevel custom object — A per-session attendance record associated to the contact, keyed by the session identifier and carrying minutes watched — zero for a no-show — written idempotently so a re-run rewrites rather than duplicates. → Recording handler (records name the session)
    • Contact rollups · CRM record · GoHighLevel custom fields — Contact-level rollups: last session date, last status, and a count of sessions attended. → Attended, not bought (session counts available)
    • Attended / no-show tags · CRM record · GoHighLevel tags — A tag pair added alongside the records, with the naming convention standardized deliberately across the whole build. → No-show recovery (no-show tag), Attended, not bought (attended tag)
  5. Stage 05

    Segmented follow-up

    Four forks: no-show, attended mid-week, attended at week end, and bought.

    • Recording handler · API / service · Serverless function — A recording-completed webhook, copying the signed-webhook pattern verbatim, that finds the session records, writes the replay URL onto every contact for that session, and tags the replay-ready signal on no-shows only. → No-show recovery (replay-ready tag, no-shows only), End-of-week branch (replay URL populated)
    • No-show recovery · Workflow · GoHighLevel — Triggered by the no-show tag, waits for the replay-ready signal, sends the replay by email and SMS, and invites the contact to the following week sessions. → The live session (invited to next week), Seat filled or recovered (replay sent, seat re-offered)
    • Attended, not bought · Decision · GoHighLevel — Triggered by the attended tag; waits until the morning after, exits if the purchase tag is present, and otherwise branches on the assigned session day. → The live session (mid-week, same link reused), End-of-week branch (last session of the week), Buyer & suppression (purchase tag present, exit)
    • End-of-week branch · Decision · GoHighLevel — Attendees who did not buy on the last session of the week get the replay over the weekend plus an invitation to the following week, rather than a separate weekend offer. → The live session (next-week invite), Seat filled or recovered (replay carries the weekend)
    • Buyer & suppression · Workflow · GoHighLevel — An order-form trigger filtered to the specific product tags the buyer, removes them from acquisition messaging, and hands off to the later ascension phase. → Seat filled or recovered (bought and suppressed)
    • Seat filled or recovered · Outcome · GoHighLevel — Every registrant ends in a known state — attended, recovered with the replay and a new seat, or bought and suppressed from acquisition.
  6. Stage 06

    Operations & safeguards

    The verification, collision and cost rules that keep the automation from fighting itself.

    • Replay ownership rule · Guard · Design decision — Replay sending is owned by exactly two workflows; a third standalone replay-delivery workflow was explicitly deprecated and left unpublished. → No-show recovery (sole replay owner)
    • Frozen send copies · Guard · GoHighLevel — The published workflow email steps had template sync disabled, so each step holds a frozen send copy while the library master and the preview URL point elsewhere. → Read-back verification (send copies proved by read-back)
    • Read-back verification · Report · CRM API — Every edit verified by reading it back: step counts unchanged, zero occurrences of the dead link, the head step confirmed to be the gate, and each email template re-fetched and asserted old-gone and new-present. → Same-night blast workflow (read-back before go-live)
    • Human operating steps · Human · CRM console — Named human steps with reasons: enroll the list before the release-window cutoff, publish the draft workflow because drafts cannot enroll, and pause a colliding recurring workflow for the night. → Same-night blast workflow (publish and enroll before cutoff)
    • Single-segment SMS rule · Guard · GoHighLevel SMS — Every SMS written ASCII-only and single-segment with an opt-out line, because smart quotes force the wide encoding and cut the per-segment character limit. → Same-night blast workflow (copy constraint)

Platforms · GoHighLevel · Zoom · Serverless functions · SMS · Email

What I built

A registration journey with a serverless function doing the work the CRM cannot, and a reconciliation path turning a raw participant report into records the follow-up workflows trigger on.

Registration and the token problem

The public form fires a workflow that tags the contact registered and sends a signed webhook to a serverless function. The function is POST-only, verifies a shared-secret header before anything else happens, and validates the payload. A mismatch is rejected having changed nothing.

It holds an account-credentials OAuth token cached in module scope with its expiry, refreshed shortly before it lapses and reused across warm invocations, because tokens expire hourly. It then registers the person on the recurring webinar without naming an occurrence. The returned join link is valid for any session, which lets a mid-week re-invite reuse it with no re-registration.

The join link is the trigger

The join link is written into a dedicated contact field, and that write is itself the trigger for the confirmation workflow. The CRM owns all registrant-facing messaging, and it can only do that if it holds the link.

Alongside it, server-side date maths in the event timezone works out which session this person belongs to. It is the next weekday the series runs, rolling to the following week for anyone registering after the last session. Reminders need a real datetime to schedule against, and the confirmation has to name the correct day rather than a hard-coded one.

A ladder that tightens

Confirmation goes out immediately by email and SMS, carrying the join link and the assigned day. Then three rungs, each on the channel that suits it: a morning-of email, an SMS an hour before, and an SMS at the start time. Email gets the day, SMS gets the hour and the minute. The whole workflow exits on the purchase tag, so a buyer stops getting acquisition reminders the moment they buy.

Attendance as records, not a field

When the session ends, a signed webhook fires. The handler captures the raw body, answers the platform's validation challenge, verifies the signature, acknowledges immediately, and does the real work in the background, because the platform retries anything slow.

Reconciliation paginates the participant report and the approved registrant list, dedupes by lowercased email, and sums durations across rejoins. One person who drops and rejoins is one attendee with combined minutes. No-shows are approved registrants absent from the attendee set.

Each result is written as a per-session attendance record on the contact, keyed by session id and carrying minutes watched, zero for a no-show. Writes are idempotent, so a re-run rewrites rather than duplicates. Cheap rollups sit on top: last session date, last status, sessions attended. Tags are added alongside on a standardised convention, because tags are what the follow-up workflows trigger on.

Four forks after the session

A recording-completed webhook, copying the signed-webhook pattern verbatim, writes the replay URL onto every contact for that session and tags the replay-ready signal on no-shows only.

No-shows wait for that signal, then get the replay by email and SMS plus an invitation to the following week. Attendees are judged the morning after, so an evening purchase still counts as bought and exits them. Mid-week attendees are re-invited to the next night on their existing link. Attendees on the last night of the week get the replay over the weekend instead, because there is no session tomorrow. Buyers are tagged from the order form and removed from acquisition messaging in one place.

Edge cases and failure handling

  • A buyer enrolled in the non-buyer list: a head-of-workflow gate dead-ends one branch per buyer tag before the invite chain can run.
  • A no-show who would receive the replay twice: replay sending is owned by exactly two workflows, and a third was deprecated and left unpublished for that reason.
  • A dead meeting link inside published emails: those steps had template sync disabled, so the frozen send copies had to be edited directly. Editing the library master or the preview would have shipped nothing.
  • A known dead branch approved for repair: I left it unrepaired with a reason and a revisit date, because that night's segmentation inverted its effect and the fix would have handed a replying buyer the wrong link.

How I verified it

Every edit was verified by reading it back, because a success response alone does not prove the content landed. I checked that step counts were unchanged, that the dead link had zero occurrences, and that the head step was in fact the gate. Each email template was re-fetched to assert the old copy gone and the new present.

The buyer gate carried its own pre-check. All seven buyer tags were verified to exist before the branch was built, because the editing tooling auto-creates unknown tags. A single typo would have produced a gate that matched nobody while looking correct on screen. The rejoin case was written into the test plan explicitly, since it is the reconciliation error that quietly inflates attendance.

What changed

The weekly training now runs itself from registration to follow-up. A registrant is confirmed, reminded on a tightening ladder, reconciled against the real participant report, and routed into one of four follow-up paths. Nobody is left in a state no workflow is watching. No-show recovery is automatic: the recording is delivered as soon as it exists, with a seat for the following week on the same join link.

One thing is worth stating honestly. The journey has named human operating steps rather than none. Enrol the list before the release-window cutoff, publish the draft workflow because drafts cannot enrol, and pause a colliding recurring workflow for the night. Miss the cutoff and the batch slips a day. Leave the recurring workflow running and its reminders land on top of the blast. Automation that quietly collides with itself is worse than a checklist, so the checklist is part of the system.

Before / after

  • Before
  • ✕ Weekly live trainings ran on hand-sent emails
  • ✕ Sessions missed, no-shows unrecovered
  • ✕ New users stalled before first login
  • After
  • ✓ Weekly trainings fully automated end to end
  • ✓ Manual admin eliminated
  • ✓ No-show recovery handled automatically

MeasuredOutcome figures on this page were measured before and after the build.

Have something close to this? Book a call. I’ll tell you if I’ve already built it.

One Manila business day to reply. All client work is under NDA; what you see here is redrawn architecture with real metrics.

Book