πŸš€ Labeah Agents Gateway

High-Performance WebSocket & API Documentation

πŸ“– Overview

The Labeah Agents Gateway provides real-time communication with AI agents through both WebSocket and REST APIs. Built with high-performance technologies (Fastify, ws, LiveKit), it supports text and audio streaming with multi-language capabilities.

πŸ’¬ Text Chat

Real-time text chat with AI agents using WebSocket streaming. Send text messages and receive streamed responses instantly.

πŸŽ™οΈ Audio Streaming

Bi-directional audio streaming with PCM16 format. Stream your voice and receive audio responses in real-time.

🌍 Multi-Language

Support for English (en), Arabic (ar), and Urdu (ur) with language-specific AI agents.

⚑

High Performance

Handles 10K-50K concurrent WebSocket connections

πŸ”’

Secure

Origin validation and token-based authentication

πŸ“‘

Real-Time

Live streaming with LiveKit integration

🎯

Production Ready

Battle-tested with Redis session management

πŸš€ Getting Started

Base URLs

API Server: http://localhost:8100
WebSocket Server: ws://localhost:8101
πŸ“ Note: Replace localhost with your actual domain in production. Both servers support HTTPS/WSS when deployed behind a reverse proxy.

Supported Languages

Language Code Description
πŸ‡¬πŸ‡§ English en English language agent
πŸ‡ΈπŸ‡¦ Arabic ar Arabic language agent
πŸ‡΅πŸ‡° Urdu ur Urdu language agent

Quick Test

Test the API and WebSocket servers using our built-in testing console:

http://localhost:8100/ - Interactive Testing Console

πŸ” Authentication & Security

Origin Validation

All API and WebSocket connections are protected by origin validation. Configure allowed origins in your environment:

# Allow specific domains ALLOWED_ORIGINS=qiwa.sa,labeah.ai,localhost # Allow all origins (development only) ALLOWED_ORIGINS=* # Leave empty to allow all (backward compatible) ALLOWED_ORIGINS=

API Keys - Two Authentication Methods

Method 1: Authorization Header (Recommended for Clients)

Clients can provide their API key via the Authorization header. This allows each client to use their own API key:

// REST API Authorization: Bearer your-api-key-here // Or without "Bearer" prefix Authorization: your-api-key-here

WebSocket Example:

const ws = new WebSocket( 'ws://localhost:8101/api/external/v1/chat/stream?id=123&lang=en', { headers: { 'Authorization': 'Bearer your-api-key-here' } } );

REST API Example:

fetch('http://localhost:8100/api/external/v1/tts/synthesize', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key-here' }, body: JSON.stringify({ text: 'Hello', lang: 'en' }) });

Method 2: Environment Variables (Server Configuration)

Configure default API keys in your .env file. These are used when clients don't provide an Authorization header:

API_KEY_EN=your-english-api-key API_KEY_AR=your-arabic-api-key API_KEY_UR=your-urdu-api-key
πŸ”‘ Priority: If an Authorization header is provided, it takes precedence over environment variables. This allows clients to use their own API keys while maintaining backward compatibility with server-configured keys.
⚠️ Security: Never expose your API keys in client-side code repositories. Store them securely and transmit them only over HTTPS/WSS in production.

πŸ’¬ Chat API (REST)

POST Send a message and receive complete response (non-streaming)
🎯 Perfect for: Simple request/response patterns, serverless functions, or when you need the complete response at once without streaming complexity.

Endpoint

POST http://localhost:8100/api/external/v1/chat

Request Body

{ "message": "What is artificial intelligence?", "lang": "en", "conversationId": "conv_1234567890_abc123" // optional }

Parameters

Field Type Required Description
message string required Your message to the AI agent
lang string optional Language code: en, ar, or ur. Defaults to en
conversationId string optional Conversation ID to maintain context. Auto-generated if not provided

Response

{ "success": true, "response": "Artificial intelligence (AI) refers to...", "conversationId": "conv_1234567890_abc123", "processingTimeMs": 2847, "connectionReused": true, // false for first message, true for subsequent "timestamp": "2025-10-16T09:30:50.343Z" }

How It Works (Connection Pooling for Reduced Setup Time)

⚑ Persistent Connection Pool Strategy:
  1. First Request: Creates LiveKit connection (~2-5s setup) + waits for AI response (~5-15s)
  2. Connection Pooled: Connection stays alive for 5 minutes
  3. Subsequent Requests: Reuses connection (saves 2-5s setup) + waits for AI response
  4. Conversation Context: Maintained automatically in the LiveKit room
  5. Smart Buffering: Uses 250ms silence detection for completion
  6. Auto-Cleanup: Idle connections cleaned up after 5 minutes
⚠️ Understanding Latency: The total response time includes AI processing (5-15s), which is the same for both REST and WebSocket. The connection pool saves setup time (2-5s), but the AI still needs time to generate the response. For better perceived performance, use WebSocket streaming where chunks appear immediately as the AI generates them.
πŸ’‘ Latency Breakdown:
β€’ Connection setup (first message only): 2-5 seconds
β€’ AI response generation: 5-15 seconds (depends on complexity)
β€’ Silence detection: 250ms (waits for completion)
β€’ Connection reused (follow-up messages): Saves 2-5 seconds ⚑

Total time: First message ~7-20s, Follow-up messages ~5-15s
Note: Most time is AI processing. Use WebSocket for perceived responsiveness.

Example: cURL

curl -X POST http://localhost:8100/api/external/v1/chat \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-api-key-here" \ -d '{ "message": "Explain quantum computing", "lang": "en" }'

Example: JavaScript/Fetch

const response = await fetch('http://localhost:8100/api/external/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key-here' }, body: JSON.stringify({ message: 'What is machine learning?', lang: 'en', conversationId: 'my-conversation-123' // optional }) }); const data = await response.json(); console.log('Response:', data.response); console.log('Processing time:', data.processingTimeMs, 'ms');

Example: Python

import requests response = requests.post( 'http://localhost:8100/api/external/v1/chat', headers={ 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key-here' }, json={ 'message': 'Explain neural networks', 'lang': 'en' } ) data = response.json() print(f"Response: {data['response']}") print(f"Processing time: {data['processingTimeMs']}ms")

Chat API vs Chat Stream

Feature Chat API (REST) Chat Stream (WebSocket)
Protocol HTTP POST WebSocket
Response Type Complete response at once Streaming chunks in real-time
Connection One per request Persistent connection
Best For Simple Q&A, serverless, batch processing Interactive chat, live updates, long conversations
Latency First: 7-20s, Subsequent: 5-15s (waits for complete response) First chunk: ~50-200ms, Full response: same as REST
Perceived Performance Slower - waits for everything Faster - see chunks immediately ⚑
Complexity Very simple Requires WebSocket handling
⏱️ Timeout Behavior: The API waits up to 30 seconds for a complete response. If the agent is still responding after 30 seconds, you'll receive whatever has been collected. For very long responses, consider using the WebSocket Chat Stream instead.

Example: Multi-Turn Conversation

// Message 1 - Creates connection (slower) const msg1 = await fetch('http://localhost:8100/api/external/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key' }, body: JSON.stringify({ message: 'What is AI?', lang: 'en', conversationId: 'my-conv-123' }) }).then(r => r.json()); console.log(msg1.connectionReused); // false (first message) console.log(msg1.processingTimeMs); // ~7000-20000ms (includes connection + AI response) // Message 2 - Reuses connection (FAST! ⚑) const msg2 = await fetch('http://localhost:8100/api/external/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key' }, body: JSON.stringify({ message: 'Can you explain more?', // Agent remembers context! lang: 'en', conversationId: 'my-conv-123' // Same conversation ID }) }).then(r => r.json()); console.log(msg2.connectionReused); // true (reused connection) console.log(msg2.processingTimeMs); // ~5000-15000ms (AI response time - saved 2-5s from reuse) // Message 3 - Still fast! const msg3 = await fetch('http://localhost:8100/api/external/v1/chat', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer your-api-key' }, body: JSON.stringify({ message: 'What about its applications?', lang: 'en', conversationId: 'my-conv-123' }) }).then(r => r.json()); console.log(msg3.connectionReused); // true (still reused!) console.log(msg3.processingTimeMs); // ~5000-15000ms (consistently fast connection) // Connection stays alive for 5 minutes of inactivity

πŸ’¬ Chat Stream (WebSocket)

WebSocket Real-time text chat with streaming responses

Connection URL

ws://localhost:8101/api/external/v1/chat/stream?id={conversationId}&lang={lang}

Query Parameters

Parameter Type Required Description
id string optional Conversation ID (UUID format). Auto-generated if not provided.
lang string required Language code: en, ar, or ur

Message Format

Sending Messages (Client β†’ Server)

// Send as JSON { "text": "Hello, how can you help me?" } // Or send as plain text "Hello, how can you help me?"

Receiving Responses (Server β†’ Client)

// Responses are sent as plain text chunks (streaming) "Hello! " "I'm " "here " "to " "help. " "What " "can " "I " "do " "for " "you?" // Error messages start with "Error:" "Error: Invalid language"

Example: JavaScript/TypeScript

// Connect to chat stream with API key const conversationId = "c43d6fde-7822-4ad0-a121-1ad37c54af02"; const lang = "en"; const apiKey = "your-api-key-here"; // Optional - omit to use server config // Create WebSocket with Authorization header const ws = new WebSocket( `ws://localhost:8101/api/external/v1/chat/stream?id=${conversationId}&lang=${lang}`, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); // Handle connection open ws.onopen = () => { console.log("Connected to chat stream"); // Send a message ws.send(JSON.stringify({ text: "What is the weather today?" })); }; // Handle incoming messages (streaming chunks) let fullResponse = ""; ws.onmessage = (event) => { const chunk = event.data; // Check for errors if (chunk.startsWith("Error:")) { console.error(chunk); return; } // Accumulate response fullResponse += chunk; console.log("Received chunk:", chunk); console.log("Full response so far:", fullResponse); }; // Handle errors ws.onerror = (error) => { console.error("WebSocket error:", error); }; // Handle close ws.onclose = () => { console.log("Disconnected from chat stream"); };
βœ… Best Practice: Keep the WebSocket connection open for the entire conversation. Each WebSocket maintains its own conversation context via the conversation ID.

πŸŽ™οΈ Audio Stream (WebSocket)

WebSocket Real-time bi-directional audio streaming

Connection URL

ws://localhost:8101/api/external/v1/audio/stream?id={conversationId}&lang={lang}

Audio Format

Property Value Description
Format PCM16 16-bit signed integer PCM
Sample Rate 16,000 Hz 16 kHz sampling rate
Channels Mono (1) Single channel audio
Encoding Binary Raw binary data (ArrayBuffer)

Sending Audio (Client β†’ Server)

// Send audio chunks as binary data (ArrayBuffer) const audioData = new Int16Array(4096); // PCM16 samples ws.send(audioData.buffer);

Receiving Audio (Server β†’ Client)

// Receive audio chunks as binary data ws.binaryType = "arraybuffer"; ws.onmessage = (event) => { if (typeof event.data === "string") { // Text message (likely error) console.log(event.data); } else { // Binary audio data const audioChunk = new Int16Array(event.data); // Play or process audio chunk playAudioChunk(audioChunk); } };

Complete Example: Browser Audio Streaming

const conversationId = "audio-conversation-123"; const lang = "en"; // 1. Connect to audio stream const ws = new WebSocket( `ws://localhost:8101/api/external/v1/audio/stream?id=${conversationId}&lang=${lang}` ); ws.binaryType = "arraybuffer"; // 2. Setup audio context for playback (16kHz) const audioContext = new AudioContext({ sampleRate: 16000 }); // 3. Capture microphone audio async function startRecording() { const stream = await navigator.mediaDevices.getUserMedia({ audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true, noiseSuppression: true } }); const captureContext = new AudioContext({ sampleRate: 16000 }); const source = captureContext.createMediaStreamSource(stream); const processor = captureContext.createScriptProcessor(4096, 1, 1); processor.onaudioprocess = (e) => { if (ws.readyState === WebSocket.OPEN) { const inputData = e.inputBuffer.getChannelData(0); // Convert Float32 to Int16 (PCM16) const pcm16 = new Int16Array(inputData.length); for (let i = 0; i < inputData.length; i++) { const s = Math.max(-1, Math.min(1, inputData[i])); pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF; } // Send to server ws.send(pcm16.buffer); } }; source.connect(processor); processor.connect(captureContext.destination); } // 4. Play received audio function playAudioChunk(pcm16Data) { // Convert Int16 to Float32 const float32Data = new Float32Array(pcm16Data.length); for (let i = 0; i < pcm16Data.length; i++) { float32Data[i] = pcm16Data[i] / (pcm16Data[i] < 0 ? 0x8000 : 0x7FFF); } // Create audio buffer and play const audioBuffer = audioContext.createBuffer(1, float32Data.length, 16000); audioBuffer.getChannelData(0).set(float32Data); const source = audioContext.createBufferSource(); source.buffer = audioBuffer; source.connect(audioContext.destination); source.start(); } // 5. Handle incoming audio ws.onmessage = (event) => { if (typeof event.data === "string") { console.log("Message:", event.data); } else { const audioData = new Int16Array(event.data); playAudioChunk(audioData); } }; // 6. Start recording when connected ws.onopen = () => { console.log("Connected to audio stream"); startRecording(); };
πŸ“ Note: Audio streaming requires microphone permissions in the browser. The gateway handles bi-directional streaming automatically.

πŸ”Š Text-to-Speech (REST API)

POST Synthesize text to speech audio

Endpoint

POST http://localhost:8100/api/external/v1/tts/synthesize

Request Body

{ "text": "Hello, welcome to Labeah AI!", "lang": "en" }

Parameters

Field Type Required Description
text string required Text to synthesize (1-5000 characters)
lang string optional Language code: en, ar, or ur. Defaults to en

Response

{ "success": true, "message": "TTS synthesis endpoint - implementation pending", "timestamp": "2025-10-16T09:30:50.343Z", "audioSize": 1024 }

Example: cURL

curl -X POST http://localhost:8100/api/external/v1/tts/synthesize \ -H "Content-Type: application/json" \ -d '{"text": "Hello world", "lang": "en"}'

Example: JavaScript

const apiKey = "your-api-key-here"; // Optional - omit to use server config const response = await fetch("http://localhost:8100/api/external/v1/tts/synthesize", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": `Bearer ${apiKey}` // Include your API key }, body: JSON.stringify({ text: "Hello, this is a test", lang: "en" }) }); const result = await response.json(); console.log(result);

🎀 Speech-to-Text (WebSocket)

WebSocket Real-time speech transcription

Connection URL

ws://localhost:8101/api/external/v1/stt/stream
⚠️ Coming Soon: This endpoint is currently a placeholder. Full implementation is pending API specification.

Planned Features

⚠️ Error Handling

HTTP Error Codes

Code Status Description
400 Bad Request Invalid request parameters or missing required fields
403 Forbidden Origin not allowed or authentication failed
500 Internal Server Error Server-side error occurred

WebSocket Close Codes

Code Reason Description
1000 Normal Closure Connection closed normally
1008 Policy Violation Origin not allowed or invalid parameters
1011 Internal Error Server encountered an error

Error Response Format

REST API Errors

{ "error": "Bad Request", "message": "Text is required", "timestamp": "2025-10-16T09:30:50.343Z" }

WebSocket Errors

// Text messages starting with "Error:" "Error: Invalid language. Must be one of: ar, en, ur" "Error: Message text is required" "Error: Failed to process your message"

Common Issues & Solutions

πŸ” Connection Rejected:
  • Origin not allowed: Add your domain to ALLOWED_ORIGINS environment variable
  • Invalid language: Use one of: en, ar, ur
  • Invalid conversation ID: Must be a valid UUID or leave empty for auto-generation

πŸ’» Complete Examples

Example 1: Simple Chat Bot

class ChatBot { constructor(lang = "en") { this.lang = lang; this.conversationId = this.generateUUID(); this.ws = null; } generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => { const r = Math.random() * 16 | 0; return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); } async connect() { return new Promise((resolve, reject) => { const url = `ws://localhost:8101/api/external/v1/chat/stream?id=${this.conversationId}&lang=${this.lang}`; this.ws = new WebSocket(url); this.ws.onopen = () => { console.log("βœ… Connected to chat"); resolve(); }; this.ws.onerror = reject; this.ws.onmessage = (event) => { if (event.data.startsWith("Error:")) { console.error(event.data); } else { process.stdout.write(event.data); // Stream output } }; }); } async sendMessage(text) { if (this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify({ text })); console.log(`\nπŸ—£οΈ You: ${text}`); process.stdout.write("πŸ€– Bot: "); } } disconnect() { if (this.ws) { this.ws.close(); console.log("\nπŸ‘‹ Disconnected"); } } } // Usage const bot = new ChatBot("en"); await bot.connect(); await bot.sendMessage("What is AI?"); // Wait for response to stream... await new Promise(r => setTimeout(r, 5000)); bot.disconnect();

Example 2: Python WebSocket Client

import asyncio import websockets import json import uuid async def chat_stream(lang="en"): conversation_id = str(uuid.uuid4()) uri = f"ws://localhost:8101/api/external/v1/chat/stream?id={conversation_id}&lang={lang}" async with websockets.connect(uri) as websocket: print("βœ… Connected to chat stream") # Send a message message = {"text": "Hello, how are you?"} await websocket.send(json.dumps(message)) print(f"πŸ—£οΈ You: {message['text']}") print("πŸ€– Bot: ", end="") # Receive streaming response while True: try: chunk = await asyncio.wait_for(websocket.recv(), timeout=30) if chunk.startswith("Error:"): print(f"\n❌ {chunk}") break print(chunk, end="", flush=True) except asyncio.TimeoutError: print("\n⏱️ Response complete") break # Run asyncio.run(chat_stream("en"))

Testing Resources

πŸ§ͺ Built-in Console

Access our interactive testing console with full UI for testing all endpoints:

πŸ“Š Health Check

Check server health and status: