# Plan d'Implémentation Ziwo - 3ème Provider VoIP

Ce document détaille l'implémentation de **Ziwo.io** comme 3ème provider VoIP dans l'architecture multi-provider, aux côtés de Twilio et Telnyx.

---

## Vue d'Ensemble de l'Architecture

### Provider Comparison

| Feature | Twilio | Telnyx | **Ziwo** |
|---------|--------|--------|----------|
| **AI Engine** | OpenAI Realtime API | Telnyx Voice AI native | **Ziwo AI Voice Agent** |
| **Integration** | WebSocket + Function Calling | Webhooks + Gather AI | **REST API + Webhooks** |
| **Data Extraction** | OpenAI Functions | Telnyx Functions | **Ziwo Workflow Variables** |
| **Pricing** | $0.30/min | $0.012/min | **Contact sales** |
| **Complexity** | High (custom) | Medium (native) | **Low (platform)** |

---

## 1️⃣ Architecture Séparée dans Package VoIP

### Structure des Composants Ziwo

```
src/main/java/com/saas/voip/
├── controller/
│   ├── TwilioVoiceController.java          ✅ Existant
│   ├── TelnyxVoiceController.java          ✅ Existant
│   ├── ZiwoVoiceController.java            🆕 NOUVEAU
│   ├── ZiwoEventController.java            🆕 NOUVEAU
│   ├── TwilioSmsCallbackController.java    ✅ Existant
│   ├── TelnyxSmsCallbackController.java    ✅ Existant
│   └── ZiwoSmsCallbackController.java      🆕 NOUVEAU
│
├── service/
│   ├── TwilioSmsService.java               ✅ Existant
│   ├── TelnyxSmsService.java               ✅ Existant
│   ├── TelnyxVoiceAIService.java           ✅ Existant
│   ├── ZiwoApiService.java                 🆕 NOUVEAU (REST client)
│   ├── ZiwoVoiceAIService.java             🆕 NOUVEAU
│   └── ZiwoSmsService.java                 🆕 NOUVEAU
│
├── extractor/
│   ├── CallDataExtractor.java              ✅ Existant (Twilio)
│   ├── TelnyxCallDataExtractor.java        ✅ Existant
│   └── ZiwoCallDataExtractor.java          🆕 NOUVEAU
│
└── websocket/
    ├── OpenAIRealtimeWebSocketHandler.java ✅ Existant (Twilio)
    └── (Pas de WebSocket pour Ziwo - REST API uniquement)
```

### Composants à Créer

#### 🆕 ZiwoVoiceController
- **Endpoint** : `POST /api/voip/ziwo/incoming-call`
- **Rôle** : Webhook Ziwo pour appels entrants
- **Actions** :
  1. Extraire données de l'appel (CallSid, From, To)
  2. Identifier tenant via numéro appelé
  3. Sauvegarder `InboundCallData`
  4. Retourner TwiML/JSON pour démarrer AI Agent

#### 🆕 ZiwoEventController
- **Endpoint** : `POST /api/voip/ziwo/events`
- **Rôle** : Receive Ziwo workflow events et AI data
- **Actions** :
  1. Événements d'appel (initiated, answered, hangup)
  2. Données extraites par AI Voice Agent
  3. Variables de workflow personnalisées
  4. Mise à jour `InboundCallRequest`

#### 🆕 ZiwoSmsCallbackController
- **Endpoint** : `POST /api/voip/ziwo/sms/status-callback`
- **Rôle** : Track SMS delivery status
- **Actions** :
  1. Receive SMS delivery events
  2. Identifier tenant via numéro expéditeur
  3. Update `InboundCallRequest.smsStatus`

#### 🆕 ZiwoApiService
- **Rôle** : Client REST pour Ziwo API
- **Méthodes** :
  - `makeOutboundCall()` - Initier un appel
  - `hangupCall()` - Raccrocher
  - `transferCall()` - Transférer
  - `getCallDetails()` - Récupérer détails appel
  - `getCallRecording()` - Télécharger enregistrement
  - `sendSms()` - Envoyer SMS

#### 🆕 ZiwoVoiceAIService
- **Rôle** : Gérer AI Voice Agent de Ziwo
- **Méthodes** :
  - `startAIAgent()` - Démarrer l'agent sur un appel
  - `stopAIAgent()` - Arrêter l'agent
  - `getConversationTranscript()` - Récupérer transcript
  - `extractPatientData()` - Parser données extraites

#### 🆕 ZiwoCallDataExtractor
- **Rôle** : Extraire données du webhook Ziwo
- **Méthodes** :
  - `extractFromZiwoRequest()` - Créer `InboundCallData`
  - `extractPatientInfo()` - Parser données patient

---

## 2️⃣ AI Voice Agent Natif (Sans OpenAI)

### Architecture Ziwo AI

```
┌─────────────────────────────────────────────────────────────┐
│                    ZIWO AI VOICE AGENT                       │
│                     (Natif - No OpenAI)                      │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
                    ┌──────────────────┐
                    │  IVR/Workflow    │
                    │   Builder        │
                    └──────────────────┘
                              │
                    ┌─────────┴─────────┐
                    ▼                   ▼
          ┌─────────────────┐   ┌─────────────────┐
          │  AI Greeting    │   │ Collect Data    │
          │  & Navigation   │   │  (Variables)    │
          └─────────────────┘   └─────────────────┘
                    │                   │
                    └─────────┬─────────┘
                              ▼
                    ┌──────────────────┐
                    │   Webhook POST   │
                    │  to Your API     │
                    └──────────────────┘
```

### Configuration AI Agent Ziwo

**Dans le Dashboard Ziwo** :
1. **IVR/Workflow Builder** : Créer un workflow visuel
2. **AI Prompts** : Configurer les instructions (français médical)
3. **Variables** : Définir les données à collecter
4. **Webhooks** : Configurer l'URL de callback

**Variables à Configurer** (similaire aux Functions OpenAI/Telnyx) :

```json
{
  "variables": {
    "patient_name": {
      "type": "string",
      "prompt": "Quel est votre nom complet ?"
    },
    "patient_phone": {
      "type": "phone",
      "prompt": "Votre numéro de téléphone ?"
    },
    "patient_birthdate": {
      "type": "date",
      "prompt": "Votre date de naissance ?"
    },
    "illness": {
      "type": "text",
      "prompt": "Quelle est la raison de votre appel ?"
    },
    "doctor_choice": {
      "type": "choice",
      "options": ["Dr. Martin", "Dr. Dupont", "Dr. Leblanc"],
      "prompt": "Quel médecin souhaitez-vous consulter ?"
    },
    "appointment_date": {
      "type": "date",
      "prompt": "Quelle date vous convient ?"
    },
    "appointment_time": {
      "type": "time",
      "prompt": "Quelle heure préférez-vous ?"
    },
    "confirmation": {
      "type": "boolean",
      "prompt": "Confirmez-vous ce rendez-vous ?"
    }
  }
}
```

**Webhook Payload Envoyé par Ziwo** :

```json
{
  "event": "call.completed",
  "call_id": "ziwo_call_123456",
  "from": "+33612345678",
  "to": "+33987654321",
  "duration": 120,
  "variables": {
    "patient_name": "Marie Dubois",
    "patient_phone": "+33612345678",
    "patient_birthdate": "1985-03-15",
    "illness": "Mal de tête persistant",
    "doctor_choice": "Dr. Sophie Martin",
    "appointment_date": "2025-10-20",
    "appointment_time": "14:30",
    "confirmation": true
  },
  "recording_url": "https://ziwo-recordings.s3.amazonaws.com/call_123456.mp3",
  "transcript": [
    {
      "speaker": "ai",
      "text": "Bonjour, Clinique La Rive Bleue, comment puis-je vous aider ?",
      "timestamp": "2025-10-15T10:00:00Z"
    },
    {
      "speaker": "caller",
      "text": "Bonjour, je voudrais prendre un rendez-vous",
      "timestamp": "2025-10-15T10:00:03Z"
    }
  ]
}
```

---

## 3️⃣ Récupération Data Appelant depuis l'Appel

### Flow de Récupération des Données

```
┌────────────────────────────────────────────────────────────┐
│  1. APPEL ENTRANT → Ziwo Platform                          │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  2. WEBHOOK → POST /api/voip/ziwo/incoming-call            │
│     Payload: {                                             │
│       "call_id": "ziwo_123",                               │
│       "from": "+33612345678",                              │
│       "to": "+33987654321",                                │
│       "caller_name": "Marie D.",  ← Caller ID si dispo     │
│       "caller_country": "France"                           │
│     }                                                      │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  3. IDENTIFIER TENANT                                       │
│     - Lookup phone_numbers table (Provider = ZIWO)         │
│     - Get tenantId → Get schemaName                        │
│     - TenantContext.setTenantId(schemaName)                │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  4. SAVE InboundCallData                                    │
│     - callSid = "ziwo_123"                                 │
│     - fromNumber = "+33612345678"                          │
│     - toNumber = "+33987654321"                            │
│     - callerName = "Marie D."                              │
│     - startTime, direction, etc.                           │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  5. AI VOICE AGENT COLLECTE DONNÉES                         │
│     → Ziwo AI pose questions en français                   │
│     → Variables collectées pendant l'appel                 │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  6. WEBHOOK EVENT → POST /api/voip/ziwo/events             │
│     Payload: {                                             │
│       "event": "variables.collected",                      │
│       "call_id": "ziwo_123",                               │
│       "variables": {                                       │
│         "patient_name": "Marie Dubois",                    │
│         "patient_phone": "+33612345678",                   │
│         "illness": "Mal de tête",                          │
│         "doctor_choice": "Dr. Martin",                     │
│         ...                                                │
│       }                                                    │
│     }                                                      │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  7. SAVE InboundCallRequest                                 │
│     - nom = "Marie Dubois"                                 │
│     - telephone = "+33612345678"                           │
│     - maladie = "Mal de tête"                              │
│     - doctorName = "Dr. Martin"                            │
│     - appointmentDateTime = parsed date/time               │
│     - confirmed = true/false                               │
└────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌────────────────────────────────────────────────────────────┐
│  8. SEND SMS CONFIRMATION (si confirmed = true)             │
│     → ZiwoSmsService.sendAppointmentConfirmation()         │
│     → Update smsSent, smsSid, smsStatus                    │
└────────────────────────────────────────────────────────────┘
```

### Données Appelant Disponibles

**De l'API Ziwo** (webhook initial) :

```java
public class ZiwoCallDataExtractor {
    public InboundCallData extractFromZiwoRequest(Map<String, Object> payload) {
        InboundCallData callData = new InboundCallData();
        
        // Données basiques
        callData.setCallSid(getString(payload, "call_id"));
        callData.setFromNumber(getString(payload, "from"));
        callData.setToNumber(getString(payload, "to"));
        callData.setDirection(getString(payload, "direction", "inbound"));
        
        // Caller ID enrichi (si disponible)
        callData.setFromName(getString(payload, "caller_name"));
        callData.setFromCity(getString(payload, "caller_location.city"));
        callData.setFromState(getString(payload, "caller_location.state"));
        callData.setFromCountry(getString(payload, "caller_location.country"));
        
        // Données de l'appel
        callData.setCallStatus(getString(payload, "status"));
        callData.setStartTime(parseDateTime(getString(payload, "start_time")));
        callData.setQueueName(getString(payload, "queue_name"));
        callData.setAgentId(getString(payload, "agent_id"));
        
        return callData;
    }
}
```

---

## 4️⃣ Récupération MAX Info de l'Appel Ziwo

### Informations Disponibles via Ziwo API

**A. Via Webhooks (Automatique)**

```json
{
  "event": "call.completed",
  "data": {
    "call_id": "ziwo_123456",
    "from": "+33612345678",
    "to": "+33987654321",
    "direction": "inbound",
    "status": "completed",
    "start_time": "2025-10-15T10:00:00Z",
    "answer_time": "2025-10-15T10:00:03Z",
    "end_time": "2025-10-15T10:02:30Z",
    "duration": 150,
    "talk_time": 147,
    "queue_name": "Medical Support",
    "agent_id": "ai_agent_001",
    "recording_url": "https://...",
    "caller_name": "Marie D.",
    "caller_location": {
      "city": "Paris",
      "state": "Île-de-France",
      "country": "France"
    },
    "call_outcome": "appointment_scheduled",
    "custom_fields": {
      "campaign_id": "med_outreach_2025",
      "source": "google_ads"
    }
  }
}
```

**B. Via REST API (À la demande)**

```java
@Service
public class ZiwoApiService {
    
    private final RestTemplate restTemplate = new RestTemplate();
    
    @Value("${ziwo.api.key}")
    private String apiKey;
    
    @Value("${ziwo.instance.name}")
    private String instanceName;
    
    private static final String BASE_URL = "https://{instance}-api.aswat.co";
    
    /**
     * Get detailed call information
     */
    public ZiwoCallDetails getCallDetails(String callId) {
        String url = BASE_URL.replace("{instance}", instanceName) + "/calls/" + callId;
        
        HttpHeaders headers = new HttpHeaders();
        headers.set("api_key", apiKey);
        
        HttpEntity<Void> request = new HttpEntity<>(headers);
        
        ResponseEntity<Map> response = restTemplate.exchange(
            url, HttpMethod.GET, request, Map.class
        );
        
        if (response.getStatusCode().is2xxSuccessful()) {
            return parseCallDetails(response.getBody());
        }
        
        return null;
    }
    
    /**
     * Get call recording URL
     */
    public String getRecordingUrl(String callId) {
        String url = BASE_URL.replace("{instance}", instanceName) 
                   + "/recordings?call_id=" + callId;
        
        HttpHeaders headers = new HttpHeaders();
        headers.set("api_key", apiKey);
        
        HttpEntity<Void> request = new HttpEntity<>(headers);
        
        ResponseEntity<Map> response = restTemplate.exchange(
            url, HttpMethod.GET, request, Map.class
        );
        
        if (response.getStatusCode().is2xxSuccessful()) {
            Map<String, Object> body = response.getBody();
            List<Map> recordings = (List<Map>) body.get("content");
            if (!recordings.isEmpty()) {
                return (String) recordings.get(0).get("url");
            }
        }
        
        return null;
    }
    
    /**
     * Get call transcript
     */
    public List<TranscriptMessage> getCallTranscript(String callId) {
        // Ziwo may provide transcript via webhook or API
        // Format depends on their implementation
        String url = BASE_URL.replace("{instance}", instanceName) 
                   + "/calls/" + callId + "/transcript";
        
        // Implementation here...
        return new ArrayList<>();
    }
}
```

### Données Complètes dans InboundCallData

```java
@Entity
@Table(name = "inbound_call_data")
public class InboundCallData {
    
    // === DONNÉES DE BASE ===
    private String callSid;              // "ziwo_123456"
    private String fromNumber;           // "+33612345678"
    private String toNumber;             // "+33987654321"
    private String fromName;             // "Marie D." (Caller ID)
    
    // === LOCALISATION ===
    private String fromCity;             // "Paris"
    private String fromState;            // "Île-de-France"
    private String fromCountry;          // "France"
    private String toCity;
    private String toState;
    private String toCountry;
    
    // === TIMESTAMPS ===
    private LocalDateTime startTime;     // 2025-10-15 10:00:00
    private LocalDateTime answerTime;    // 2025-10-15 10:00:03
    private LocalDateTime endTime;       // 2025-10-15 10:02:30
    private Integer duration;            // 150 (secondes)
    private Integer talkTime;            // 147 (sans IVR)
    
    // === STATUS & ROUTING ===
    private String callStatus;           // "completed", "no-answer", "busy"
    private String direction;            // "inbound", "outbound"
    private String queueName;            // "Medical Support"
    private String agentId;              // "ai_agent_001"
    
    // === RECORDINGS & MEDIA ===
    private String recordingUrl;         // URL S3 de l'enregistrement
    private Integer recordingDuration;   // Durée enregistrement
    private String recordingSid;         // ID unique recording
    
    // === ZIWO SPECIFIC ===
    private String callOutcome;          // "appointment_scheduled"
    private String campaignId;           // "med_outreach_2025"
    private String callSource;           // "google_ads", "direct"
    
    // === CUSTOM FIELDS ===
    @Column(columnDefinition = "JSON")
    private String customFields;         // JSON libre
}
```

### Données Complètes dans InboundCallRequest

```java
@Entity
@Table(name = "inbound_call_request")
public class InboundCallRequest {
    
    // === PATIENT INFO (via AI) ===
    private String nom;                  // "Marie Dubois"
    private String dateNaissance;        // "1985-03-15"
    private String telephone;            // "+33612345678"
    private String maladie;              // "Mal de tête persistant"
    
    // === APPOINTMENT INFO ===
    private String doctorName;           // "Dr. Sophie Martin"
    private LocalDateTime appointmentDateTime;  // 2025-10-20 14:30
    private String motifVisite;          // Détails supplémentaires
    private Boolean appointmentConfirmed; // true/false
    
    // === SMS TRACKING ===
    private Boolean smsSent;             // true
    private String smsSid;               // "ziwo_sms_789"
    private String smsStatus;            // "delivered"
    
    // === CONVERSATION ===
    @Column(columnDefinition = "JSON")
    private String conversationTranscript;  // JSON array messages
    
    // === METADATA ===
    private String callSource;           // Tracking source
    private String campaignId;           // Marketing campaign
}
```

---

## 5️⃣ Endpoints API à Configurer

### Dans le Dashboard Ziwo

**A. Webhooks Voix**

| Event | URL | Description |
|-------|-----|-------------|
| `call.initiated` | `POST /api/voip/ziwo/incoming-call` | Appel entrant |
| `call.answered` | `POST /api/voip/ziwo/events` | Appel répondu |
| `call.completed` | `POST /api/voip/ziwo/events` | Appel terminé |
| `variables.collected` | `POST /api/voip/ziwo/events` | Données AI extraites |

**B. Webhooks SMS**

| Event | URL | Description |
|-------|-----|-------------|
| `message.sent` | `POST /api/voip/ziwo/sms/status-callback` | SMS envoyé |
| `message.delivered` | `POST /api/voip/ziwo/sms/status-callback` | SMS livré |
| `message.failed` | `POST /api/voip/ziwo/sms/status-callback` | SMS échoué |

---

## 6️⃣ Variables d'Environnement

```bash
# Ziwo Configuration
ZIWO_INSTANCE_NAME=your-company        # Nom de votre instance Ziwo
ZIWO_API_KEY=your-api-key-here         # API Key Ziwo
ZIWO_ACCESS_TOKEN=optional-token       # Alternative: Access Token (login)
```

---

## 7️⃣ Comparaison avec Twilio & Telnyx

| Aspect | Twilio | Telnyx | **Ziwo** |
|--------|--------|--------|----------|
| **AI Setup** | Code WebSocket | Config webhook | **Dashboard UI** |
| **Data Extraction** | Function Calling | Gather AI | **Workflow Variables** |
| **Complexity** | Haute | Moyenne | **Basse** |
| **Customization** | Maximale | Haute | **Limitée** |
| **Recording** | Via API | Via API | **Auto inclus** |
| **Transcript** | Manual parse | API | **Auto inclus** |
| **Analytics** | Custom | Custom | **Dashboard natif** |
| **Cost** | $0.30/min | $0.012/min | **À définir** |

---

## 8️⃣ Fichiers à Créer

```
Nouveaux fichiers Ziwo :

1. src/main/java/com/saas/voip/controller/ZiwoVoiceController.java
2. src/main/java/com/saas/voip/controller/ZiwoEventController.java
3. src/main/java/com/saas/voip/controller/ZiwoSmsCallbackController.java
4. src/main/java/com/saas/voip/service/ZiwoApiService.java
5. src/main/java/com/saas/voip/service/ZiwoVoiceAIService.java
6. src/main/java/com/saas/voip/service/ZiwoSmsService.java
7. src/main/java/com/saas/voip/extractor/ZiwoCallDataExtractor.java
8. src/main/java/com/saas/voip/dto/ZiwoCallDetails.java (optionnel)
9. docs/ZIWO_CONFIGURATION_GUIDE.md

Modifications :

1. src/main/java/com/saas/shared/enums/Provider.java
   → Ajouter ZIWO dans l'enum
   
2. .env.example
   → Ajouter variables Ziwo
   
3. replit.md
   → Documenter intégration Ziwo
```

---

## 9️⃣ Avantages de Ziwo

✅ **Setup Simple** : Configuration via dashboard, pas de code complexe  
✅ **AI Natif** : Pas besoin d'OpenAI API, tout inclus  
✅ **Recording Auto** : Enregistrements automatiques avec URL  
✅ **Analytics** : Dashboards intégrés (40+ KPIs)  
✅ **Multi-canal** : Voice + WhatsApp + SMS dans une plateforme  
✅ **Enterprise Grade** : 99.99% uptime, support 24/7  

---

## 🔟 Inconvénients de Ziwo

❌ **Coût Inconnu** : Pas de pricing public (licensing + telecom)  
❌ **Vendor Lock-in** : Plateforme propriétaire vs API standard  
❌ **Moins de Contrôle** : Configuration UI vs code custom  
❌ **Overkill** : Call center complet pour un simple voicebot  

---

## Résumé de l'Implémentation

**Ce qui sera créé :**

1. ✅ **7 nouveaux fichiers Java** (controllers, services, extractor)
2. ✅ **Architecture parallèle** à Twilio/Telnyx
3. ✅ **AI Voice Agent natif** (pas d'OpenAI)
4. ✅ **Récupération complète** des données appelant
5. ✅ **Max d'infos** : call details, recording, transcript, analytics
6. ✅ **Multi-tenant** : Même pattern de routing via phone_numbers
7. ✅ **SMS tracking** : Delivery status via webhooks

**Flow complet :**
```
Appel → Ziwo AI Agent → Collect Data → Webhook → Save DB → Send SMS → Track Status
```

**Données récupérées :**
- 📞 Call metadata (30+ champs)
- 👤 Caller info (name, location)
- 🗣️ AI conversation variables
- 📝 Transcript complet
- 🎙️ Recording URL
- 📊 Analytics (duration, outcome, queue)
- 📱 SMS delivery tracking

---

## Prochaines Étapes

**Si vous validez ce plan, je vais :**

1. ✅ Ajouter `ZIWO` dans l'enum Provider
2. ✅ Créer tous les controllers/services Ziwo
3. ✅ Implémenter l'extraction de données
4. ✅ Configurer les webhooks
5. ✅ Tester la compilation
6. ✅ Créer le guide de configuration Ziwo

**Voulez-vous que je commence l'implémentation ? 🚀**
