Phase 1 Notification Flow
This document defines the proposed notification flow for the BCI app and admin portal before implementation.
No code changes are assumed from this document alone.
Objective
Build a notification module that supports:
- app push notification delivery
- in-app notification listing/history
- member-level notification preferences from App Settings
- system-triggered notifications from existing modules
- admin-triggered general notifications
Scope
Phase 1 covers:
- notification data model
- notification preference model
- device token registration
- notification creation flow
- recipient resolution flow
- app settings API flow
- app notification inbox API flow
- admin general notification API flow
Current Implementation Status
Current backend status:
Done
- notification master/data tables
- notification type seeder
- member-level notification settings
- member device token registration
- app notification inbox list
- mark one notification as read
- mark all notifications as read
- admin general notification create API
- app general notification create API
- admin notification logs API
- queued recipient fan-out for large audiences
- Firebase FCM push delivery integration
- package-backed Firebase integration using
kreait/laravel-firebase - Firebase service methods for:
- single token send
- multicast send
- batch send with mixed payloads
- system trigger wiring for:
GENERALEVENTREFERRALSESSIONBUSINESS_EXCHANGENEW_MEMBER_JOINEDEVENT_ATTENDANCESUBSCRIPTIONCHAPTER_ASSIGNED
Not done yet
GRIEVANCEstatus-change trigger flow- notification detail
GET /notifications/{notificationRecipient}API - end-to-end production verification with real Firebase credentials and mobile devices
Important:
- real mobile push delivery is now integrated through Firebase FCM when
FIREBASE_PUSH_ENABLED=true - if Firebase push is disabled, backend skips external push and keeps internal notification flow working for development/testing
- Firebase setup details are documented in firebase-push-notification-setup.md
Phase 1 does not require:
- website notifications
- email notification fallback
- SMS notification fallback
- notification scheduling UI
- rich campaign/reporting dashboard
Notification Types
Current required types:
| Code | Source | Recipient |
|---|---|---|
GENERAL |
Manual | members |
SUBSCRIPTION |
System | member |
REFERRAL |
System | sender, receiver, chapter admin |
SESSION |
System | sender, receiver, chapter admin |
BUSINESS_EXCHANGE |
System | sender, receiver, chapter admin |
EVENT |
System | chapter-wise members |
EVENT_ATTENDANCE |
System | selected attended member |
NEW_MEMBER_JOINED |
System | other chapter members and chapter admins |
GRIEVANCE |
System | member |
CHAPTER_ASSIGNED |
System | member |
Recommended Status Contract
Notification delivery status:
| Value | Meaning |
|---|---|
PENDING |
notification row created, dispatch not attempted yet |
SENT |
push dispatch attempted successfully |
FAILED |
push dispatch failed |
READ |
recipient opened/read notification |
App preference status:
| Value | Meaning |
|---|---|
ENABLED |
member wants this notification type |
DISABLED |
member opted out for this notification type |
App Settings Scope
Notification settings should be stored member-wise, not device-wise.
Reason:
- a member expects the same toggle behavior across devices
- module logic already works around member/member-chapter identities
- device tokens should only decide delivery target, not business preference
Recommended rule:
- preference is saved against
member_id - notification recipients are still resolved chapter-wise where needed
- delivery happens to all active devices of that member unless preference is disabled
Recommended Tables
1. notification_types
Master table for notification categories.
Suggested columns:
| Column | Notes |
|---|---|
id |
UUID |
code |
unique, e.g. REFERRAL |
name |
display name |
user_type |
MEMBER, CHAPTER_ADMIN, BOTH |
can_disable |
bool |
status |
ACTIVE, INACTIVE |
created_at, updated_at, deleted_at |
standard |
Why table instead of enum only:
- app settings UI can be driven dynamically
- display names can change without code-only dependency
- future admin control is easier
2. member_notification_settings
Per-member preference table.
Suggested columns:
| Column | Notes |
|---|---|
id |
UUID |
member_id |
FK |
notification_type_id |
FK |
is_enabled |
bool |
created_at, updated_at, deleted_at |
standard |
Unique index:
member_id + notification_type_id
3. member_device_tokens
Stores app push tokens.
Suggested columns:
| Column | Notes |
|---|---|
id |
UUID |
member_id |
FK |
device_token |
push token |
platform |
ANDROID, IOS |
device_name |
optional |
app_version |
optional |
is_active |
bool |
last_used_at |
timestamp |
created_at, updated_at, deleted_at |
standard |
Unique index:
device_token
4. notifications
System notification definition row.
Suggested columns:
| Column | Notes |
|---|---|
id |
UUID |
notification_type_id |
FK |
title |
title |
message |
body text |
image_path |
optional image |
source_type |
model name, e.g. Referral, Event |
source_id |
related source row id |
created_by_user_id |
nullable |
created_by_member_id |
nullable |
metadata |
JSON payload |
status |
PENDING, SENT, FAILED |
sent_at |
nullable |
created_at, updated_at, deleted_at |
standard |
5. notification_recipients
Recipient rows for a notification.
Suggested columns:
| Column | Notes |
|---|---|
id |
UUID |
notification_id |
FK |
member_id |
FK |
member_chapter_id |
nullable, useful for chapter-context notifications |
delivery_status |
PENDING, SENT, FAILED, READ |
read_at |
nullable |
delivered_at |
nullable |
failure_reason |
nullable |
created_at, updated_at, deleted_at |
standard |
Unique index:
notification_id + member_id
This avoids duplicate delivery to the same member for the same notification.
Initial App Settings Keys
Your screenshot shows these toggles:
| Key | Notification Type | Default |
|---|---|---|
REFERRAL_RECEIVED |
REFERRAL |
enabled |
ONE_TO_ONE_RECEIVED |
SESSION |
enabled |
NEW_MEMBER_JOINED_CHAPTER |
NEW_MEMBER_JOINED |
enabled |
Recommended Phase 1 rule:
- keep settings dynamic in backend
- UI can initially show only these three toggles
- other notification types remain enabled by default until UI is expanded
Alternative:
- expose all can-disable notification types immediately
Current safer recommendation:
- backend supports all types
- app settings UI starts with only the three toggles already designed
Recipient Resolution Rules
1. GENERAL
Source:
- manual create from admin/app-admin flow
Inputs:
titlemessageimage- audience
Recommended audience contract:
| Value | Meaning |
|---|---|
ALL_MEMBERS |
all approved members |
CURRENT_CHAPTER_MEMBERS |
only members of a chapter |
Required rule:
- if audience is
CURRENT_CHAPTER_MEMBERS,chapter_idis required
2. SUBSCRIPTION
Recipients:
- the member whose chapter subscription is expiring, expired, or renewed
Events:
EXPIRING: chapter subscription expires within the configured warning window, currently 7 daysEXPIRED: chapter subscription expiry date is before the current server timeRENEWED: common notification service method exists, but automatic renewal trigger is pending until renewal storage/logic is added
Recommended source:
member_chapters.date_of_joining- current active
chapter_fees.duration_value - current active
chapter_fees.duration_unit
Current command:
php artisan notifications:send-subscription-status
Options:
--days=7controls the expiring warning window.--dry-runcounts eligible rows without creating notifications.
Scheduler:
- runs daily at
09:00server time.
Duplicate rule:
- the command creates at most one notification per
member_chapterand subscription status (EXPIRINGorEXPIRED).
3. REFERRAL
Recipients:
- sender member
- receiver member
- chapter admins of the relevant chapter
Preference key:
- receiver toggle should control receiver delivery
- sender delivery can stay always enabled or configurable later
4. SESSION
Recipients:
- session creator
- session-with member
- chapter admins of the relevant chapter
5. BUSINESS_EXCHANGE
Recipients:
- referred by member
- referred to member
- chapter admins of the relevant chapter
Shared two-party push data for REFERRAL, SESSION, and BUSINESS_EXCHANGE:
sender_member_id: sender/from member idreceiver_member_id: receiver/to member idreferring_by_member_id: sender/from member id alias for referral-style screensreferring_to_member_id: receiver/to member id alias for referral-style screens
6. EVENT
Recipients:
- chapter-wise members depending on visibility logic
Recommended mapping:
| Event Visibility | Recipients |
|---|---|
ALL or app-wide equivalent |
all approved members |
CHAPTER |
members of event chapter |
INVITE_ONLY |
only explicitly assigned members if invite table exists, otherwise no push until invite logic exists |
7. NEW_MEMBER_JOINED
Recipients:
- other approved members of the joined chapter
- chapter admins of the joined chapter
Exclusions:
- the newly joined member
- the creator/action taker, when that user is linked to a member
Current trigger points:
- signup member approval from the admin portal
- member creation from the admin portal
- member creation from the app by a chapter admin
- new chapter assignment for an already approved member
Important clarification:
A pure signup event has no chapter until chapter assignment exists, so this notification is sent only after the member has an assigned chapter and is approved.
Push data:
type:NEW_MEMBER_JOINEDsource_id: notification source row idmember_id: newly joined member id
8. EVENT_ATTENDANCE
Recipients:
- selected member whose attendance was marked
Exclusions:
- the creator/action taker, when the creator is the same selected member
Current trigger point:
- app chapter admin submits event attendance from
POST /events/attendance/submit
Future trigger point:
- admin portal attendance marking should call the same common event attendance notification service when portal-side attendance is added later
Important rule:
- notification is created only when the member is newly marked present for that event; repeated submit for an already-present member should not create duplicate notifications
9. GRIEVANCE
Recipients:
- the grievance creator member
Trigger:
- when grievance status changes
- optionally also when admin adds a resolution remark later
10. CHAPTER_ASSIGNED
Recipients:
- member whose chapter row was newly added
Trigger:
- only when a chapter is newly assigned
- not when existing row is only updated
Delivery Preference Rule
Recipient resolution and preference filtering should be separated.
Flow:
- determine candidate recipients by business logic
- remove duplicates
- check member preference for that notification type
- create
notification_recipients - dispatch push to active device tokens
Special case:
- mandatory notifications can bypass preference if required later
Phase 1 recommendation:
CHAPTER_ASSIGNED,GRIEVANCE, andSUBSCRIPTIONshould remain mandatory- user can disable only user-facing feed-style notifications such as referral/session/new-member-joined
Push Delivery Flow
Recommended service split:
NotificationService
Responsibilities:
- create notification row
- resolve recipients
- insert recipient rows
NotificationPreferenceService
Responsibilities:
- load member preference
- decide if member can receive type
PushNotificationService
Responsibilities:
- send to device tokens
- mark delivery status
- isolate FCM/APNS integration
NotificationTemplateService
Responsibilities:
- generate title/message consistently from source models
Proposed App APIs
Base:
/api/app
1. App Settings - Notification Preferences
GET /settings/notifications
Authorization: Bearer {app_access_token}
Response:
{
"message": "Notification settings found.",
"data": [
{
"key": "REFERRAL_RECEIVED",
"type": "REFERRAL",
"label": "Referral Received",
"is_enabled": true
},
{
"key": "ONE_TO_ONE_RECEIVED",
"type": "SESSION",
"label": "One to One Received",
"is_enabled": true
},
{
"key": "NEW_MEMBER_JOINED_CHAPTER",
"type": "NEW_MEMBER_JOINED",
"label": "New Member Joined Chapter",
"is_enabled": true
}
]
}
2. Update Notification Preferences
POST /settings/notifications
Authorization: Bearer {app_access_token}
Request:
{
"settings": [
{
"key": "REFERRAL_RECEIVED",
"is_enabled": false
},
{
"key": "ONE_TO_ONE_RECEIVED",
"is_enabled": true
}
]
}
3. Register Device Token
POST /notifications/device-token
Authorization: Bearer {app_access_token}
Request:
{
"device_token": "push-token",
"platform": "ANDROID",
"device_name": "Samsung A54",
"app_version": "1.0.0"
}
Behavior:
- create or update device token row
- mark active
- attach to logged-in member
4. Notification Inbox
GET /notifications
Authorization: Bearer {app_access_token}
Suggested filters:
pagelimitis_readtype
5. Notification Detail / Read
GET /notifications/{notificationRecipient}
POST /notifications/{notificationRecipient}/read
POST /notifications/read-all
Proposed Admin APIs
Base:
/api/admin
1. Send General Notification
POST /notifications/general
Authorization: Bearer {admin_access_token}
Content-Type: multipart/form-data
Request:
{
"title": "Monthly Meeting Reminder",
"message": "Meeting starts at 6 PM.",
"audience": "CURRENT_CHAPTER_MEMBERS",
"chapter_id": "uuid"
}
With optional image upload.
2. Notification Logs
GET /notifications
Authorization: Bearer {admin_access_token}
Filters:
typedelivery_statusmember_idchapter_idfrom_dateto_date
This is recommended for admin audit/debugging.
Trigger Matrix
| Type | Trigger Point | Delivery Timing |
|---|---|---|
GENERAL |
manual create API | immediate |
SUBSCRIPTION |
notifications:send-subscription-status scheduler |
daily |
REFERRAL |
referral create | immediate |
SESSION |
session create/update | immediate |
BUSINESS_EXCHANGE |
business exchange create | immediate |
EVENT |
event create | immediate |
NEW_MEMBER_JOINED |
approved member joins a chapter | immediate |
EVENT_ATTENDANCE |
event attendance submitted by chapter admin | immediate |
GRIEVANCE |
grievance status update | immediate |
CHAPTER_ASSIGNED |
chapter added to member | immediate |
Suggested Message Pattern
Store final generated title/message in notifications.
Examples:
REFERRAL
- title:
New Referral - message:
{sender_name} sent a referral to {receiver_name}
SESSION
- title:
One to One Session - message:
{sender_name} scheduled a one to one session with {receiver_name}
BUSINESS_EXCHANGE
- title:
Business Exchange - message:
{sender_name} recorded a business exchange of Rs. {amount}
EVENT
- title:
New Event - message:
{event_name} has been created for {chapter_name}
NEW_MEMBER_JOINED
- title:
New Member Joined - message:
{member_name} has joined {chapter_name}.
EVENT_ATTENDANCE
- title:
Event Attendance - message:
Your attendance has been marked for {event_title}.
SUBSCRIPTION
- title:
Subscription Expiring - message:
Your {chapter_name} subscription is expiring on {expiry_date}. - title:
Subscription Expired - message:
Your {chapter_name} subscription expired on {expiry_date}. - title:
Subscription Renewed - message:
Your {chapter_name} subscription has been renewed.
GRIEVANCE
- title:
Grievance Update - message:
Your grievance status has been updated to {status}
CHAPTER_ASSIGNED
- title:
Chapter Assigned - message:
You have been assigned to {chapter_name}
Delivery Ordering
Recommended order of work:
- device token registration
- notification type seed data
- member notification settings API
- notification inbox API
- admin general notification API
- system-triggered notification service
- scheduler for subscription expiry reminders
This order is safer because:
- preferences need types
- delivery needs device tokens
- system triggers should use one common notification service
Required Clarifications Before Coding
These are the points that can create wrong behavior if assumed incorrectly:
-
NEW_MEMBER_JOINED- confirm trigger should be chapter assignment, not bare signup
-
SUBSCRIPTION- confirm exact trigger for
subscribed - is it member approval, chapter assignment, payment success, or some future subscription table event
- confirm exact trigger for
-
EVENT- confirm exact recipient mapping by event visibility
- especially for invite-only events
-
App Settings
- confirm whether Phase 1 UI should show only the 3 toggles in your screenshot
- or all configurable notification types
-
Admin/App source for
GENERAL- your note says “from app”
- confirm whether this means:
- admin portal only
- app-admin from mobile app
- both
Recommended Build Decision
The safest implementation path is:
- build notification data model
- build app settings APIs
- build device token registration
- build notification inbox
- then connect module triggers one by one
That avoids hard-coding push logic inside referral/session/event controllers directly.