npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@voice-ai-labs/web-sdk

v1.0.3

Published

Web SDK for Voice.ai - Easy integration of voice agents into JavaScript applications

Downloads

219

Readme

Voice.ai Web SDK

The official Voice.ai SDK for JavaScript/TypeScript applications.

Installation

npm install @voice-ai-labs/web-sdk

Quick Start

import VoiceAI from '@voice-ai-labs/web-sdk';

// Initialize with your API key
const voiceai = new VoiceAI({ apiKey: 'vk_your_api_key' });

// Connect to a voice agent
await voiceai.connect({ agentId: 'your-agent-id' });

// Listen for transcriptions
voiceai.onTranscription((segment) => {
  console.log(`${segment.role}: ${segment.text}`);
});

// Disconnect when done
await voiceai.disconnect();

Features

The SDK provides a unified interface for:

Real-time Voice

Connect to an Agent

await voiceai.connect({
  agentId: 'agent-123',
  autoPublishMic: true  // default: true
});

// Test mode: preview paused agents before deploying
await voiceai.connect({ agentId: 'agent-123', testMode: true });

connect() supports these top-level request shapes:

  • agentId for a saved agent
  • dynamicVariables for optional runtime variables passed at call start
  • agentOverrides for optional call-scoped runtime config overrides
  • testMode to preview paused agents before deploying

Events

// Transcriptions (user and agent speech)
voiceai.onTranscription((segment) => {
  console.log(`${segment.role}: ${segment.text}`);
  console.log('Final:', segment.isFinal);
});

// Connection status
voiceai.onStatusChange((status) => {
  if (status.connected) console.log('Connected!');
  if (status.error) console.error('Error:', status.error);
});

// Agent state (listening, speaking, thinking)
voiceai.onAgentStateChange((state) => {
  console.log('Agent is:', state.state);
});

// Audio levels (for visualizations)
voiceai.onAudioLevel((level) => {
  console.log('Level:', level.level, 'Speaking:', level.isSpeaking);
});

// Errors
voiceai.onError((error) => {
  console.error('Error:', error.message);
});

Each handler returns a function to unsubscribe: const stop = voiceai.onTranscription(...); stop();

Microphone Control

await voiceai.setMicrophoneEnabled(true);   // Enable
await voiceai.setMicrophoneEnabled(false);  // Disable

Send Text Message

await voiceai.sendMessage('Hello agent!');

Disconnect

await voiceai.disconnect();

Status (read-only)

voiceai.isConnected();
voiceai.getStatus();          // { connected, connecting, callId, error }
voiceai.getAgentState();      // { state, agentParticipantId }
voiceai.getMicrophoneState(); // { enabled, muted }

Text-to-Speech

The TTS API provides speech generation and voice management.

Generate Speech

// Non-streaming: returns complete audio as Blob
const audio = await voiceai.tts.synthesize({
  text: 'Hello, welcome to Voice AI!',
  voice_id: 'voice-123',
  language: 'en',
  audio_format: 'mp3',
});
const url = URL.createObjectURL(audio);
new Audio(url).play();

// Streaming: returns Response with readable body
const response = await voiceai.tts.synthesizeStream({
  text: 'Hello, welcome!',
  voice_id: 'voice-123',
  language: 'en',
});
const reader = response.body!.getReader();
// Read chunks: reader.read()

Managed pronunciation dictionaries can be attached to direct TTS requests and saved agent configs with:

const audio = await voiceai.tts.synthesize({
  text: 'Schedule a follow-up for Thailand.',
  voice_id: 'voice-123',
  language: 'en',
  dictionary_id: 'dict-123',
  dictionary_version: 2,
});

Voice Management

// List all available voices
const voices = await voiceai.tts.listVoices();

// Clone a voice from audio file (MP3/WAV/M4A, max 7.5MB)
const voice = await voiceai.tts.cloneVoice({
  file: audioFile,
  name: 'My Voice',
  language: 'en',
  voice_visibility: 'PRIVATE',
});

// Get voice status (PENDING -> PROCESSING -> AVAILABLE)
await voiceai.tts.getVoice(voice.voice_id);

// Update voice metadata
await voiceai.tts.updateVoice('voice-123', { name: 'Renamed', voice_visibility: 'PUBLIC' });

// Delete voice
await voiceai.tts.deleteVoice('voice-123');

Pronunciation Dictionaries

// List dictionaries
const dictionaries = await voiceai.tts.listPronunciationDictionaries();

// Get one dictionary
const dictionary = await voiceai.tts.getPronunciationDictionary('dict-123');

// Create from rules
const created = await voiceai.tts.createPronunciationDictionaryFromRules({
  name: 'Medical Terms',
  language: 'en',
  rules: [
    { word: 'Thailand', replacement: 'tie-land' },
    { word: 'router', replacement: 'row-ter', ipa: 'ˈraʊtɚ', case_sensitive: false },
  ],
});

// Create from a .pls file
await voiceai.tts.createPronunciationDictionaryFromFile({
  file: dictionaryFile,
  name: 'Imported Dictionary',
  language: 'en',
});

// Rename
await voiceai.tts.updatePronunciationDictionary('dict-123', { name: 'Medical Terms v2' });

// Replace all rules
await voiceai.tts.setPronunciationDictionaryRules('dict-123', [
  { word: 'SQL', replacement: 'sequel', case_sensitive: false },
]);

// Add rules
await voiceai.tts.addPronunciationDictionaryRules('dict-123', [
  { word: 'gif', replacement: 'jif', case_sensitive: false },
]);

// Remove rules by stable rule ID
await voiceai.tts.removePronunciationDictionaryRules('dict-123', ['rule-1', 'rule-2']);

// Download a specific version as a Blob
const plsBlob = await voiceai.tts.downloadPronunciationDictionaryVersion('dict-123', 3);

// Delete
await voiceai.tts.deletePronunciationDictionary('dict-123');

Agent Management

// List agents
const agents = await voiceai.agents.list();

// Create an agent
const agent = await voiceai.agents.create({
  name: 'Customer Support',
  config: {
    prompt: 'You are a helpful customer support agent.',
    greeting: 'Hello! How can I help you today?',
    recording_enabled: true,
    tts_params: {
      voice_id: 'my-voice-id',
      model: 'voiceai-tts-v1-latest',
      language: 'en'
    }
  }
});

// Deploy the agent
await voiceai.agents.deploy(agent.agent_id);

// Update an agent
await voiceai.agents.update(agent.agent_id, {
  name: 'Updated Name'
});

// Pause an agent
await voiceai.agents.pause(agent.agent_id);

// Delete an agent
await voiceai.agents.disable(agent.agent_id);

// Create an outbound call (server-side only)
// NOTE: Outbound is restricted to approved accounts.
// If your account is not approved, this endpoint may return 403.
await voiceai.agents.createOutboundCall({
  agent_id: agent.agent_id,
  target_phone_number: '+15551234567',
  dynamic_variables: { case_id: 'abc-1' },
  agent_overrides: {
    tts_params: {
      voice_id: 'voice_priority_customer',
      dictionary_id: 'dict-123',
      dictionary_version: 2,
    },
  },
});

Managed Tools

The managed tools surface is generic. Today it covers Google Calendar, Google Sheets, and Gmail.

Managed tools are exposed under voiceai.managedTools.

Today the SDK includes one Google entry:

  • voiceai.managedTools.google
    • Use it to start OAuth, check status, and disconnect Google managed tools for an agent
    • Pass the individual managed tool configs under managedTools.google_calendar, managedTools.google_sheets, and managedTools.google_gmail

Use the managed Google surface to connect an agent to Google Calendar, Sheets, or Gmail.

For a working browser example, see demo/managed-tools.html.

import VoiceAI, {
  GOOGLE_CALENDAR_OPERATION_OPTIONS,
  IANA_TIMEZONE_OPTIONS,
  getGoogleReconnectState,
  hasEnabledGoogleManagedTools,
} from '@voice-ai-labs/web-sdk';

const voiceai = new VoiceAI({ apiKey: 'vk_your_api_key' });

const start = await voiceai.managedTools.google.startOAuth('agent-123', {
  returnUrl: window.location.href,
  managedTools: {
    google_calendar: {
      enabled: true,
      timezone: 'America/Los_Angeles',
      selected_operations: ['google_calendar_check_availability', 'google_calendar_create_event'],
    },
  },
});

window.open(start.auth_url, 'google-oauth', 'popup,width=540,height=720');

const status = await voiceai.managedTools.google.getStatus('agent-123');
const reconnect = getGoogleReconnectState(
  {
    google_calendar: {
      enabled: true,
      selected_operations: ['google_calendar_check_availability', 'google_calendar_create_event'],
    },
  },
  status
);

Connection flow:

  1. Call startOAuth(...) with the agent ID, returnUrl, and the currently enabled Google managed tool config for the agent.
  2. Open the returned auth_url in a popup or browser tab.
  3. Google redirects to the VoiceAI backend callback.
  4. The backend completes OAuth and then returns the user to your returnUrl.
  5. Refresh or poll voiceai.managedTools.google.getStatus(agentId) to confirm the connection state.

Connection semantics:

  • startOAuth(...) manages Google managed-tools access for the agent
  • the Google connection is shared across Calendar, Sheets, and Gmail for that agent
  • enabling more Google operations later can require reconnecting to grant access for the currently enabled Google tools
  • disconnect(...) removes Google managed-tools access for all Google tools on that agent

Available helpers:

  • voiceai.managedTools.google.startOAuth(agentId, { returnUrl, managedTools })
  • voiceai.managedTools.google.getStatus(agentId)
  • voiceai.managedTools.google.disconnect(agentId)
  • GOOGLE_CALENDAR_OPERATION_OPTIONS, GOOGLE_SHEETS_OPERATION_OPTIONS, GOOGLE_GMAIL_OPERATION_OPTIONS
  • getManagedToolSelectedOperations(...), toggleManagedToolOperation(...)
  • getRequiredGoogleScopes(...), getGoogleReconnectState(...), hasEnabledGoogleManagedTools(...)
  • IANA_TIMEZONE_OPTIONS for timezone selectors. Each option includes { value, label, offsetMinutes }, where value is the canonical IANA timezone like America/Los_Angeles

Managed tool config fields:

  • google_calendar
    • enabled: boolean
    • default_calendar_id?: string
    • timezone?: string
    • selected_operations?: GoogleCalendarOperation[]
  • google_sheets
    • enabled: boolean
    • spreadsheet_id?: string
    • sheet_name?: string
    • selected_operations?: GoogleSheetsOperation[]
  • google_gmail
    • enabled: boolean
    • selected_operations?: GoogleGmailOperation[]

Supported Calendar operations:

  • google_calendar_check_availability — Check whether a calendar is free during a time window
  • google_calendar_list_upcoming_events — Read the next upcoming events from the calendar
  • google_calendar_create_event — Create a new calendar event
  • google_calendar_update_event — Modify an existing calendar event
  • google_calendar_cancel_event — Cancel or delete an existing calendar event

Supported Sheets operations:

  • google_sheets_append_row — Write a new row into a spreadsheet
  • google_sheets_list_sheets — List worksheet tabs and spreadsheet metadata
  • google_sheets_read_rows — Read rows from a worksheet range

Supported Gmail operations:

  • google_gmail_search_messages — Search Gmail and return readable message summaries
  • google_gmail_get_message — Fetch a specific Gmail message by ID
  • google_gmail_send_email — Send an email from the connected Gmail account

Notes:

  • Google Calendar, Sheets, and Gmail all use the voiceai.managedTools.google surface.
  • startOAuth(...) uses the Google managed tool config you pass in managedTools to determine the scopes requested for the shared agent-level Google connection.
  • If you enable additional Google operations later, getStatus() / getGoogleReconnectState(...) may report reconnect_required until you reconnect and grant access for the currently enabled Google tools.
  • voiceai.managedTools.google.disconnect(agentId) removes Google managed-tools access for the agent.
  • returnUrl is your app/frontend return target after the VoiceAI backend callback completes. It is not the Google-registered OAuth redirect URI.
  • Calendar timezone should be an IANA timezone like America/Los_Angeles
  • If selected_operations is omitted, the SDK helpers treat that managed tool config as “all supported operations selected”
  • If selected_operations is an empty array, the managed tool config remains present but requests no additional provider scopes

Outbound access control: POST /api/v1/calls/outbound is restricted to approved accounts. If you need outbound enabled for your account/workspace, please contact Voice.ai support.

Update behavior: voiceai.agents.update() is a partial update. Omit a field to leave it unchanged. For nullable scalar fields like prompt, greeting, phone_number, or nested tts_params fields, pass null to clear the current value. See section-specific notes below for webhook clearing behavior.

Recording: config.recording_enabled defaults to true for new agents. Set it to false to disable recording for future calls.

Dynamic Variables

Pass optional dynamic_variables at call start and reference them in your prompt or greeting with {{variable_name}}:

await voiceai.agents.update(agent.agent_id, {
  config: {
    allow_outbound_calling: true,
    prompt: 'You are helping {{customer_name}} with order {{order_id}} for the {{account_tier}} account.',
    greeting: 'Hi {{customer_name}}, I can help with your {{city}} delivery today.'
  }
});

await voiceai.connect({
  agentId: agent.agent_id,
  dynamicVariables: {
    customer_name: 'Alice',
    order_id: '12345',
    account_tier: 'gold',
    city: 'Seattle'
  }
});
  • dynamic_variables must be a flat object of string, number, or boolean values.
  • Extra variables are allowed.
  • Variables that are not referenced by the runtime prompt or greeting are ignored.
  • The runtime is responsible for interpolating these variables into the prompt and greeting.
  • If the same placeholder appears multiple times, the same runtime value is used for every occurrence.

Runtime Agent Overrides

Pass optional agentOverrides at call start when you need a call-scoped config patch rather than prompt interpolation:

await voiceai.connect({
  agentId: agent.agent_id,
  dynamicVariables: {
    customer_name: 'Alice',
  },
  agentOverrides: {
    tts_params: {
      voice_id: 'voice_vip_alice',
      temperature: 0.8,
    },
  },
});
  • agentOverrides is separate from dynamicVariables.
  • dynamicVariables is for prompt and greeting interpolation.
  • agentOverrides is for allowlisted runtime config changes.
  • v1 supports only tts_params.voice_id, temperature, top_p, dictionary_id, and dictionary_version.
  • agentOverrides uses the same nested shape as agent config, so voice overrides live under tts_params.

Knowledge Base

// Create a knowledge base
const kb = await voiceai.knowledgeBase.create({
  name: 'Product FAQ',
  documents: [
    { content: 'Return policy: 30 days for full refund.' },
    { content: 'Shipping: Free on orders over $50.' }
  ]
});

// Assign to an agent
await voiceai.agents.assignKnowledgeBase(agentId, kb.kb_id);

// List knowledge bases
const kbs = await voiceai.knowledgeBase.list();

// Update a knowledge base
await voiceai.knowledgeBase.update(kb.kb_id, {
  documents: [{ content: 'Updated content' }]
});

// Delete a knowledge base
await voiceai.knowledgeBase.remove(kb.kb_id);

Phone Numbers

// Search available numbers
const numbers = await voiceai.phoneNumbers.search({
  country_code: 'US',
  area_code: '415'
});

// Select a number
await voiceai.phoneNumbers.select('+14155551234');

// List your numbers
const myNumbers = await voiceai.phoneNumbers.list();

// Release a number
await voiceai.phoneNumbers.release('+14155551234');

Analytics

// Get call history
const history = await voiceai.analytics.getCallHistory({
  page: 1,
  limit: 20,
  agent_ids: ['agent-123'],
  agent_name: 'support',
  sort_by: 'duration',
  sort_dir: 'desc'
});

// Get transcript URL
const transcript = await voiceai.analytics.getTranscriptUrl(callId);

// Get recording status or URL
const recording = await voiceai.analytics.getRecordingUrl(callId);
if (recording.status === 'ready' && recording.url) {
  window.open(recording.url, '_blank');
}

// Get stats summary
const stats = await voiceai.analytics.getStatsSummary();

Webhooks

Configure webhooks when creating or updating an agent.

webhooks.events[], webhooks.inbound_call, and webhooks.tools use different contracts:

  • webhooks.events[] supports secret (write-only on create/update) and has_secret (read-only on fetch), with fan-out across enabled endpoints.
  • webhooks.inbound_call supports secret (write-only on create/update) and has_secret (read-only on fetch).
  • webhooks.tools define outbound API calls and do not use secret.

Configure Webhook Events and Tools

// Create agent with webhook events and tools
const agent = await voiceai.agents.create({
  name: 'Support Agent',
  config: {
    prompt: 'You are a helpful support agent.',
    webhooks: {
      events: [
        {
          url: 'https://your-server.com/webhooks/voice-events',
          secret: 'your-hmac-secret',  // Event webhook signing secret
          events: ['call.started', 'call.completed'],  // Or omit for all events
          timeout: 5,
          enabled: true
        }
      ],
      inbound_call: {
        url: 'https://your-server.com/webhooks/inbound-call',
        secret: 'your-inbound-call-secret',  // Inbound call webhook signing secret
        timeout: 5,
        enabled: true
      },
      tools: [
        {
          name: 'get_account_status',
          description: 'Fetches current account status for a customer.',
          url: 'https://your-server.com/webhooks/tools/account-status',
          parameters: {
            customer_id: 'string'
          },
          method: 'POST',
          execution_mode: 'sync',
          auth_type: 'api_key',
          auth_token: 'your-api-key',
          headers: {
            'X-Service-Version': '2026-02'
          },
          response: {
            type: 'object',
            properties: {
              status: { type: 'string' },
              tier: { type: 'string' }
            }
          },
          timeout: 10
        }
      ]
    }
  }
});

// Update webhook config on existing agent
await voiceai.agents.update(agentId, {
  config: {
    webhooks: {
      events: [
        {
          url: 'https://your-server.com/webhooks',
          events: ['call.completed'],  // Only receive call.completed
          enabled: true
        }
      ],
      tools: [
        {
          name: 'search_knowledge_base',
          description: 'Searches KB and returns ranked snippets.',
          url: 'https://your-server.com/webhooks/tools/search-kb',
          parameters: {
            query: 'string',
            top_k: 'number'
          },
          method: 'GET',
          execution_mode: 'async',
          auth_type: 'custom_headers',
          headers: {
            'X-Internal-Token': 'your-internal-token'
          },
          timeout: 20
        }
      ]
    }
  }
});

Webhook configuration requiredness

  • webhooks.events[]
    • Required per endpoint: url
    • Optional per endpoint: secret, events, timeout (default 5), enabled (default true)
    • On update: omit webhooks.events to preserve the current list, set webhooks.events: null to remove it, or pass a full array to replace the list
    • When replacing the array: omitted secret values are preserved only for entries whose url exactly matches an existing endpoint, secret: null clears the signing secret for that endpoint, and duplicate URLs are invalid
    • Use events: [] on an endpoint to receive all event types
  • webhooks.inbound_call
    • Required: url
    • Optional: secret, timeout (default 5), enabled (default true)
    • On update: omit inbound_call to preserve it, set inbound_call: null to remove it, and set secret: null to clear only the signing secret
  • webhooks.tools
    • Required per tool: name, description, parameters, url, method, execution_mode, auth_type
    • Optional per tool: auth_token, headers, response, timeout (default 10)
    • On update: omit tools to leave the current tool list unchanged, set tools: null to clear all tools, or pass a new array to replace the current list

If a field is optional and omitted, the service uses the documented default. Prefer omitting optional fields instead of sending null unless you explicitly intend to clear behavior in a supported way.

Event Types

| Event | Description | |-------|-------------| | call.started | Call connected, agent ready | | call.completed | Call ended, includes transcript and usage data |

Event Webhook Payload

Your event webhook URL receives POST requests with this structure:

interface WebhookEvent {
  event: 'call.started' | 'call.completed' | 'test';
  timestamp: string;  // ISO 8601
  call_id: string;
  agent_id: string;
  data: {
    call_type: 'web' | 'sip_inbound' | 'sip_outbound';
    // call.started: started_at, from_number?, to_number?
    // call.completed: duration_seconds, credits_used, transcript_uri, transcript_summary
  };
}

Webhook Tool Request Shape

For webhook tools, Voice.ai makes outbound HTTP requests directly to each tool url.

  • method: 'GET' | 'DELETE': tool arguments are sent as query parameters.
  • method: 'POST' | 'PUT' | 'PATCH': tool arguments are sent as JSON body.
  • For body methods, Voice.ai always sends Content-Type: application/json.
  • If you configure a Content-Type header in headers, it is ignored for webhook tools.
  • Metadata headers are always sent:
    • X-VoiceAI-Request-Id
    • X-VoiceAI-Tool-Name
    • X-VoiceAI-Agent-Id
    • X-VoiceAI-Call-Id
GET /webhooks/tools/search-kb?query=refund+policy&top_k=3
X-VoiceAI-Request-Id: req_123
X-VoiceAI-Tool-Name: search_knowledge_base
X-VoiceAI-Agent-Id: agent_123
X-VoiceAI-Call-Id: call_123
POST /webhooks/tools/account-status
Content-Type: application/json
X-VoiceAI-Request-Id: req_456
X-VoiceAI-Tool-Name: get_account_status
X-VoiceAI-Agent-Id: agent_123
X-VoiceAI-Call-Id: call_123

{"customer_id":"cust_789"}

Webhook Tool Authentication

  • auth_type: 'none': no auth headers added.
  • auth_type: 'bearer_token': sends Authorization: Bearer <auth_token>.
  • auth_type: 'api_key': sends X-API-Key: <auth_token>.
  • auth_type: 'custom_headers': sends your configured headers map, except Content-Type, which is agent-managed for body methods.

Webhook Tool Response Behavior

  • execution_mode: 'sync': waits for downstream response body; non-2xx fails the tool call.
  • execution_mode: 'async': treats any 2xx as accepted and does not require a response payload.

Signature Verification (Event Webhooks)

If you configure webhooks.events[].secret, verify the HMAC-SHA256 signature:

import crypto from 'crypto';

function verifyEventWebhook(body: string, headers: Headers, secret: string): boolean {
  const signature = headers.get('x-webhook-signature');
  const timestamp = headers.get('x-webhook-timestamp');
  
  if (!signature || !timestamp) return false;
  
  const message = `${timestamp}.${body}`;
  const expected = crypto.createHmac('sha256', secret).update(message).digest('hex');
  
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

Inbound Call Webhook Payload

If you configure webhooks.inbound_call, Voice.ai sends inbound call personalization requests with this shape:

interface InboundCallWebhookRequest {
  agent_id: string;
  call_id: string;
  from_number: string;
  to_number: string;
}

Your endpoint should respond with:

interface InboundCallWebhookResponse {
  dynamic_variables?: Record<string, string | number | boolean>;
  agent_overrides?: {
    tts_params?: {
      voice_id?: string;
      temperature?: number;
      top_p?: number;
      dictionary_id?: string;
      dictionary_version?: number;
    };
  };
}

If you configure webhooks.inbound_call.secret, verify the HMAC-SHA256 signature using the same X-Webhook-Timestamp and X-Webhook-Signature headers shown above for event webhooks.

Security

connect() fetches connection details and connects in one call. To keep your API key off the browser, split into two steps:

// Step 1: Backend — get connection details (requires API key)
const details = await voiceai.getConnectionDetails({ agentId: 'agent-123' });
// Returns: { serverUrl, participantToken, callId, endToken }

// Step 2: Frontend — connect with pre-fetched details (no API key needed)
const voiceai = new VoiceAI();
await voiceai.connectRoom(details);

Important: Pass endToken from your backend to the frontend. The SDK uses it on disconnect() to free the concurrency slot immediately.

REST methods (agents.*, tts.*, analytics.*, etc.) require an API key and are CORS-blocked from browsers.

Error Handling

Connection errors

The connect() method throws an Error if connection fails. Common error cases:

try {
  await voiceai.connect({ agentId: 'agent-123' });
} catch (error) {
  if (error.message.includes('insufficient_credits')) {
    console.error('Out of credits. Please add more credits to continue.');
  } else if (error.message.includes('Authentication failed')) {
    console.error('Invalid API key');
  } else if (error.message.includes('agent_not_deployed')) {
    console.error('Agent is not deployed');
  } else {
    console.error('Connection failed:', error.message);
  }
}

Errors are also emitted via onError and reflected in onStatusChange:

voiceai.onError((error) => {
  console.error('Error:', error.message);
});

voiceai.onStatusChange((status) => {
  if (status.error) {
    console.error('Connection error:', status.error);
  }
});

REST API errors (agents, TTS, analytics, etc.)

REST methods throw VoiceAIError:

import { VoiceAIError } from '@voice-ai-labs/web-sdk';

try {
  const agent = await voiceai.agents.getById('nonexistent');
} catch (error) {
  if (error instanceof VoiceAIError) {
    // error.message, error.status (401, 403, 404, 422), error.code, error.detail
    if (error.status === 404) console.error('Agent not found');
  }
}

try {
  const audio = await voiceai.tts.synthesize({ text: '...', voice_id: 'voice-123' });
} catch (error) {
  if (error instanceof VoiceAIError) {
    // error.status: 400 (validation), 401 (auth), 404 (voice not found), 422 (invalid request)
    if (error.status === 404) console.error('Voice not found');
  }
}

TypeScript

Full TypeScript support. Exported types:

import VoiceAI, { VoiceAIError } from '@voice-ai-labs/web-sdk';
import type {
  VoiceAIConfig,
  ConnectionOptions,
  ConnectionDetails,
  ConnectionStatus,
  TranscriptionSegment,
  AgentState,
  AgentStateInfo,
  AudioLevelInfo,
  MicrophoneState,
  Agent,
  VoiceResponse,
  VoiceStatus,
  VoiceAgentWidgetOptions,
  VoiceAgentWidgetTheme,
} from '@voice-ai-labs/web-sdk';

Browser Support

Chrome, Firefox, Safari (latest versions). Requires microphone permission for voice features.

License

MIT