Vansaarthi
A donor-first NGO platform — UPI rails, OAuth, automated outreach, and a small chatbot that did most of the warmth.
Context
An NGO based in Gujarat had outgrown a static donation page. Their team was juggling UPI screenshots in WhatsApp, donor receipts in a shared Google Sheet, and an email follow-up flow that was equal parts manual and incomplete.
I came in to build the whole thing end-to-end — the donation rail, the donor accounts behind it, the operations dashboard, and a soft chat surface that could handle the same five donor questions the team kept answering by hand.
Approach
Three layers, kept honest. One: a Postgres schema with normalized tables for donors, donations, campaigns, and follow-ups — every row traceable, no JSON blobs masquerading as data. Two: a Node + Express API with Passport.js handling Google OAuth and JWT for session continuity, plus a Nodemailer pipeline that fires on real-world events (donation cleared, follow-up due, campaign closing).
The third layer was the chatbot. Not a flashy LLM front door — a small grounded agent that knew the campaign list, donation methods, and FAQ, and could escalate cleanly to a human. That’s where the engagement lift came from.
Decisions & tradeoffs
- UPI deep-link, not gateway. Lower cost, fewer middlemen, but I had to reconcile manually for the first three weeks until the webhook was solid.
- Passport.js over a hosted auth. The team needed RBAC and audit logs, not a magic link UX. Passport gave us the strategy plug-points we needed.
- Email over SMS. Cheaper, more auditable, and donors actually open NGO email — counter-intuitive but real.
Outcome
The team uses it weekly without needing me on call. Donor sessions trended +20% after the chatbot landed, replies to follow-up emails climbed +35%, and the ops dashboard absorbed what used to be three spreadsheets.
The unsexy win: the team trusts the data now. No-one screenshots UPI receipts into WhatsApp anymore.
What I would change
I’d separate the chatbot’s knowledge base from the codebase from day one — we ended up doing that in week four, but it cost a refactor. And I’d put the email pipeline behind a job queue earlier; nodemailer-in-a-controller is fine until it isn’t.