NexGate Messaging — Product Requirements & Feature Flows NexGate / QBIT SPARK | Version 1.0 What the messaging platform does — rules, flows, permissions, scenarios Table of Contents Document Purpose User Identity & Discovery Contact Sync Inbox Model Messaging Permissions Call Permissions 1:1 Messaging Flows Group Chat Flows Commerce DM Flows Offer Session Flows Shareable Content Message Interactions Voice & Video Call Flows Group Call Flows Offline & Notification Flows Privacy & Safety Shop Inbox & Staff Access Notification Settings Edge Cases & Scenarios Follow System — Users & Shops Group Join Model Shop Chat Initiation Broadcast Channels — Why Not Needed Commerce Attach — 1:1 vs Group 1. Document Purpose This document defines WHAT NexGate messaging does — the rules, user flows, permissions, and scenarios for every messaging feature. It is the product reference document. It does not describe HOW features are built technically. For technical architecture see: Doc 3: Phase 2 Production Architecture Doc 5: Private Chat & Calls Deep Dive Audience: Product, mobile developers, QA, design. 2. User Identity & Discovery Identity on NexGate Every NexGate user has: @username unique, chosen at registration shown publicly on VP Feed used for messaging: @kibuti Display name full name shown in conversations can differ from username Phone number used for registration + auth NOT shown publicly by default discoverable only with user permission NexGate ID internal UUID never shown to users How Users Find Each Other Discovery mechanism Requires contact sync? ────────────────────────────────────────────────────── VP Feed follow ❌ No VP Shop (buyer/seller) ❌ No Username search (@kibuti) ❌ No QR code scan ❌ No Shareable profile link ❌ No Group chat membership ❌ No Phone number search ❌ No (type manually) Contact sync (phonebook) ✅ Yes (optional) NexGate has multiple discovery paths. Contact sync is one option — not the only one. Discoverability Settings Two independent settings: "Sync my contacts" Find NexGate users in my phonebook Default: OFF (user must opt in) "Let others find me via phone number" If someone has my number → can find me Default: ON (can turn off) These are INDEPENDENT: I can sync contacts without being findable I can be findable without syncing contacts 3. Contact Sync Rules ✅ Optional — never mandatory ✅ Can be enabled/disabled anytime ✅ Can delete synced data anytime from settings ✅ Only finds users who have discoverability ON ❌ Not required to use messaging ❌ Not required for commerce DMs ❌ Contacts data never shared with third parties ❌ Non-NexGate contacts never stored on server Privacy-Preserving Implementation How it works when user accepts: Step 1: Phone numbers hashed ON DEVICE sha256(+255712345678) → hash Actual numbers never leave the device Step 2: Hashes uploaded to NexGate server Not real phone numbers Server cannot reverse hashes Step 3: Server matches hashes Against registered users who have "Let others find me via phone number" = ON Returns: which hashes are NexGate users Step 4: App shows matched users "3 of your contacts are on NexGate" [Connect] buttons shown Non-NexGate contacts: Their hashes deleted from server immediately Never stored Never tracked First Launch Prompt Shown once at first launch after onboarding: ┌──────────────────────────────────────────────┐ │ 📱 Pata Marafiki Wako NexGate │ │ │ │ Ruhusu NexGate kutumia contacts zako │ │ kupata marafiki wanaotumia NexGate tayari │ │ │ │ ✓ Contacts zako hazitashirikiwa na mtu │ │ ✓ Unaweza kufuta ruhusa wakati wowote │ │ ✓ Nambari za simu zinabadilishwa kuwa │ │ msimbo kabla ya kupakiwa │ │ │ │ [Ruhusu Contacts] [Sasa Hivi Sio] │ └──────────────────────────────────────────────┘ If declined: App works fully ✅ Remind once after 7 days Never prompt again after second decline Always accessible in Settings Sync Scenarios Scenario 1 — User accepts sync: Hashes uploaded Matched users shown User can message/follow matched users Sync runs again when contacts change Scenario 2 — User declines sync: App works fully Can still find people via username/QR/VP Feed No contacts uploaded Scenario 3 — User accepts then disables: Settings → Privacy → Contact Sync → OFF All hash data deleted from server Matched contacts remain as connections (connections not removed — only future sync stops) Scenario 4 — User wants to delete contact data: Settings → Privacy → Delete Contacts Data All hashes deleted from server immediately Confirmation shown: "Contacts data imefutwa" Scenario 5 — Contact not on NexGate: Their hash deleted from server User sees option: "Invite Juma to NexGate" Tap → share NexGate invite link via SMS/WhatsApp 4. Inbox Model Two Separate Inboxes Every NexGate user has TWO completely separate inboxes: Personal Inbox: 1:1 DMs with other users (personal) Group chats (personal) Friend conversations Private — only the account owner sees this NEVER accessible to shop staff Shop Inbox (one per shop owned): All customer conversations for that shop Commerce DMs with buyers Shared with authorized shop staff Completely separate from personal inbox Customer sees shop identity — not personal name Inbox Tabs User with 2 shops sees: ┌─────────────────────────────────────────────┐ │ 💬 Inbox │ │ │ │ [Personal ●3] [TechStore ●12] [ClothingHub]│ │ │ │ Personal tab: │ │ Regular DMs + group chats │ │ Private — owner only │ │ │ │ TechStore tab: │ │ Customer commerce conversations │ │ Shared with assigned staff │ │ │ │ ClothingHub tab: │ │ Separate shop conversations │ │ Different staff assigned │ └─────────────────────────────────────────────┘ Staff member (Amina assigned to TechStore): ┌─────────────────────────────────────────────┐ │ [Personal] [TechStore ●12] │ │ │ │ ClothingHub: NOT visible (no access) │ │ Owner personal: NEVER visible │ └─────────────────────────────────────────────┘ Conversation Types Type Owner Participants ──────────────────────────────────────────────────── DM USER 2 users GROUP USER 3-500 users COMMERCE SHOP 1 shop + 1 buyer BROADCAST USER or SHOP 1 sender + N followers 5. Messaging Permissions Who Can Message Who Relationship Can message? How ──────────────────────────────────────────────────────────── Saved contacts (phone) ✅ Always Direct Mutual followers (VP Feed) ✅ Always Direct Commerce relationship ✅ Always Direct (shop inbox) Group chat members ✅ Always Via group only Strangers (no relationship) ⚠️ Request Message request first Blocked users ❌ Never Blocked Message Request System Stranger (no relationship) wants to message you: They can send ONE message request: ┌─────────────────────────────────────────┐ │ 📩 Maombi ya Ujumbe │ │ │ │ Juma Mwangi anataka kukutumia ujumbe │ │ @juma_mwangi · 245 wafuasi │ │ │ │ "Habari, nilikuona kwenye VP Shop..." │ │ │ │ [Kubali] [Kataa] [Zuia] │ └─────────────────────────────────────────┘ If Kubali (Accept): Full conversation opens Juma can now message freely Can call after conversation established If Kataa (Decline): Juma cannot send another request One attempt only per account Juma sees: "Ujumbe haukukubaliwa" If Zuia (Block): Juma blocked immediately Cannot contact again in any way Juma sees: nothing (as if you don't exist) If Ignored (no response): Request stays in request folder Juma cannot send more messages No call attempt allowed Request expires after 30 days Message Request Limits Per account per day: Max 10 message requests sent Prevents spam Per account to same person: 1 request only (ever) If declined → cannot request again Request folder: Separate from main inbox User checks it voluntarily No notification for low-priority requesters (unless they have mutual connections) 6. Call Permissions Permission Tiers Tier Voice call Video call ──────────────────────────────────────────────────────── Saved contacts ✅ ✅ Mutual followers ✅ ✅ Commerce relationship ✅ (shop) ✅ (shop) Active conversation partner ✅ ✅ (message request accepted) Group chat member ✅ (group) ✅ (group) Strangers ❌ ❌ Blocked users ❌ ❌ Commerce Call Rules Buyer can call: The SHOP JID — not the owner personally techstore@shops.nexgate.com Any available staff answers as TechStore Seller (shop) can call: Buyers they have active commerce DM with Call goes from shop JID to buyer Buyer sees "TechStore" calling — not a personal name Protecting personal contact: Owner's personal phone/JID never revealed Commerce calls always through shop identity Staff cannot reveal personal number to customers Anti-Harassment Call Rules Call rate limiting: Max 3 unanswered calls to same person in 24 hours 4th attempt → blocked for 24 hours automatically Resets daily Silence unknown callers: Setting: "Pumzisha simu kutoka kwa wasio contacts" Calls from non-contacts ring silently Notification appears after — not during First call warning: First EVER call from this person: ┌───────────────────────────────────────┐ │ 📞 Simu ya kwanza kutoka │ │ Juma Mwangi (@juma_mwangi) │ │ │ │ [Jibu] [Kataa] [Zuia] │ └───────────────────────────────────────┘ Quick block during call: Report button visible during call One tap → ends call + reports + blocks Privacy Settings for Calls Who can call me: ○ Mutual followers + contacts (default) ○ My contacts only ○ Nobody User can change anytime in Settings → Privacy → Calls Call Permission Check Flow Kibuti tries to call Juma: │ Spring Boot permission check: Is Juma blocked by Kibuti? ❌ → reject immediately Is Kibuti blocked by Juma? ❌ → reject immediately Are they saved contacts? ✅ → allow Are they mutual followers? ✅ → allow Active commerce relationship? ✅ → allow Active accepted conversation? ✅ → allow Does Juma's privacy setting allow? ✅ → allow None of above → ❌ reject │ If rejected: App shows: "Huwezi kupiga simu mtu huyu" No Jingle stanza sent No TURN credentials issued If allowed: TURN credentials generated Jingle session-initiate sent via Ejabberd Juma's device rings 7. 1:1 Messaging Flows Send Text Message Happy path: User types message Taps send Message shows: pending ⏳ Server receives → single tick ✓ Recipient receives → double tick ✓✓ Recipient reads → blue tick ✓✓ Offline path: User taps send Single tick ✓ (server received) Recipient offline → queued FCM push fires to recipient device Recipient comes online → message delivered Double tick ✓✓ appears Send Voice Note User holds mic button: "Kibuti anarekodia..." shown to recipient (if online) Recording in progress Timer shown: 0:01, 0:02... User releases: Voice note sent Waveform shown in thread Duration shown: 0:15 User swipes up while holding: Lock mode — records without holding Release sends User swipes left while holding: Cancel recording — nothing sent Limits: Basic users: max 2 minutes Pro users: max 15 minutes File size: max 16MB Send Media Images: Max 10 images per message Formats: JPG, PNG, WebP, HEIC Max 16MB per image Auto-compressed for EA networks Videos: Max 1 video per message Max 2 minutes (short video) Max 64MB Auto-transcoded to HLS by File Thunder Files/Documents: Max 100MB per file Formats: PDF, DOCX, XLSX, ZIP, etc ClamAV scanned before delivery Available for 30 days then archived 8. Group Chat Flows Create Group User creates group: Tap new group Add members (min 2, max 500) Set group name (required) Set group photo (optional) Tap create Group created: Members notified: "Kibuti amekuongeza kwenye kikundi" All members see welcome message Creator is automatically OWNER Group Roles & Permissions Role Add members Remove Send msg Delete msg Change info ────────────────────────────────────────────────────────────────── OWNER ✅ ✅ ✅ Any msg ✅ ✅ ADMIN ✅ ✅ ✅ Any msg ✅ ✅ MEMBER ❌ ❌ ✅ Own only ✅ ❌ Group Size Limits Free group: max 50 members Pro group: max 500 members At 500 members: Consider using a Public Group instead Public group + announcement mode = large audience Group = two way community discussion Leave / Remove Member leaves: "Juma ameacha kikundi" shown in thread No longer receives messages Admin removes member: "Amina ameondolewa na Kibuti" shown Member notified: "Umeondolewa" Deleted group (owner only): All members removed All messages deleted (for everyone) Cannot be undone 9. Commerce DM Flows Flow 1 — Buyer Initiates from Product Page Step 1: Buyer on product page Sees: [Chat na Muuzaji] button Tap button Step 2: Conversation opens New DM opens OR existing conversation if already had one Conversation type: COMMERCE Conversation owner: SHOP (not personal) Product card auto-appears: ┌──────────────────────────────────────┐ │ 📦 Samsung A15 │ │ TZS 450,000 │ │ Inapatikana: Vipande 12 │ │ TechStore │ │ [Jibu] [Angalia Bidhaa] │ └──────────────────────────────────────┘ NOTE: Price in card is frozen at this moment Public price changes later → does NOT affect this card Step 3: Negotiation Buyer and seller exchange text messages Normal conversation — no restrictions on text Step 4: Agreement reached Seller taps attach (+) → From My Shop Selects product Sets custom price for THIS buyer ONLY Public product price: unchanged ✅ Sends price offer card Step 5: Buyer receives offer ┌──────────────────────────────────────┐ │ 💰 Bei Maalum Kwako │ │ Samsung A15 │ │ ~~TZS 450,000~~ │ │ TZS 400,000 (-TZS 50,000) │ │ Inaisha: dakika 28 │ │ Idadi: [─ 1 +] │ │ [Kataa] [Endelea Kulipa →] │ └──────────────────────────────────────┘ Step 6: Buyer taps "Endelea Kulipa" Redirected to checkout flow (OUTSIDE inbox) Checkout at custom offer price Quantity confirmed in checkout Step 7: Order placed Confirmation message appears in same thread: ┌──────────────────────────────────────┐ │ ✅ Agizo Limethibitishwa │ │ Ord #ORD-XYZ-789 │ │ Samsung A15 × 1 │ │ TZS 400,000 imelipwa │ │ [Fuatilia Agizo] │ └──────────────────────────────────────┘ Flow 2 — Seller Attaches from Inside Any Chat Step 1: Seller inside any DM conversation Taps attach (+) button Menu appears: 📷 Picha 🎵 Sauti 📄 Faili 🏪 Kutoka Dukani ← this one 📅 Tukio 👥 Ununuzi wa Pamoja Step 2: Seller taps "Kutoka Dukani" Their shop product list opens Seller browses and selects product Sets custom price (optional) Sets quantity limit (optional) Adds note (optional): "Bei hii ni leo tu" Taps Send Step 3: Buyer receives offer card Same offer card UI as Flow 1 Same checkout redirect Same order confirmation in thread Commerce DM Rules ✅ Buyer can initiate from any product page ✅ Seller can attach from inside any conversation ✅ Custom price is private to this buyer only ✅ Public product price never changes ✅ Checkout always happens outside inbox ✅ Order confirmation appears in thread ✅ Multiple offers allowed in one conversation ❌ Buyer cannot request specific price (only negotiate via text) ❌ Offer cannot be edited after sending (send new one instead) ❌ Offer cannot be forwarded to other conversations ❌ Order confirmation cannot be deleted or edited 10. Offer Session Flows Offer Lifecycle PENDING → offer sent, waiting for buyer response │ ├──▶ DECLINED buyer tapped "Kataa" │ seller notified │ seller can send new offer │ ├──▶ EXPIRED timer ran out │ both parties notified in thread │ seller can send new offer │ ▼ ACCEPTED → buyer tapped "Endelea Kulipa" │ buyer enters checkout ▼ CHECKOUT → buyer in payment flow │ ├──▶ ABANDONED buyer left checkout without paying │ offer returns to PENDING? NO │ offer marked ABANDONED │ seller must send new offer │ ▼ COMPLETED → order placed successfully │ cannot be reversed here (order system handles) ▼ CANCELLED → order cancelled (handled by order system) offer marked CANCELLED Multiple Offers — Same Conversation Seller can send multiple offers: Each is independent session Previous offers remain in thread with their status Thread shows history: ┌──────────────────────────────────────┐ │ 💰 Bei Maalum: TZS 430,000 │ │ ❌ Ilikataliwa │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 💰 Bei Maalum: TZS 410,000 │ │ ⏰ Imeisha muda │ └──────────────────────────────────────┘ ┌──────────────────────────────────────┐ │ 💰 Bei Maalum: TZS 400,000 │ │ ✅ Imekubaliwa │ └──────────────────────────────────────┘ Offer Expiry Scenarios Scenario 1 — Expires while buyer is reading: Offer card shows live countdown timer Timer hits 0:00 Card updates: "Imeisha muda" Proceed button disabled automatically Buyer sees: [Omba Bei Mpya] button Scenario 2 — Expires while buyer in checkout: Buyer was on checkout page when offer expired Checkout validates offer at payment time If expired: payment rejected Buyer returned to conversation "Bei yako imeisha muda. Omba bei mpya" Scenario 3 — Seller sends same price again: Allowed — new offer session created New 30-minute timer Both offers visible in thread (old + new) Scenario 4 — Buyer tries to use expired offer link: Deep link from notification opens expired offer Shows: "Bei hii imeisha muda" [Rudi kwa Mazungumzo] button Price Security Rules ✅ Offer price stored server-side only ✅ Checkout validates price from server (not client) ✅ Client cannot manipulate price ✅ Offer is single-use (cannot complete twice) ✅ Offer belongs to specific buyer (others cannot use) ❌ Seller cannot change price after offer sent (must send new offer) ❌ Buyer cannot change price ❌ Public product price never affected 11. Shareable Content What Can Be Shared Into Any DM or Group Content Who can share Restrictions ──────────────────────────────────────────────────────────── Text message Anyone None Voice note Anyone Max 2 min (basic) Image/Video Anyone Size limits Product card Anyone Shows public price Custom price offer Seller only Private — NOT forwardable Event card Anyone None Bei ya pamoja card Anyone None VP Feed post Anyone None VP Live stream Anyone None Audio Space Anyone None File/Document Anyone Max 100MB Forwarding Rules Forward chain tracking: chain 1: "↪ Imetumwa kutoka Juma Mwangi" chain 2-4: "↪ Imetumwa" chain 5+: "↪ Imetumwa mara nyingi" (misinformation warning — different icon) Multi-forward limit: Max 5 conversations per forward action Prevents spam broadcasting Cannot forward: Custom price offers (private deal) Order confirmations (private record) Payment confirmations (private record) System messages 12. Message Interactions Editing Messages Rules: ✅ Only original sender can edit ✅ Text messages only (not media captions yet) ✅ Within 15 minutes of sending ✅ Shows "Imehaririwa" label after edit ✅ Original send time stays the same ✅ Works in group chats (sender edits their own) ❌ Commerce cards cannot be edited (immutable) ❌ System messages cannot be edited ❌ Voice notes cannot be edited ❌ After 15 minutes: edit option disappears UI: Long press message → [Hariri] option (within 15 min) After edit: message updates in place "Imehaririwa" label appears below message Position in thread unchanged (no jump to bottom) Deleting Messages Delete for Me: ✅ Any message, any time, no limit ✅ Only removed from your view ✅ Recipient still sees it ✅ Works on any message type Delete for Everyone: ✅ Only original sender ✅ Within 15 minutes of sending ✅ Removed from all screens ✅ Shows: "Ujumbe huu umefutwa" ✅ Works in group chats ❌ Commerce cards: NOT allowed ❌ System messages: NOT allowed ❌ After 15 minutes: option disappears Nothing is ever permanently deleted: Soft delete only For legal compliance + dispute resolution Admin can view deleted messages for support cases Users cannot recover deleted messages Reactions Rules: ✅ Any message can be reacted to ✅ One reaction per user per message ✅ Change reaction: send new emoji (replaces old) ✅ Remove reaction: tap same emoji again ✅ Works on commerce cards (reactions don't modify content) ❌ System messages: no reactions Available emojis at launch: ❤️ 👍 😂 😮 😢 🙏 Expand to full emoji keyboard: Phase 3 Group reaction display: 1 type: "👍 3" 2 types: "👍 3 ❤️ 2" 3+ types: "👍 3 ❤️ 2 +2 zaidi" Tap reaction to see who reacted: Bottom sheet opens List of names per emoji Replying to Messages Rules: ✅ Anyone in conversation can reply to any message ✅ Reply shows quoted original above new message ✅ Tap quote → scrolls to original ✅ Works in group chats ✅ Works on any message type ❌ Cannot reply to deleted messages (shows: "Ujumbe umefutwa") UI: Swipe right on message → reply mode OR long press → [Jibu] option Quote appears in input field Send normally Forwarding Rules: ✅ Anyone can forward allowed content ✅ Max 5 conversations per forward action ✅ Forward chain tracked and shown ✅ Original sender name shown (chain 1 only) ✅ Media: references original file (no re-upload) ❌ Custom price offers: not forwardable ❌ Order/payment records: not forwardable ❌ System messages: not forwardable UI: Long press message → [Tuma] option Conversation picker opens Select up to 5 conversations Tap send 13. Voice & Video Call Flows Initiating a Call Kibuti taps call button on Juma's profile or conversation: │ Permission check: Relationship exists? ✅ Juma's privacy allows? ✅ Kibuti not blocked? ✅ │ TURN credentials generated (server) │ Juma's phone rings: ┌──────────────────────────────────────┐ │ │ │ 📞 Simu Inayoingia │ │ │ │ [Kibuti Mwangi] │ │ @kibuti │ │ │ │ [❌ Kataa] [✅ Jibu] │ │ │ └──────────────────────────────────────┘ Works even if: App is closed (FCM HIGH wakes it) Screen is locked (full screen notification) App is in background During a Call — Controls Voice call controls: 🔇 Mute/unmute microphone 🔊 Speaker on/off 📷 Enable camera (upgrade to video) ❌ End call Video call controls: 🔇 Mute/unmute microphone 📷 Camera on/off 🔄 Switch camera (front/rear) 🖥️ Share screen 🔊 Speaker on/off ❌ End call Switching Audio ↔ Video Voice → Video: Kibuti taps camera button during voice call Juma sees: "Kibuti anataka kuongeza video" Auto-accepted (based on Juma's settings) OR Juma taps Accept Video starts — same call session continues Audio uninterrupted during upgrade Video → Audio (manual): Kibuti taps camera OFF Video stops immediately for both Audio continues No renegotiation needed Video → Audio (automatic): Network degrades below video threshold Video disabled automatically Banner shown: "Video imezimwa — mtandao dhaifu" Audio continues Video resumes when network improves Screen Sharing Start screen share: Tap screen share icon during video call System permission dialog appears (Android/iOS) "Ruhusu NexGate kunasa skrini yako?" User accepts Screen share starts Other party sees your screen During screen share: Your camera: small PiP (picture in picture) Their view: your screen (large) + your face (small) Your view: normal call view + "Unaonyesha skrini" banner Stop screen share: Tap stop button Returns to normal video call Call Quality Indicators Signal bars shown during call: ████ Excellent (WiFi / 4G strong) ███░ Good (4G) ██░░ Fair (3G) — may show quality banner █░░░ Poor (2G) — video disabled, audio only ░░░░ Very poor — "Mtandao dhaifu sana" banner Quality banner examples: "Ubora wa sauti umepungua kwa sababu ya mtandao" "Video imezimwa — data ndogo" "Unaunganika tena..." Call End Scenarios Normal end: One party taps end Other party sees call ended Duration shown in conversation: "Simu ya dakika 4:32" Declined: Juma taps Kataa Kibuti sees: "Simu ilikataliwa" Missed call notification NOT sent (was declined) No answer (timeout 45 seconds): Kibuti sees: "Hakujibu" Juma sees: "Simu iliyokosekana kutoka Kibuti" notification Network failure: Both lose connection App attempts reconnect (10 seconds) If reconnect fails: Call marked as FAILED "Simu ilikatizwa" shown to both Kibuti can redial 14. Group Call Flows Starting a Group Call From a group chat: Tap call icon in group header Choose: Voice only OR Video All group members receive incoming call notification Members who join → enter call Members who don't → miss it (missed call shown) From a 1:1 conversation: Not supported directly Must create group first OR use "Add person" button during active 1:1 call Add person to active 1:1 call: During call → tap "Ongeza Mtu" Pick from allowed contacts They receive group call invitation They join → 1:1 becomes group call Group Call Limits Voice only group call: Max 8 participants (comfortable for 3G) Up to 12 possible but discouraged on EA networks Video group call: Max 4 video feeds shown simultaneously 5th person onwards: audio only tile shown Active speaker highlighted (larger tile) Tap any tile to pin/feature them Layout options: Grid view: all tiles equal size Speaker view: active speaker large, others small Auto: switches based on who is talking Group Call Permission Rules Within a group chat: Any member can start group call ✅ Any member can join ✅ No extra permission check needed (group membership = call permission) Adding someone outside the group: Same call permission check as 1:1 Must have relationship (contact/follower/commerce) If no relationship → cannot add 15. Offline & Notification Flows Message Notification Levels Level When FCM SMS after ────────────────────────────────────────────────────────────── CRITICAL Order placed/paid/failed ✅ 0 min Delivery update Payment confirmation IMPORTANT Commerce DM from buyer ✅ 10 min Custom price offer received Bei ya pamoja threshold reached Call missed (commerce context) NORMAL Regular DM ✅ Never Group message Reaction Follow notification Offline Message Delivery User offline — message sent to them: │ ├── FCM HIGH priority push fired immediately │ (all message levels) │ ├── CRITICAL: Textfy SMS sent simultaneously │ ├── IMPORTANT: Textfy SMS after 10 min │ (if FCM not acknowledged) │ └── Message queued in RabbitMQ User comes back online: │ ├── WS connects → presence registered ├── Queued messages drained (priority order) │ CRITICAL first → IMPORTANT → NORMAL │ └── Catch-up banner shown: "Umekosa: maagizo 2, ujumbe 8" [Angalia Maagizo] [Ona Ujumbe] Textfy SMS Templates Order placed (to seller): "NexGate: Agizo jipya kutoka [Buyer]! TZS [amount]. Kagua: nexgate.app/orders/[id]" Payment received: "NexGate: Malipo ya TZS [amount] yamepokelewa kutoka [Buyer]. nexgate.app/wallet" Commerce DM (to seller): "NexGate: [Buyer] anakuuliza kuhusu [Product]. Jibu: nexgate.app/chat/[id]" Bei ya pamoja: "NexGate: Watu [n]/[target] wamejiunga! nexgate.app/group-buy/[id]" All SMS: Swahili first ✅ Deep link included ✅ Shop name shown (not staff name) ✅ 16. Privacy & Safety Privacy Settings Matrix Setting Options Default ──────────────────────────────────────────────────────────────── Who can message me Everyone / Followers Followers + Contacts / Contacts + Contacts / Nobody Who can call me Followers+Contacts / Contacts Contacts / Nobody only Last seen Everyone / Contacts / Contacts Nobody Profile picture Everyone / Contacts / Everyone Nobody Be found by phone number On / Off On Contact sync On / Off Off Read receipts (blue ticks) On / Off On (off = others see delivered but not read) Block System User A blocks User B: User B cannot: ❌ See User A's profile ❌ Send messages to User A ❌ Call User A ❌ See User A in search ❌ See User A's VP Feed posts ❌ See User A's online status User B sees: Profile: "Mtumiaji huyu hayupo" Messages: appear sent but never delivered User A can: ✅ Unblock anytime from Settings ✅ See User B's profile still (A blocked B — not reverse) Group chats: If in same group: messages visible but cannot DM or call each other directly Admin can remove either from group Report System Report options: Spam Harassment / Vitisho Inappropriate content Fake account Scam / Udanganyifu Other Report flow: Long press message → [Ripoti] OR profile → [...] → [Ripoti] Choose reason Optional: add description Submit After report: User not notified they were reported Report goes to NexGate moderation queue Automatic temporary restrictions may apply for high-volume reporters Block on report: "Ripoti na Zuia" option available Blocks immediately + sends report Safety for Women — Specific Features This is important for EA platform trust: Silence unknown callers: ON by default for new accounts Calls from non-contacts ring silently User sees missed call — no disruption Call rate limiting: 3 unanswered calls per day to same person Automatic block after 4th attempt Quick block during call: One tap visible during any call Ends + reports + blocks in single action Message request system: Strangers cannot freely DM Must send request first User controls who enters their inbox Default privacy settings: New accounts: strict defaults Users open up if they choose Better to protect by default 17. Shop Inbox & Staff Access The Golden Rule — No Staff Name Ever This is the most important rule in this section: Customer NEVER sees staff name In ANY communication channel: ❌ Not in DM replies ❌ Not in commerce messages ❌ Not in offer cards ❌ Not in order updates ❌ Not in system messages ❌ Not in call screen ("TechStore calling" not "Amina calling") ❌ Not in read receipts ❌ Not in typing indicators ("TechStore anaandika..." not "Amina...") ❌ Not in voice/video calls ❌ Not in notifications Customer always sees: ✅ Shop name only: "TechStore" ✅ Shop avatar only ✅ Shop JID only Why this rule exists: Customer relationship is with THE SHOP Not with individual staff members Staff privacy protected Staff turnover invisible to customer Brand consistency always maintained Cannot be broken by any staff action System enforces this — not just a guideline How Others Handle Staff Access Facebook Pages: Staff have OWN Facebook accounts Owner assigns role via Page Settings Staff switches to page context Posts/replies as page — not personally ✅ No password sharing WhatsApp Business API: Connected to CRM (Zendesk, Freshdesk) Each agent logs into CRM with own account CRM sends via WhatsApp API as business Customer sees business name only ✅ No password sharing Shopify: Owner invites staff via email Staff creates own Shopify login Access scoped to their role Customer always sees store name ✅ No password sharing NexGate follows same pattern: Staff have own NexGate accounts Owner invites staff to shop Staff accesses shop context Customer sees shop name only ✅ No password sharing ever Shop Tiers Basic shop (free): Owner manages inbox alone No staff assignment Standard inbox features Shop name shown to customers ✅ Pro shop (paid): Staff roles unlocked Multiple staff share shop inbox Advanced analytics Full audit logs Priority support Staff management dashboard Shop name shown to customers ✅ (same rule) Staff Roles & Permissions Role Inbox Send msg Products Analytics Settings Staff mgmt ────────────────────────────────────────────────────────────────────────────── OWNER ✅ ✅ ✅ ✅ ✅ ✅ MANAGER ✅ ✅ ✅ ✅ ❌ ❌ SUPPORT_AGENT ✅ ✅ ❌ ❌ ❌ ❌ READ_ONLY 👁️ only ❌ ❌ ❌ ❌ ❌ Notes: OWNER: full control — only they can invite/remove staff only they can change shop tier only they can delete shop MANAGER: day-to-day shop management can send price offers can view product catalog cannot change settings or manage staff SUPPORT_AGENT: inbox only can reply to customers as shop can send price offers cannot see/edit products READ_ONLY: view conversations only cannot reply useful for supervisors/auditors Staff Invitation Flow Step 1 — Owner sends invitation: Shop Settings → Staff → Alika Mfanyakazi ┌──────────────────────────────────────────┐ │ ➕ Alika Mfanyakazi │ │ │ │ Barua pepe au nambari ya simu: │ │ [amina@gmail.com ] │ │ │ │ Jukumu: │ │ ○ Msimamizi (Manager) │ │ ● Wakala wa Msaada (Support Agent) │ │ ○ Soma Tu (Read Only) │ │ │ │ [Tuma Mwaliko] │ └──────────────────────────────────────────┘ System: Generate secure invitation token Token expires: 48 hours Send to Amina via SMS + email: "Kibuti amekualika kuwa mfanyakazi wa TechStore kwenye NexGate. Bonyeza hapa: nexgate.app/invite/TOKEN Mwaliko unaisha baada ya masaa 48." Step 2 — Staff receives invitation: Case A — Amina already has NexGate account: Taps invitation link Logs in with OWN credentials Sees invitation screen: ┌──────────────────────────────────────────┐ │ 🏪 Mwaliko wa TechStore │ │ │ │ Kibuti Mwangi anakualika kujiunga na │ │ TechStore kama: │ │ Wakala wa Msaada │ │ │ │ Utaweza: │ │ ✓ Kujibu ujumbe wa wateja │ │ ✓ Kutuma ofa za bei │ │ ✓ Kuona mazungumzo yote ya duka │ │ │ │ Hutaweza: │ │ ✗ Kuona mazungumzo ya kibinafsi ya owner│ │ ✗ Kubadilisha mipangilio ya duka │ │ │ │ [Kubali] [Kataa] │ └──────────────────────────────────────────┘ Taps Kubali → linked to TechStore immediately Case B — Amina has no NexGate account: Taps invitation link Registration page opens Registers with own phone number + PIN Invitation auto-accepted after registration Linked to TechStore as Support Agent Case C — Token expired (>48 hours): "Mwaliko huu umeisha muda" Owner must send new invitation Step 3 — Staff accesses shop: Amina logs into NexGate normally: Her own phone number Her own PIN Her own account entirely Her inbox shows tabs: [Personal ●3] [TechStore ●12] Personal tab: Her own DMs and groups Completely private TechStore cannot see this TechStore tab: All TechStore customer conversations Shared with all TechStore staff She replies as "TechStore" ✅ Step 4 — Staff replies to customer: Amina opens TechStore conversation Context indicator clearly shown: ┌──────────────────────────────────────────┐ │ 🏪 Unajibu kama: TechStore │ │ (Wakala wa Msaada) │ │──────────────────────────────────────────│ │ Customer: "Je, Samsung A15 ipo?" │ │ │ │ [Andika ujumbe kama TechStore...] │ │ │ │ [📷] [🎤] [📎] [➤ Tuma] │ └──────────────────────────────────────────┘ Customer receives: TechStore: "Ndio, Samsung A15 ipo!" No "Amina" anywhere ✅ No mention of staff ✅ Just TechStore brand ✅ What Customer Sees vs What Owner Sees Customer view of conversation: 10:30 [Customer]: "Je, A15 ipo?" 10:32 [TechStore]: "Ndio, ipo! Bei TZS 450,000" 10:35 [Customer]: "Naweza kupata punguzo?" 10:36 [TechStore]: "Nitaona ninachoweza kufanya" 10:40 [TechStore]: 💰 Bei Maalum: TZS 400,000 [Offer card] Customer never knows: Who replied at 10:32 Who replied at 10:36 Whether same person replied How many staff exist Whether it's the owner or staff Owner/Manager audit view: 10:30 Customer: "Je, A15 ipo?" 10:32 Amina (Support Agent): "Ndio, ipo! Bei TZS 450,000" 10:35 Customer: "Naweza kupata punguzo?" 10:36 Amina (Support Agent): "Nitaona ninachoweza kufanya" 10:40 John (Manager): Bei Maalum TZS 400,000 imetumwa Staff Identity Rules — Complete List Staff name NEVER appears in: ❌ DM message content ("TechStore" always) ❌ Offer cards (shop name only) ❌ Order confirmation messages ❌ Typing indicator ("TechStore anaandika...") ❌ Read receipts (customer sees shop read it) ❌ Call screen ("TechStore" calling / answering) ❌ Missed call notification ("Simu kutoka TechStore") ❌ Voice note sender name ❌ Reaction attribution (customer sees shop reacted) ❌ System messages Staff identity ONLY appears in: ✅ Internal audit log (owner + manager view) ✅ Staff management dashboard ✅ Internal analytics (who handled most conversations) ✅ Staff's own inbox context indicator "Unajibu kama: TechStore (Wakala wa Msaada)" This rule is enforced by the system: Staff JID: techstore@shops.nexgate.com/amina Customer sees: techstore@shops.nexgate.com Resource (/amina) stripped before sending to customer Cannot be overridden by any staff action Typing Indicator — Shop Name Not Staff Amina is typing reply: Customer sees: "TechStore anaandika..." NOT: "Amina anaandika..." NOT: "Mfanyakazi anaandika..." Two staff typing simultaneously: Customer sees: "TechStore anaandika..." (same — no way to know it's two people) This is correct behavior Ejabberd handles this via shop JID: Composing stanza from: techstore@shops.nexgate.com Resource stripped before routing to customer Voice/Video Call — Staff Anonymity Customer calls TechStore: Whoever answers (any available staff) Call screen shows to customer: "TechStore" (shop name) Shop avatar NOT staff name Staff receives call: Their screen shows: Customer name (from conversation) "Simu kwa TechStore" indicator They answer as TechStore Staff initiates call to customer: Customer's incoming call screen shows: "TechStore" calling NOT "Amina" calling NOT a personal number Call log in customer's conversation: "Simu na TechStore — dakika 4:32" NOT "Simu na Amina" Owner Managing Staff Staff management dashboard (owner only): ┌──────────────────────────────────────────────────────┐ │ 👥 Wafanyakazi wa TechStore │ │ │ │ Amina Hassan │ │ Support Agent · Imeunganishwa: Jan 15, 2026 │ │ Imetumika mara ya mwisho: Leo 14:30 │ │ Mazungumzo 47 wiki hii │ │ [Badilisha Jukumu] [Simamisha] [Ondoa] │ │ │ │ John Doe │ │ Manager · Imeunganishwa: Feb 1, 2026 │ │ Imetumika mara ya mwisho: Jana 09:15 │ │ Mazungumzo 23 wiki hii │ │ [Badilisha Jukumu] [Simamisha] [Ondoa] │ │ │ │ [+ Alika Mfanyakazi Mpya] │ └──────────────────────────────────────────────────────┘ Owner actions: ✅ Invite new staff ✅ Change role (Manager ↔ Support Agent ↔ Read Only) ✅ Suspend temporarily (keeps access frozen, not removed) ✅ Remove permanently (instant access revocation) ✅ View activity stats per staff ✅ View full audit log Remove staff — what happens: Access revoked IMMEDIATELY TechStore tab disappears from their inbox All pending conversations stay in shop inbox (conversations not lost — just staff can't see them) No notification sent to customers Historical messages remain attributed in audit log Multi-Shop Staff Amina works for two shops: Shop A: TechStore (Support Agent) Shop B: ClothingHub (Manager) — Different owner (different business) Her inbox shows: [Personal] [TechStore] [ClothingHub] Rules: TechStore conversations: visible ✅ ClothingHub conversations: visible ✅ Each shop completely isolated from other ✅ TechStore cannot see ClothingHub data ✅ ClothingHub cannot see TechStore data ✅ Her personal inbox: only she sees ✅ Security Rules ✅ Each staff has own NexGate account ✅ Owner never shares password ✅ Invitation token secure + expires 48 hours ✅ Role-based access strictly enforced ✅ Customer always sees shop name (system enforced) ✅ Audit log tracks every action ✅ Owner can revoke access instantly ✅ Staff cannot access unassigned shops ✅ Staff cannot access owner personal inbox ✅ Staff cannot change their own role ✅ Staff cannot invite other staff (Manager+ only) ✅ Removed staff cannot export conversation history ❌ No shared passwords ever ❌ No staff name to customers ever ❌ No personal contact revealed to customers ❌ No cross-shop data access ❌ No personal inbox access by staff Database Schema shop_staff_invitations ───────────────────────────────────────────── invitation_id UUID shop_id UUID invited_by UUID owner userId invitee_email TEXT invitee_phone TEXT role ENUM MANAGER / SUPPORT_AGENT / READ_ONLY token_hash TEXT hashed secure token status ENUM PENDING / ACCEPTED / DECLINED / EXPIRED expires_at TIMESTAMPTZ 48 hours from creation created_at TIMESTAMPTZ responded_at TIMESTAMPTZ shop_staff_members ───────────────────────────────────────────── id UUID shop_id UUID user_id UUID staff NexGate userId role ENUM OWNER / MANAGER / SUPPORT_AGENT / READ_ONLY invited_by UUID joined_at TIMESTAMPTZ last_active_at TIMESTAMPTZ status ENUM ACTIVE / SUSPENDED / REMOVED suspended_at TIMESTAMPTZ removed_at TIMESTAMPTZ removed_by UUID shop_staff_audit_log ───────────────────────────────────────────── log_id UUID shop_id UUID staff_user_id UUID action ENUM MESSAGE_SENT / OFFER_SENT / OFFER_CREATED / CALL_ANSWERED / PRODUCT_VIEWED / MEMBER_ADDED / MEMBER_REMOVED / ROLE_CHANGED conversation_id UUID message_id UUID timestamp TIMESTAMPTZ metadata JSONB action-specific details 18. Notification Settings Per-Conversation Settings Each conversation has: Notifications: ○ All messages ○ Mentions only (groups) ○ Muted (until: 8 hours / 1 week / forever) Media auto-download: ○ WiFi only ○ WiFi + Mobile data ○ Never Global Notification Settings Message notifications: On / Off Call notifications: On / Off Reaction notifications: On / Off Group notifications: On / Off Commerce notifications: On / Off (cannot turn off CRITICAL) Sound: Default / Custom / Silent Vibration: On / Off In-app preview: Show / Hide content CRITICAL Notifications — Always On These cannot be turned off by user: Order placed (seller receives) Payment confirmed (buyer receives) Payment failed (buyer receives) Order cancelled (both receive) Reason: Financial events User could miss critical money information Platform liability without guaranteed delivery Textfy SMS fallback ensures they always arrive 19. Edge Cases & Scenarios Messaging Edge Cases User deletes their account: Their messages remain visible in conversations Name shows: "Mtumiaji aliyefuta akaunti" Profile picture: default avatar Cannot be messaged or called User changes username: All conversations update automatically Old username links still work (redirect) No broken references Very long message (>4000 chars): Truncated in thread: first 200 chars + "...Soma zaidi" Tap to expand full message Not split into multiple messages Same message sent twice (duplicate): Detected by temp_id Only one stored in DB User sees single message (not duplicate) Message sent to blocked user: Appears sent (single tick) Never delivered (stays at single tick) User not informed of block Commerce Edge Cases Product goes out of stock while offer pending: Offer still valid (was frozen at send time) Checkout validates stock at payment time If out of stock at payment: Payment rejected "Bidhaa hii imeisha" Buyer returned to conversation Seller notified to resend offer or cancel Product price changes while offer pending: Offer price locked at creation ✅ Public price change does NOT affect offer Buyer pays the offer price always Seller deletes product while offer pending: Offer still valid (snapshot frozen) Checkout validates product existence If product deleted at payment: Payment rejected "Bidhaa hii haipatikani tena" Two buyers get offers for last item: Both offers exist simultaneously First to complete checkout gets the item Second buyer's payment rejected: "Bidhaa hii imeisha" Standard e-commerce race condition handling Seller sends offer to wrong person: Cannot retract offer Offer expires naturally Seller can contact NexGate support if urgent Buyer in checkout when offer expires: Payment attempt fails "Bei yako imeisha muda" Must request new offer from seller Call Edge Cases Call drops mid-conversation: WebRTC detects loss Auto-reconnect attempted (10 seconds) If reconnect success: call continues If reconnect fails: call ended Both see: "Simu ilikatizwa" Kibuti can redial immediately Both call each other simultaneously: Race condition — both see "Simu inayoingia" System picks one (first to reach server) Other cancelled automatically One call established Call during active call: Second call: goes to missed calls Busy signal not sent (no UX for this) After first call: notification of missed call Phone runs out of battery during call: WebRTC detects disconnect Same as network drop Other party sees: "Simu ilikatizwa" Someone calls during Do Not Disturb (DND): DND on device: depends on device DND rules NexGate mute: call goes to missed calls silently CRITICAL commerce calls: bypass mute (configurable) Group Chat Edge Cases Owner leaves group: Must transfer ownership first OR system auto-assigns to oldest admin OR if no admins: oldest member becomes owner Last person leaves group: Group archived automatically Messages preserved for 30 days Then permanently deleted Adding member who blocked you: Cannot add blocked users to groups System rejects silently No error shown (privacy) Member blocked in group: They stay in group (cannot remove via block) Cannot DM each other Can both still see group messages Admin can remove either from group 20. Follow System — Users & Shops Two Entity Types — Two Sigils NexGate has two distinct entity types: User: @kibuti (a person) Shop: $techstore (a business) The sigil makes it immediately clear what type of entity you are interacting with @ = social relationship $ = commercial relationship Follow Button — Users On @kibuti profile page: [+ Follow] What following a user gives you: Their VP Feed posts appear in your feed Their VP Live streams appear in your feed Their events appear in your feed You can message them (mutual relationship) They can message you back Mutual follow: Both @kibuti and @juma follow each other = stronger relationship = can call each other = message requests not needed One-way follow: @kibuti follows @juma @juma does NOT follow @kibuti Kibuti sees Juma's content Juma does not see Kibuti's content Kibuti can message Juma (follower relationship) Juma sees message request (not mutual) Follow Button — Shops On $techstore profile page: [+ Follow Shop] NOT "Subscribe" because: Subscribe implies payment (Netflix, Spotify) EA users might think it costs money Follow is free and familiar ✅ "Follow Shop" label sets clear expectation NOT just "Follow" because: "Follow Shop" makes clear it's a business Sets expectation: commercial content Not personal/social content What following a shop gives you: Shop products appear in your VP Feed Shop promotions appear in feed Shop VP Live streams appear in feed Shop events appear in feed FCM notification: "TechStore posted new products" Commerce DM permission (can initiate chat) Can be invited to shop customer groups Can receive Bei ya pamoja from shop Shop Following Rules Shops do NOT follow anyone: No "Following" count on shop profile Shops are followed — they don't follow Like a Facebook Page Like a YouTube channel Shop profile shows: Followers: 3,420 ← people following the shop No "Following" count User profile shows: Followers: 1,240 Following: 856 Both counts shown (social graph) Unfollow Behavior User unfollows @kibuti: Kibuti NOT notified ✅ (standard) Kibuti's content leaves your feed ✅ Can still message Kibuti ✅ (if previously connected) Relationship weakened but not broken User unfollows $techstore: Shop NOT notified ✅ Shop content leaves your feed ✅ Can still initiate commerce DM ✅ (unfollow ≠ block) Cannot be added to shop groups ✅ (lost follower relationship) Existing order conversations remain ✅ Follow Count Display @kibuti profile: ┌────────────────────────────────┐ │ @kibuti │ │ Kibuti Mwangi │ │ │ │ 1,240 856 │ │ Followers Following │ │ │ │ [+ Follow] [Message] │ └────────────────────────────────┘ $techstore profile: ┌────────────────────────────────┐ │ $techstore │ │ TechStore │ │ Electronics · Dar es Salaam │ │ │ │ 3,420 │ │ Followers │ │ │ │ [+ Follow Shop] [Message] │ └────────────────────────────────┘ 21. Group Join Model Two Group Types PRIVATE GROUP: Closed — controlled membership Not discoverable in search Default when creating a group Like WhatsApp groups PUBLIC GROUP: Open — anyone can join Discoverable in NexGate search Explicit choice by creator Like Telegram public groups How People Join Groups — The Philosophy NexGate principle: Nobody ends up in a group without choosing to be there No WhatsApp-style direct add (added before you know it) Two mechanisms instead: 1. Consent DM invitation (proactive) 2. Invite link (self-service) Both require the person to actively choose to join Mechanism 1 — Consent DM Invitation Admin selects people from: Their contacts ✅ Their followers ✅ Their commerce relationships ✅ NOT random strangers ❌ Each selected person receives a DM: ┌──────────────────────────────────────────┐ │ 📨 Group Invitation │ │ │ │ Kibuti Mwangi invited you to join: │ │ │ │ 🏘️ Business Friends │ │ 47 members · Private Group │ │ "Discussion for Dar founders" │ │ │ │ [Accept & Join] [Decline] │ └──────────────────────────────────────────┘ If Accept: member immediately ✅ If Decline: not added ✅ admin NOT notified (privacy) If Ignored: auto-declined after 48 hours ✅ Why this model: ✅ Familiar to EA users (like WhatsApp add) ✅ But with consent (unlike WhatsApp) ✅ Commerce groups work (seller invites customers) ✅ Event groups work (organizer invites attendees) ✅ Family/friends groups easy to start ✅ User always in control Mechanism 2 — Invite Link Admin generates invite link: nexgate.app/join/abc-xyz-def Person taps link → sees group preview: PRIVATE GROUP link: ┌─────────────────────────────────────────┐ │ 🔒 Private Group │ │ Business Friends │ │ 47 members │ │ Created by Kibuti Mwangi │ │ │ │ This group requires admin approval │ │ │ │ [Request to Join] │ └─────────────────────────────────────────┘ Admin sees request: Name, username, mutual connections [Approve] [Decline] If approved → member ✅ If declined → person not notified (privacy) PUBLIC GROUP link: ┌─────────────────────────────────────────┐ │ 🌍 Public Group │ │ Dar Tech Community │ │ 1,247 members │ │ Created by @kibuti │ │ "Discussion for Dar tech founders" │ │ │ │ [Join Group] │ └─────────────────────────────────────────┘ Tap [Join Group] → member immediately ✅ No approval needed Invite Link Settings Admin controls (Group Settings → Invite Link): ┌──────────────────────────────────────────┐ │ Invite Link │ │ nexgate.app/join/abc-xyz │ │ [Copy] [Share] [Revoke] │ │ │ │ Expiry: │ │ ● Never │ │ ○ 24 hours │ │ ○ 7 days │ │ ○ 30 days │ │ │ │ Max joins: [Unlimited ▾] │ │ Options: 10 / 25 / 50 / 100 / Unlimited │ └──────────────────────────────────────────┘ Revoke link: Old link immediately dead "This invite link is no longer valid" New link auto-generated Invitation Limits (Anti-Spam) Per group per day: Admin can send max 50 consent DM invitations Prevents mass-invite spam Per user per day: User can receive max 10 group invitations 11th → goes to group requests folder User reviews when ready Rate limiting: If 80%+ of your invitations declined System flags account Temporary invite restriction applied Group Admin System OWNER (1 per group): Created the group OR ownership transferred Cannot be removed by anyone Full control over everything Can delete the group Can transfer ownership ADMIN (multiple): Appointed by OWNER Can add/remove members Can remove any message Can pin messages Can change group info Cannot remove OWNER Cannot remove other ADMINS MEMBER (everyone else): Can send messages Can react, reply, forward Can delete own messages only Cannot manage others Group Admin Permissions Matrix Action Owner Admin Member ──────────────────────────────────────────────────── Send messages ✅ ✅ ✅ Delete own messages ✅ ✅ ✅ Delete any message ✅ ✅ ❌ Send consent DM invite ✅ ✅ ❌ Remove members ✅ ✅ ❌ Make someone admin ✅ ❌ ❌ Remove admin ✅ ❌ ❌ Change group name/photo ✅ ✅ ❌ Generate invite link ✅ ✅ ❌ Revoke invite link ✅ ✅ ❌ Pin messages ✅ ✅ ❌ Announcement mode ✅ ❌ ❌ Transfer ownership ✅ ❌ ❌ Delete group ✅ ❌ ❌ Leave group ✅* ✅ ✅ *must transfer ownership first Group Settings Panel Admin opens Group Settings: ┌──────────────────────────────────────────┐ │ ⚙️ Group Settings │ │ │ │ Group Name │ │ [Business Friends ] │ │ │ │ Description │ │ [Discussion for Dar founders ] │ │ │ │ Group Type │ │ ● 🔒 Private (invite + approval) │ │ ○ 🌍 Public (anyone can join) │ │ │ │ Who can send messages? │ │ ● All members │ │ ○ Admins only (announcement mode) │ │ │ │ Who can send invitations? │ │ ● Admins only │ │ ○ All members │ │ │ │ Invite Link ──────────────────────── │ │ nexgate.app/join/abc-xyz │ │ [Copy] [Share] [Revoke] │ │ Expiry: [Never ▾] │ │ Max joins: [Unlimited ▾] │ │ │ │ ─────────────────────────────────────── │ │ 🗑️ Delete Group │ └──────────────────────────────────────────┘ NexGate Natural Group Contexts These groups form naturally without strangers: VP Events: Organizer creates event group Ticket buyers receive consent DM automatically "Dar Tech Summit invited you to Attendees group" All attendees = commerce relationship ✅ VP Shop customer group: Seller creates customer community group Past buyers receive consent DM invitation "TechStore invited you to VIP Customers group" All = previous buyers ✅ VP Feed creator community: Creator makes PUBLIC group Shares link on VP Feed Followers join themselves All = followers (relationship exists) ✅ Friends/family: Admin sends consent DM to close contacts Small group bootstrapped easily All = contacts/followers ✅ 22. Shop Chat Initiation The Core Principle Customer relationship = permission to initiate No relationship = cannot initiate This applies to shops messaging customers Same rule as personal messaging Consistent across all of NexGate When Shop Can Initiate ALWAYS ALLOWED (transactional): Order placed → shop sends confirmation Order shipped → shop sends update Order issue → shop contacts buyer Payment problem → shop contacts buyer → Goes directly to customer commerce inbox → Customer expects this ✅ → Cannot be turned off ALLOWED with relationship: Customer bought from shop before ✅ Customer sent message to shop before ✅ Customer follows the shop ($techstore) ✅ → Shop can initiate from order/customer list → Goes to existing commerce thread → OR new message request if no thread yet NEVER ALLOWED: Cold message to random NexGate users ❌ Mass promotional outreach ❌ Message to users who never interacted ❌ → System blocks this → "You can only message customers who have interacted with your shop" How Shop Staff Initiates From existing order: Shop → Orders → Find Kibuti's order Tap [Message Customer] → Opens existing commerce thread → Send message as TechStore ✅ From customer list: Shop → Customers → Find Kibuti Tap [Send Message] → Opens existing thread OR → New message request to Kibuti → Kibuti sees: "TechStore wants to send you a message" From active conversation: Staff sees conversation in shop inbox Replies as TechStore Normal response flow ✅ From VP Feed (not messaging — different): Shop posts on VP Feed Followers see it in feed Interested followers DM the shop Shop replies ✅ Transactional Messages — Auto System These fire automatically from Spring Boot No manual staff action needed: Order placed: → System message in commerce thread: "Your order ORD-789 has been confirmed Samsung A15 × 1 — TZS 400,000 [Track Order]" Order shipped: → "Your order has been shipped Expected: 2-3 days [Track Delivery]" Order delivered: → "Your order has been delivered! How was your experience? [Leave Review]" Payment failed: → "Payment issue with your order Please update your payment method [Fix Payment]" All go directly to commerce inbox ✅ Customer always expects these ✅ Cannot be disabled by customer ✅ Customer Controls Settings → Privacy → Shop Messages: Who can send me shop messages? ● Shops I have bought from (default) ○ Shops I follow + bought from ○ Nobody (all go to requests) Allow shops to send me promotions? ○ Yes ● No (default) Allow order updates from shops? ● Always (cannot turn off) (transactional — always needed) 23. Broadcast Channels — Why Not Needed The Question Should NexGate have broadcast channels? Like Telegram channels or WhatsApp channels? One-way: creator/shop → followers What VP Feed Already Covers VP Feed does everything a broadcast channel does: Public announcements: Shop posts new product → VP Feed ✅ Creator posts update → VP Feed ✅ All followers see it ✅ Urgent alerts: FCM HIGH priority notification on post ✅ "TechStore posted: Flash sale today!" Same urgency as channel message ✅ Exclusive content: VP Feed close friends feature ✅ Post visible to selected followers only Covers "exclusive subscriber" use case ✅ Shop promotions: Shop posts on VP Feed ✅ Product tags + sale tags ✅ Followers see in feed ✅ The question becomes: What does a broadcast channel add that VP Feed with notifications doesn't? Honestly — very little Why NOT to Build Broadcast Channels Duplication: Two places creator manages content Two places follower checks for updates "Should I post this to feed or channel?" Confusing for both creators and followers WeChat lesson: WeChat has both feed (Moments) AND official accounts (channels) Users find it confusing what goes where Even WeChat admits this overlap NexGate should be cleaner Simpler is better: One place for content → VP Feed One place for conversation → Messaging No hybrid in between Clean product with clear purpose Inbox stays clean: Personal DMs Group chats Shop commerce DMs That's it No broadcast section cluttering inbox What Covers Each Use Case Instead Use case Solution (no broadcast needed) ────────────────────────────────────────────────────────── Public announcements VP Feed post ✅ Urgent alerts VP Feed + FCM notification ✅ Flash sales VP Feed with sale tag ✅ Exclusive content VP Feed close friends ✅ Private community Group chat ✅ Personal deals 1:1 Commerce DM ✅ Group deals Group + Bei ya pamoja ✅ Customer updates Transactional DMs (auto) ✅ Order updates Commerce thread (auto) ✅ Decision Broadcast channels: NOT BUILT ✅ VP Feed is NexGate's content distribution layer Messaging is NexGate's conversation layer They serve different purposes They stay separate No overlap needed 24. Commerce Attach — 1:1 vs Group The Clean Separation Custom price offer: 1:1 DMs ONLY Private negotiation between one seller and one buyer Makes no sense in a group (why negotiate privately in front of everyone?) Bei ya pamoja (group purchase): Group chats (primary home) Also shareable in 1:1 DMs Group power buying Dynamic pricing Perfect for group context Attach Menu — 1:1 DM Inside a 1:1 conversation: Tap attach (+) 📷 Image 🎵 Voice Note 📄 File 🏪 From My Shop → custom price offer (private deal — this buyer only) 📅 Event Card → share any event 👥 Bei ya pamoja → share a group buy (recipient can join or share further) Attach Menu — Group Chat Inside a group conversation: Tap attach (+) 📷 Image 🎵 Voice Note 📄 File 🏪 From My Shop → product card only (public price shown) (NO custom price in group) 📅 Event Card → share any event 👥 Bei ya pamoja → start or share group buy THIS is group commerce ✅ Why Custom Price NOT in Groups Custom price = private negotiation "I'll give you a special deal" Said to ONE person in private Makes sense in 1:1 ✅ Custom price in group = awkward "I'll give everyone TZS 400,000" In front of 50 group members Why is this price special? How is it different from a sale? It's NOT private anymore Loses its meaning ❌ Bei ya pamoja = group power "If we get 10 people together we save" Collective action in a group Group chat is the PERFECT home for this ✅ Dynamic pricing makes sense collectively ✅ Bei ya Pamoja in Group — Flow Someone shares Bei ya pamoja in group: ┌──────────────────────────────────────────┐ │ 👥 Group Purchase │ │ Samsung A15 │ │ │ │ Public price: TZS 450,000 │ │ Group price: TZS 350,000 (10 people) │ │ │ │ Progress: ████████░░ 8 / 10 │ │ 2 more people needed │ │ │ │ Expires: 23 hours 45 minutes │ │ │ │ [Join Group Buy] │ └──────────────────────────────────────────┘ Group members: See card in group thread ✅ Anyone can tap [Join Group Buy] ✅ Progress updates in real time ✅ When 10 people join → all checkout ✅ Card updates: "Target reached! Proceeding..." ✅ Summary Table Feature 1:1 DM Group Chat ────────────────────────────────────────────────── Text message ✅ ✅ Voice note ✅ ✅ Media (image/video) ✅ ✅ File/document ✅ ✅ Product card ✅ (public) ✅ (public) Custom price offer ✅ seller only ❌ not available Bei ya pamoja ✅ shareable ✅ primary home Event card ✅ ✅ Post card ✅ ✅ Stream card ✅ ✅ Summary Updates NexGate's messaging platform has been refined with five additional decisions: Follow System : Users are followed with [@Follow], shops are followed with [Follow Shop]. The $ sigil already signals commercial relationship. Shops have followers but do not follow anyone — like a Facebook Page. "Subscribe" is avoided because it implies payment to EA users. Group Join Model : Nobody enters a group without choosing to. Two mechanisms: consent DM invitation (admin proactively invites their network — each person accepts or declines) and invite link (self-service joining). Private groups require admin approval on link join. Public groups allow instant join. No WhatsApp-style forced adding. Shop Chat Initiation : Shops can only initiate chat with customers who have an existing relationship (previous buyer, follower, or active conversation). Transactional messages (order updates) fire automatically. Cold outreach to strangers is blocked at system level. Customer controls what types of shop messages they receive. Broadcast Channels : Not built. VP Feed already covers all content distribution use cases. Adding channels would duplicate VP Feed and confuse users about where to post content. The inbox stays clean: personal DMs, group chats, and shop commerce DMs only. Commerce Attach Separation : Custom price offers live in 1:1 DMs only — private negotiation has no place in a group context. Group commerce is Bei ya pamoja — dynamic group buying where price drops as more members join. Each feature has one clear home and one clear purpose. NexGate Messaging — Product Requirements & Feature Flows v1.0 QBIT SPARK | Rules · Flows · Permissions · Scenarios · Edge Cases