APPLICANT REMINDER AUTOMATION
Building a Controlled Batch-Reminder Workflow for Missing-Document Cases
A Google Sheets and Google Apps Script workflow that joins three client data feeds, prepares personalised applicant reminders, and sends them in one reviewed batch — with the decision to send left deliberately in human hands.
| Prepared by | Khaled Medhat — Team Leader, Case Validation |
| Environment | Google Workspace — Google Sheets and Google Apps Script |
| Status | Built August 2025 — in continuous use since |
| Users | Built and operated by me, and used by two performers who work with me |
| Date of this document | October 2026 |
A. Executive Snapshot
| Business problem | A new client in education-document verification went live without the follow-up feature the standard workflow relies on: when an applicant has not sent a requested document, no automatic reminder reaches them after the first insufficiency email. Reminders were prepared and sent by hand, one applicant at a time, from data held in three separate client exports. |
| My role and contribution | I designed the workflow end to end: the three-source join, the barcode key, the two-tab sheet structure, the personalised email template, the validation rules, the send-state controls and the three-day operating routine. I used AI as the implementation partner to write and revise the Apps Script, evaluated the generated code against my specification, tested it against live data, and have maintained the workflow ever since. The client system and the internal API were existing services; my contribution was the reminder workflow built around them. |
| Solution developed | A Google Sheets workflow with a main working sheet and an email sender tab, driven by a Google Apps Script. One barcode typed into the working sheet populates the applicant's name, address and requirement by formula. A "Send Emails" button on the sender tab sends the queued reminders as one batch, marks each row done, and writes the reminder date back to the working sheet. |
| Methods and tools | Google Sheets (tabs, formulas, button), Google Apps Script (retrieval, joins, template population, batch send, state write-back), Gmail sending with deliberate per-message pacing, three existing internal API feeds, AI-assisted development under my specification and validation. |
| Project status | Built August 2025 and still in regular use in October 2026 — roughly fourteen months of continuous operation on a three-day reminder cycle. |
| Verified outcomes | Effort per reminder cycle reduced from an estimated full employee-day of manual preparation and sending to one reviewed button press and about five minutes of machine time. A typical run sends 250–300 reminders; the pending queue ranges from roughly 150 to 500 cases. Manual-effort and volume figures are owner-reported estimates, not audited measurements. |
| Important limitations | No delivery or bounce rate was recorded, and no applicant-response rate was measured, so the business effect on case turnaround is expected rather than demonstrated. "Done" records send processing, not arrival. The client, applicants, cases and barcodes are not identified anywhere in this document, and the sheet views shown are labelled reconstructions. |
Contents
- B — Executive summary
- C — Business context and problem definition
- D — Data discovery and root cause analysis
- E — Solution design and AI-directed development
- F — Project management and implementation
- G — Preventive improvement and controls
- H — Testing and validation
- I — Business impact and performance measurement
- J — Lessons learned and future roadmap
- K — Skills demonstrated
B. Executive Summary
A client in education-document verification went live without the feature our other clients have. In the standard workflow an applicant receives an insufficiency email when we need a document they have not sent, and if nothing arrives about three days later they receive a reminder. For this client that follow-up did not exist, so every reminder was written and sent by hand.
The hard part was never the wording. It was the assembly. To send one correct reminder I needed three things that lived in three separate exports: who the applicant is and where to reach them, which of their cases is still waiting for a document, and the exact requirement that was communicated to them. The case barcode was the only thing those records had in common. Every reminder therefore started as a manual search across three files, and every one of them was an opportunity to reach the right person with the wrong requirement.
I built a workflow around that join. A Google Apps Script retrieves the three feeds into a Google Sheet, and a barcode typed into the main working sheet pulls the applicant's name, email address and requirement into place by formula — no copying, no re-typing. A second tab, the email sender tab, lists the cases to contact and carries a "Send Emails" button. Pressing it sends the whole list as one batch, writes "Done" against every row it sent, and records the reminder date back on the working sheet. A typical run covers 250–300 applicants and takes about five minutes; the work it replaces took close to a full working day of preparation and individual sending.
One part of the design I deliberately did not automate. Deciding whether a reminder is still required stayed a human decision, because a reminder that is no longer needed is a needless contact with an applicant. The script decides nothing about eligibility: I read the queue, check the last-reminder date on each case, queue the barcodes that are still due, review the list, and then press the button. Three days is the rhythm I work to, not a rule the script enforces.
The workflow was built in August 2025 and has run continuously since. It is used by me and by two performers who work with me. What it changed is the effort and the consistency of the contact, not the size of the queue — and I can demonstrate the first, while the second I can only state as an expectation until delivery and response data are captured.
C. Business Context and Problem Definition
The process that had to be worked around
Our operation verifies education documents for institutional clients. When a document is missing, the applicant is told what is needed through an insufficiency email. On the clients whose systems carry the follow-up feature, the system then watches for the upload and, if nothing arrives, sends the reminder automatically about three days later. The applicant receives two contacts and the case moves forward without anyone chasing it.
This client went live without that feature. The insufficiency email still went out, because that is part of our own operation, but nothing followed it. The reminders that the client system would have sent became our job, and the only way to send them was one at a time, by hand.
The manual baseline
Preparing and sending one cycle of applicant reminders took close to a full employee-day. The work consisted of pulling the three exports, matching them case by case on the barcode, reading the requirement that had been communicated to the applicant, composing the message around that requirement, and sending it individually — then repeating the whole sequence for the next case. I am reporting that as an estimate based on my own recollection; it was not measured with a time study, and I would not present it as an audited figure.
Two things made it worse than a simple time cost. First, the three sources were joined by eye, so a requirement could be attached to the wrong applicant. Second, because each contact was sent individually, the effort scaled directly with the number of pending cases — and the pending queue for this client routinely sits between roughly 150 and 500 cases.
Who was affected
- The applicants, who were waiting on a case while the only contact they received was the one that asked for the document in the first place.
- The team preparing the reminders, who spent the cycle on message preparation instead of case work.
- The cases in the queue, which aged while the reminders were being written one by one.
Constraints and the decision to build in parallel
The client system and the internal API belonged to the company and were out of scope: I could not add a follow-up feature to the client system, and changing the API was not on the table. The solution had to work inside the Google Workspace environment the operation already used, and it had to read the data those systems already exposed rather than replace them. Building the workflow alongside the platform, rather than waiting for the platform to change, was the practical decision — with the trade-off that the automation lives in a spreadsheet rather than inside the system of record, and therefore depends on the data it reads being current. Figure 1 sets the two cycles side by side.
Figure 1 — The manual reminder cycle and the workflow that replaced it, reconstructed from the operating process. Failure points on the left, controls on the right.
D. Data Discovery and Root Cause Analysis
Before designing anything I compared what the three exports held and how a single case appears across them. That comparison produced the causal chain the whole project rests on.
| Symptom | Contributing factor | How the workflow addresses it |
|---|---|---|
| Reminders took close to a working day per cycle | The content of one reminder was assembled by hand, case by case | One barcode entry replaces the per-case join; every other field is formula-driven |
| Effort scaled with the queue, and the queue ran from ~150 to ~500 cases | Each applicant was contacted individually, in sequence | One batch covers 250–300 applicants in about five minutes |
| A requirement could be attached to the wrong applicant | Three exports joined by eye, with the barcode as the only shared key | The barcode drives a single lookup, so name, address and requirement come from the same case record |
| Nothing recorded who had actually been contacted | Nothing in the working view recorded who had been contacted | A Done flag per row and a reminder date per case in the working sheet |
| An incomplete message could reach an applicant | Nothing stood between a partially populated record and the send button | Rows with a missing name, address or requirement are skipped and never sent |
| The follow-up stopped when the client feature was missing | The client system carried the reminder step for other clients, but not for this one | The workflow reproduces that step on our side, on our data, without changing the client system |
The root cause
The root cause was structural, not behavioural: the reminder content lived in three disconnected exports, and the case barcode was the only key they shared. That is what made the manual work heavy and what made it risky — not the speed of the team, and not the behaviour of applicants. The client system could not carry the follow-up step for this client, so the join had to be made and the send had to be performed on our side.
Two explanations that might be expected here are worth ruling out explicitly, because they are not the cause. The team was not slow: the effort per case was seconds of work repeated hundreds of times, which is a scaling problem rather than a performance problem. And applicants are not unresponsive by nature: the workflow changes the frequency and consistency of contact, but whether a given applicant responds is outside what this project can claim.
One explanation was considered and deliberately not adopted: a fully scheduled sender, which would remove the human step entirely. I rejected it for a stated reason rather than by default — an automated scheduler cannot tell whether a reminder is still required, and an unnecessary reminder is an unnecessary contact with an applicant. That trade-off shaped the design more than any other decision in the project.
E. Solution Design and AI-Directed Development
I wrote the specification before any code existed, because the specification was the part I owned and the code was the part I could produce quickly with AI. The specification had six elements:
- Inputs — three client feeds: applicant details (barcode, name, email address), cases still awaiting a document upload, and the insufficiency comment that states the requirement.
- Join key — the case barcode, entered once, with every other field resolved by formula from the same case record.
- Working structures — a main working sheet that holds the case record, and an email sender tab that holds what is to be sent and what has been sent.
- Template variables — applicant name, requirement text, and the barcode in the subject line, so every message carries the case it belongs to.
- Validation rules — a row with a missing name, address or requirement is skipped rather than sent.
- State write-backs — a Done flag on the sender tab and the reminder date on the working sheet, so a second run cannot contact the same case again.
How the workflow is put together
The script retrieves the three feeds into the sheet. A barcode typed into the working sheet triggers a formula lookup that returns the applicant's name, email address and requirement from the same case record. The email sender tab pulls the name and address onto each row and carries a "Send Emails" button. Pressing it walks the list, substitutes the variables into the template, sends each message, marks the row Done and writes today's date into the reminder column on the working sheet. Because every field is resolved by barcode, the same case cannot pick up another case's requirement. Figure 2 traces that path from the three feeds to the sent message and back to the state written on the sheet.
Figure 2 — Data flow and state write-back for the reminder workflow, reconstructed from the built system.
The sheet and the template
The working sheet is deliberately plain. The main tab carries the case record — barcode, start date, requirement, number of reminders, date of last reminder, applicant name and applicant email — and the email sender tab carries what is queued and what has been sent. Only the barcode is entered by hand; the applicant name, address and requirement resolve by formula from the same case, and the sender tab pulls the name and address across by barcode. The Send Emails button sits on the right of the sheet.
Figure 3 shows the two tabs as they work, and Figure 4 shows the message it produces. The message is a fixed template with three variables. The name opens the greeting, the requirement states what is still needed, and the barcode in the subject line ties the message to the case without anyone having to retype it. The body stays constant so no message can accidentally omit the request.
Main working sheet
| barcode | start date | requirement | number of reminders | date of last reminder | applicant name | applicant email |
|---|---|---|---|---|---|---|
| CASE-2025-10482 | 2025-08-12 | Certified transcript issued by the university | 1 | 2025-08-15 | Sample Applicant 01 | applicant01@example.com |
| CASE-2025-10733 | 2025-08-13 | Official equivalency certificate | 2 | 2025-08-19 | Sample Applicant 02 | applicant02@example.com |
| CASE-2025-11090 | 2025-08-14 | Signed copy of the secondary certificate | 0 | — | Sample Applicant 03 | applicant03@example.com |
Seven columns. The barcode is the only manual entry; every other cell is a formula. Names, addresses and barcodes shown here are synthetic.
Email sender tab
| applicant name | barcode | applicant email | email status |
|---|---|---|---|
| Sample Applicant 01 | CASE-2025-10482 | applicant01@example.com | done |
| Sample Applicant 02 | CASE-2025-10733 | applicant02@example.com | not done |
| Sample Applicant 03 | CASE-2025-11090 | applicant03@example.com | not done |
Four columns. The name and address are pulled from the working sheet by barcode; the status is written by the script after each send.
Figure 3 — The two working tabs, reconstructed from the live structure. The live sheet cannot be captured for this document, so every value shown is synthetic.
Dear {{applicant name}},
We are following up on a document we asked you to submit for the above case. Our records show that we have not yet received it.
What we still need from you:
{{requirement}}
If you have already sent it, or if you believe you do not need to send it, please reply to this message so we can check the case and close it.
Kind regards,
Verification Operations Team
Figure 4 — The reminder template. The structure and the three variables are faithful to the built message; the surrounding wording is representative and the data is synthetic.
How I used AI to build it
I used AI as the implementation partner for the Apps Script, and I kept the judgement that the script could not make. The sequence was consistent throughout the project: I wrote the requirements, the join logic, the tab structure, the template variables and the validation rules; I asked for the script that implemented them; I read the generated code against my specification rather than accepting it; I tested the behaviour against live data; and I directed the revisions until the script did what I had specified. The specification was the part I owned, and the specification was where the real decisions sat — the join key, the three-day rhythm, the point where a person stays in the loop, and the rule that an incomplete record is skipped rather than sent.
That division is what made the workflow maintainable. Because the rules live in a written specification and a sheet, a change in behaviour is a change in the specification first, and the script follows it.
Figure 5 — The directed-development loop used to build and maintain the workflow.
Two engineering decisions worth naming
Pacing the send. The script sends one message per second, so a 250–300 message run takes about five minutes. That is deliberate: sending faster risks being throttled or blocked by the sending channel, and a run that fails halfway is worse than a run that takes five minutes. The pacing is the reason a full batch is safe rather than merely fast.
Skipping rather than sending. A row with a missing name, address or requirement is skipped and no message goes out. The alternative — sending a partially populated message — would put an incomplete request in front of an applicant and waste the contact. The skip is silent today, which is a weakness of the design and is listed as a next step in section J.
F. Project Management and Implementation
| Phase | What happened | What it produced |
|---|---|---|
| Gap identification | The client went live without the client-side follow-up feature, and the reminder step fell back to the team. | A decision to solve it on our side rather than wait for the platform |
| Specification | The join key, the two tabs, the template variables, the validation rules and the state write-backs were written down first. | A written specification that the script was built against |
| Build | Google Apps Script and Google Sheets implemented the specification, with AI as the implementation partner. | A working sheet, a sender tab and a Send Emails button |
| First cycles | The workflow was run on the real pending queue and the behaviour was checked against the specification. | Confirmed join, skip rule, Done flag and date write-back |
| Steady state | August 2025 onwards: a three-day cycle, run by me, with two performers working from the same sheet. | Continuous operation for roughly fourteen months |
Scope decisions
- Build beside the platform, not inside it. The client system and the internal API were left untouched, which kept the work inside the environment the team already used and left nothing to undo in the client system.
- No change to the message the applicant receives. The message structure matches the insufficiency email the applicant already received, so the follow-up is recognisable rather than a second, different instruction.
- The eligibility decision stays human. Automation was scoped to assembly and sending, not to deciding who deserves a reminder.
- No delivery infrastructure. The script sends and records what it sent; it does not manage bounces or track replies, which would need a delivery log the current send does not produce.
Risks and how they were handled
| Risk | How it was handled | Residual exposure |
|---|---|---|
| Contacting an applicant who no longer needs the reminder | The send trigger is manual; eligibility is never decided by the script | The guarantee depends on a person running the cycle on time |
| Sending a requirement that belongs to another case | Every field resolves from one barcode lookup, so a case cannot borrow another case's fields | A stale source feed could still pair a correct requirement with an outdated case state |
| Sending a message with missing details | Rows with a blank name, address or requirement are skipped | Skipped rows are not logged, so a silent skip can go unnoticed |
| Sending the same case twice | The Done flag is written after each send and is checked before a row is processed | A row deliberately re-queued for a second reminder must be cleared by hand |
| The run being throttled or blocked part-way | Paced at about one message per second | A run interrupted by a failure has no automatic resume, and no skip log |
| The sheet being read in a state where a queued row is out of date | Nothing — this control is not built | Listed as the first next step in section J |
Adoption and continuity
The sheet and the reminder bot are used by me and by two performers who work with me. The people preparing the reminders are the same people who can see the queue, and working from one sheet kept the operating routine common. The workflow has run without interruption since August 2025, and the maintenance load is low because the rules are visible in the sheet and the script follows a written specification.
Figure 6 — The build and the period of continuous use. Only two dates are known: the build in August 2025 and the present; the intermediate points are reconstructed from the operating history.
G. Preventive Improvement
Most of the value in this project is preventive. It is not in how fast a message goes out; it is in the errors that can no longer happen, and in the one error that was designed out on purpose. Figure 7 sets out every control in the workflow, and separates the six that are built from the four that are not.
| Control | What it prevents | Status | How it works |
|---|---|---|---|
| Human send trigger | Contacting an applicant who no longer needs a reminder | Implemented | Eligibility is never decided by the script. I review the queued cases and press the button. |
| Barcode-keyed join | A requirement being sent with another case's details | Implemented | Name, address and requirement resolve from one lookup on the same case. |
| Required-field check | An incomplete message reaching an applicant | Implemented | A row missing the name, address or requirement is skipped and not sent. |
| Done flag | The same case being contacted twice in a cycle | Implemented | Written after each successful send and checked before a row is processed. |
| Reminder date write-back | Losing track of when a case was last contacted | Implemented | The date is written to the working sheet and is the reference used to decide what is due. |
| Send pacing | The run being throttled or blocked part-way | Implemented | About one message per second, so a 250–300 run completes in roughly five minutes. |
| Skip log | A silently skipped case going unnoticed | Proposed | Not built. A reason column on the sender tab would make every skip visible. |
| Stale-row check | Sending from a row whose source data has since changed | Proposed | Not built. A timestamp on the retrieved data would flag a row queued before the last refresh. |
| Delivery check | An undeliverable address going unnoticed | Proposed | Not built. It needs a bounce or delivery log that the current send does not produce. |
| Suggested-due list | A due case being missed because the operator overlooked it | Proposed | Not built. A read-only list of cases past three days, proposed but never sent, would help without removing the human decision. |
Figure 7 — Controls in the built workflow and the controls identified but not built. The last four are named in section J as the next improvements.
The control that mattered most
Automating eligibility was the obvious next move, and I did not take it. The reason is worth stating in the company's own terms: a reminder that is not required is not a neutral message — it is an unnecessary contact with an applicant who is already waiting on a decision about their own documents. The cost of that error falls on the applicant, and it cannot be detected by a script that has no view of whether the request is still live. So the design keeps the assembly and the sending automatic, and keeps the judgement with the person who can see the case.
The controls below that judgement rather than replace it. The Done flag and the reminder date make a run safe to repeat; the required-field check makes an incomplete record impossible to send; the barcode join makes a mismatched message improbable; and the pacing makes a full run dependable.
Where the design does not yet protect, it should be said plainly. The workflow prevents unnecessary contact, mismatched content and duplicate sends. It does not currently detect a stale row, a silent skip, or a failed delivery — three of the four remaining risks in the matrix above — and it does not measure whether the contact actually changes the outcome for the applicant.
H. Testing and Validation
Validation here was practical rather than formal: the workflow was exercised on the live pending queue and its behaviour was checked against the written specification, cycle after cycle, over roughly fourteen months. There was no test suite, no regression set, and no formal sign-off record, and I am not going to present continuous use as a substitute for one.
| Behaviour | How it was checked | Result |
|---|---|---|
| A barcode resolves the correct case | Comparing the populated fields against the source records for a set of live cases | Confirmed in use; the name, address and requirement come from the same case record |
| Incomplete records are not sent | Observing the sender tab where a record was missing a required field | Confirmed in use; the row is skipped and no message is produced |
| A repeated run does not resend | Re-running against a list that had already been sent | Confirmed in use; rows marked Done are not processed again |
| The reminder date is recorded | Checking the working sheet after a run | Confirmed in use; the date is written back and is the reference for the next cycle |
| The batch survives full volume | Running at the typical 250–300 size | Confirmed in use; the run completes in about five minutes at one message per second |
| Message content is correct | Reading the sent messages against the case record | Confirmed by review; the template has three variables and no free text |
The test set I would build next
These are proposed tests, not executed ones, and they are small enough to run on a copy of the sheet:
| Test case | Expected result | Acceptance rule |
|---|---|---|
| A barcode with a complete record | Name, address and requirement populate; the row appears on the sender tab | All three fields match the source record |
| A barcode whose record has a blank requirement | The row is excluded from the send list and no message is produced | No message produced, and the skip is visible in a log |
| A barcode with no matching case | No fields populate and the row is not queued | No message produced, and the row is flagged for review |
| A second run over an already-sent list | No message is produced for rows marked Done | Zero duplicate sends |
| A queued row whose source data is older than the last refresh | The row is flagged before sending | A stale row is never sent silently |
| A run interrupted part-way | The remaining rows are identifiable and can be resumed | No case is sent twice and no case is silently dropped |
I. Business Impact and Performance Measurement
| Metric | Baseline | Result | Evidence status | Validation method |
|---|---|---|---|---|
| Effort to prepare and send one reminder cycle | ≈ 1 employee-day | One reviewed button press; ≈ 5 minutes of machine time | Estimated (owner-reported) / Reported | A 30-minute time-and-motion sample of one manual cycle would convert the estimate into a measured baseline |
| Reminders sent per run | One applicant at a time | 250–300 in a single run | Reported | Count of Done flags on the sender tab |
| Machine time per run | Not applicable | ≈ 300 seconds, paced at ≈ 1 s per message | Reported / Observed | Script execution log |
| Pending cases awaiting a document | ≈ 150 to 500 cases | ≈ 150 to 500 cases | Reported | Pending-document feed; the workflow changes the effort, not the queue |
| Records assembled per reminder | Three sources joined by hand per case | One barcode entry; every other field formula-driven | Documented | Inspection of the tab structure |
| Records with missing data | Not applicable | Skipped; no message sent | Documented | A deliberate blank in a test copy, with a skip log |
| Repeat sends within a cycle | Not applicable | Prevented by the Done flag | Documented | Run twice against a test list |
| Delivery to the applicant's inbox | Not recorded | Not recorded | Not measured | Mail service log — proposed |
| Applicant response within three days of a reminder | Not measured | Not measured | Not measured | Company case system joined to send records — proposed |
| Share of due reminders sent inside the three-day window | Not measured | Not measured | Proposed KPI | Send dates against the reminder date on the working sheet |
| Cases closed within the cycle | Not measured | Not measured | Proposed KPI | Company case system — proposed |
Figure 8 — Manual preparation and sending effort compared with the runtime of one batched cycle. The baseline is an owner-reported estimate and is labelled as such on the figure.
What the business case still needs
The effort comparison is the part of this project I can defend, and it is enough to justify the work on its own. The stronger claim — that continuous, correctly timed contact gets documents to applicants faster — is not yet supported by anything I can point to, because the workflow does not record whether a message arrived or whether the applicant responded. Three measurements would close that gap, and each has a simple source:
| KPI | Formula | Data source | Period and target |
|---|---|---|---|
| Application rate | Reminders sent ÷ reminders that were due | Sender tab and pending-document feed | Per cycle; a complete cycle is the target |
| Cycle adherence | Reminders sent within 3 days of the last reminder ÷ reminders due | Reminder date column against run dates | Per cycle; target set after the first four cycles |
| Response within the cycle | Uploads ÷ reminders sent, within 3 days of the reminder | Company case system joined to send records | Per cycle; establishes the outcome effect |
Until those exist, the correct statement of the outcome is the one I make in the summary: the workflow removed close to a full employee-day of repetitive preparation and sending per cycle and made the contact consistent, and the effect on case turnaround is an expectation rather than a measured result.
J. Lessons Learned and Future Roadmap
What I learned
- The join is the work. The email was never the hard part. Assembling one applicant's details from three exports, case by case, was the whole cost — and the whole opportunity.
- Knowing where to stop is part of the design. Automating the send was straightforward; automating the decision to send was a choice with a cost attached, and keeping it human was the right one. The script is only defensible because it does not decide who deserves a reminder.
- State written back is what makes a tool safe to re-run. The Done flag and the reminder date are what turn a batch job into something a person can run every three days without fear of contacting the same applicant twice.
- Reliability is often a pace, not a speed. Sending at one message per second looks unhurried until it is compared with the manual alternative, and it is the reason a 300-message run completes rather than being blocked half-way.
- A control you can see beats a control you trust. The Done flag and the reminder date are visible on the sheet, so the state of the work is legible to anyone working from it — including the two performers who use it.
- The measurement gap is my weakest point, and I know where it is. I can defend the effort saved. I cannot yet defend the effect on case turnaround, because nothing in the workflow records whether a message arrived or what happened next. I would rather name that than let a reader assume it.
What I would build next
- A skip log. One column on the sender tab recording why a row was not sent, so no case disappears silently. This is the cheapest meaningful improvement to the workflow.
- A stale-row check. A timestamp on the retrieved data would let the script flag any row queued before the last refresh, removing the risk of sending from an outdated record.
- A suggested-due list. A read-only list of cases whose reminder date is more than three days old, which I review and queue. It keeps the human decision exactly where it is while removing the risk of overlooking a case.
- A delivery check. Capturing the send result per row, so an undeliverable address is visible instead of inferred from silence.
- A small regression sheet. A handful of representative cases with their expected fields and expected behaviour, checked whenever the template or the rules change.
- Three cycle metrics. Application rate, cycle adherence and response within the cycle, so the business effect can be measured rather than assumed.
None of these is built today. They are the design I would take to the next version, and each one is deliberately small: the workflow earns trust by being predictable, and that is worth protecting.
K. Skills Demonstrated
| Competency | My contribution | Supporting evidence | Business or technical value |
|---|---|---|---|
| Requirements definition and business analysis | Turned "remind applicants about missing documents" into a specification: three inputs, one join key, two tab structures, three template variables, a validation rule and two state write-backs | Written specification; the sheet structure; the template | The specification is what makes the workflow reproducible and maintainable by someone other than its author |
| Root cause analysis | Identified the structural cause — reminder content split across three exports with only the barcode as a shared key — and ruled out the obvious explanations of team speed and applicant behaviour | Comparison of the three feeds; the join design that followed | Prevents solving the wrong problem, and explains why the design is a join rather than a mail merge |
| Systems and workflow design | Designed the full path from barcode to sent message, including the review step, the skip rule and the state write-back that makes a repeat run safe | The live workflow; the control matrix | Replaces a manual cycle with one that is repeatable, auditable and safe to re-run |
| AI-directed development | Specified the behaviour, used AI to write and revise the Apps Script, evaluated the generated code against the specification, tested it against live data, and directed the changes | The specification and the built script | Delivered a working system inside the Google Workspace environment the operation already used |
| Risk judgement and control design | Kept the send trigger human to prevent unnecessary applicant contact, and added the checks that make an automated message safe to send | The trigger decision; blank-field skip; Done flag; pacing | Protects the applicant relationship, not just the process — the cost of a wrong reminder falls on the applicant |
| Operational process management | Ran and maintained a three-day cycle for roughly fourteen months, and kept two performers working from the same sheet | Continuous operation since August 2025 | Turns a workflow into a routine that survives without constant attention |
| Data analysis and measurement | Established the operating scale of the queue and the cycle, and identified precisely which measures do and do not exist | Volume and runtime figures; the KPI plan in section I | Separates what the project achieved from what it still needs to prove |
| Confidentiality and responsible disclosure | Kept the client, applicants, cases and barcodes out of the published material, and labelled every visual as a reconstruction | This document; the status labels on every figure | The case study can be shared publicly without exposing the client or any applicant |
How the competencies combine
The project is not a collection of skills applied in sequence; it is one chain. Business analysis produced the specification, the specification made the AI-directed development possible, the development produced controls that made the workflow safe to run every three days, and the operating routine is what turned a script into something with fourteen months of continuous use. The root cause analysis is the hinge: it is the reason the work was a data join rather than a mail merge, and the reason the control set is shaped around what a script cannot know — namely, whether a reminder is still required.
Note on data and evidence. The client, applicants, cases and barcodes are not identified in this document. Figures marked as owner-reported are my own estimates or recollections and were not independently audited; figures marked as observed come from the running system. Every figure in this document is labelled with its status: reconstructed process diagram, reconstructed architecture diagram, reconstructed control matrix, reconstructed timeline, illustrative mockup with synthetic data, or an owner-reported estimate. No screenshot of the live sheet appears, because the live environment cannot be captured for publication; the sheet and message views shown are faithful reconstructions of the documented structure with synthetic values, and the three API feeds and the client system were existing company services that I did not build or modify.
Prepared by Khaled Medhat — Team Leader, Case Validation — October 2026.