Conversations
The inbox rebuilt with the things HubSpot's does not have: seven filters,
saved views with live counts, bulk assign, an
oldest-unanswered queue order, and the
approve / edit / reject draft loop. HubSpot's inbox stays live and
remains the sender until each message type is flipped — the parallel-running rule.
Sending is not flipped: every message type still routes to HubSpot,
so replies are still sent from HubSpot's inbox. The routing table below is the
authority on that.
Pick a conversation.
WhatsApp send routing (per message type)
Inbox
Every WhatsApp conversation, live. Active Leads, Customers, Dead and Unknown are this same list split by lead status — Unknown has no contact in the CRM yet.
Active Leads
The part of the WhatsApp Inbox still in play — everyone in the CRM who hasn't paid and isn't dead. Conversations with nobody matched in the CRM have their own page, Unknown, so nothing falls out of the split.
Customers
WhatsApp conversations with people who have paid — lead status "Paid". This is the customer-service view: everyone here is already a customer, so replies are support, not sales.
Dead
WhatsApp conversations with contacts marked Dead. They still get the win-back messages, so replies do land here — worth checking, because a reply from this list is someone coming back.
Unknown
WhatsApp conversations from numbers nobody has matched in the CRM — usually new inbound leads. Open one and use "Create contact" to put them in the CRM; their future messages then link automatically and the conversation moves to Active Leads.
Costs
Everything the app spends with outside vendors, across every feature — not just the dialler. OpenAI (gpt-4.1) powers every AI feature here: the voice agents, WhatsApp message classification, the social inbox, and assessment outreach. ElevenLabs and Deepgram are voice-only. There's no Claude/Anthropic usage anywhere in this app.
By vendor
WhatsApp template messages
Business-initiated WhatsApp sends Meta bills per message — the booking reminder ladder, drips, and this app's template outreach — counted from Meta's own delivery receipts, so HubSpot's sends are included. Replies inside a customer's free 24-hour service window don't count. Always shown per calendar month (how Meta bills), regardless of the time range above. Tracking began mid-July 2026, so July is a part month.
| Month | Billable templates | Marketing | Utility | Free (service window etc.) |
|---|
Where the OpenAI cost comes from
| Feature | Calls | Cost |
|---|
Daily breakdown
Assessment outreach arrives in bursts around each Introduction Session rather than spread evenly — this is where those spikes actually show up, day by day.
| Date | Feature | Calls | Cost |
|---|
Cost notes
Workflows
Every automated thing that happens to a lead — this app's and HubSpot's — on one map. Nothing on this page changes anything; it is here so nobody has to guess what already went out. Click any flow to see exactly what triggers it and what it writes.
HubSpot workflows
| NAME | ON OR OFF | OBJECT TYPE | LAST UPDATED |
|---|
Automation trees
Each automation as a branch tree — trigger at the root, conditions as forks, actions as leaves. A badge on a node means that write starts other workflows; click it to see which, and jump to them.
On right now
The live switches. Everything else on this page runs from code and has no toggle.
The lifecycle map
Columns are where the lead is. Rows are who acts. Scroll sideways for the later stages.
Always running
Engines and workers that run on a clock regardless of where any lead is.
Dial campaigns
The only automation in the system that is real configuration rather than code — edited on the Automations page.
Automation engine
The app-side flow engine (the HubSpot-workflow replacement). Each definition runs in a mode: off, shadow — the whole ladder runs on real contacts and every send is recorded instead of made — or live. Enabling anything is a cutover decision, not a switch-flip.
What a parent actually receives
Each row starts from one thing this app writes, and plots everything both systems then send. This is where messages pile up.
Collisions & gaps
Places the two systems fight, or where something is missing. Each one names the file or the HubSpot flow to go and look at.
Dashboard
Needs a human
Leads
Acquisition economics (Meta ads)
| Ad campaign | Spend | Leads | Cost per lead | Customers | Cost per customer |
|---|---|---|---|---|---|
| Loading… | |||||
Meta spend in the period against the leads created and the contacts who turned Paid carrying that campaign's ad tag — first-touch attribution from the UTMs hard-coded in the ads. Below the campaigns: every lead registered in the same window with the same spend spread across all of them, then how many leads were made dead — a separate count, never subtracted from the total, since a lead that arrived and died the same day is still a lead the spend bought. Days run midnight-to-midnight UK time. Spend is what Meta reports in GBP. The owner filter above does not cut this card; ad spend has no owner.
Introduction Sessions
| Session | Booked | Of which Dead / Paid |
|---|---|---|
| Loading… | ||
Previous sessions
| Session | Booked | Attended | Paid | New | No-shows | Show-up rate | “Offer” WhatsApps | Msg rate | Status |
|---|---|---|---|---|---|---|---|---|---|
| Loading… | |||||||||
Attended is everyone marked for that night, customers included; Paid and New split it. “Offer” WhatsApps is how many then messaged us to book in — replies keep counting as they arrive.
WhatsApp unreachable
| Contact | Undeliverable on | Booked on | Session | Working text number | Email address |
|---|---|---|---|---|---|
| Loading… | |||||
Meta answers within about a minute when a number is not on WhatsApp. A contact leaves this list by itself when a later WhatsApp to that number is delivered or read, or they write to us on WhatsApp. Rarely, a contact stays listed after their failed number moves to another contact and is reached there: they leave when a WhatsApp to a number they still hold is delivered or read, or they write to us on WhatsApp from one. Nothing is sent from here.
Dialler
The quick read before you commit more spend to the dialler: how much it called, how often someone picked up, what it cost to get one real result, and how many people who missed a session it won back. Every figure here is the same calculation the Dialler Overview shows in full — this is just the short version.
Full dialler breakdown →Reminders
Manual tasks that need a human before the next Introduction Session — nothing here happens automatically.
Performance
Is the dialler working, and what does it cost — results and money on separate tabs, both cut to the period below.
Key metrics
The Dashboard's Dialler panel is a cut of exactly these figures — same endpoint, same definitions. Picked up means the call completed and a human (not an answerphone) answered. A result logged means the agent finished the call having actually recorded something: confirmed, rescheduled, or can't attend.
| Result logged | Calls | Share of answered calls |
|---|
No-show recovery
Of the people who booked a session in this period and didn't turn up: how many we chased, how many took a new seat, and how many actually attended it the second time. Recovery rate is the number that decides whether chasing no-shows is worth the spend.
| Session | No-shows | Called back | Spoke to | Rebooked | Re-attended | Recovered |
|---|
Show-up rate & what the calls are worth
Booked-to-attended across every session in the period, then the comparison that actually tests the dialler: did the people it spoke to in the week before a session turn up more often than the people it never reached?
Best time to call
Pickup rate by UK hour and weekday, over the selected period — where to point the calling-hours window on each agent. Hours with fewer than 10 calls are shown but their rate is too thin to act on.
| Hour (UK) | Calls | Picked up | Pickup rate |
|---|
| Day | Calls | Picked up | Pickup rate |
|---|
How hard people are to reach
Retry-ladder health, across all time (retry state isn't dated per attempt). Contacts whose ladder ran out without a single answered call are pure wasted spend — if that number is high, the list is wrong or the retry schedule is too long.
The funnel, by attempt number and daypart
Whether ladder steps 3–5 earn their cost, on this house's own numbers instead of faith. An attempt number is a call's position in that campaign's full history with the contact, oldest first. The weighted outcome score below is the measure that replaces the 250-calls-a-day KPI: outcomes are worth points (a sign-up ask 10, a booking 8, a confirmation 5, a callback 2, an asked-for send 1), so volume alone scores nothing.
| Attempt | Calls | Answered | Answer rate | Bookings | Booking rate | Cost / booking |
|---|
| Daypart (UK) | Calls | Answered | Answer rate | Bookings | Booking rate | Cost / booking |
|---|
All-time call outcomes
Every AI call ever made — not cut to the period bar or the Agent filter.
Cost by campaign
| Campaign | Calls | Twilio | Deepgram | OpenAI | ElevenLabs | Total |
|---|
Campaign performance
| Campaign | Calls | Picked up | Pickup rate | Voicemail | Missed | Outcomes logged | Sales | Cost | Cost per sale |
|---|
A sale is a contact whose lead status turned Paid in the selected period, credited to the campaign of the last call they received in the 30 days before paying. Picked up means the call completed and wasn't voicemail; missed covers no answer, busy, failed, or cancelled.
Ways to reduce cost
Overview
Read top to bottom: the banner is the verdict, "Deal with these" is your to-do list (each card is one decision, with its buttons on it), "On trial" is any new agent still earning real dialling, and "The six gates" is the evidence behind the verdict — you rarely need to open a passing gate. The two tables at the bottom are diagnosis: what keeps going wrong, and questions to answer. Test and training calls are excluded from every figure here (the sweep still reviews them).
DiagnosisWhat keeps going wrong
Not a gate and not a to-do — the recurring faults, ordered by severity (what a fault costs), not by count. Lesson filing counts test and training calls; this table does not, so the two frequencies can differ.
| Fault | Calls affected | Severity rank | Route out |
|---|
ActQuestions the agents couldn't answer
From the promise tool (the agent knew it didn't know) and the judge (the agent guessed), grouped over the last 30 days — the clock starts at a question's first ask. Only genuinely open questions show here: anything already answered, dismissed, or matched in Common Questions is dealt with and sits behind the toggle. Each open row takes a drafted answer, your edit, the agents it applies to, and Save — or Dismiss if it needs no entry.
| Question (as a parent asked it) | Times asked | Agents | First asked | Sources | Answer |
|---|
Call Logs
Every call — the AI dialler and the team's Aircall phone in one list, filtered by Source. Click a row for the transcript or the recording.
| Contact | Phone | Agent | Started | Duration | Cost | Outcome | Status | Ended reason |
|---|
Tasks
The callback register. HubSpot stays the system of record — every task is created there first (callback requests, exhausted-retries handoffs, "claims already paid" checks) — and this is where the team works them. It defaults to due today or overdue, across all owners. That is deliberate: HubSpot's own "All" task view ships a hidden Assigned to = me filter, and it kept 655 open Call tasks — some due since December 2025 — off everyone's screen for eight months while the page said "You're all caught up on tasks. Nice work." Ownership is a column here, never a filter default.
| Subject | Contact | Due | Type | Status | Assignee |
|---|
Lead Pool
Parents who attended an Introduction Session and haven't paid — the highest-intent leads in the business. Nobody owns them: the AI Dialler and the sales team both work this queue, and whoever claims a lead first gets it. Claiming assigns the contact to you in HubSpot immediately, so a lead can only ever be taken once. Leads a seller already owns never appear here — if you booked them in, they stay yours.
Parents who reached the checkout and haven't paid a day later — they chose subjects, saw the price, and stopped at the last step. Same rules as the Lead Pool: nobody owns them, whoever claims first gets them, and a parent a seller already owns never appears here. The basket column shows what they were about to buy — open with that.
| Name | Phone | Lead type | Attended | In pool | Last AI call |
|---|
Recently claimed
Who got there first. Useful for the question this page exists to answer: is the dialler beating the team to these leads, or the other way round?
| Name | Claimed by | How | When | Waited |
|---|
Agents
All agents
Platform dialling
Shared by every agent, live from the next dial — agents can carry lower caps of their own on their Dialling tab.
Why isn’t it dialling? — check every gate
Runs every switch, cap, window and pool that can stop an automated call, right now, and names where each one is controlled. Legal guards (opt-outs, suppression, the send ledger) are not listed because they have no off switch — they stop one number each, never the dialler.
Who each agent dials
Every active lead — not dead, not paid — should belong to exactly one agent. The leads column is what that agent would work right now if switched on, from the same pool the test-call picker below searches.
Test call —
Rings your own phone so you can hear this agent for yourself. No contact behind it,
nothing written to HubSpot — so {{firstname}} and
{{session_date}} come out empty. To hear it with a real name and date,
use Training inside the agent and fill in the rehearsal fields.
Spoken lines
The fixed lines around every call — goodbyes, the mid-call hiccup line, the silence check-ins, the missed-call text. Edit a line and it is live from the next call. Lines marked locked are compliance or safety wording; you can read them here, and the note says why they stay put.
Loading…
Call a real contact
A live call to a real person, logged against their record. Pick the agent deliberately — this one is not a test. Lock a contact to keep them here for repeated test calls — no re-searching, one click to dial again, and the values the agent’s prompt will reference shown before it says them.
Keeps this contact callable whatever the booking, cool-off or last outcome says, and runs the agent you pick below instead of the one the booking date would choose. Nobody else is affected. Do-not-call, opt-outs, the Ofcom 72-hour rule and the outbound kill switch still apply.
New Agent
⚡ Speed to lead
Calls a brand-new ad lead within minutes of the form landing in HubSpot, before it ever reaches the Attempted backlog. Only dials contacts the segment engine classifies as New Lead (no status, no owner, has a phone number), one call per contact — retries then belong to the normal Cold Outreach cadence. Honours both pause switches, calling hours and all daily caps. Leads arriving out of hours queue until the window opens.
Call flow
Every branch this agent can take, drawn from its own tools — so it always matches what the agent can actually do, rather than a picture someone drew once. Emma’s lines are shown in full — click any blue box to edit what she says; it changes the live prompt from her next call.
Anything in double curly braces — like {{session_date}} — is filled in with the real value on each call, so keep it in the line.
Questions this agent answers
Every answer below is appended to this agent’s prompt on each call. The core knowledge tiles are shared by all the agents and are edited once on the Agents page; only on this agent tiles belong to this one alone. Click any tile to edit it.
Loading…
Training
Teach this agent by phone and review what it learned. Coach it out loud during the call (“no — when they say that, you should say…”) and each correction lands below as a draft. Only lessons you approve reach real calls. Click any lesson to edit it.
Call me to train role-plays a call — best for finding out what it gets wrong. Speak a lesson just listens: say what you want it to do differently and it writes the lesson up as a draft, no call and no answering back. Both land below for your approval.
Rehearse as
A training call has no contact behind it, so the agent knows no name and no session date unless you give it one. These are the reference fields this agent’s own prompt uses — search a real contact to fill them in, or just type whatever scenario you want to rehearse.
Live playbook
The exact text appended to this agent’s prompt on every real call — only approved lessons appear here. Drafts below change nothing until you approve them. “Check playbook” replays each of these through the live prompt and grades the reply.
Lessons
Drafts first. Click a row to edit, approve or retire it. Shared lessons apply to every agent and show which agent they were taught on.
| Status | Caller says | Agent should say | Rule | Scope | Last check | Added |
|---|
Lesson
Actions
Things she can do at any point in the call
The tool-backed actions, one tile each — who carries it, and whether the wording is the same everywhere. Built from the same live rules each agent’s call-flow chart draws, so a difference between two agents shows up here by itself. Tiles marked always on are safety rules enforced in code and cannot be switched off from any dashboard.
Loading…
Behaviour
How she conducts herself
The conduct rules — the welds and the built-in call machinery — one tile each, with who carries it and whether the wording is the same everywhere. Spoken lines in the charts are edited on each agent’s chart; tiles marked always on are safety rules enforced in code and cannot be switched off from any dashboard.
Loading…
Common Questions
Questions and answers default
The answers the dialler agents give, one tile per question — each shows the question and which agents give the answer. Open a tile with Edit, change the wording or the agent ticks, then press Save answers — the agents use the new wording on their next call, nothing is buried in the code. Every agent is ticked unless you decide otherwise, and the Closer also skips the four session-logistics questions automatically, because it only speaks to people who have already attended.
Keep {{held_details}} where it is in the joining-link answer — it is
replaced on each call with that parent’s own email and mobile.
Loading…
Team
| Name | Role | Access | 2FA |
|---|
Pending invites
Each link works once and expires 7 days after it was created. Copy it and send it to the person yourself — nothing is emailed automatically.
| Name | Role | Expires |
|---|
Account & Security
Change Password
Other devices signed in to your account will be logged out.
Two-Factor Authentication
Scan with Google Authenticator, 1Password, or similar — then enter the 6-digit code to confirm.
Recovery Codes
Ten one-time codes. Each works once, in place of the 6-digit code, if you lose your phone. Keep them somewhere that is not your phone — a password manager, or printed and put away. Generating a new set destroys the old one.
This is the only time these are shown. Save them before you leave this page — nobody, including support, can read them back.
Introduction Sessions
When Introduction Sessions run. This calendar drives the booking page, the dates the AI offers for reschedules, and the attendance register — click a day to add, block or restore a session.
When the sessions run
The weekly pattern every Introduction Session follows. This is what /book offers, what the attendance register expects, and what the reminders are timed from — change it here and all of them follow. Use the calendar below to block a single date or add a one-off.
What the public booking page will offer. These apply to /book only — the register, the AI's reschedule offers and the reminder texts always see every session.
weekly session one-off session blocked · Click a session day to block it, a blocked day to restore it, or an empty future day to add a one-off session.
Upcoming sessions overview
How many people are currently booked into each of the next few sessions. Cancelled counts people who dropped out — they are already excluded from Booked, from the reminders and from the register. The confirmed columns read the session's confirmation ledger, split the way the Introduction Sessions board splits them: on WhatsApp is parents who replied to a WhatsApp reminder, by dialler is confirmations the Session Confirmation agent took on a call, and Total confirmed includes the remainder confirmed by SMS reply, the HubSpot reminder flow, or by hand.
| Session | Booked | Cancelled | Confirmed on WhatsApp | Confirmed by dialler | Total confirmed | Breakdown |
|---|
Bulk reschedule
Moves everyone booked on one session to another. Each move updates HubSpot, re-registers them on Zoom when connected, and is logged so it can be undone per person. Anyone already marked as attended keeps that.
HubSpot's own WhatsApp confirmation + reminder ladder re-fires in full on any date change and cannot be picked from here.
Later bookings after attending
When a parent comes to a session while still booked on a later one, the CRM takes them off the later session (no message of its own) and their codes message says so. The rows below are the ones it did not clear — a guard refused it (the office moved the booking, the parent replied after the codes, they are a Customer…) or HubSpot kept failing the write. Their later booking still stands: clear it on the record, or tell the parent it stands.
| Parent | Came on | Later booking | Outcome | Why |
|---|
Test-site bookings
Bookings made on the test site's booking page. They are saved here for the team to see and send nothing: no text, no email, no joining link, nothing in HubSpot.
| Parent | Number | Session | State | Booked at |
|---|
Calendly sync
The reconcile worker checks every 30 minutes that everyone booked on the Calendly Introduction Session page exists in the CRM. Safe gaps are booked in automatically (they get the normal confirmation); anything with a status that must not be messaged — Dead, Customer, Unreachable — waits here for your call. Other Calendly event types (interviews, line management) are never touched.
| Flagged — needs you | Session | Why flagged | Found |
|---|
| Recent automatic actions | Session | Action | When |
|---|
Session Analytics
How the Introduction Sessions have been going: each past session's register numbers, and the same history compared evening by evening. The range picker applies to everything on the page.
Previous sessions
How each past session went. Attended is everyone marked as attending that night, paying customers included — if they were in the room, they attended. Paid and New split that total, so the same row answers how many turned up and how many of them were prospects. Late marks keep being counted for a day and a half after a session, so a figure can still rise the next morning.
| Session | Booked | Attended | Paid | New | No-shows | Show-up rate | “Offer” WhatsApps | Msg rate | Status |
|---|
Which evenings perform
The sessions in the range above, compared by the evening they ran. Pick an evening to see only its sessions — the count on each chip is how many of them are in the current range. Show-up divides attended by everyone booked in; asked to book counts the people who messaged for the offer afterwards, as a share of everyone in the room.
Show-up rate per session
Asked to book, share of the room
Introduction Session Register
Attendance is marked automatically. Sessions run on Zoom (since 4 Sep; the old Google Meet register has been removed). Since 11 Sep the platform's register marks each booked family Yes or No on HubSpot after every session, and from 20:15 on a session night the CRM reads that register and applies each family's lead-status move and session event. A booking still blank a day after its session is marked No by the CRM's catch-up sweep. The rows below are rebuilt from the marks already on HubSpot, four hours after each session, and recounted for a day and a half as late marks land.
Confirmed for the next session
Who has already told us they are coming, and which channel heard it first. A parent counted here is not rung by the dialler for that session — the confirmation call has one question in it and they have answered it.
| Contact | Heard by | When | What they said |
|---|
Show-up rate per month
Show-up rate per session
Register
Click a session's date for its full named roster — who attended (and the display name they joined under), who no-showed, and any names that never matched a contact.
| Session | Total bookings | Customer | Dead | Attended | Paid | New | No-shows | Show-up rate | “Offer” WhatsApps | Msg rate | Unmatched names | Status |
|---|
Duplicate contacts
A daily scan of the whole HubSpot base finds contacts sharing a phone number, email
inbox, or full name, graded by how likely they really are the same person — highest
confidence first. Open Review fields on a group to see every field
side by side, live from HubSpot, and pick which value to keep where they disagree.
Merges cannot be undone, so nothing merges without your click.
Two things worth knowing: a shared number with different names is usually one
household, not a duplicate. And on merge HubSpot keeps both email addresses (the
loser's moves to “Additional emails”) but only one phone number —
so if the numbers differ, choose the one to keep in the field review first.
Email changes that need a hand
When a family changes their email on the platform, the CRM updates their HubSpot card by itself. These are the changes from the last 30 days it could not finish. Sort each one, then tick Done to take it off the list.
| Date | Platform account | Old → new address | What the CRM did | HubSpot card |
|---|
Search HubSpot contacts
Look up any contact by name, phone, or email — not just ones flagged as duplicates.
Unknown WhatsApp numbers
Numbers messaging us on WhatsApp with no contact card. Approve a suggested match to save the number onto that contact, so their future messages attach to the right card.
| Number | First message | Suggested contact |
|---|
Mistyped emails
Families whose email address was typed wrong at checkout, or refused by HubSpot, as the platform reports them. Each open row says what its flag means, where the platform gives a reason we know. Ring them, check the address, and fix it on the platform with Fix on the platform. Once the address is fixed (by you or by the family) the row moves to Corrected and leaves the list a week later. Nothing here sends anything.
To follow up
| Family | Email as typed | Why | Since | What they bought |
|---|
Corrected
Fixed on the platform. Each row leaves this list seven days after its correction.
| Family | Email now | Why | Corrected | What they bought |
|---|
To Enrol
The enrolment desk's queue — one row per student, from structured
rows instead of the Onboarding Info blob the team used to read and retype. Filter
by state, watch the ageing, and move a record with the status picker on its row.
Every move is stamped with your name; nothing else in the system can write an
enrolment status.
HubSpot's To Enrol view stays the desk's working
surface until whoever works it signs this screen off — parallel running, both
reachable.
| Student | Parent | Status | In state | Subjects (tier · board) | Year | Delivery | Payment | Onboarding | Move to |
|---|
Chat Facts
Details parents told us on WhatsApp — year group, subjects, exam board — read out
of the conversation and waiting for your approval. Nothing here has been
written to a record. Approving fills the field; rejecting discards it.
Only empty fields are ever proposed, so approving can never
overwrite something already known.
Where a family has more than one child, the row asks
you which one it belongs to rather than guessing.
| Contact | Field | Value | What they said | Which child | Decide |
|---|
Contacts
All contacts, in the same saved views, columns and order as HubSpot.
CRM Sync
A read-only local copy of HubSpot contacts. Nothing here writes to HubSpot, and nothing else in the app reads from this mirror yet — this exists purely to prove it stays accurate before anything is built on top of it. An incremental sync runs automatically every 15 minutes.
One-time backfills
Full backfill re-pulls every contact (~20k — takes several minutes) and runs automatically once on the first sync after a fresh deploy. Lead-status history backfill pulls HubSpot's full status-change history for every contact already in the mirror — needed once for sales attribution, since that history can't be reconstructed after the fact. Both are safe to re-run.
Segment membership — defined vs counted
Two different timestamps, shown separately on purpose. Defined is when the segment's definition was last written; counted is when its membership was last materialised. HubSpot's list page shows only the first and labels it "Last updated", which is how a list defined in June and recounted continuously reads as three months stale. Nothing here can be misread that way.
| Segment | Priority | Members | Defined | Counted |
|---|
Event log coverage
The sixteen named events reports 6–12 read. A name at zero is either not wired yet or wired and silent — the difference matters, so every name is listed whether or not it has ever fired.
| Event | Count | Last seen |
|---|
Recent sync errors
WhatsApp via Meta — inbound shadow (Phase 3a)
Our own Meta app is subscribed to the same WhatsApp number as HubSpot's, which Meta permits — so both receive every inbound message. This side is read-only: it logs what arrives and sends nothing, because a reply from here would double up on the one HubSpot already sends. The point is to watch that what we receive matches HubSpot's before anything depends on it.
Last 20 messages received via Meta
What our own webhook actually stored, newest first — the eyeball check that the inbound counts above describe real messages. Cross-reference a row against HubSpot's inbox to confirm both sides saw the same thing.
| Received | From | Message |
|---|
Message templates
Mirrored from Meta's Message Templates API. Category is the cost lever — Marketing templates are billed per message, Utility ones are free inside an open 24-hour service window — so this is the visibility needed before any volume moves across.
| Template | Language | Category | Status |
|---|
Template outreach — contacts with no WhatsApp thread
Roughly a quarter of any session's attendees have never messaged us, so the codes and the assessment question have nothing to reply into. This queue collects them, and the free-assessment entrants with no WhatsApp thread whom the codes batch admits (each row names its rule: attended, or entered the free assessment during the session); a pre-approved template opens the conversation, and the moment they reply every normal send path takes over. One template per contact per kind, ever — no repeats to people who don't respond. Nothing sends until a template is picked and the switch below is on.
| Contact | Kind | Session | Status | Detail |
|---|
Segments
The lists this portal holds, mirrored from HubSpot.
| NAME | SIZE | TYPE | OBJECT | LAST UPDATED | CREATOR | USED IN (COUNT) |
|---|
Marketing Email
Every marketing email, from the HubSpot export. Read and preview now; editing lands next.
| EMAIL NAME | SUBJECT | TYPE | LAST UPDATED |
|---|
Our templates — editable copies and AI-made drafts. Nothing here sends anything.
| NAME | SUBJECT | BLOCKS | LAST EDITED |
|---|
Forms
The HubSpot forms feeding the lead flow, mirrored. Submission analytics stay in HubSpot for now — this page will not show a zero it cannot stand behind.
| NAME | STATUS | TYPE | LAST UPDATED |
|---|
Calls
Every call — the AI dialler's and the team's — in one place.
| CALL TITLE | ACTIVITY DATE | CALL DIRECTION | CALL OUTCOME | ASSIGNED TO |
|---|
Dialler Queues
HubSpot's six dialler lists, worked out again from the CRM's own copy of HubSpot and refreshed every 5 minutes. HubSpot's own lists stay authoritative — the scheduler is not pointed at these yet. The diff below is exactly what "watch it converge" means: once every segment's drift stays at zero for a few days, the scheduler can safely switch over.
| Segment | Priority | Local count | HubSpot list | HubSpot count | Matching | Only local | Only HubSpot |
|---|
Agent segment assignments
Which segments each agent works, highest-value first (lower number = worked first). The scheduler queues from these local segments — trigger lists are legacy and no longer read — so this IS the live routing once an agent's automation is switched on by a human. The legacy six trigger-list agents stay off with no segments; only agents assigned segments here (e.g. the seeded Cold Outreach / Session Agent / Closer) can dial.
Read Source
Five kinds of read can come from the CRM's own copy of HubSpot instead of HubSpot, each behind its own switch. HubSpot stays the system of record for every write regardless of these flags — flipping a function to "local" only changes which side application code reads from, and can be rolled back instantly. Verify with the compare tool below before flipping anything real.
| Function | Current source |
|---|
Compare against HubSpot
Runs BOTH implementations for the same input right now, regardless of the flag above, and diffs the results. Use this to verify a function before flipping it.
Attendees
Every attendee gets the free-assessment link automatically once they are marked attended — the page asks the year group itself, so nothing here asks for one. The rows below are the older year-group review list (before 2 Sep 2026), kept readable and still approvable by hand. The button re-runs the link sweep for a session.
| Who | Assessment(s) to tick | Summary (AI) | Full chat & notes | Status |
|---|
After the session — questions & concerns
Every attendee with a live WhatsApp conversation, triaged: what they raised after the session, whether their last message is still unanswered, and how urgent it looks. Unanswered and high-priority rows sort to the top. Tick a row off once you've dealt with it — if they message again afterwards, it reopens on the next build. Covers WhatsApp only: an attendee who has never messaged us can't appear here.
| Who | Unanswered | Priority | Topic | What they raised (AI) | Last message |
|---|
Configurations
Things you set once and that then run in the background — no separate tab needed for each.
WhatsApp Automations — master switch
One switch for every automated WhatsApp message the app sends: offer codes, the nightly bump, reschedule confirmations, assessment asks, unknown-sender chases, template outreach, and the property writes that make HubSpot send. Replies a person types in the Inbox (and manual resends from the dashboard) still work while this is off — use Pause ALL outbound on Automations to stop those too.
Send Guard
The anti-spam layer added after the 5 Aug Meta restriction. Every app send obeys it: Dead leads are never messaged, paying customers never get marketing, volume above the caps trips the outbound kill switch, and any run wanting to message more people than the batch threshold waits here for an approval before a single message goes.
Caps are ceilings against runaways, not targets. Hitting the hourly or daily cap pauses ALL outbound contact until someone turns it back on from Automations. "Per contact" counts texts and WhatsApps to one person; emails to one person are counted separately against their own cap, so the session emails never use up the night's codes messages (and the other way round). Session-roster sends (reminders to tonight's booked, codes to tonight's attendees) skip the approval click up to the auto-limit; anything larger, or any other bulk run over the threshold, still holds for a yes.
Email sending (Resend)
The Phase 4 email send path — the foundation the nurture / no-show / attended ladders will run on. Off by default and it has never sent anything. Every send is ledgered before it goes, checks the person and address suppression stores, obeys the outbound kill switch and the Send Guard caps above, and carries one-click unsubscribe headers. The daily cap follows the domain-warming schedule: a brand-new sending domain that bursts gets junk-foldered for everyone, so the cap only rises week by week.
Setting the warming week snaps the daily cap to that week's ceiling (50 → 200 → 500 → 1,000 → 2,000 → 3,500 → 4,000); type a cap to override it downwards. Hold or step back a week if bounces pass 2% or Gmail Postmaster spam passes 0.1%. Nothing sends until the switch above is on and the founder has approved the Week-1 warming audience.
Session emails
Two emails to parents who came to an Introduction Session and have not bought. The first — the session notes and the night's 20% code — goes as soon as tonight's register has landed. The second — the code again, with the Get started button, the notes and the class timetable — goes at the time below to anyone who still has not bought (never sooner than 15 minutes after their first email, if a late register made that one late). Customers, Dead leads, cancelled bookings and anyone who has opted out are always skipped, and every email goes through the email switch, the Send Guard and the warming cap above. While either switch is off, nothing sends: each session night writes a dry report below instead, saying who it would have gone to.
Page patterns: {{date}} becomes the session's London date (YYYY-MM-DD), for example https://app.mobiletutors.co.uk/sessions/{{date}}/notes for the notes page. The platform hosts one recording page today, not a page per session date — leave Recording page pattern blank and use the plain Recording link above.
Variables: {{firstname}}, {{notes_link}}, {{code20}} (the night's code, read live at the moment of sending), {{get_started_link}}, {{timetable_link}}, {{sample_link}} (the sample assessment link above), {{headline}} (E1's headline, chosen by the rule that admitted the parent), {{opening}} (E1's first sentence, chosen the same way), {{recording_line}} (on its own line), {{recording_link}} (only inside the recording line) and {{expires}} ("tonight at 11:59pm"). Both emails must keep {{code20}}. An email will not send while it uses {{notes_link}} and no notes link is set (both default emails carry the notes button), or while it uses {{sample_link}} (E1's sample assessment button) and no sample link is set. The unsubscribe line is added to every email automatically.
E1 — notes and code
E2 — code reminder
E3 — the extension code (the morning after the session)
Sent at the extension window's opening to attendees who have not paid; nothing while the codes switch is off.
Variables: {{firstname}}, {{code}} (the session's extension code, read from the platform when the email is sent), {{expires}} (the expiry words, from the platform's end of the window), {{get_started_link}} and {{notes_link}}. Both bodies must keep {{code}} and {{expires}}; E3 never mentions the night's code or its midnight.
Recent nights
One row per session night and email. Dry = a switch was off: who it would have gone to, nothing sent. Live = sent, with anyone held back (a cap, the kill switch, a batch approval) tried again every two minutes. Missed = the night ended with people owed an email that never went.
Sent, opened, link clicked
Opened and Recording clicked are upper bounds, not proof: some mail apps report an open without the email being read, and a link preview or mail security scanner that fetches the link counts as a click, so neither shows who watched the recording. Notes clicked and Get Started clicked are upper bounds in the same way: they show that a link was fetched, never who read the notes or started. Sample clicked is an upper bound in the same way.
Non-attendee email
One email to parents the register marked as not attending an Introduction Session, at the time below on the next day: the recording, the session notes, and a personal link that opens the booking page with their details filled in and locked, so they only pick a new session and confirm (the link works once and for 14 days). It goes once that session's register has landed, never from 21:00, and once per parent per session. Anyone who has rebooked since, came to a later session, has paid or is a customer, is a Dead lead, or has opted out is always skipped, and every email goes through the email switch, the Send Guard and the warming cap above. While the switch is off, nothing sends. When it goes on, switch off HubSpot's two "Webinar Not Attended" flows (the email and the WhatsApp) in the same hour, so no parent gets two follow-ups.
Variables: {{firstname}} ("there" when blank), {{rebook_link}} (the parent's personal rebook link, exactly once: the one main button), {{recording_link}} and {{notes_link}} (the recording and the notes for the session they missed, from the Session emails card above). The unsubscribe line is added to the email automatically.
The email
The test sends the SAVED email to the office address above, with the subject starting "[TEST for the office, sent to nobody else]", sample data and the plain booking page in place of a personal link.
Recent days
One row per missed session (the day after it): the parents the register marked absent, how many were sent, held for the next try, gone (a HubSpot card deleted or merged away, missing on two reads two minutes apart: left out that evening), blocked or opted out, and why the rest were skipped. Nothing is judged while the switch is off, so no row appears until it is on.
Sent, opened, link clicked
Opened and every Clicked column are upper bounds, not proof: some mail apps report an open without the email being read, and a link preview or mail security scanner that fetches a link counts as a click.
Abandoned-cart emails
Two emails to parents who leave the website's checkout without paying, from Emily. Email 1 goes 30 minutes after the basket was last changed; if that falls in the quiet hours below, it waits until they end. Email 2 goes on the third London day after the basket, from the time below, only if Email 1 went. Everything is checked at the moment of sending: anyone who has paid, is in the free-assessment flow, is a customer or Dead, is set to non-marketing or has opted out is skipped. Email 2 also skips anyone who clicked Email 1's sample button, and is held for good for anyone a person has talked to since Email 1 (a reply, a WhatsApp from the reply screen, or an answered call). While either switch is off, nothing sends.
Variables: {{firstname}} (the parent's first name; the greeting reads "Hi there," without one, and the subject with no first name is used), {{get_started_link}}, {{sample_link}} (the free-assessment page; when a parent clicks, the CRM adds a one-off code so the page fills in their name and email, never the name or email itself) and {{whatsapp_link}} (a chat with our WhatsApp number and the message already typed). Each link appears exactly once in each email. The word "account" is refused in both emails. The unsubscribe line is added to every email automatically.
Email 1 — 30 minutes after checkout
Email 2 — day 3
A test send goes only to the saved office test address, with the subject starting "[TEST for the office, sent to nobody else]", the sample first name and the plain links (no code, nothing counted). It still needs the email switch above to be on.
Decisions in the last 30 days
One row per email, decision and reason, counted per basket. Sent and Held are final. Skipped is judged again at every tick, so a basket shows its latest reason. Failed is tried again.
Sent, opened, link clicked
Opened and every Clicked column are upper bounds, not proof: some mail apps report an open without the email being read, and a link preview or mail security scanner that fetches a link counts as a click. Sample page filled in counts the free-assessment page reading the email's code.
Offer Message
Discount codes sent on WhatsApp after each Introduction Session: automatically to every attendee once the register lands (21:00 at the latest), and drafted as an internal note for anyone who messages "offer" so a person can send them straight away. The button below is only needed to send again by hand. Anyone who is already a customer, has no WhatsApp chat, or already has the codes in their chat — including a copy sent by a person in HubSpot — is skipped.
On in production (user, 5 Aug: session-night post-session sends stay automatic). Runs itself once the platform's register has landed for the night (every verdict applied), straight after the assessment link sweep, with 9pm as the net if the register never lands. With it off, the batch becomes the Preview + Send buttons below and the 9pm backstop and late-attendee sweep go dormant — somebody has to remember to press it (the 29 July failure).
An "offer" message gets the codes drafted as an internal note on the WhatsApp thread — copy it into a reply in HubSpot to send. The app never sends them itself in reply.
The code drops in wherever {{code20}} appears, and {{expiry2}} becomes the right expiry day automatically — two days after the Introduction Session. Preview shows the exact final message before anything is sent. Send the same evening: WhatsApp only allows replies within 24 hours of a contact's last message.
Only for a parent who came tonight while still booked on a later session: the CRM quietly takes them off that later session (no cancellation message, no call-back) and adds this one line to their codes — its own paragraph above "Thanks." on WhatsApp, the last line of the text message. Must keep {{later_day}}, which becomes the later session's day, like "Wednesday 30 September". Plain characters only: it goes by text message too.
Send to a session's attendees
Assessment Message
The free-assessment link every attendee gets on WhatsApp once they are marked attended, and anyone who messages "offer" on the night. One link per family, ever — sent once, whatever day it reaches them. The codes message above still goes as well.
{{code_block}} on its own line switches the code line on; take it out of the message and the code line is off. {{firstname}} and {{link}} are filled in for each parent, and {{link}} must stay in.
Must keep {{code20}} — the night's 20% code is read live at the moment of sending. The line appears only on the parent's own session night, on WhatsApp, and only to parents the codes are for (never a customer or a cancelled booking); on any later day it is left out and the message still goes. The live code rotates Thursday 01:00 → Monday 01:00, so "expires tonight at 11:59pm" is true on the night the line can appear.
Bump Message
A light "any final questions?" nudge sent automatically once per session, at the time below (UK, same evening as the session), to anyone who attended and hasn't paid yet. Customers and Dead contacts are always skipped. Runs on its own — no button needed for future sessions.
{{firstname}} drops in automatically. WhatsApp only allows replies within 24 hours of a contact's last message, same as the offer message above.
Manual send for a session
Only needed to send early, or re-send a session the automatic worker already skipped past. A session it has already processed can't be sent again from here.
Assessment Mapping
HubSpot's Send Assessment property has exactly one checkbox per row below — confirmed live against HubSpot, there's no separate box for, say, Year 3 vs Year 4. So whenever a child anywhere in that range is mentioned, the AI ticks that one box; it doesn't need to pick a specific year within the range. The rightmost column is that checkbox's internal HubSpot value — it's a legacy name that doesn't match its own displayed label on purpose (e.g. the box labelled "KS3" is internally called "Year 7"). Only change it if the property itself changes in HubSpot.
| Year group | Covers | HubSpot checkbox (legacy value) |
|---|
New Enquiry Auto-Reply
The only place in the app where a message written by the AI is sent to a customer without anyone reading it first. It applies to one narrow case: the very first WhatsApp message from a number with no contact card, that reads like a tuition enquiry. They get the Introduction Session reply with the next two real dates; if they pick one, they're asked for their name and email, and their answer creates the contact card. Anything else — a complaint, a refund, anything legal or about safeguarding, a long or specific message, a real question, or anyone already on a contact card — is drafted for a human exactly as before.
Off by default. With this off, new enquiries are drafted for a human to send, which is how every WhatsApp thread worked before 3 Sep 2026.
Added to the prompt that writes the first reply. It cannot loosen the safety checks: a reply is still refused, and drafted for a human instead, if it states any price, fee, discount, guarantee, qualification or policy, contains a link, or uses any number other than the two session dates.
Messages sent automatically
Every unreviewed message this feature has sent, newest first. Worth reading regularly while it is switched on.
| When | Thread | Stage | They said | We sent |
|---|
Unknown Sender Messages
For WhatsApp numbers with no contact card that ask for the offer codes or the assessment on a session night. The identification ask below goes out first (the codes follow it straight away — they're never gated on who the person is), and anyone who then goes quiet gets the chase message once, the same evening only. When they reply with their details the contact card is created and the normal assessment flow takes over.
With this off, unknown numbers get nothing automatically — their messages still appear on the Inbox's Unknown page for a human to work by hand.
The chase goes out at most once, and only on the same UK evening as the ask — past midnight the moment is gone and nobody is chased. Minimum 5 minutes.
Payment messages
Two WhatsApps to a paying family whose renewal payment failed that morning — the platform emails them too. The first goes in the morning; the second about an hour before that evening's lesson, saying the child can't join until it's sorted. Only on the day of the failure, only while it is still unpaid, and never to a Dead or opted-out contact; nothing on later days. Off until you switch it on — while it is off, the table below shows what each family WOULD have got.
SMS: the same two texts by SMS, UK mobiles only, while Meta withholds WhatsApp sending.
A family who hasn't written to us in the last 24 hours can only be sent a template Meta has APPROVED, so each message is a template name, picked from the templates synced from Meta (CRM Sync → Message templates → Sync from Meta). Nothing sends until the chosen template is APPROVED for the language below.
A morning text on a day with no lesson the child cannot join leaves the join sentence out: when the platform names no lesson for that evening, the family is told only that the payment did not go through and where to update their card. By SMS that text needs nothing more; on WhatsApp it waits for its own approved template, chosen above, and is held until then.
The platform sends each family's own link to update their card, and the message carries that. This link is the fallback; with neither, that family's message is held.
A failed charge is tried between 05:00 and 06:00, so the morning message waits for this hour (8 = from 08:00; 0 = straight away). The evening message always goes about an hour before the lesson. The CRM checks every 5 minutes and messages at most the number above in one check (the rest wait for the next); more than the send guard's batch limit in one check waits for an admin's approval on Send guard.
| Message | Channel | Decision | Reason | Families |
|---|
WhatsApp after 24 hours
WhatsApp lets us answer a family freely only for 24 hours after they last wrote. After that only a message Meta has approved may go. With this on, a person can send ONE approved message from the family's row on Needs Reply, at most once a day to each family, between 08:00 and 20:00. It invites them to reply: it is not an answer, so the family stays on the list until someone answers them. Off until all three locks below are open.
This message goes to WhatsApp straight from the CRM, not through HubSpot, so HubSpot's inbox does not show it. When the family replies, HubSpot shows their reply on its own, without the message that invited it. The family's record in the CRM shows both: the approved message (who sent it and its words) and their reply.
WhatsApp Reply Drafting
For genuine customer questions the other flows above don't handle — every reply is posted as an internal HubSpot comment for a team member to review, edit and send; nothing is ever sent to a customer automatically.
Only for figures that have actually gone wrong in front of a customer. These override the knowledge base, and any number in here is accepted by the figure guard — a draft stating a number that appears in neither these, the knowledge base, nor the conversation is withheld rather than posted.
Discount questions
A discount or price question gets a suggested draft like any other message, written from the knowledge base's prices and labelled for a person to check before sending; a draft never gives a code. The one exception: a recent Introduction Session attendee asking about the discount gets one of the two fixed sentences below as a draft, pointing at the text that carries the code. Never type a code here; {{firstname}} is the only variable.
Learned corrections
Captured automatically when a team member edits a suggested reply before sending — recent examples are fed back into later drafts so they match the team's own voice. Delete anything that isn't a genuine correction (e.g. an unrelated templated message that happened to follow a draft).
| Customer said | Drafted | Actually sent | When |
|---|
AI drafts
AI drafts on the reply screen. Off: no drafts. Trial: drafts for the people you tick, made when they open a family, beside the reply box. On: drafts in the reply box for everyone. A person always presses Send — nothing is ever sent by itself.
Loading…
Needs Reply
Every family waiting on a reply from us on the portal chat or WhatsApp, the longest wait first. Answer them in the list: the answer goes where they last wrote.
Everyone waiting on a reply
| Waiting since | Who | Their last message and your reply |
|---|
One row per family per channel, the longest wait first: they wrote on the portal chat or WhatsApp and nobody has answered them since. Click a row, or Tab to it, to open its reply box. Not matched to a contact (the Not matched chip): a family the CRM holds no contact for (or more than one). They are still waiting and can be answered here; a Replied needs a contact record, so link the contact for that. Answer them here — on the portal chat or on WhatsApp — and the answer goes where they last wrote. When they were waiting on both, the other channel is marked answered with one line pointing to the answer ("We've answered you on WhatsApp" / "We've replied in your portal messages"); nobody gets two full replies. Attachments cannot be sent from here yet — text only. Families' emails to support@ are listed here while reading support@ is switched on (Settings → Email): answer them in Gmail, and the row clears within 2 minutes of your answer. When the CRM cannot read support@, a banner says so, and a row clears only once it reads support@ again.
Portal chat: not yet known whether these are still open
Portal conversations the CRM cannot yet say are open or answered, because it is still reading the portal's older conversations. They do not raise the late-reply alert.
| Channel | Last message at | Who | Their last message |
|---|
Automatic emails and emails from our own addresses
Counted, never listed as waiting. If one is a family, list the sender and their emails to support@ come onto the list.
Emails whose sender could not be read
The From line of these emails is not one address the CRM can read, so they cannot wait on this list or be put on a family's record. Open each in Gmail and answer it there.
Emails the CRM could not read in
Gmail or the CRM refused these emails again and again, so the CRM set them aside to keep reading the rest. It tries each again about once an hour; meanwhile open them in Gmail and answer them there.
Hidden addresses
Addresses the office marked "Not a family". Their emails are counted, never listed.
Closed by a message sent from HubSpot (person or workflow unknown)
WhatsApp conversations whose last message was followed only by a send the CRM did not make. The setting that lets such a send take the family off the list is on.
| Channel | Their last message | Closed at | Who |
|---|
WhatsApp conversations about to lose the 24-hour window
WhatsApp lets a free reply go only within 24 hours of the family's last message. These conversations have had no message of any kind from us for 18 hours or more, so under six hours are left. This table has its own rule: any message sent to them on WhatsApp takes a conversation off it — a person's, one sent from HubSpot or an automated one — and pressing Replied does not.
| Time left | Contact | Number | Their last message | Received |
|---|
Callback Requests
Inbound WhatsApp messages are checked every 10 minutes for callback requests and buying signals. Pick an agent next to a lead, then press Call to dial them back. This is a queue of detected signals, not a segment of the Inbox — the same person can appear here and in Active Leads.
| Detected | Contact | Intent | Lead status | Wants call at | Summary |
|---|
Reschedule
Anyone who messages on WhatsApp saying they missed or can't make an Introduction Session — whether that's minutes before it starts or days after they no-showed. They're automatically rebooked into the next available session (or their requested date, if they named one) and sent a WhatsApp confirmation signed Emily. Turn automation off below to review each one by hand instead.
| Detected | Contact | Previous session | Message / summary | Status | Confirmed |
|---|
{{firstname}} and {{new_date}} fill in automatically — new_date matches exactly what's written to the contact's HubSpot record. The right message is picked automatically by comparing when they messaged to the UK calendar day of the session they were originally booked into. The rejoin message uses {{meet_link}} instead of {{new_date}} and never changes their booking. The ask-which-date message uses {{dates}} ("Thu 4 Sept at 7pm, Sun 7 Sept at 7pm or Tue 9 Sept at 7pm") and must keep that placeholder.
SMS Session Reminders
Text reminders for the Introduction Session, sent from the Twilio number. Only contacts whose lead status is exactly Webinar Booked and who are booked into the session are ever messaged — Paid, Dead, opted-out and unrecognised statuses are excluded by construction. Each rung below has its own switch; a rung that misses its window is recorded as missed, never sent late.
Test with your own number
Sends ONE real SMS to the number you type — the exact message a recipient would get, with tonight's time, link and the live discount codes filled in. Logged in the Message Log as a test. Nobody else receives anything.
SMS Inbox
Every reply that comes back to the SMS number. Nothing is dropped: a normal reply also files a task, and a STOP suppresses that contact from every future send. What went out is on Message Log.
Inbox — conversations
One row per person — their texts grouped into a conversation, newest first. Open a row to read the whole back-and-forth (theirs and ours) and reply in place. A STOP names the exact message that opted them out; replies to that number are refused.
| Latest | From | Conversation | Handling |
|---|
Reschedule asks
Replies that read like "can't make it / missed it / can I have another date" — anyone paid or closed off never appears here. Attendance says whether they made the session the ask is about, so nobody chases a parent who turned up. Two ways to answer: Ask which date texts them the next three sessions and books nothing until they reply; Move & text books the chosen session and sends the same confirmation the WhatsApp reschedules send.
| When | From | Message | Booked session | Attendance | Status | Confirmation | Actions |
|---|
SMS Message Log
Every SMS this system has sent — the exact rendered text, Twilio's delivery verdict and the cost — broadcast by broadcast and message by message. Replies are on SMS Inbox.
Campaign delivery
One row per broadcast: delivery, link taps, replies, and who actually attended.
How these columns are counted
SMS has no read receipts, so there is no true open rate — "delivered" (the handset accepted the message) is the closest measurable thing, and "clicked" (they tapped the session link in the message) is the real engagement signal. The delivery rate counts concluded receipts only; messages still awaiting their receipt sit in their own column instead of dragging the rate down. Click rate is over everything sent; a campaign whose message has no link shows "—". Replied is a text back (not STOP) within 48 hours of the message; STOPs are shown alongside. Attended reads the session's register: how many of the people sent this message turned up, and — the number that says whether a reminder did its job — how many of those who tapped the link turned up. Both show "—" until the register exists.
| Campaign | Session | Sent | Delivered | Not delivered | Awaiting receipt | Delivery rate | Clicked | Click rate | Replied | Attended | Tapped → attended | Cost |
|---|
Sent
| When | To | Type | Session | Message | Length | Status | Cost |
|---|
Introduction Sessions
Where each Introduction Session stands: bookings, confirmations, and how the confirmation round went. Click a session row for the named list — who’s confirmed, who needs a person, who hasn’t been reached.
How these columns are counted
Booked now is who currently holds a place (a past session’s number decays as people are rebooked — the confirmed and can’t-attend columns are that session’s permanent record). Confirmed counts the session’s ledger, split into its two main channels: Via WhatsApp is parents who replied to a WhatsApp reminder, By dialler is confirmations the Session Confirmation agent took on a call. The two won’t always sum to the total — the remainder confirmed by SMS reply, through the HubSpot reminder flow, or by hand. The call columns cover the Session Confirmation agent’s round for that session. In the expanded list a contact appears in exactly one group — the ledger outranks their latest call.
| Session | Booked now | Confirmed | Via WhatsApp | By dialler | Can’t attend | Contacts called | Answered | Under 10s | Unreached | No answer | Hostile |
|---|
Call routing
Where a live call's human transfer connects. Paid contacts go to customer service; everyone else — including a Dead contact who calls us — goes to sales, split by the percentage weights across the salespeople in schedule right now. When nobody is in schedule the transfer falls back to the landline, and a failed transfer still files the callback task.
Loading seats…
Recent transfers
The last 7 days of routed transfers per person — the same ledger the weighted split is judged against, so this is the fairness the pick actually sees.
| Person | Role | In schedule now | Weight | Transfers (7d) |
|---|
Change history
Every routing change, ledgered — who, when, old → new. Rows are never deleted.
| When | Who | Seat | Field | Old | New |
|---|
SMS Configuration
Shared settings for every SMS the system sends. The join link below is the
shared Zoom room — do not blank it. Reminders prefer each parent's own
personal join link and fall back to this one, so {{link}} always resolves
to somewhere that admits them.
Instagram post comments and DMs, answered by the AI. In draft mode every reply waits here for your approval — edit, send, or dismiss. Switch a surface to auto once you trust its answers.
AI instructions (shared by Instagram and Facebook)
Facebook page comments and DMs, answered by the AI. In draft mode every reply waits here for your approval — edit, send, or dismiss. Switch a surface to auto once you trust its answers.
AI instructions (shared by Instagram and Facebook)
Integrations
Loading…
Background jobs
Every recurring worker records a heartbeat. Stale means it hasn't completed for several of its intervals — either the worker is dead or every run is erroring; failing means recent runs are erroring. Either one is the silence that hid the 24 Jul drafting outage and would otherwise go unnoticed again.
| Job | Cadence | Last success | Last error | Status |
|---|
Scaling limits
What stops us doubling the volume tomorrow, and whose limit it is. Theirs is a supplier's ceiling — a failure here is not the dialler failing, and no amount of debugging our code will move it. Ours is a cap we set on purpose; raise it deliberately when we want more. Unknown is a real ceiling nobody can currently read, which is the dangerous kind — it gets discovered live, on a call, by a person.
| Constraint | Whose limit | Ceiling | Now | Status |
|---|
AI contact owner
When the AI calls or messages a lead that nobody owns, it sets this HubSpot user as the contact owner. Leads already owned by a team member are never taken over.
Seller assignment — attended, not paid
When a lead attends an Introduction Session and hasn't paid, give them an owner. Leads already owned by a team member are never taken over, and deactivated HubSpot users are skipped automatically.
| When | Lead | Outcome | Assigned to |
|---|