Overview
Single standard protocol for email, SMS, WhatsApp, Signal, and all messaging - true communication interoperability.
🎯 This is what msgs.global is already becoming!
Problem Statement
Communication is fragmented: - Email (SMTP/IMAP) - SMS (carrier-specific APIs, Telnyx, Twilio) - WhatsApp (proprietary API) - Signal (separate protocol) - Slack, Teams, Discord, etc.
Each requires: - Different protocols - Separate clients - Isolated conversations - No cross-platform search - Multiple notification streams
Vision
Universal Message Protocol (UMP)
Single API/Protocol that supports:
✅ Text messages (SMS, email, chat)
✅ Rich media (images, video, files)
✅ Real-time delivery (chat) and async (email)
✅ End-to-end encryption
✅ Read receipts, typing indicators
✅ Groups and channels
✅ Threads and conversations
✅ Voice and video calls
One inbox, one search, one unified experience
Current msgs.global Integration
You're already integrating:
- Email (SMTP, IMAP, Postfix, Dovecot)
- SMS (Telnyx webhooks: app/sms_webhooks.py)
- WhatsApp (Telnyx/Twilio webhooks: app/whatsapp_webhooks.py)
- Signal (Docker: docker-compose.signal.yml, webhooks: app/signal_webhooks.py)
Challenge: Each uses different APIs, storage, and presentation
Unified Protocol Design
1. Core Message Format
{
"id": "ump:msg:abc123",
"protocol_version": "1.0",
"type": "message",
"channel": "sms|email|whatsapp|signal|custom",
"from": {
"id": "ump:user:alice123",
"display_name": "Alice Smith",
"identifiers": {
"email": "alice@msgs.global",
"phone": "+15551234567",
"whatsapp": "+15551234567",
"signal": "+15551234567",
"did": "did:msgs:alice123"
}
},
"to": [
{
"id": "ump:user:bob456",
"display_name": "Bob Jones",
"identifiers": {...}
}
],
"conversation_id": "ump:conv:xyz789",
"timestamp": "2026-03-07T20:00:00Z",
"content": {
"type": "text|rich|media",
"text": "Hello from unified protocol!",
"html": "<p>Hello from <b>unified</b> protocol!</p>",
"attachments": [
{
"type": "image/jpeg",
"url": "https://msgs.global/attachments/abc.jpg",
"size": 524288,
"hash": "sha256:..."
}
]
},
"metadata": {
"priority": "normal",
"encryption": "e2ee",
"read_receipt_requested": true,
"expires_at": "2026-03-08T00:00:00Z"
},
"delivery_status": {
"sent": "2026-03-07T20:00:00Z",
"delivered": "2026-03-07T20:00:05Z",
"read": "2026-03-07T20:01:00Z"
}
}
2. Unified API
class UnifiedMessageAPI:
"""Single API for all messaging channels"""
def send_message(self, message):
"""Send message via any channel"""
# 1. Determine best channel for recipient
channel = self.select_channel(message.to)
# 2. Transform to channel-specific format
if channel == 'email':
return self.send_via_smtp(message)
elif channel == 'sms':
return self.send_via_telnyx_sms(message)
elif channel == 'whatsapp':
return self.send_via_telnyx_whatsapp(message)
elif channel == 'signal':
return self.send_via_signal(message)
def receive_message(self, channel, raw_message):
"""Receive message from any channel and normalize"""
# 1. Parse channel-specific format
if channel == 'email':
message = self.parse_smtp_message(raw_message)
elif channel == 'sms':
message = self.parse_telnyx_sms(raw_message)
elif channel == 'whatsapp':
message = self.parse_whatsapp(raw_message)
elif channel == 'signal':
message = self.parse_signal(raw_message)
# 2. Convert to unified format
unified_message = self.normalize_message(message, channel)
# 3. Store in unified inbox
self.store_message(unified_message)
return unified_message
def select_channel(self, recipient):
"""Intelligently select best channel for recipient"""
# Check recipient preferences
prefs = self.get_recipient_preferences(recipient)
# Factors:
# - Recipient's preferred channel
# - Message urgency (SMS for urgent, email for formal)
# - Content type (rich media -> WhatsApp, simple text -> SMS)
# - Online status (real-time chat if online, email if offline)
# - Cost (SMS premium, email free)
if prefs.get('urgent') and recipient.has_sms:
return 'sms'
elif recipient.has_signal and prefs.get('encrypted'):
return 'signal'
elif recipient.has_whatsapp and message.has_media:
return 'whatsapp'
else:
return 'email' # Default fallback
3. Unified Storage
-- Single messages table for all channels
CREATE TABLE unified_messages (
id VARCHAR(64) PRIMARY KEY,
conversation_id VARCHAR(64),
channel VARCHAR(20), -- sms, email, whatsapp, signal
from_user_id INTEGER REFERENCES users(id),
from_identifier JSONB, -- {email, phone, etc.}
to_users JSONB[], -- Array of recipient identifiers
content JSONB, -- Unified message content
metadata JSONB,
delivery_status JSONB,
created_at TIMESTAMP DEFAULT NOW(),
-- Channel-specific fields (optional)
email_message_id VARCHAR(255),
sms_message_id VARCHAR(100),
whatsapp_message_id VARCHAR(100),
signal_message_id VARCHAR(100)
);
-- Unified conversations (threads)
CREATE TABLE unified_conversations (
id VARCHAR(64) PRIMARY KEY,
participants JSONB[], -- Array of user identifiers
subject TEXT, -- Email-style subject (optional for SMS/chat)
channel VARCHAR(20), -- Primary channel
created_at TIMESTAMP DEFAULT NOW(),
last_message_at TIMESTAMP,
message_count INTEGER DEFAULT 0
);
-- User identifiers (link all user's contact methods)
CREATE TABLE user_identifiers (
user_id INTEGER REFERENCES users(id),
identifier_type VARCHAR(20), -- email, phone, whatsapp, signal, did
identifier_value VARCHAR(255),
verified BOOLEAN DEFAULT FALSE,
primary_for_channel BOOLEAN DEFAULT FALSE,
UNIQUE(identifier_type, identifier_value)
);
CREATE INDEX idx_messages_conversation ON unified_messages(conversation_id);
CREATE INDEX idx_messages_user ON unified_messages(from_user_id);
CREATE INDEX idx_identifiers_user ON user_identifiers(user_id);
4. Integration Layer
File: app/unified_messaging.py
from flask import Blueprint, request, jsonify
from app.models import db, UnifiedMessage, UnifiedConversation
from app.sms_webhooks import send_telnyx_sms
from app.whatsapp_webhooks import send_whatsapp_message
from app.signal_webhooks import send_signal_message
unified_bp = Blueprint('unified', __name__)
@unified_bp.route('/api/v1/unified/send', methods=['POST'])
def send_unified_message():
"""Send message via any channel"""
data = request.json
# 1. Resolve recipient (find best identifier)
recipient = resolve_recipient(data['to'])
# 2. Select channel
channel = select_best_channel(recipient, data)
# 3. Create unified message
message = UnifiedMessage(
conversation_id=data.get('conversation_id', generate_conversation_id()),
channel=channel,
from_user_id=request.current_user.id,
content=data['content']
)
# 4. Send via selected channel
if channel == 'sms':
result = send_telnyx_sms(
to=recipient.phone,
message=data['content']['text']
)
message.sms_message_id = result['message_id']
elif channel == 'whatsapp':
result = send_whatsapp_message(
to=recipient.whatsapp,
message=data['content']
)
message.whatsapp_message_id = result['message_id']
elif channel == 'signal':
result = send_signal_message(
to=recipient.signal,
message=data['content']['text']
)
message.signal_message_id = result['message_id']
elif channel == 'email':
result = send_smtp_message(
to=recipient.email,
subject=data.get('subject', '(no subject)'),
body=data['content']['text']
)
message.email_message_id = result['message_id']
# 5. Store message
db.session.add(message)
db.session.commit()
return jsonify(message.to_dict()), 201
@unified_bp.route('/api/v1/unified/inbox')
def get_unified_inbox():
"""Get messages from all channels in one inbox"""
user = request.current_user
# Get all user's identifiers
identifiers = UserIdentifier.query.filter_by(user_id=user.id).all()
# Query messages across all channels
messages = UnifiedMessage.query.filter(
or_(*[
UnifiedMessage.to_users.contains([{
'type': ident.identifier_type,
'value': ident.identifier_value
}])
for ident in identifiers
])
).order_by(UnifiedMessage.created_at.desc()).limit(100).all()
return jsonify([msg.to_dict() for msg in messages])
@unified_bp.route('/api/v1/unified/conversations')
def get_conversations():
"""Get all conversations across channels"""
user = request.current_user
conversations = UnifiedConversation.query.filter(
UnifiedConversation.participants.contains([{
'user_id': user.id
}])
).order_by(UnifiedConversation.last_message_at.desc()).all()
return jsonify([
{
'id': conv.id,
'subject': conv.subject,
'participants': conv.participants,
'channel': conv.channel,
'last_message': conv.last_message_at,
'message_count': conv.message_count,
'unread_count': get_unread_count(conv.id, user.id)
}
for conv in conversations
])
# Webhook handlers (convert to unified format)
@unified_bp.route('/webhooks/sms/telnyx', methods=['POST'])
def telnyx_sms_webhook():
"""Receive SMS and store as unified message"""
data = request.json
message = UnifiedMessage(
channel='sms',
from_identifier={'phone': data['from']},
to_users=[{'phone': data['to']}],
content={'text': data['text']},
sms_message_id=data['message_id']
)
db.session.add(message)
db.session.commit()
return '', 200
# Similar webhooks for WhatsApp, Signal, Email...
Webmail Integration
Unified Inbox UI
// Webmail: Single inbox showing all channels
function renderUnifiedInbox(messages) {
messages.forEach(msg => {
const channelIcon = {
'email': '📧',
'sms': '💬',
'whatsapp': '💚',
'signal': '🔒'
}[msg.channel];
renderMessage({
icon: channelIcon,
from: msg.from.display_name,
subject: msg.content.text.substring(0, 50),
timestamp: msg.timestamp,
channel: msg.channel
});
});
}
// Smart compose: Auto-select channel
function composeMessage(recipient) {
// Show available channels for recipient
const availableChannels = getRecipientChannels(recipient);
// Suggest best channel
const suggested = suggestChannel(recipient, messageType);
showComposer({
to: recipient,
channels: availableChannels,
suggested: suggested
});
}
Channel Routing Logic
Intelligent Channel Selection
def select_best_channel(recipient, message):
"""AI-powered channel selection"""
# Factors to consider:
factors = {
'urgency': message.get('priority') == 'urgent',
'has_media': len(message.get('attachments', [])) > 0,
'formal': is_formal_content(message['content']),
'encrypted_required': message.get('encryption') == 'required',
'cost_sensitive': sender_preferences.get('minimize_cost'),
'recipient_online': is_recipient_online(recipient)
}
# Decision tree
if factors['encrypted_required']:
if recipient.has_signal:
return 'signal'
elif recipient.has_whatsapp:
return 'whatsapp'
else:
return 'email' # With PGP
if factors['urgency']:
if recipient.has_sms:
return 'sms'
elif recipient.has_whatsapp and factors['recipient_online']:
return 'whatsapp'
else:
return 'email' # With high priority flag
if factors['has_media']:
if recipient.has_whatsapp:
return 'whatsapp'
else:
return 'email'
if factors['formal']:
return 'email'
# Default: Use recipient's preferred channel
return recipient.preferred_channel or 'email'
Advantages for msgs.global
Current State
- 4 separate systems (email, SMS, WhatsApp, Signal)
- 4 different APIs
- 4 storage schemas
- Complex user experience
With Unified Protocol
- ✅ Single API for developers
- ✅ One search across all messages
- ✅ Unified conversations (email thread + SMS replies)
- ✅ Automatic channel failover
- ✅ Cost optimization (route via cheapest available)
- ✅ Better user experience
Migration Path
Phase 1: Unified Storage (Current → 3 months)
- [x] Already have separate tables (messages, sms_messages, etc.)
- [ ] Create
unified_messagestable - [ ] Migrate existing messages to unified format
- [ ] Create
user_identifierslinking table
Phase 2: Unified API (3-6 months)
- [ ] Build unified send/receive API
- [ ] Wrap existing channel APIs
- [ ] Implement channel selection logic
- [ ] Update webhooks to normalize to unified format
Phase 3: Webmail Integration (6-9 months)
- [ ] Unified inbox view
- [ ] Cross-channel search
- [ ] Smart compose (channel selection)
- [ ] Conversation threading across channels
Phase 4: Advanced Features (9-12 months)
- [ ] AI-powered channel selection
- [ ] Cross-channel read receipts
- [ ] Unified notifications
- [ ] Cost analytics and optimization
Protocol Specification
Standard compliance
- Email: RFC 5322, SMTP, IMAP
- SMS: Telnyx/Twilio APIs (industry standard)
- WhatsApp: WhatsApp Business API
- Signal: Signal Protocol (double ratchet)
Unified extensions
- UMP-Header: Metadata for cross-channel messages
- UMP-Conversation-ID: Thread messages across channels
- UMP-Channel-Preference: Recipient's preferred channel
Status
🚀 ALREADY IN PROGRESS (msgs.global has all the pieces!)
Immediate Next Steps
- [ ] Design unified message schema
- [ ] Create migration from existing tables
- [ ] Build unified API wrapper
- [ ] Update webmail to show all channels
- [ ] Implement intelligent channel routing
- [ ] Cross-channel conversation threading
This is your competitive differentiator! No other platform unifies email, SMS, WhatsApp, and Signal like this.