Skip to content

Voice API Reference

Version: 1.0.0 Last Updated: 2026-01-29 Base URL: https://api.bayit.plus/api/v1


Table of Contents

  1. Authentication
  2. Unified Voice Endpoint
  3. Frontend Services
  4. Types and Interfaces
  5. Error Codes
  6. Rate Limits
  7. Examples

Authentication

All voice API endpoints require Firebase authentication.

Headers

http
Authorization: Bearer <firebase_id_token>
Content-Type: application/json

Getting Firebase Token

typescript
import { getAuth } from 'firebase/auth';

const auth = getAuth();
const user = auth.currentUser;
const token = await user.getIdToken();

Unified Voice Endpoint

POST /api/v1/voice/unified

Process a voice command with intent classification and routing.

Request

http
POST /api/v1/voice/unified
Authorization: Bearer <token>
Content-Type: application/json

Request Body:

json
{
  "transcript": "חפש סרטי פעולה",
  "language": "he",
  "conversation_id": "conv_abc123",
  "platform": "ios",
  "trigger_type": "wake-word"
}

Parameters:

FieldTypeRequiredDescription
transcriptstring✅ YesVoice command transcription
languagestring✅ YesLanguage code (he, en, es, etc.)
conversation_idstring❌ NoConversation tracking ID (auto-generated if omitted)
platformstring✅ YesPlatform: web, ios, android, tvos
trigger_typestring✅ YesTrigger: manual, wake-word

Response

Success (200 OK):

json
{
  "intent": "SEARCH",
  "spoken_response": "מצאתי 15 סרטי פעולה",
  "action": {
    "type": "navigate",
    "route": "/search",
    "params": {
      "query": "פעולה",
      "genre": "action"
    }
  },
  "conversation_id": "conv_abc123",
  "confidence": 0.95,
  "gesture": {
    "type": "point",
    "duration": 2000
  }
}

Response Fields:

FieldTypeDescription
intentVoiceIntentIntent classification: CHAT, SEARCH, NAVIGATION, PLAYBACK, SCROLL, CONTROL
spoken_responsestringTTS response text (max 500 chars)
actionobjectAction payload (optional)
conversation_idstringConversation tracking ID
confidencenumberIntent confidence score (0.0-1.0)
gestureobjectAvatar gesture state (optional)

Intent Types

CHAT

Natural conversation with AI assistant.

Example:

json
{
  "transcript": "מה זה סרט דוקומנטרי?",
  "language": "he",
  "platform": "web",
  "trigger_type": "manual"
}

Response:

json
{
  "intent": "CHAT",
  "spoken_response": "סרט דוקומנטרי הוא סרט המציג עובדות על נושא מסוים",
  "confidence": 0.98,
  "gesture": {
    "type": "explain",
    "duration": 3000
  }
}

Content search queries.

Example:

json
{
  "transcript": "חפש סרטים של כריסטופר נולן",
  "language": "he",
  "platform": "ios",
  "trigger_type": "wake-word"
}

Response:

json
{
  "intent": "SEARCH",
  "spoken_response": "מצאתי 12 סרטים של כריסטופר נולן",
  "action": {
    "type": "navigate",
    "route": "/search",
    "params": {
      "query": "Christopher Nolan"
    }
  },
  "confidence": 0.93
}

App navigation commands.

Example:

json
{
  "transcript": "עבור למועדפים",
  "language": "he",
  "platform": "tvos",
  "trigger_type": "manual"
}

Response:

json
{
  "intent": "NAVIGATION",
  "spoken_response": "עובר למועדפים שלך",
  "action": {
    "type": "navigate",
    "route": "/favorites"
  },
  "confidence": 0.97
}
PLAYBACK

Media playback controls.

Example:

json
{
  "transcript": "נגן את פאודה",
  "language": "he",
  "platform": "android",
  "trigger_type": "wake-word"
}

Response:

json
{
  "intent": "PLAYBACK",
  "spoken_response": "מנגן את פאודה",
  "action": {
    "type": "play",
    "content_id": "series_fauda_123",
    "episode": 1,
    "season": 1
  },
  "confidence": 0.96
}
SCROLL

Screen scrolling commands.

Example:

json
{
  "transcript": "גלול למטה",
  "language": "he",
  "platform": "web",
  "trigger_type": "manual"
}

Response:

json
{
  "intent": "SCROLL",
  "spoken_response": "גולל למטה",
  "action": {
    "type": "scroll",
    "direction": "down",
    "amount": 500
  },
  "confidence": 0.99
}
CONTROL

System control commands.

Example:

json
{
  "transcript": "סגור חלון",
  "language": "he",
  "platform": "ios",
  "trigger_type": "manual"
}

Response:

json
{
  "intent": "CONTROL",
  "spoken_response": "סוגר חלון",
  "action": {
    "type": "close",
    "target": "modal"
  },
  "confidence": 0.95
}

Error Responses

Authentication Error (401):

json
{
  "detail": "Invalid authentication token",
  "error_code": "AUTH_INVALID_TOKEN"
}

Rate Limit Error (429):

json
{
  "detail": "Rate limit exceeded. Try again in 60 seconds",
  "error_code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 60
}

Server Error (500):

json
{
  "detail": "Voice command processing failed",
  "error_code": "INTERNAL_SERVER_ERROR"
}

Frontend Services

OlorinVoiceOrchestrator

Main orchestrator service for all voice interactions.

Import

typescript
import { createVoiceOrchestrator } from '@bayit/shared/services/olorinVoiceOrchestrator';
import type { VoiceConfig, VoiceCommandResponse } from '@bayit/shared/types/voiceAvatar';

Methods

initialize(config?: Partial<VoiceConfig>): Promise<void>

Initialize the orchestrator with configuration.

Example:

typescript
const orchestrator = createVoiceOrchestrator({
  platform: 'web',
  language: 'he',
  wakeWordEnabled: false,
  streamingMode: true,
  initialAvatarMode: 'full',
  autoExpandOnWakeWord: true,
  collapseDelay: 5000,
});

await orchestrator.initialize();
startListening(trigger: VoiceTrigger): Promise<void>

Start listening for voice input.

Parameters:

  • trigger: 'manual' | 'wake-word'

Example:

typescript
// Manual trigger (button press)
await orchestrator.startListening('manual');

// Wake word trigger
await orchestrator.startListening('wake-word');
stopListening(): Promise<void>

Stop listening for voice input.

Example:

typescript
await orchestrator.stopListening();
interrupt(): Promise<void>

Interrupt current voice processing.

Example:

typescript
await orchestrator.interrupt();
processTranscript(transcript: string, conversationId?: string): Promise<VoiceCommandResponse>

Process a voice transcript and get response.

Example:

typescript
const response = await orchestrator.processTranscript(
  'חפש סרטים',
  'conv_abc123' // optional
);

console.log(response.intent);           // 'SEARCH'
console.log(response.spokenResponse);   // 'מחפש סרטים...'
console.log(response.confidence);       // 0.95
setAvatarVisibility(mode: AvatarMode): void

Change avatar visibility mode.

Example:

typescript
orchestrator.setAvatarVisibility('compact');
setWakeWordEnabled(enabled: boolean): void

Enable/disable wake word detection.

Example:

typescript
orchestrator.setWakeWordEnabled(true);
setStreamingMode(enabled: boolean): void

Enable/disable streaming mode.

Example:

typescript
orchestrator.setStreamingMode(false); // Use batch mode

useVoiceOrchestrator Hook

React hook for voice orchestrator integration.

Import

typescript
import { useVoiceOrchestrator } from '@bayit/shared/hooks/useVoiceOrchestrator';

Usage

typescript
function VoiceFeature() {
  const {
    orchestrator,
    isInitialized,
    startListening,
    stopListening,
    interrupt,
    processTranscript,
  } = useVoiceOrchestrator({
    platform: 'web',
    language: 'he',
    wakeWordEnabled: false,
  });

  const handleVoiceCommand = async () => {
    await startListening('manual');
  };

  return (
    <button onClick={handleVoiceCommand} disabled={!isInitialized}>
      Start Voice
    </button>
  );
}

Return Values

PropertyTypeDescription
orchestratorOlorinVoiceOrchestrator | nullOrchestrator instance
isInitializedbooleanInitialization status
startListening(trigger: VoiceTrigger) => Promise<void>Start listening
stopListening() => Promise<void>Stop listening
interrupt() => Promise<void>Interrupt processing
processTranscript(transcript: string, convId?: string) => Promise<VoiceCommandResponse>Process transcript

useVoiceAvatarMode Hook

React hook for avatar mode management.

Import

typescript
import { useVoiceAvatarMode } from '@bayit/shared/hooks/useVoiceAvatarMode';

Usage

typescript
function AvatarDisplay() {
  const {
    avatarMode,
    dimensions,
    showAnimations,
    showWaveform,
    showTranscript,
    showWizard,
  } = useVoiceAvatarMode('web');

  return (
    <View style={{ width: dimensions.web.width, height: dimensions.web.height }}>
      {showWizard && <WizardAvatar />}
      {showWaveform && <Waveform />}
      {showTranscript && <Transcript />}
    </View>
  );
}

Return Values

PropertyTypeDescription
avatarModeAvatarModeCurrent mode: full, compact, minimal, icon_only
dimensionsDimensionsPlatform-specific dimensions
showAnimationsbooleanShow animations flag
showWaveformbooleanShow waveform flag
showTranscriptbooleanShow transcript flag
showWizardbooleanShow wizard flag

Types and Interfaces

VoiceIntent

typescript
export type VoiceIntent =
  | 'CHAT'        // Natural conversation
  | 'SEARCH'      // Content search
  | 'NAVIGATION'  // App navigation
  | 'PLAYBACK'    // Media controls
  | 'SCROLL'      // Screen scrolling
  | 'CONTROL';    // System controls

AvatarMode

typescript
export type AvatarMode =
  | 'full'        // Complete wizard with animations
  | 'compact'     // Floating circular panel
  | 'minimal'     // Waveform bar only
  | 'icon_only';  // Hidden (FAB only)

VoiceTrigger

typescript
export type VoiceTrigger =
  | 'manual'      // Button press
  | 'wake-word';  // Wake word detected

VoiceConfig

typescript
export interface VoiceConfig {
  platform: 'web' | 'ios' | 'android' | 'tvos';
  language: string;              // Language code
  wakeWordEnabled: boolean;      // Enable wake word detection
  streamingMode: boolean;        // Use streaming (faster) vs batch
  initialAvatarMode: AvatarMode; // Starting mode
  autoExpandOnWakeWord: boolean; // Auto-expand on wake word
  collapseDelay: number;         // Auto-collapse delay (ms)
}

VoiceCommandResponse

typescript
export interface VoiceCommandResponse {
  intent: VoiceIntent;
  spokenResponse: string;
  action?: {
    type: string;
    [key: string]: any;
  };
  conversationId: string;
  confidence: number;
  gesture?: {
    type: string;
    duration: number;
  };
}

AvatarModeConfig

typescript
export interface AvatarModeConfig {
  dimensions: {
    web: { width: number; height: number };
    mobile: { width: number; height: number };
    tv: { width: number; height: number };
  };
  showAnimations: boolean;
  showWaveform: boolean;
  showTranscript: boolean;
  showWizard: boolean;
}

Error Codes

Authentication Errors (401)

CodeDescriptionSolution
AUTH_MISSING_TOKENNo authentication token providedInclude Authorization header
AUTH_INVALID_TOKENInvalid or expired tokenGet new Firebase token
AUTH_USER_NOT_FOUNDUser not foundEnsure user is registered

Validation Errors (400)

CodeDescriptionSolution
VALIDATION_TRANSCRIPT_EMPTYEmpty transcriptProvide non-empty transcript
VALIDATION_LANGUAGE_INVALIDInvalid language codeUse valid language code
VALIDATION_PLATFORM_INVALIDInvalid platformUse: web, ios, android, tvos

Rate Limit Errors (429)

CodeDescriptionSolution
RATE_LIMIT_EXCEEDEDRate limit exceededWait and retry (retry_after)
RATE_LIMIT_USERUser-specific limitWait 60 seconds
RATE_LIMIT_IPIP-based limitWait 60 seconds

Server Errors (500)

CodeDescriptionSolution
INTERNAL_SERVER_ERRORUnexpected server errorRetry with exponential backoff
INTENT_CLASSIFICATION_FAILEDIntent classification failedCheck transcript clarity
HANDLER_EXECUTION_FAILEDHandler execution failedContact support

Rate Limits

Rate Limit Tiers

User TypePer MinutePer HourPer Day
Free1050200
Premium605002000
Admin12010005000

Rate Limit Headers

All responses include rate limit headers:

http
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1706544000

Handling Rate Limits

typescript
async function sendVoiceCommand(transcript: string) {
  try {
    const response = await fetch('/api/v1/voice/unified', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ transcript, language: 'he', platform: 'web', trigger_type: 'manual' }),
    });

    if (response.status === 429) {
      const data = await response.json();
      console.log(`Rate limited. Retry after ${data.retry_after} seconds`);
      await new Promise(resolve => setTimeout(resolve, data.retry_after * 1000));
      return sendVoiceCommand(transcript); // Retry
    }

    return response.json();
  } catch (error) {
    console.error('Voice command failed:', error);
    throw error;
  }
}

Examples

Example 1: Basic Voice Search (Web)

typescript
import { useVoiceOrchestrator } from '@bayit/shared/hooks/useVoiceOrchestrator';
import { useSupportStore } from '@bayit/shared/stores/supportStore';

function SearchComponent() {
  const { processTranscript } = useVoiceOrchestrator({ platform: 'web' });
  const [results, setResults] = useState([]);

  const handleVoiceSearch = async (transcript: string) => {
    const response = await processTranscript(transcript);

    if (response.intent === 'SEARCH' && response.action) {
      // Navigate to search results
      router.push({
        pathname: response.action.route,
        query: response.action.params,
      });
    }
  };

  return (
    <div>
      <button onClick={() => handleVoiceSearch('חפש סרטים')}>
        Voice Search
      </button>
    </div>
  );
}

Example 2: Wake Word Detection (Mobile)

typescript
// In voiceManager initialization
private async _initializeOrchestrator(): Promise<void> {
  this.orchestrator = createVoiceOrchestrator({
    platform: 'ios',
    language: 'he',
    wakeWordEnabled: true,
    autoExpandOnWakeWord: true,
  });
  await this.orchestrator.initialize();
}

// Wake word detection handler
private async _onWakeWordDetected(detection: any): Promise<void> {
  if (this.orchestrator) {
    await this.orchestrator.startListening('wake-word');
  }

  // Update UI
  const store = useSupportStore.getState();
  store.onWakeWordDetected();

  // Haptic feedback
  await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium);
}

Example 3: Avatar Mode Switching

typescript
import { useSupportStore } from '@bayit/shared/stores/supportStore';

function AvatarSettings() {
  const { avatarVisibilityMode, setAvatarVisibilityMode } = useSupportStore();

  const modes = ['full', 'compact', 'minimal', 'icon_only'] as const;

  return (
    <div>
      {modes.map((mode) => (
        <button
          key={mode}
          onClick={() => setAvatarVisibilityMode(mode)}
          className={avatarVisibilityMode === mode ? 'selected' : ''}
        >
          {mode}
        </button>
      ))}
    </div>
  );
}

Example 4: Streaming vs Batch Mode

typescript
import { useVoiceOrchestrator } from '@bayit/shared/hooks/useVoiceOrchestrator';

function VoiceComponent() {
  const { orchestrator } = useVoiceOrchestrator({ platform: 'web' });

  const handleQuickCommand = async () => {
    // Use streaming for fast responses
    orchestrator?.setStreamingMode(true);
    await orchestrator?.processTranscript('play next episode');
  };

  const handleComplexQuery = async () => {
    // Use batch for complex processing
    orchestrator?.setStreamingMode(false);
    await orchestrator?.processTranscript('recommend movies similar to inception');
  };

  return (
    <div>
      <button onClick={handleQuickCommand}>Quick Command</button>
      <button onClick={handleComplexQuery}>Complex Query</button>
    </div>
  );
}

Example 5: Error Handling

typescript
async function processVoiceCommand(transcript: string) {
  try {
    const response = await orchestrator.processTranscript(transcript);
    return response;
  } catch (error) {
    if (error.status === 401) {
      // Re-authenticate
      const newToken = await refreshFirebaseToken();
      return processVoiceCommand(transcript); // Retry
    } else if (error.status === 429) {
      // Rate limited
      const retryAfter = error.retry_after || 60;
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      return processVoiceCommand(transcript); // Retry
    } else {
      // Show error to user
      toast.error('Voice command failed. Please try again.');
      throw error;
    }
  }
}


Document Version: 1.0.0 Last Updated: 2026-01-29 Maintained By: Platform Team Review Cycle: Quarterly

Released under the MIT License.