# WhatsApp Shared Account Model - Implementation Guide ## Overview This guide documents the **Shared WhatsApp Business Account** implementation for the bakery-ia pilot program. This model simplifies WhatsApp setup by using a single master WhatsApp Business Account with phone numbers assigned to each bakery tenant. --- ## Architecture ### Shared Account Model ``` ┌─────────────────────────────────────────────┐ │ Master WhatsApp Business Account (WABA) │ │ - Centrally managed by platform admin │ │ - Single set of credentials │ │ - Multiple phone numbers (up to 120) │ └─────────────────────────────────────────────┘ │ ┌─────────────┼─────────────┐ │ │ │ Phone #1 Phone #2 Phone #3 Bakery A Bakery B Bakery C ``` ### Key Benefits ✅ **Zero configuration for bakery users** - No Meta navigation required ✅ **5-minute setup** - Admin assigns phone number via UI ✅ **Lower support burden** - Centralized management ✅ **Predictable costs** - One WABA subscription ✅ **Perfect for pilot** - Quick deployment for 10 bakeries --- ## User Experience ### For Bakery Owners (Non-Technical Users) **Before (Manual Setup):** - Navigate Meta Business Suite ❌ - Create WhatsApp Business Account ❌ - Create message templates ❌ - Get credentials (3 different IDs) ❌ - Copy/paste into settings ❌ - **Time:** 1-2 hours, high error rate **After (Shared Account):** - Toggle WhatsApp ON ✓ - See assigned phone number ✓ - **Time:** 30 seconds, zero configuration ### For Platform Admin **Admin Workflow:** 1. Access WhatsApp Admin page (`/app/admin/whatsapp`) 2. View list of tenants 3. Select tenant 4. Assign phone number from dropdown 5. Done! --- ## Technical Implementation ### Backend Changes #### 1. Tenant Settings Model **File:** `services/tenant/app/models/tenant_settings.py` **Changed:** ```python # OLD (Per-Tenant Credentials) notification_settings = { "whatsapp_enabled": False, "whatsapp_phone_number_id": "", "whatsapp_access_token": "", # REMOVED "whatsapp_business_account_id": "", # REMOVED "whatsapp_api_version": "v18.0", # REMOVED "whatsapp_default_language": "es" } # NEW (Shared Account) notification_settings = { "whatsapp_enabled": False, "whatsapp_phone_number_id": "", # Phone # from shared account "whatsapp_display_phone_number": "", # Display format "+34 612 345 678" "whatsapp_default_language": "es" } ``` #### 2. WhatsApp Business Service **File:** `services/notification/app/services/whatsapp_business_service.py` **Changed `_get_whatsapp_credentials()` method:** ```python async def _get_whatsapp_credentials(self, tenant_id: str) -> Dict[str, str]: """ Uses global master account credentials with tenant-specific phone number """ # Always use global master account access_token = self.global_access_token business_account_id = self.global_business_account_id phone_number_id = self.global_phone_number_id # Default # Fetch tenant's assigned phone number if self.tenant_client: notification_settings = await self.tenant_client.get_notification_settings(tenant_id) if notification_settings and notification_settings.get('whatsapp_enabled'): tenant_phone_id = notification_settings.get('whatsapp_phone_number_id', '') if tenant_phone_id: phone_number_id = tenant_phone_id # Use tenant's phone return { 'access_token': access_token, 'phone_number_id': phone_number_id, 'business_account_id': business_account_id } ``` **Key Change:** Always uses global credentials, but selects the phone number based on tenant assignment. #### 3. Phone Number Management API **New File:** `services/tenant/app/api/whatsapp_admin.py` **Endpoints:** | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/v1/admin/whatsapp/phone-numbers` | List available phone numbers from master WABA | | GET | `/api/v1/admin/whatsapp/tenants` | List all tenants with WhatsApp status | | POST | `/api/v1/admin/whatsapp/tenants/{id}/assign-phone` | Assign phone to tenant | | DELETE | `/api/v1/admin/whatsapp/tenants/{id}/unassign-phone` | Remove phone assignment | **Example: Assign Phone Number** ```bash curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \ -H "Content-Type: application/json" \ -d '{ "phone_number_id": "123456789012345", "display_phone_number": "+34 612 345 678" }' ``` **Response:** ```json { "success": true, "message": "Phone number +34 612 345 678 assigned to tenant 'Panadería San Juan'", "tenant_id": "uuid-here", "phone_number_id": "123456789012345", "display_phone_number": "+34 612 345 678" } ``` ### Frontend Changes #### 1. Simplified Notification Settings Card **File:** `frontend/src/pages/app/database/ajustes/cards/NotificationSettingsCard.tsx` **Removed:** - Access Token input field - Business Account ID input field - Phone Number ID input field - API Version selector - Setup wizard instructions **Added:** - Display-only phone number (green badge if configured) - "Contact support" message if not configured - Language selector only **UI Before/After:** ``` BEFORE: ┌────────────────────────────────────────┐ │ WhatsApp Business API Configuration │ │ │ │ Phone Number ID: [____________] │ │ Access Token: [____________] │ │ Business Acct: [____________] │ │ API Version: [v18.0 ▼] │ │ Language: [Español ▼] │ │ │ │ ℹ️ Setup Instructions: │ │ 1. Create WhatsApp Business... │ │ 2. Create templates... │ │ 3. Get credentials... │ └────────────────────────────────────────┘ AFTER: ┌────────────────────────────────────────┐ │ WhatsApp Configuration │ │ │ │ ✅ WhatsApp Configured │ │ Phone: +34 612 345 678 │ │ │ │ Language: [Español ▼] │ │ │ │ ℹ️ WhatsApp Notifications Included │ │ WhatsApp messaging is included │ │ in your subscription. │ └────────────────────────────────────────┘ ``` #### 2. Admin Interface **New File:** `frontend/src/pages/app/admin/WhatsAppAdminPage.tsx` **Features:** - Lists all available phone numbers from master WABA - Shows phone number quality rating (GREEN/YELLOW/RED) - Lists all tenants with WhatsApp status - Dropdown to assign phone numbers - One-click unassign button - Real-time status updates **Screenshot Mockup:** ``` ┌──────────────────────────────────────────────────────────────┐ │ WhatsApp Admin Management │ │ Assign WhatsApp phone numbers to bakery tenants │ ├──────────────────────────────────────────────────────────────┤ │ 📞 Available Phone Numbers (3) │ ├──────────────────────────────────────────────────────────────┤ │ +34 612 345 678 Bakery Platform [GREEN] │ │ +34 612 345 679 Bakery Support [GREEN] │ │ +34 612 345 680 Bakery Notifications [YELLOW] │ └──────────────────────────────────────────────────────────────┘ ┌──────────────────────────────────────────────────────────────┐ │ 👥 Bakery Tenants (10) │ ├──────────────────────────────────────────────────────────────┤ │ Panadería San Juan ✅ Active │ │ Phone: +34 612 345 678 [Unassign] │ ├──────────────────────────────────────────────────────────────┤ │ Panadería Goiko ⚠️ Not Configured │ │ No phone number assigned [Assign phone number... ▼] │ └──────────────────────────────────────────────────────────────┘ ``` --- ## Setup Instructions ### Step 1: Create Master WhatsApp Business Account (One-Time) **Prerequisites:** - Meta/Facebook Business account - Verified business - Phone number(s) to register **Instructions:** 1. **Create WhatsApp Business Account** - Go to [Meta Business Suite](https://business.facebook.com) - Add WhatsApp product - Complete business verification (1-3 days) 2. **Add Phone Numbers** - Add at least 10 phone numbers (one per pilot bakery) - Verify each phone number - Note: You can request up to 120 phone numbers per WABA 3. **Create Message Templates** - Create `po_notification` template: ``` Category: UTILITY Language: Spanish (es) Message: "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}." ``` - Submit for approval (15 min - 24 hours) 4. **Get Master Credentials** - Business Account ID: From WhatsApp Manager settings - Access Token: Create System User or use temporary token - Phone Number ID: Listed in phone numbers section ### Step 2: Configure Environment Variables **File:** `services/notification/.env` ```bash # Master WhatsApp Business Account Credentials WHATSAPP_BUSINESS_ACCOUNT_ID=987654321098765 WHATSAPP_ACCESS_TOKEN=EAAxxxxxxxxxxxxxxxxxxxxxxxxxx WHATSAPP_PHONE_NUMBER_ID=123456789012345 # Default/fallback phone WHATSAPP_API_VERSION=v18.0 ENABLE_WHATSAPP_NOTIFICATIONS=true WHATSAPP_WEBHOOK_VERIFY_TOKEN=random-secret-token-here ``` **Security Notes:** - Store `WHATSAPP_ACCESS_TOKEN` securely (use secrets manager in production) - Rotate token every 60 days - Use System User token (not temporary token) for production ### Step 3: Assign Phone Numbers to Tenants **Via Admin UI:** 1. Access admin page: `http://localhost:5173/app/admin/whatsapp` 2. See list of tenants 3. For each tenant: - Select phone number from dropdown - Click assign - Verify green checkmark appears **Via API:** ```bash # Assign phone to tenant curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{tenant_id}/assign-phone \ -H "Content-Type: application/json" \ -d '{ "phone_number_id": "123456789012345", "display_phone_number": "+34 612 345 678" }' ``` ### Step 4: Test Notifications **Enable WhatsApp for a Tenant:** 1. Login as bakery owner 2. Go to Settings → Notifications 3. Toggle WhatsApp ON 4. Verify phone number is displayed 5. Save settings **Trigger Test Notification:** ```bash # Create a purchase order (will trigger WhatsApp notification) curl -X POST http://localhost:8003/api/v1/orders/purchase-orders \ -H "Content-Type: application/json" \ -H "X-Tenant-ID: {tenant_id}" \ -d '{ "supplier_id": "uuid", "items": [...] }' ``` **Verify:** - Check notification service logs: `docker logs -f notification-service` - Supplier should receive WhatsApp message from assigned phone number - Message status tracked in `whatsapp_messages` table --- ## Monitoring & Operations ### Check Phone Number Usage ```bash # List all tenants with assigned phone numbers curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq ``` ### View WhatsApp Message Logs ```sql -- In notification database SELECT tenant_id, recipient_phone, template_name, status, created_at, error_message FROM whatsapp_messages WHERE created_at > NOW() - INTERVAL '24 hours' ORDER BY created_at DESC; ``` ### Monitor Meta Rate Limits WhatsApp Cloud API has the following limits: | Metric | Limit | |--------|-------| | Messages per second | 80 | | Messages per day (verified) | 100,000 | | Messages per day (unverified) | 1,000 | | Conversations per 24h | Unlimited (pay per conversation) | **Check Quality Rating:** ```bash curl -X GET "https://graph.facebook.com/v18.0/{PHONE_NUMBER_ID}" \ -H "Authorization: Bearer {ACCESS_TOKEN}" \ | jq '.quality_rating' ``` **Quality Ratings:** - **GREEN** - No issues, full limits - **YELLOW** - Warning, limits may be reduced - **RED** - Quality issues, severely restricted --- ## Migration from Per-Tenant to Shared Account If you have existing tenants with their own credentials: ### Automatic Migration Script ```python # services/tenant/scripts/migrate_to_shared_account.py """ Migrate existing tenant WhatsApp credentials to shared account model """ import asyncio from sqlalchemy import select from app.core.database import database_manager from app.models.tenant_settings import TenantSettings async def migrate(): async with database_manager.get_session() as session: # Get all tenant settings result = await session.execute(select(TenantSettings)) all_settings = result.scalars().all() for settings in all_settings: notification_settings = settings.notification_settings # If tenant has old credentials, preserve phone number ID if notification_settings.get('whatsapp_access_token'): phone_id = notification_settings.get('whatsapp_phone_number_id', '') # Update to new schema notification_settings['whatsapp_phone_number_id'] = phone_id notification_settings['whatsapp_display_phone_number'] = '' # Admin will set # Remove old fields notification_settings.pop('whatsapp_access_token', None) notification_settings.pop('whatsapp_business_account_id', None) notification_settings.pop('whatsapp_api_version', None) settings.notification_settings = notification_settings print(f"Migrated tenant: {settings.tenant_id}") await session.commit() print("Migration complete!") if __name__ == "__main__": asyncio.run(migrate()) ``` --- ## Troubleshooting ### Issue: Tenant doesn't receive WhatsApp messages **Checklist:** 1. ✅ WhatsApp enabled in tenant settings? 2. ✅ Phone number assigned to tenant? 3. ✅ Master credentials configured in environment? 4. ✅ Template approved by Meta? 5. ✅ Recipient phone number in E.164 format (+34612345678)? **Check Logs:** ```bash # Notification service logs docker logs -f notification-service | grep whatsapp # Look for: # - "Using tenant-assigned WhatsApp phone number" # - "WhatsApp template message sent successfully" # - Any error messages ``` ### Issue: Phone number assignment fails **Error:** "Phone number already assigned to another tenant" **Solution:** ```bash # Find which tenant has the phone number curl http://localhost:8001/api/v1/admin/whatsapp/tenants | \ jq '.[] | select(.phone_number_id == "123456789012345")' # Unassign from old tenant first curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{old_tenant_id}/unassign-phone ``` ### Issue: "WhatsApp master account not configured" **Solution:** Ensure environment variables are set: ```bash # Check if variables exist docker exec notification-service env | grep WHATSAPP # Should show: # WHATSAPP_BUSINESS_ACCOUNT_ID=... # WHATSAPP_ACCESS_TOKEN=... # WHATSAPP_PHONE_NUMBER_ID=... ``` ### Issue: Template not found **Error:** "Template po_notification not found" **Solution:** 1. Create template in Meta Business Manager 2. Wait for approval (check status): ```bash curl -X GET "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates" \ -H "Authorization: Bearer {TOKEN}" \ | jq '.data[] | select(.name == "po_notification")' ``` 3. Ensure template language matches tenant's `whatsapp_default_language` --- ## Cost Analysis ### WhatsApp Business API Pricing (as of 2024) **Meta Pricing:** - **Business-initiated conversations:** €0.0319 - €0.0699 per conversation (Spain) - **User-initiated conversations:** Free (24-hour window) - **Conversation window:** 24 hours **Monthly Cost Estimate (10 Bakeries):** - Assume 5 PO notifications per bakery per day - 5 × 10 bakeries × 30 days = 1,500 messages/month - Cost: 1,500 × €0.05 = **€75/month** **Shared Account vs. Individual Accounts:** | Model | Setup Time | Monthly Cost | Support Burden | |-------|------------|--------------|----------------| | Individual Accounts | 1-2 hrs/bakery | €75 total | High | | Shared Account | 5 min/bakery | €75 total | Low | **Savings:** Time savings = 10 hrs × €50/hr = **€500 in setup cost** --- ## Future Enhancements ### Option 1: Template Management API Automate template creation for new tenants: ```python async def create_po_template(waba_id: str, access_token: str): """Programmatically create PO notification template""" url = f"https://graph.facebook.com/v18.0/{waba_id}/message_templates" payload = { "name": "po_notification", "language": "es", "category": "UTILITY", "components": [{ "type": "BODY", "text": "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}." }] } response = await httpx.post(url, headers={"Authorization": f"Bearer {access_token}"}, json=payload) return response.json() ``` ### Option 2: WhatsApp Embedded Signup For scaling beyond pilot: - Apply for Meta Business Solution Provider program - Implement OAuth-style signup flow - Users click "Connect WhatsApp" → auto-configured - Estimated implementation: 2-4 weeks ### Option 3: Tiered Pricing ``` Basic Tier (Free): - Email notifications only Standard Tier (€29/month): - Shared WhatsApp account - Pre-approved templates - Up to 500 messages/month Enterprise Tier (€99/month): - Own WhatsApp Business Account - Custom templates - Unlimited messages - White-label phone number ``` --- ## Security & Compliance ### Data Privacy **GDPR Compliance:** - WhatsApp messages contain supplier contact info (phone numbers) - Ensure GDPR consent for sending notifications - Provide opt-out mechanism - Data retention: Messages stored for 90 days (configurable) **Encryption:** - WhatsApp messages: End-to-end encrypted by Meta - Access tokens: Stored in environment variables (use secrets manager in production) - Database: Encrypt `notification_settings` JSON column ### Access Control **Admin Access:** - Only platform admins can assign/unassign phone numbers - Implement role-based access control (RBAC) - Audit log for phone number assignments ```python # Example: Add admin check @router.post("/admin/whatsapp/tenants/{tenant_id}/assign-phone") async def assign_phone(tenant_id: UUID, current_user = Depends(require_admin_role)): # Only admins can access pass ``` --- ## Support & Contacts **Meta Support:** - WhatsApp Business API Support: https://business.whatsapp.com/support - Developer Docs: https://developers.facebook.com/docs/whatsapp **Platform Admin:** - Email: admin@bakery-platform.com - Phone number assignment requests - Template approval assistance **Bakery Owner Help:** - Settings → Notifications → Toggle WhatsApp ON - If phone number not showing: Contact support - Language preferences can be changed anytime --- ## Appendix ### A. Database Schema Changes **Migration Script:** ```sql -- Add new field, remove old fields -- services/tenant/migrations/versions/00002_shared_whatsapp_account.py ALTER TABLE tenant_settings -- The notification_settings JSONB column now has: -- + whatsapp_display_phone_number (new) -- - whatsapp_access_token (removed) -- - whatsapp_business_account_id (removed) -- - whatsapp_api_version (removed) ; -- No ALTER TABLE needed (JSONB is schema-less) -- Migration handled by application code ``` ### B. API Reference **Phone Number Info Schema:** ```typescript interface WhatsAppPhoneNumberInfo { id: string; // Meta Phone Number ID display_phone_number: string; // E.164 format: +34612345678 verified_name: string; // Business name verified by Meta quality_rating: string; // GREEN, YELLOW, RED } ``` **Tenant WhatsApp Status Schema:** ```typescript interface TenantWhatsAppStatus { tenant_id: string; tenant_name: string; whatsapp_enabled: boolean; phone_number_id: string | null; display_phone_number: string | null; } ``` ### C. Environment Variables Reference ```bash # Notification Service (services/notification/.env) WHATSAPP_BUSINESS_ACCOUNT_ID= # Meta WABA ID WHATSAPP_ACCESS_TOKEN= # Meta System User Token WHATSAPP_PHONE_NUMBER_ID= # Default phone (fallback) WHATSAPP_API_VERSION=v18.0 # Meta API version ENABLE_WHATSAPP_NOTIFICATIONS=true WHATSAPP_WEBHOOK_VERIFY_TOKEN= # Random secret for webhook verification ``` ### D. Useful Commands ```bash # View all available phone numbers curl http://localhost:8001/api/v1/admin/whatsapp/phone-numbers | jq # View tenant WhatsApp status curl http://localhost:8001/api/v1/admin/whatsapp/tenants | jq # Assign phone to tenant curl -X POST http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/assign-phone \ -H "Content-Type: application/json" \ -d '{"phone_number_id": "XXX", "display_phone_number": "+34 612 345 678"}' # Unassign phone from tenant curl -X DELETE http://localhost:8001/api/v1/admin/whatsapp/tenants/{id}/unassign-phone # Test WhatsApp connectivity curl -X GET "https://graph.facebook.com/v18.0/{PHONE_ID}" \ -H "Authorization: Bearer {TOKEN}" # Check message template status curl "https://graph.facebook.com/v18.0/{WABA_ID}/message_templates?fields=name,status,language" \ -H "Authorization: Bearer {TOKEN}" | jq ``` --- **Document Version:** 1.0 **Last Updated:** 2025-01-17 **Author:** Platform Engineering Team **Status:** Production Ready for Pilot