Back to all deployments

Ed-Tech

ScholarPath

Multi-tenant SaaS for India's scholarship exam market — live with paying customers.

Customer

Maharashtra MSCE scholarship exam market — parents, students, exam coordinators

Timeline

2024–Present

Status

Live in production

Capability

Full-StackSaaSProduction

Stack

ReactTypeScriptFastAPISupabaseRazorpayMCP

Outcome

Live
In production
Paying customers
124
E2E tests
Full test coverage plan
3
Exam verticals
Generically extensible config
Shipped
Status
Iterating in market

Customer Context

Who they are and what world they live in

The Maharashtra MSCE scholarship exam is a high-stakes test for students in classes 5 and 8. Parents in this market are deeply invested in their child's preparation but have no structured digital tool — exam prep is fragmented across WhatsApp groups, photocopied question papers, and individual tutors. The parent is the decision-maker and the paying customer, but the student is the user. That distinction drives everything about the architecture.

The Problem

The fuzzy ask, translated

The gap wasn't 'there's no app' — there were apps. The gap was: no app understood the parent-as-gateway model. Every existing tool gave children direct accounts. But in Maharashtra's scholarship ecosystem, parents manage preparation, parents pay, and parents need visibility into progress. The real problem was building an access model that matched how families actually work, not how SaaS companies assume they work.

The Constraints

Time · Budget · Regulatory · Technical · Organizational

01

Parent-as-gateway: parent owns the account and manages multiple child profiles — each child may have a different exam tier and access level

02

Razorpay INR payments with webhook reliability in a market where payment failures are common

03

Mobile-first for rural Maharashtra users on older Android devices with unreliable connectivity

04

Generically extensible: the exam-category configuration must support rapid expansion to new exam verticals without re-architecting

05

Live customer support during onboarding — real users, real issues, real iteration pressure

Architecture Decisions

What I chose. What I rejected. Why.

Authentication and access model

Chosen

Parent-as-gateway: parent record owns all child sessions, child profiles inherit access from parent's tier

Rejected

Direct child authentication / per-child accounts

Why

Matches how families actually operate in this market. Simplified the threat model significantly — no child authentication surface. Supabase RLS policies scoped to parent_id rather than user_id per child.

Exam configuration

Chosen

Generic exam-category configuration table — exam type, grade level, subject, question count, timer rules all configurable per category

Rejected

Hardcoded exam types

Why

MSCE is the launch vertical but not the only one. Every configuration parameter being data-driven means adding a new exam type is a database row, not a deployment.

AI-assisted delivery

Chosen

Supabase MCP integration with Claude Code for content generation and AI-assisted development

Rejected

Manual content pipeline

Why

MCP server integration with Claude Code means content workflows run inside the development environment — schema-aware, context-aware, dramatically faster than a separate CMS.

The Hard Problem

The one thing that almost broke the deployment

Multi-tenant data isolation when one parent has multiple children with different access tiers — and the children are not paying users. Supabase Row Level Security was designed for single-user tenancy. Extending it to a parent-owns-children model required a custom access-control layer: every query scoped to the parent's subscription tier, with child profiles inheriting rather than holding their own access grants.

The Fix

Rebuilt the permission model: parent record is the tenant root. Child profiles are owned rows under the parent, with no independent authentication. Access tier is resolved at query time from the parent's active subscription, propagated to child profile reads via a parent_id join. No child can access anything their parent hasn't paid for — and no parent can see another family's data.

Production Reality

What I had to fix in week 2

Razorpay webhooks arrived multiple times for the same payment event. The initial payment handler wasn't idempotent — early users were seeing duplicate payment records. Added idempotency keys on the Razorpay order ID, with a processed_webhooks table to deduplicate before updating subscription state. Payment integration in a regulated market has more compliance edges than features.

Lessons Carried Forward

What this taught me that I apply to every deployment

01

Match the access model to how the customer's family actually operates — not how SaaS assumes they operate

02

Payment webhook idempotency is not optional in markets with unreliable payment infrastructure

03

Generic configuration tables pay off immediately — the second exam vertical was two database rows and one afternoon

04

MCP server integration with Claude Code is a legitimate force multiplier for content-heavy platforms

05

Customer feedback in week 1 changes the spec faster than 6 months of planning

Related Deployments