Skip to content

Batch Keywords — Generating & Sharing Registration Links with Schools

Audience: ops / program team setting up self-registration for a school. Scope: how a batch keyword is created (School → Batch → Batch onboarding), how it becomes a WhatsApp registration link, and how to share it with schools safely. Last updated: 2026-06-26 Companion docs: Student Onboarding — Operations Runbook §2 (the scratch path this feeds), Student Onboarding — System Architecture §2.


What a batch keyword is (and why it matters)

A batch keyword (e.g. GRSU47XQ) is the entry point for self-registration ("scratch" onboarding). A student taps a WhatsApp link that pre-fills tapschool:<keyword>, sends it, and the Glific registration flow starts — collecting their details and creating their Student record. The keyword is what tells TAP LMS which school and which batch the student is registering into.

One keyword = one Batch onboarding record = one (school, batch) pairing. Share the keyword's link with a school, and their students can self-register into that batch.


Step 1 — Create the School

In Desk: School → New.

  • name1 is the school's display name. Its first two letters seed the keyword, so a sensible name matters (e.g. "Greenfield" → GR…).

Step 2 — Create the Batch

In Desk: Batch → New.

  • name1 — batch display name (first two letters also seed the keyword, e.g. "Summer" → …SU…).
  • batch_id — the batch identifier shown in the sheet.
  • activemust be checked for registration to work and for the batch to appear in the sheet.
  • start_date / end_date — the cohort window.
  • regist_end_dateregistration cutoff. Once this date passes, registration is rejected and the batch drops out of the sheet. Set it to when self-registration should close.

Step 3 — Create the Batch onboarding (this generates the keyword)

In Desk: Batch onboarding → New.

  • school — link to the Step-1 school.
  • batch — link to the Step-2 batch.
  • kit_less — set per the school's kit status (affects course-level resolution downstream).
  • Leave batch_skeyword blank. On save, it is auto-generated.

The keyword is built as: <2 letters of school name1> + <2 letters of batch name1> + <2-digit number> + <2 random letters> — e.g. GRSU47XQ. It is collision-checked against existing keywords, so each is unique. (If you need a specific keyword, you can type one into batch_skeyword before saving and it will be kept — but auto-generated is the norm.)


For a keyword GRSU47XQ, the WhatsApp registration link is:

https://api.whatsapp.com/send?phone=918454812392&text=tapschool:GRSU47XQ

When a student opens it, WhatsApp launches with the message tapschool:GRSU47XQ pre-filled to the TAP WhatsApp number. They send it, and the registration flow begins. This link is what you share with the school — it is safe to distribute widely (it contains no credentials).

Note the tapschool: prefix and the keyword are case- and spelling-sensitive — always copy the link from the sheet rather than typing it by hand.


The auto-populating Google Sheet

A Google Sheet with a bound Apps Script fetches all currently active batches and lists, per batch: School Name, Batch Keyword, Batch ID, Registration Link. It refreshes when the sheet is opened.

It calls one endpoint (replace <tap-lms-host> with your TAP LMS base URL):

GET https://<tap-lms-host>/api/method/tap_lms.api.list_batch_keyword?api_key=<API_KEY>

list_batch_keyword returns a row only for batches where active is checked and regist_end_date is today or later. So the sheet is a live view of "what can students register into right now."

Apps Script — keep the key out of the source

Store the key in Script Properties (Project Settings → Script Properties → add API_KEY) instead of pasting it into the code. Then:

function refreshBatchKeywords() {
  var apiKey = PropertiesService.getScriptProperties().getProperty('API_KEY');
  var url = 'https://<tap-lms-host>/api/method/'
          + 'tap_lms.api.list_batch_keyword?api_key=' + encodeURIComponent(apiKey);

  var resp = UrlFetchApp.fetch(url, { method: 'get', muteHttpExceptions: true });
  if (resp.getResponseCode() !== 200) {
    Logger.log('Error: ' + resp.getResponseCode() + ' - ' + resp.getContentText());
    return;
  }

  var rows = JSON.parse(resp.getContentText()).message || [];
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  sheet.clear();
  sheet.getRange(1, 1, 1, 4)
       .setValues([['School Name', 'Batch Keyword', 'Batch ID', 'Registration Link']]);

  var data = rows.map(function (r) {
    return [r.School_name, r.batch_keyword, r.batch_id, r.Batch_regLink];
  });
  if (data.length) sheet.getRange(2, 1, data.length, 4).setValues(data);
  sheet.autoResizeColumns(1, 4);
}

function onOpen() { refreshBatchKeywords(); }

This is functionally the same as the original script, with the key moved out of the source so a sheet viewer can't read it. Anyone with edit access to the script can still see Script Properties — so still don't share the script-bearing sheet externally.


What to actually send a school

Send the school only:

  1. Their batch keyword (e.g. GRSU47XQ).
  2. Their registration link (the api.whatsapp.com/send?... URL).
  3. A one-line instruction: "Students tap this link, send the pre-filled message, and follow the prompts to register."

Do not send the Sheet, the Apps Script, or the API key.


Troubleshooting

Symptom Likely cause Fix
A batch isn't in the sheet active unchecked, or regist_end_date has passed Re-check active / extend regist_end_date on the Batch
Sheet shows an error / no rows Bad or rotated API key Confirm the key in Script Properties matches an enabled API Key record
Student gets "Invalid batch_skeyword" on registration Link has a stale/mistyped keyword Re-copy the link from the sheet; confirm the Batch onboarding still exists
Student gets "The batch is not active" / "Registration … has ended" Batch closed Re-activate the batch or extend regist_end_date (see the onboarding runbook §2.4)
Two batches collided on a keyword Shouldn't happen — keywords are uniqueness-checked on save If you hand-set batch_skeyword, ensure it's unique

Pointers