Koovira Dev Docs
30-core/notifications.md

Notifications

Status: 🟒 Production (email + WhatsApp live; in-app + push low-volume) · Last validated: 2026-09-06

Multi-channel delivery: in-app, push (FCM + Web Push), email, SMS, WhatsApp. Validated against classs/PushNotificationModule.php, classs/KvMailService.php, classs/KvWhatsAppCloud.php + schema.


Channels & stores (2026-09-06)

ChannelClassStoreRows
In-app(notification writes)notifications0 (live schema, low data)
EmailKvMailService (static)email_queue (+ email_templates 35)1,673 (recounted; was 1,555)
WhatsAppKvWhatsAppCloud / KvWhatsAppFaqBotwa_outbound155
Push (web)PushNotificationModulepush_subscriptions0
Push (Android)PushNotificationModule (FCM)FCM tokensβ€”
OTP(auth)notification_otp_codes25
Drift caught: fp_notify (referenced in some older notes) is not present in this DB β€” the in-app store is notifications. notification_master/notification_queue/sms_log also do not exist. Validate the channel table before writing to it.

Email (KvMailService, static API)

  • KvMailService::queue(to, subject, htmlBody, opts): int (:160) β€” enqueue to email_queue.
  • recipientDeliverable(email) (:44) β€” suppression/deliverability check.
  • template(key) (:98) + render(tpl, vars) (:110) β€” templated mail from email_templates.
  • userAllows(userId, category) (:139) β€” respects per-user notification preferences before

sending. Always honored.

  • htmlToText(html) (:127) β€” plain-text alt.
  • Delivery: the cron/email_queue_processor.php cron drains email_queue.

Push (PushNotificationModule)

  • saveWebPushSubscription(userId, subscription) (:72), saveFcmToken(userId, token, deviceType)

(:121) β€” register endpoints (push_subscriptions + FCM tokens).

  • sendToUser(userId, title, body, url, data) (:197), sendToUsers(userIds, …) (:470) β€” send.
  • removeSubscription (:160), getUserSubscriptions (:179).

WhatsApp

KvWhatsAppCloud (Cloud API) writes wa_outbound; inbound + FAQ via KvWhatsAppFaqBot (the "Rishi" bot). Outbound is gated to opted-in contacts (wa_optins) β€” see memory whatsapp-outbound-reality. Template sync via cron/wa_template_sync.php.

The event β†’ notification pattern

Business event (order placed, KYC approved, new lead, refund…)
  β†’ module decides channels + recipient
  β†’ KvMailService::userAllows(userId, category)  (pref gate)
  β†’ per channel: notifications (in-app) Β· KvMailService::queue (email) Β· PushNotificationModule::sendToUser Β· KvWhatsAppCloud (WhatsApp)
  β†’ cron workers deliver (email_queue_processor, marketing_*_worker, social_media_publisher)

Representative events (fill per module)

Order created/accepted/dispatched/delivered; payment success/failure; refund initiated/completed; booking confirmed; KYC approved/rejected; new lead; new message; membership renewal reminder. Each module's page section J lists its own events.

Business rules

  • BR-NOTF-001 β€” always gate on KvMailService::userAllows(userId, category) before user email.
  • BR-NOTF-002 β€” WhatsApp outbound only to opted-in contacts (wa_optins).
  • BR-NOTF-003 β€” email is queued (email_queue) and delivered by cron, never sent inline on the request.

Errors (prefix NOTF-E)

  • NOTF-E001 email not delivered β€” check email_queue status + email_queue_processor cron +

recipientDeliverable suppression.

  • NOTF-E002 push not received β€” no push_subscriptions/FCM token for the user.
  • NOTF-E003 WhatsApp not sent β€” recipient not in wa_optins.

Change-impact

Every module fires notifications; changing the pref gate or a channel affects all of them. Re-test: pref gate honored, email queued+drained, push to a subscribed user, WhatsApp to an opted-in contact.