Student Onboarding — Operations Runbook
Audience: ops team running onboarding (no code required). Scope: getting students into TAP LMS via the two live paths, monitoring a run, and recovering failures. Last updated: 2026-06-26 Companion doc: Student Onboarding — System Architecture (how it works under the hood).
One-line mental model. Two ways in: Scratch = students self-register over WhatsApp by sending a batch keyword that starts the flow (real-time, one at a time). Backend = you bulk-upload a roster and the system creates everyone in the background (two phases: database, then WhatsApp/Glific), then you trigger the WhatsApp flow for the whole group.
What counts as "registered." Creating the student record is not the finish line. A student is only fully registered once they start interacting with the TAP Buddy WhatsApp bot. Your job isn't done when the set says "Processed" — it's done when students are actually responding to TAP Buddy. Everything below builds toward that.
1. Before you start — know which path you're running
| Scratch (self-registration) | Backend (bulk) | |
|---|---|---|
| Who triggers it | The student, by clicking a WhatsApp link | You, from a Desk page |
| Volume | Trickle, continuous | Hundreds–thousands at once |
| Your job | Set up the keyword + batch, then watch the numbers | Upload roster → process → monitor → reconcile |
| Where students can be lost | They abandon the WhatsApp flow, or hit an error and don't retry | A student is created but the WhatsApp (Glific) link fails to sync |
If someone says "onboard these 1,000 kids from a spreadsheet," that's Backend (§3). If you're enabling a school to let students sign themselves up, that's Scratch (§2).
2. Running the Scratch (self-registration) path
You don't "run" scratch per student — you enable it by configuring the batch and its keyword, then monitor.
2.1 Setup checklist (per batch)
- Batch exists and is open. In Batch, confirm
activeis checked andregist_end_dateis in the future. If the batch is inactive or the date has passed, every registration is rejected ("The batch is not active" / "Registration for this batch has ended"). - Batch keyword exists. In Batch onboarding, there is a row linking this batch + school, and it has a
batch_skeyword. The keyword is generated automatically when the row is created. This is the keyword baked into the WhatsApp link. - Course levels exist for every grade × vertical combination students will pick. A missing mapping makes registration fail with "Course selection failed…". If in doubt, ask the tech team to confirm the Grade Course Level Mapping covers the grades/verticals for this batch.
- Languages exist. Each language students can choose must exist in TAP Language, else "No TAP Language found…".
2.2 What a student experiences
Clicks link → Glific asks for name, phone, gender, grade, language, vertical → on submit they either get a success message with their student ID or an error with a retry option.
2.3 Monitoring scratch
- Registrations landing: new Student records with
joined_on= today and an enrollment on the batch. - Already-registered check:
get_batch_keywords_by_phone(used inside the flow) tells whether a phone is already in a batch. - Caveat — limited visibility. The scratch endpoint does not keep detailed logs, and the "started but didn't finish" funnel lives only in Glific's own analytics, not in TAP LMS. To answer "how many dropped off mid-registration," pull Glific flow analytics — TAP LMS can only show who completed. (See architecture §6.)
How to fix this (in order of effort):
1. Today, no code — use Glific. In Glific, open the registration flow and read its analytics: how many entered the flow vs how many reached the end node. The drop between them is your mid-registration churn. Export per-flow stats periodically to track it over time. This is the only place the "started" count exists right now.
2. Small tech request — log every create_student call. Ask the tech team to restore durable logging on the scratch endpoint (it was stripped out): record each call with outcome (success / which error) to a queryable log or a lightweight "registration attempt" record. That turns the error side of the funnel (invalid keyword, batch closed, course-level miss) into something you can count and chart in TAP LMS instead of guessing.
3. Proper fix — capture a "first TAP Buddy interaction" event. The durable answer is an inbound webhook from Glific that records when a student first responds to TAP Buddy, written to TAP LMS as an event. That single signal is what makes completion (and therefore churn) measurable end-to-end for both paths — it's the well-built version of the tracking that was scrapped (architecture §4.4, §6). This is a build project, not an ops toggle.
2.4 Common scratch errors and what they mean
| Message the student/flow sees | Cause | Fix |
|---|---|---|
| Invalid API key | Glific flow misconfigured | Tech team: check the API key in the Glific webhook node |
| All fields are required | A field wasn't collected | Check the Glific flow collected all 6 inputs |
| Invalid batch_skeyword | Wrong/old keyword in the link | Re-issue the link with the current keyword |
| The batch is not active | Batch.active unchecked |
Re-activate the batch |
| Registration for this batch has ended | regist_end_date passed |
Extend the date if registration should continue |
| Invalid vertical label | Vertical name mismatch | Confirm the vertical label matches Course Verticals |
| No TAP Language found… | Language missing | Add it to TAP Language |
| No matching course level / stage | Course-level gap | Tech team: add the Grade Course Level Mapping |
3. Running the Backend (bulk) path
This is the main operational task. It has two phases and a run is not finished until both complete.
3.1 Step 1 — Create the onboarding set
- TAP LMS → Backend Student Onboarding → + Add Backend Student Onboarding.
- Give it a Set Name, add the student rows (or import via Data Import), Save.
- Keep sets to ~1,000 students each (the throughput work was tuned around 1,000-row sets; on a CSV import, put
Set Nameon the first row only).
Each student row needs: name, phone, gender, school, grade, language, batch (and batch_skeyword / vertical so the course level can be resolved).
Data gaps are normal here. Unlike scratch (where TAP Buddy collects everything in conversation), bulk rosters often miss fields — commonly gender or language. The bot can collect/confirm some of these when the student interacts later, but a missing field that the course-level routing depends on can stall a student. Before processing, sanity-check that the fields driving routing (grade, vertical, batch) are present; the conversational fields (gender/language) can be filled later but are better supplied up front.
3.2 Step 2 — Process the set
- Go to the
backend_onboarding_processpage. - Select your Onboarding Set from the dropdown (shows Draft/Processing/Failed sets).
- Click Start Processing, then Confirm.
- A background job starts. The page shows a progress bar (it polls job status).
Permissions: only a TAP Admin can start processing. Concurrency: the system blocks starting the same set twice while a job is active — if you get a "job already active" message, it's already running; don't re-trigger.
3.3 Step 3 — Understand the two phases (what "done" means)
| Phase | What happens | Speed | Field that tracks it |
|---|---|---|---|
| Phase 1 — Database | Creates the Student + enrollment + progress state. No WhatsApp yet. |
Fast (thousands/min) | Backend Students.processing_status: Pending → Success / Failed |
| Phase 2 — Glific (WhatsApp) | Creates/links the WhatsApp contact and adds to the group. | Slow (network-bound) | Backend Students.glific_sync_status: pending → synced / failed |
A set's status goes Draft → Processing → Processed (all Phase-1 succeeded) / Failed / stays Processing (partial). But even at Processed, Phase 2 may still be running or have failures. Phase 2 is what creates the WhatsApp contact and adds every student to the set's Glific collection (group) — that collection is what you'll trigger the flow against in the next step. Phase 2 is complete only when glific_sync_status is synced for everyone (a few failed are expected and reconcilable).
Even then, the students are only reachable, not yet registered. Registration completes when they respond to TAP Buddy (Step 5).
3.4 Step 4 — Monitor a run
- Progress bar on the processing page (Phase 1).
- Set record:
student_countvsprocessed_student_count. - Phase-1 failures: filter Backend Students where
processing_status = Failed; theprocessing_notessays why. - Phase-2 failures: filter Backend Students where
glific_sync_status = failed— these students exist in the LMS but are not yet reachable on WhatsApp. This is the churn-risk bucket; see §4. - Errors: Error Log (in Desk) carries structured entries for dead-lettered Glific syncs.
3.5 Step 5 — Trigger the TAP Buddy flow (in Glific, by collection id)
Once Phase 2 has put the students into the set's Glific collection (group), start the onboarding flow in Glific against that collection/group id so TAP Buddy reaches out. This is the step that actually begins registration completion.
- Find the set's collection / group id — it's the
group_idon theGlificContactGrouplinked to your onboarding set (also visible in Glific as the collection). - In Glific, start the onboarding flow on that collection/group.
- Watch for students replying to TAP Buddy — that's the real "registered" signal.
Note: this trigger is done on the Glific side, not from a TAP LMS page. The old in-app "Onboarding Flow Trigger" page is not the supported way to do this (§5). If a whole collection gets no replies, check the flow is published in Glific and that the collection actually has members (Phase 2 finished).
4. Recovering failures
4.1 Phase-1 failures (processing_status = Failed)
The student was not created (or only partially). Cause is in processing_notes (e.g. course-level miss, bad data). Fix the underlying data (add the mapping, correct the row), then re-run the set — re-processing only picks up rows still Pending/Failed, so it won't duplicate the ones that already succeeded.
4.2 Phase-2 failures (glific_sync_status = failed)
The student exists in the LMS with a valid enrollment; only the WhatsApp/Glific link is missing. The system already retried up to 3 times before marking it failed and writing an Error Log entry. This is safe to retry later — re-running the set re-enqueues the Glific sync for pending/failed rows only, and it will not create duplicate contacts (it looks the contact up first). If a whole batch failed Phase 2, suspect a Glific outage or rate-limiting (429) — wait, confirm Glific is healthy, then re-run.
4.3 When to escalate to the tech team
- Many Phase-2 failures at once, or the Error Log shows timeouts → likely Glific-side; if a background worker appears stuck/unresponsive, escalate immediately (a hung Glific call can wedge a worker — there is history of this; see architecture §4.3/§5).
- Course-level errors across many students → a missing Grade Course Level Mapping, not a per-student fix.
- The processing page won't start a job and no job is running → tech team checks the queue/workers.
5. What is NOT available (set expectations)
The system was meant to also track each student's journey (who started, who's stuck) and automatically send nudges and escalate over WhatsApp. That layer was scrapped because the WhatsApp/Glific calls timed out and failed unreliably, so it could never be trusted to drive nudges.
Practically, for ops this means:
- Starting the initial onboarding flow works — you do it in Glific against the collection/group id (Step 5). What's missing is the automated follow-up: there is no working "nudge the students who haven't started / escalate the stuck ones" automation for onboarding. The in-app Onboarding Flow Trigger page still exists but is not currently used — whether to bring in-LMS triggering back is a pending stakeholder workflow decision, so do not use that page for live nudging unless/until that decision is made and the tech team confirms it.
- "Who registered but never engaged" is not tracked inside TAP LMS for the onboarding stage. Use Glific analytics for the WhatsApp-side funnel.
- Automated reminders/escalation for the live cohort were rebuilt separately in the Summer Program system — a different surface from onboarding.
If nudges/escalation are needed, that's a build request to the tech team, not an ops toggle. (Architecture §4.4 lists what a rebuild must handle.)
6. Quick reference
| I need to… | Where |
|---|---|
| Let students self-register | Configure Batch (active + open) and Batch onboarding (keyword); §2.1 |
| Bulk-onboard a roster | Backend Student Onboarding → backend_onboarding_process page; §3 |
| Start TAP Buddy for a processed set | Trigger the flow in Glific against the set's collection/group_id; §3.5 |
| See if a bulk run finished (system) | Set status = Processed and all glific_sync_status = synced; §3.3 |
| See if students are actually registered | Students replying to TAP Buddy (Glific analytics) — not shown in TAP LMS; §5 |
| Find students not on WhatsApp yet | Backend Students where glific_sync_status = failed; §4.2 |
| Retry failures | Re-run the set (picks up Pending/Failed only); §4 |
| Understand a registration error | Error tables in §2.4; processing_notes for backend |
| Measure mid-registration drop-off | Glific analytics (not in TAP LMS); §2.3, architecture §6 |
Related runbooks: docs/deployment-runbook.md, docs/operator-tutorial.md, and the technical companion docs/student-onboarding-architecture.md.