# NOMAZRA API Endpoints — Real-Time Voice Call (WebSocket)
Base URL: `wss://nomazra.kelthos.com/ws/nomazra/`

This is the "crown jewel" feature — real-time AI voice consultation via WebSocket.

**Architecture:**
- WebSocket connection with Django Channels
- Audio streamed from Flutter client → server
- Server uses OpenAI Realtime API for streaming AI voice responses
- Whisper transcription of user audio
- OpenAI TTS for AI voice output
- Full call transcript saved post-call

---

## WebSocket Connection
```
wss://nomazra.kelthos.com/ws/nomazra/call/{conversation_id}/
```

**Auth:** Token passed as query param:
```
wss://nomazra.kelthos.com/ws/nomazra/call/{conversation_id}/?token=<access_token>
```

**Connection Flow:**
1. Client connects with JWT token
2. Server authenticates token, creates VoiceSession
3. Server sends `call.connected` event
4. Client starts sending audio chunks
5. Server processes audio → Whisper transcription → GPT response → TTS audio
6. Server streams back audio + transcript
7. On disconnect, call summary generated and saved

---

## Client → Server Messages

### Start Call
```json
{
  "type": "call.start",
  "data": {
    "language": "km",
    "voice": "nova",
    "context": "I need help with a land dispute"
  }
}
```

### Send Audio Chunk
```json
{
  "type": "audio.chunk",
  "data": {
    "audio": "<base64-encoded-pcm-16bit-16khz>",
    "sequence": 1,
    "is_final": false
  }
}
```
- Audio format: PCM 16-bit, 16kHz, mono
- Chunk size: ~0.5s of audio (~16000 bytes)
- `is_final: true` when user stops speaking (silence detection on client)

### Stop Speaking (VAD - Voice Activity Detection)
```json
{
  "type": "audio.stop",
  "data": {
    "sequence": 15
  }
}
```
Tells server user stopped speaking. Server will finalize transcription and generate response.

### End Call
```json
{
  "type": "call.end",
  "data": {
    "reason": "user_ended"
  }
}
```

### Interrupt (User speaks while AI is responding)
```json
{
  "type": "call.interrupt"
}
```
Stops current AI audio playback, server cancels ongoing TTS stream.

---

## Server → Client Messages

### Call Connected
```json
{
  "type": "call.connected",
  "data": {
    "session_id": "uuid",
    "message": "Connected. You can start speaking."
  }
}
```

### Transcription (User's speech → text)
```json
{
  "type": "transcription.partial",
  "data": {
    "text": "I have a problem with my...",
    "is_final": false
  }
}
```
```json
{
  "type": "transcription.final",
  "data": {
    "text": "I have a problem with my neighbor's fence on my land",
    "language": "km",
    "confidence": 0.94
  }
}
```

### AI Response Text (streaming)
```json
{
  "type": "response.text.delta",
  "data": {
    "text": "Based on Cambodian property law, ",
    "is_final": false
  }
}
```
```json
{
  "type": "response.text.done",
  "data": {
    "text": "Based on Cambodian property law, Article 85 of the Civil Code...",
    "tokens_used": 234
  }
}
```

### AI Response Audio (streaming)
```json
{
  "type": "response.audio.delta",
  "data": {
    "audio": "<base64-encoded-mp3-chunk>",
    "sequence": 1
  }
}
```
```json
{
  "type": "response.audio.done",
  "data": {
    "duration_seconds": 12.5,
    "sequence_count": 25
  }
}
```

### Call Summary (sent on call.end)
```json
{
  "type": "call.summary",
  "data": {
    "session_id": "uuid",
    "duration_seconds": 180,
    "transcript": [
      {"role": "user", "text": "I have a problem with my neighbor's fence on my land", "timestamp": "00:05"},
      {"role": "assistant", "text": "Based on Cambodian property law...", "timestamp": "00:08"}
    ],
    "summary": "User asked about property boundary dispute...",
    "tokens_used": 1523,
    "advice": [
      "Consider hiring a licensed surveyor",
      "File a complaint at the commune level first",
      "Relevant law: Civil Code Articles 85-90"
    ]
  }
}
```

### Call Ended
```json
{
  "type": "call.ended",
  "data": {
    "reason": "user_ended",
    "session_id": "uuid"
  }
}
```

### Error
```json
{
  "type": "error",
  "data": {
    "code": "auth_failed",
    "message": "Invalid or expired token"
  }
}
```

Error codes: `auth_failed`, `session_error`, `transcription_error`, `ai_error`, `tts_error`, `rate_limit`, `server_error`

---

## Call Processing Pipeline

```
User speaks → audio.chunk messages
                    ↓
        Buffer audio chunks (0.5s each)
                    ↓
        audio.stop / silence detected
                    ↓
        Concatenate buffered audio
                    ↓
        OpenAI Whisper transcription
                    ↓
        Send transcription.final to client
                    ↓
        GPT-4o-mini with conversation history + Cambodian law context
                    ↓
        Stream response.text.delta to client
                    ↓
        OpenAI TTS (nova voice) on complete response
                    ↓
        Stream response.audio.delta to client (chunk by chunk)
                    ↓
        Client plays audio, shows transcript
```

## Flutter Client Integration Notes

```dart
// Connect to WebSocket
final channel = WebSocketChannel.connect(
  Uri.parse('wss://nomazra.kelthos.com/ws/nomazra/call/$conversationId/?token=$token'),
);

// Listen for messages
channel.stream.listen((message) {
  final data = jsonDecode(message);
  switch (data['type']) {
    case 'call.connected': // Show connected UI
    case 'transcription.partial': // Show live text
    case 'transcription.final': // Show final text, add to chat
    case 'response.text.delta': // Show streaming AI text
    case 'response.audio.delta': // Buffer + play audio
    case 'response.audio.done': // Audio complete
    case 'call.summary': // Show summary screen
    case 'call.ended': // Clean up
    case 'error': // Show error
  }
});

// Send audio chunks (from microphone)
channel.sink.add(jsonEncode({
  'type': 'audio.chunk',
  'data': {
    'audio': base64Encode(pcmBytes),
    'sequence': sequenceNum++,
    'is_final': false,
  },
}));
```

## Audio Requirements (Flutter side)
- Record: PCM 16-bit, 16kHz, mono
- Use `record` or `flutter_sound` package
- Chunk every 500ms and send
- Implement client-side VAD (voice activity detection) for `audio.stop`
- Playback: Decode base64 MP3 chunks, buffer and play sequentially
