Product architecture

How TutorNest works under the hood

A single platform that protects each family's recurring slot, tracks every minute of every package, and keeps tutors, parents and admins in sync.

Core booking flow

Parent sign-in
Family scoped to their own slots
Visibility filter
Only protected slots are returned
Booking engine
Validates package + slot rules
Package debit
Atomic minute deduction
Notify all parties
Email · SMS · in-app
Tutor confirms
Session marked scheduled
Booking engine
Recurring slot reservation, conflict detection and 14-day rolling visibility window per family.
Slot privacy rules
Postgres row-level scoping ensures families never see another household's slot — even via direct API calls.
Package ledger
Append-only minute ledger; each completed session writes a debit row with idempotency keys.
Billing & top-ups
Stored card on file, automatic invoice generation at session completion, support for one-off top-ups.
Notification fabric
Templated emails, SMS via gateway provider, and an in-app inbox — all sharing one event bus.
Reschedule rules
Configurable cancellation window (default 24h); auto-credits the package on policy-allowed cancellations.
Roles & permissions
Parent · Tutor · Admin. Each role gets a scoped API surface and tailored dashboard.
AI roadmap
Tutor session summarisation, parent weekly digests, smart slot recommendations.
Mobile-ready
Same API powers a planned React Native app — no second integration.

Rules engine

rule "protected-slot-visibility" {
  when  request.role == "parent"
  then  filter slots where slot.family_id == request.family_id
}

rule "auto-debit-on-completion" {
  when  session.status -> "completed"
  then  ledger.debit(package_id = session.package_id,
                     minutes    = session.duration,
                     ref        = session.id)
}

rule "free-reschedule-window" {
  when  action == "reschedule" AND now < session.start - 24h
  then  allow AND credit_back(minutes = session.duration)
  else  deny  AND charge(fee = 0)
}

rule "low-balance-nudge" {
  when  package.remaining_minutes <= 60
  then  notify(parent, template = "top_up_reminder")
}

Data model

families
  • · id
  • · name
  • · primary_parent_id
  • · joined_at
children
  • · id
  • · family_id
  • · grade
  • · subject
  • · tutor_id
packages
  • · id
  • · child_id
  • · minutes_total
  • · minutes_used
  • · expires_at
sessions
  • · id
  • · child_id
  • · tutor_id
  • · start_at
  • · status
invoices
  • · id
  • · family_id
  • · amount
  • · status
  • · issued_at
notifications
  • · id
  • · user_id
  • · kind
  • · read_at
ledger_entries
  • · id
  • · package_id
  • · delta
  • · ref
  • · created_at
audit_log
  • · id
  • · actor
  • · action
  • · subject
  • · at