Four timed messages per registrant, exactly once, with no message broker
A registration platform for a live hiring event — a public page, WhatsApp OTP verification, an admin console, and a time-aware automation engine running its own delivery queue on serverless infrastructure.
- Client
- ExcelR
- Industry
- EdTech & Training
What was the problem?
Registrations had to be real rather than spam, and every registrant had to receive a sequence of reminders at exactly the right moment on the channel they read. The real engineering challenge: guarantee each person gets each message exactly once, on serverless infrastructure with no message broker, no always-on worker, and a 30-second execution ceiling.
What made it hard
Commercially
- A duplicate message to a real applicant is a visible, brand-damaging failure
- The event date is fixed; late registrants still need the full sequence
Technically
- Serverless infrastructure with no message broker, no always-on worker and a 30-second execution ceiling
- A cron tick and an admin button-click can fire in the same second
- A run can crash mid-flight, leaving claimed work stranded
- A message can send successfully while the status write fails
Operationally
- Admins needed per-person delivery visibility and the ability to push any message batch manually
- Registration had to be switchable on and off
At scale
- Batches large enough that the queue must drain across multiple invocations rather than one long-running process
Security
- OTPs are an obvious abuse target — by phone, by IP and by brute force
- No credential could reach the browser
- The scheduler endpoint is publicly addressable
What we built
An applicant verifies a WhatsApp OTP before they can register. The submission is normalised, window-checked and written inside a transaction blocking duplicates by phone and email at once. A welcome WhatsApp, a branded email, an admin alert and an n8n webhook fire immediately, and the lead enters the automation queue where the engine owns it for 48 hours. Admins oversee everything from a console with search, filters, sorting, cursor pagination, CSV export, per-person delivery status and manual batch push.
What it does
WhatsApp OTP verification
Six-digit OTP via Infobip with HMAC-hashed storage, expiry, cooldown and rate limits.
Dual-key duplicate prevention
Transactional blocking by phone and email simultaneously.
Four-message automation sequence
Across WhatsApp and email, with per-person, per-channel delivery state.
IST-aware scheduling
All timing logic computed in IST regardless of where the server runs.
Quiet hours
WhatsApp never fires between 9 PM and 8 AM; messages are held and released at 8 AM.
Adaptive delays
A message normally sent an hour after signup compresses to 10 then 5 minutes for last-minute registrants.
Cutoffs and exclusions
Messages that no longer make sense are skipped with a recorded reason.
Message sequencing
A later reminder waits for the earlier message to actually complete, so messages never arrive out of order.
Admin console
Search, filters, sorting, cursor pagination, CSV export and manual batch push.
Registration window switch
Registration can be opened and closed operationally.
Run history and delivery reports
Runs grouped by day with per-message delivery reporting.
n8n webhook
Fires on registration for downstream workflow automation.
Engineering decisions
Choices that could have gone another way, and what each one bought.
The database is the queue
Every lead carries a delivery record for each message and channel, moving through pending → sending → sent, failed or skipped. There is no separate broker to fall out of sync with the data.
Why it matters. Serverless with no broker and no always-on worker is a genuinely constrained environment. Making the data the queue removes an entire class of consistency bugs rather than adding infrastructure.
Transactional claiming as distributed locking
Before any send, the worker claims that lead's channel inside a Firestore transaction. Two workers can never claim the same message.
Why it matters. A cron tick and an admin button-click firing in the same second cannot double-send to a real person — the guarantee is structural rather than a race the code tries to win.
Cursor checkpointing and time-budget governance
Each message type persists its own scan position. A tick sends one batch, saves the cursor and returns, and the runner continuously checks its remaining budget, refusing to start a batch without enough headroom to finish and respond cleanly.
Why it matters. This is what keeps the runner inside a hard 30-second platform timeout instead of dying halfway through a batch — the queue drains across ticks rather than demanding one long process.
A sent message with a failed status write extends the lock
In the one genuinely dangerous case — the message went out but the database write recording it did not — the lock is extended rather than released.
Why it matters. Releasing the lock would let the next tick resend. Extending it means a database blip can never cause a duplicate message to a real person.
Scheduling that behaves like a person would
All logic computed in IST regardless of server location. WhatsApp never fires between 9 PM and 8 AM. A message normally sent an hour after signup compresses to 10 then 5 minutes for last-minute registrants, and messages that no longer make sense are skipped with a recorded reason.
Why it matters. Exactly-once delivery is the engineering guarantee, but the scheduling rules are the part the recipient actually feels — someone registering on the morning of the event still gets everything in time, and nobody registering on event day is told the event is tomorrow.
OTPs that are never stored
Only HMAC-SHA256 hashes, generated with a CSPRNG and compared in constant time, behind per-phone and per-IP hourly caps, resend cooldowns, attempt limits and single-use verification markers with a short TTL.
Why it matters. An OTP endpoint is one of the most attacked surfaces on any public form, and a plaintext OTP store turns a database read into account takeover.
Technical detail
Collapsed by default. Open whichever part you are evaluating.
- Frontend
- Next.js 14 (App Router)
- TypeScript
- Tailwind CSS
- React Hook Form + Zod
- Backend
- Next.js API routes on serverless with a 30-second execution ceiling
- Database
- Firebase Firestore with deny-all client rules and server-only writes
- Authentication
- HMAC-signed admin session cookies with timing-safe comparison
- WhatsApp OTP for applicants
- Authorization
- Bearer-token protected scheduler endpoint
- Background processing
- Database-as-queue with transactional claiming, stale-claim recovery, global run lock, cursor checkpointing and time-budget governance
- Caching
- Redis (Upstash)
- Messaging
- Infobip WhatsApp API
- Nodemailer / SparkPost email
- Deployment
- Vercel
- Monitoring
- Run history grouped by day
- Per-message delivery reports
- Failure alerting by email
- Frontend
- Next.js 14
- TypeScript
- Tailwind CSS
- React Hook Form
- Zod
- Backend
- Next.js API routes
- TypeScript
- Database
- Firebase Firestore
- Cloud
- Vercel
- Redis (Upstash)
- Integrations
- Infobip WhatsApp API
- Nodemailer / SparkPost
- n8n
- Tools
- Vitest
- Authentication
- WhatsApp OTP gate for applicants
- HMAC-signed admin session cookies compared in timing-safe fashion
- Authorization
- Bearer-token protected scheduler endpoint
- Server-only Firestore writes
- Data protection
- OTPs are never stored in plaintext — only HMAC-SHA256 hashes, generated with a CSPRNG and compared in constant time
- Every credential stays server-side
- Firestore rules deny all direct client access
- Abuse prevention
- Per-phone hourly caps
- Per-IP hourly caps
- Resend cooldowns
- Attempt limits
- Single-use verification markers with short TTL
- Auditability
- Per-person, per-channel delivery records
- Run history grouped by day
- Complete event log of everything sent
- Privacy
- Phone numbers normalised to E.164 and stored server-side only
Mechanisms
- Database-as-queue with a per-recipient, per-channel state machine
- Transactional claiming as distributed locking
- Stale-claim recovery after a 5-minute expiry
- TTL-based owner-tokened global run lock
- Per-message-type cursor checkpointing
- Time-budget governance before starting each batch
- Provider-level batching with per-recipient result mapping
- Write-retry with exponential backoff
- Bounded concurrency pools
- Idempotent runs
Failure scenarios handled
- A cron tick and an admin button-click fire in the same second
- A run crashes mid-flight leaving work claimed
- Overlapping scheduler triggers
- The 30-second execution ceiling is reached mid-batch
- One invalid number inside a 40-recipient batch
- A message sends successfully but the status write fails
- A run is re-triggered after a partial completion
Idempotency. Every run is safe to re-run. Already-delivered leads are skipped automatically, and claiming happens inside a transaction so two workers can never claim the same message.
Recovery. Claims expire rather than needing cleanup, so a crashed run's leads are reclaimed on the next tick instead of being stranded. Cursors mean a tick resumes exactly where the previous one stopped. And in the one genuinely dangerous case — a message sent but its status write failing — the lock is extended rather than released, so a database blip can never cause a duplicate message to a real person.
Before and after
Before
- Registration open to unverified and duplicate entries
- No systematic reminder sequence
- No record of who had been contacted on which channel
After
- WhatsApp OTP required before a registration can exist
- One record per person, enforced transactionally by phone and email
- Four timed touchpoints delivered exactly once per registrant
- Per-person, per-channel delivery status visible in an admin console
The build, by the numbers
What was built, at what scale. These describe the system's size, not its business results.
- automated tests
- 122automated testsCovering OTP security, scheduling mathematics, cron budgeting and provider integration
- timed message types
- 4timed message typesDelivered across WhatsApp and email over 48 hours per registrant
- recipients per API request
- 40recipients per API requestBatched WhatsApp sends with results mapped back per recipient
- external systems integrated
- 5external systems integratedFirestore, Infobip, SparkPost, Upstash Redis and n8n
- days to delivery
- ~11days to deliveryFully deployed and live under an urgent client timeline
What changed as a result?
Delivered in roughly 11 days under an urgent timeline, fully deployed and live, with 122 automated tests covering OTP security, phone normalisation, IST scheduling mathematics, cron budgeting, admin sessions and provider integration.
- Built exactly-once message delivery on serverless with no message broker
- Blocked fake registrations with a mandatory WhatsApp OTP gate
- Delivered four timed touchpoints per registrant across two channels
- Made every run safe to re-run, with automatic recovery from crashed runs
- Shipped and deployed in roughly 11 days under an urgent timeline
Why does this matter in EdTech & training?
Training providers usually have the harder half solved already: an audience that trusts them. What leaks is the path from interested to enrolled. WezvaTech reaches over ten thousand learners; the work was making the site convert that reach rather than admire it.
A large audience and a thin enrolment path
Reach does not convert on its own. Course pages, a visible enrolment route and instant-response contact options are what turn followers into paying learners.
Placement claims nobody can verify
Every training provider claims placements. The ones that convert show named outcomes, real project work and reviews a prospect can check, rather than a percentage with no source.
Enquiries arriving where nobody is watching
Technical learners message on WhatsApp and expect a fast reply. An enquiry that waits overnight is usually an enrolment lost to whoever answered first.
No visible difference between courses
When a flagship bootcamp, an internship and a webinar all look alike, prospects default to the cheapest. Distinct pages with distinct outcomes let people self-select correctly.
Need something like this for EdTech & training?
Tell us what is slowing the business down and we will tell you whether software is the right fix. If it is not, we will say so.
Free 30 minutes · no obligation
Related work
- Education / Internal SaaS
Little Lumos School Management Portal
Custom-built school management SaaS that automates end-to-end school operations
- AI Workflow Platform
Little Lumos — AI Monthly Progress Reports
A preschool was hand-writing 70+ monthly progress reports, costing 3–4 working days every month. Softivum built the full workflow: teachers speak, AI drafts, admins approve, and branded letters reach both parents with per-recipient delivery tracking.
- Marketplace Platform
Helpora — Hyperlocal Neighbour-Help Marketplace
A marketplace where neighbours pay neighbours for everyday help. Softivum built the consumer app, the marketplace engine, the operations console and the brand site — verifying identity against government records, holding every payment in escrow until the job is done, and tracking helpers live on a map.