@wespoke/web-sdk
v0.2.1
Published
Official JavaScript SDK for embedding Wespoke AI voice assistants in any website
Maintainers
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-sdkCDN (UMD)
<script src="https://unpkg.com/@wespoke/web-sdk/dist/wespoke.umd.js"></script>Quick Start
1. Get Your API Key
- Log in to your Wespoke Dashboard
- Navigate to Web Embedding
- Create a new API key with your domain whitelist
- Copy your
pk_live_xxxorpk_test_xxxkey
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:8080React Example
Modern React app with TypeScript and custom useWespoke hook.
cd node_modules/@wespoke/web-sdk/examples/react-example
npm install
npm run devAPI 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.ERRORgetCallId()
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 connecteddisconnected: Call disconnected (receives optional reason string)reconnecting: Connection is reconnectingreconnected: Successfully reconnectedconnectionStateChanged: Connection state changed (receives ConnectionState)connectionQualityChanged: Connection quality changed (receives ConnectionQuality)stateChange: Call state changed (receives CallState)
Conversation Events
message: Conversation message receivedwespoke.on('message', (message) => { console.log(`${message.role}: ${message.content}`); });Messages may stream in multiple chunks; use
message.isComplete/message.isFirstChunkto detect streaming progress.transcription: Real-time transcription eventwespoke.on('transcription', (transcription) => { console.log('Transcription:', transcription.text); });assistantSpeaking: Assistant speaking state changedwespoke.on('assistantSpeaking', (speaking) => { console.log('Assistant speaking:', speaking); });
Audio Events
microphoneMuted: Microphone mute state changedwespoke.on('microphoneMuted', (muted) => { console.log('Microphone muted:', muted); });
Tool Events
toolEvent: Tool execution eventwespoke.on('toolEvent', (event) => { console.log('Tool:', event.toolName, 'Type:', event.type); });
Call Events
callEnding: Call is endingwespoke.on('callEnding', (event) => { console.log('Call ending:', event.reason); });bargeIn: User interrupted the assistantmetrics: Call metrics updated
Error Events
error: Error occurredwespoke.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
- Use Environment-Specific Keys: Use
pk_test_keys for development andpk_live_keys for production - Domain Whitelisting: Always whitelist specific domains, avoid using wildcards unless necessary
- Never Expose Keys: Don't commit API keys to version control
- Monitor Usage: Track API key usage in the Wespoke dashboard
- 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
