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

@wespoke/web-sdk

v0.2.1

Published

Official JavaScript SDK for embedding Wespoke AI voice assistants in any website

Readme

Wespoke Web SDK

Official JavaScript SDK for embedding Wespoke AI voice assistants and chat experiences in any website.

Features

  • 🎙️ Real-time Voice Conversations: Connect to AI voice assistants powered by LiveKit
  • 💬 Text Chat Sessions: Start chat-only sessions with the same assistants
  • 🎯 Simple API: Start a call with just 2 lines of code
  • 📦 Multiple Formats: UMD, ESM, and CommonJS builds
  • 🔒 Secure: API key authentication with domain whitelisting
  • 📊 Event-Driven: Subscribe to call events, transcriptions, and tool executions
  • 🌐 Browser Support: Works in all modern browsers
  • 📘 TypeScript: Full type definitions included

Installation

NPM/Yarn

npm install @wespoke/web-sdk
# or
yarn add @wespoke/web-sdk

CDN (UMD)

<script src="https://unpkg.com/@wespoke/web-sdk/dist/wespoke.umd.js"></script>

Quick Start

1. Get Your API Key

  1. Log in to your Wespoke Dashboard
  2. Navigate to Web Embedding
  3. Create a new API key with your domain whitelist
  4. Copy your pk_live_xxx or pk_test_xxx key

2. Basic Usage

import { Wespoke } from '@wespoke/web-sdk';

// Initialize the SDK
const wespoke = new Wespoke({
  apiKey: 'pk_live_your_api_key_here'
});

// Start a call
await wespoke.startCall('your-assistant-id');

// Listen for events
wespoke.on('connected', () => {
  console.log('Connected to assistant!');
});

wespoke.on('message', (message) => {
  console.log('Message:', message.content);
});

// End the call
await wespoke.endCall();

Running Examples

The package includes ready-to-run example applications in the examples/ directory:

Vanilla JavaScript Example

Pure HTML/CSS/JavaScript - no build tools required.

cd node_modules/@wespoke/web-sdk/examples/vanilla-js-example
python3 -m http.server 8080
# Open http://localhost:8080

React Example

Modern React app with TypeScript and custom useWespoke hook.

cd node_modules/@wespoke/web-sdk/examples/react-example
npm install
npm run dev

API Reference

Constructor

new Wespoke(config: WespokeConfig)

Config Options:

interface WespokeConfig {
  apiKey: string;           // Required: Your API key (pk_live_xxx)
  apiUrl?: string;          // Optional: API endpoint (default: 'https://api.wespoke.ai')
  debug?: boolean;          // Optional: Enable debug logging (default: false)
  maxRetryAttempts?: number; // Optional: Connection retry attempts (default: 3)
  retryDelay?: number;      // Optional: Retry delay in ms (default: 2000)
}

Methods

startChatSession(assistantId, options?)

Start a text chat session with an assistant (no microphone/audio required).

await wespoke.startChatSession('assistant-id', {
  metadata: { userId: 'user-123' },
  assistantOverrides: {
    systemPrompt: 'You are a concise support bot.',
    temperature: 0.2
  }
});

sendChatMessage(content)

Send a message during an active chat session. Responses stream through the message event.

await wespoke.sendChatMessage('How can I reset my password?');

endChatSession()

End the current chat session.

await wespoke.endChatSession();

startCall(assistantId, options?)

Start a voice call with an assistant.

await wespoke.startCall('assistant-id', {
  metadata: {
    userId: 'user-123',
    sessionId: 'session-xyz',
    customData: { source: 'website' }
  },
  assistantOverrides: {
    systemPrompt: 'You are a friendly concierge for ACME Hotels.',
    temperature: 0.4,
    variableValues: { guestName: 'Sam' }
  }
});

Call/Chat options

  • metadata: Custom data passed through to your assistant for analytics or routing.
  • assistantOverrides: Per-session prompt/behavior tweaks (e.g., systemPrompt, firstMessage, temperature, maxResponseTokens, variableValues). When provided, overrides are forwarded for both voice calls and chat sessions.

endCall()

End the current call.

await wespoke.endCall();

toggleMute()

Toggle microphone mute state. Returns true if microphone is muted, false if unmuted.

const muted = await wespoke.toggleMute();
console.log('Microphone muted:', muted);

isMuted()

Check if microphone is currently muted.

const muted = wespoke.isMuted();

isAssistantSpeaking()

Check if assistant is currently speaking.

const speaking = wespoke.isAssistantSpeaking();

getState()

Get current call state.

import { CallState } from '@wespoke/web-sdk';

const state = wespoke.getState();
// Returns: CallState.IDLE | CallState.CONNECTING | CallState.CONNECTED | CallState.DISCONNECTING | CallState.DISCONNECTED | CallState.ERROR

getCallId()

Get current call ID.

const callId = wespoke.getCallId();

destroy()

Destroy the SDK instance and clean up resources.

wespoke.destroy();

Events

Subscribe to events using .on(event, handler):

wespoke.on('connected', () => {
  console.log('Call connected');
});

Connection Events

  • connected: Call successfully connected
  • disconnected: Call disconnected (receives optional reason string)
  • reconnecting: Connection is reconnecting
  • reconnected: Successfully reconnected
  • connectionStateChanged: Connection state changed (receives ConnectionState)
  • connectionQualityChanged: Connection quality changed (receives ConnectionQuality)
  • stateChange: Call state changed (receives CallState)

Conversation Events

  • message: Conversation message received

    wespoke.on('message', (message) => {
      console.log(`${message.role}: ${message.content}`);
    });

    Messages may stream in multiple chunks; use message.isComplete/message.isFirstChunk to detect streaming progress.

  • transcription: Real-time transcription event

    wespoke.on('transcription', (transcription) => {
      console.log('Transcription:', transcription.text);
    });
  • assistantSpeaking: Assistant speaking state changed

    wespoke.on('assistantSpeaking', (speaking) => {
      console.log('Assistant speaking:', speaking);
    });

Audio Events

  • microphoneMuted: Microphone mute state changed

    wespoke.on('microphoneMuted', (muted) => {
      console.log('Microphone muted:', muted);
    });

Tool Events

  • toolEvent: Tool execution event

    wespoke.on('toolEvent', (event) => {
      console.log('Tool:', event.toolName, 'Type:', event.type);
    });

Call Events

  • callEnding: Call is ending

    wespoke.on('callEnding', (event) => {
      console.log('Call ending:', event.reason);
    });
  • bargeIn: User interrupted the assistant

  • metrics: Call metrics updated

Error Events

  • error: Error occurred

    wespoke.on('error', (error) => {
      console.error('Error:', error.message);
    });

Error Handling

The SDK provides specific error types for different scenarios:

import {
  WespokeError,
  AuthenticationError,
  InsufficientCreditsError,
  ConnectionError,
  ConfigurationError,
  AssistantNotFoundError,
  MediaDevicesError
} from '@wespoke/web-sdk';

try {
  await wespoke.startCall('assistant-id');
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid API key or domain not whitelisted');
  } else if (error instanceof InsufficientCreditsError) {
    console.error('Insufficient credits to start call');
  } else if (error instanceof MediaDevicesError) {
    console.error('Microphone access denied');
  } else if (error instanceof ConnectionError) {
    console.error('Connection failed:', error.message);
  }
}

Advanced Usage

Complete Example with UI

<!DOCTYPE html>
<html>
<head>
  <title>Wespoke Voice Assistant</title>
</head>
<body>
  <button id="startCall">Start Call</button>
  <button id="endCall" disabled>End Call</button>
  <button id="toggleMute" disabled>Mute</button>
  <div id="status">Idle</div>
  <div id="messages"></div>

  <script type="module">
    import { Wespoke, CallState } from '@wespoke/web-sdk';

    const wespoke = new Wespoke({
      apiKey: 'pk_live_your_api_key_here',
      debug: true
    });

    const startBtn = document.getElementById('startCall');
    const endBtn = document.getElementById('endCall');
    const muteBtn = document.getElementById('toggleMute');
    const status = document.getElementById('status');
    const messages = document.getElementById('messages');

    // Event listeners
    wespoke.on('stateChange', (state) => {
      status.textContent = state;
      endBtn.disabled = state !== CallState.CONNECTED;
      muteBtn.disabled = state !== CallState.CONNECTED;
    });

    wespoke.on('message', (message) => {
      const div = document.createElement('div');
      div.textContent = `${message.role}: ${message.content}`;
      messages.appendChild(div);
    });

    wespoke.on('microphoneMuted', (muted) => {
      muteBtn.textContent = muted ? 'Unmute' : 'Mute';
    });

    wespoke.on('error', (error) => {
      alert('Error: ' + error.message);
    });

    // Button handlers
    startBtn.onclick = async () => {
      try {
        startBtn.disabled = true;
        await wespoke.startCall('your-assistant-id');
      } catch (error) {
        console.error('Failed to start call:', error);
        startBtn.disabled = false;
      }
    };

    endBtn.onclick = async () => {
      await wespoke.endCall();
      startBtn.disabled = false;
    };

    muteBtn.onclick = async () => {
      await wespoke.toggleMute();
    };
  </script>
</body>
</html>

Using with React

import { Wespoke, CallState } from '@wespoke/web-sdk';
import { useState, useEffect, useRef } from 'react';

function VoiceAssistant() {
  const [callState, setCallState] = useState(CallState.IDLE);
  const [messages, setMessages] = useState([]);
  const wespokeRef = useRef(null);

  useEffect(() => {
    const wespoke = new Wespoke({
      apiKey: process.env.REACT_APP_WESPOKE_API_KEY
    });

    wespoke.on('stateChange', setCallState);
    wespoke.on('message', (message) => {
      setMessages((prev) => [...prev, message]);
    });

    wespokeRef.current = wespoke;

    return () => {
      wespoke.destroy();
    };
  }, []);

  const startCall = async () => {
    try {
      await wespokeRef.current.startCall('assistant-id');
    } catch (error) {
      console.error('Failed to start call:', error);
    }
  };

  const endCall = async () => {
    await wespokeRef.current.endCall();
  };

  return (
    <div>
      <button onClick={startCall} disabled={callState !== CallState.IDLE}>
        Start Call
      </button>
      <button onClick={endCall} disabled={callState !== CallState.CONNECTED}>
        End Call
      </button>
      <div>Status: {callState}</div>
      <div>
        {messages.map((msg, i) => (
          <div key={i}>{msg.role}: {msg.content}</div>
        ))}
      </div>
    </div>
  );
}

Security Best Practices

  1. Use Environment-Specific Keys: Use pk_test_ keys for development and pk_live_ keys for production
  2. Domain Whitelisting: Always whitelist specific domains, avoid using wildcards unless necessary
  3. Never Expose Keys: Don't commit API keys to version control
  4. Monitor Usage: Track API key usage in the Wespoke dashboard
  5. Rotate Keys: Periodically rotate API keys and revoke old ones

Browser Support

  • Chrome/Edge: 90+
  • Firefox: 88+
  • Safari: 14+
  • Mobile Safari: 14+
  • Chrome Android: 90+

Troubleshooting

Microphone Not Working

wespoke.on('error', (error) => {
  if (error instanceof MediaDevicesError) {
    alert('Please allow microphone access');
  }
});

Domain Not Whitelisted

// Make sure your domain is added to the API key's allowed origins
// Error: AuthenticationError with code 'DOMAIN_NOT_WHITELISTED'

Connection Issues

// Enable debug mode to see detailed logs
const wespoke = new Wespoke({
  apiKey: 'pk_live_xxx',
  debug: true
});

License

MIT

Support