ekoe-webclient-sdk
v1.0.0
Published
Ekoe-branded SDK for embedding voice AI calls on any website
Downloads
19
Maintainers
Readme
Ekoe Web Client SDK
Embed an Ekoe voice AI agent on any website with a few lines of JavaScript. The SDK handles token exchange with your Ekoe backend, establishes the WebRTC audio stream, and exposes a clean event-driven API — you bring your own UI.
Requirements
- An Ekoe account with a configured voice agent
- Your Ekoe backend URL, client ID, and voice agent ID
- A browser with WebRTC support (Chrome 60+, Firefox 55+, Safari 12+, Edge 79+)
- HTTPS (required for microphone access;
localhostis also accepted)
Installation
NPM / Yarn
npm install ekoe-webclient-sdkyarn add ekoe-webclient-sdkCDN (no build step)
<script src="https://unpkg.com/ekoe-webclient-sdk@latest/dist/index.umd.js"></script>When loaded via CDN the class is available as the global EkoeWebClient.
Quick start
<!-- 1. Load the SDK -->
<script src="https://unpkg.com/ekoe-webclient-sdk@latest/dist/index.umd.js"></script>
<!-- 2. Your own buttons -->
<button id="start-btn">Start Call</button>
<button id="end-btn" disabled>End Call</button>
<!-- 3. Wire it up -->
<script>
var client = new EkoeWebClient({
backendUrl: 'https://your-ekoe-backend.com',
clientId: 'your-client-id',
voiceAgentId: 'your-voice-agent-id',
});
document.getElementById('start-btn').addEventListener('click', function () {
client.startCall();
});
document.getElementById('end-btn').addEventListener('click', function () {
client.endCall();
});
client.on('callConnected', function () {
document.getElementById('start-btn').disabled = true;
document.getElementById('end-btn').disabled = false;
});
client.on('callEnded', function () {
document.getElementById('start-btn').disabled = false;
document.getElementById('end-btn').disabled = true;
});
client.on('error', function (err) {
console.error('Call error:', err.message);
});
</script>Configuration
Pass a config object to the constructor.
| Property | Type | Required | Description |
|---|---|---|---|
| backendUrl | string | ✅ | Base URL of your Ekoe backend, e.g. https://api.ekoe.com. Trailing slash is stripped automatically. |
| clientId | string | ✅ | Your Ekoe client ID. |
| voiceAgentId | string | ✅ | ID of the voice agent to connect to. |
const client = new EkoeWebClient({
backendUrl: 'https://api.ekoe.com',
clientId: 'abc123',
voiceAgentId: 'agent_xyz',
});API
startCall(): Promise<void>
Fetches a short-lived access token from your Ekoe backend, then opens a WebRTC audio stream to the voice agent. Resolves when the connection is established. Rejects (and emits error) on failure.
client.startCall().catch(function (err) {
console.error('Failed to start:', err.message);
});endCall(): Promise<void>
Closes the active call. Safe to call at any time — does nothing if no call is active. Always emits callEnded.
client.endCall();getState(): EkoeWebClientState
Returns a snapshot of the current call state. The returned object does not update; call getState() again to get fresh values.
var state = client.getState();
console.log(state.isCallActive); // boolean
console.log(state.isConnected); // boolean
console.log(state.callDuration); // seconds (number)
console.log(state.error); // Error | null| Property | Type | Description |
|---|---|---|
| isCallActive | boolean | true after startCall() succeeds, false after endCall() or disconnect. |
| isConnected | boolean | true once the WebRTC room is connected. |
| callDuration | number | Elapsed call time in seconds. Resets to 0 when the call ends. |
| error | Error \| null | Most recent error, or null. |
on(event, callback): void
Register a listener for a call event.
client.on('callConnected', function () { /* ... */ });off(event, callback): void
Remove a previously registered listener.
function handleConnected() { /* ... */ }
client.on('callConnected', handleConnected);
// later…
client.off('callConnected', handleConnected);destroy(): Promise<void>
End the call (if active) and clear all event listeners. Call this when you are done with the instance, e.g. on page unload or component unmount.
window.addEventListener('beforeunload', function () {
client.destroy();
});Events
| Event | Callback signature | When it fires |
|---|---|---|
| callConnected | () => void | The WebRTC room is connected and audio is live. |
| callEnded | () => void | The call has ended — either by endCall(), the agent hanging up, or a network disconnect. |
| update | (transcript: TranscriptEntry[]) => void | The conversation transcript was updated. Receives the full transcript so far. |
| agentStartTalking | () => void | The voice agent began speaking. |
| agentStopTalking | () => void | The voice agent stopped speaking. |
| error | (error: Error) => void | An error occurred (token fetch failed, WebRTC error, etc.). |
TranscriptEntry
Each entry in the update transcript array:
interface TranscriptEntry {
role: 'agent' | 'user';
content: string;
}Example — render a live transcript:
client.on('update', function (transcript) {
var box = document.getElementById('transcript');
box.textContent = ''; // clear previous entries
transcript.forEach(function (entry) {
var p = document.createElement('p');
p.textContent = (entry.role === 'agent' ? 'Agent: ' : 'You: ') + entry.content;
box.appendChild(p);
});
});Example — show a speaking indicator:
var indicator = document.getElementById('speaking-indicator');
client.on('agentStartTalking', function () {
indicator.style.display = '';
});
client.on('agentStopTalking', function () {
indicator.style.display = 'none';
});TypeScript usage
The SDK ships with full type declarations.
import { EkoeWebClient, EkoeWebClientConfig, TranscriptEntry } from 'ekoe-webclient-sdk';
const config: EkoeWebClientConfig = {
backendUrl: 'https://api.ekoe.com',
clientId: 'abc123',
voiceAgentId: 'agent_xyz',
};
const client = new EkoeWebClient(config);
client.on('update', (transcript: TranscriptEntry[]) => {
transcript.forEach(entry => {
console.log(`[${entry.role}] ${entry.content}`);
});
});
await client.startCall();Framework examples
React
import { useEffect, useRef } from 'react';
import { EkoeWebClient } from 'ekoe-webclient-sdk';
function VoiceButton() {
const clientRef = useRef<EkoeWebClient | null>(null);
const [active, setActive] = useState(false);
useEffect(() => {
clientRef.current = new EkoeWebClient({
backendUrl: 'https://api.ekoe.com',
clientId: 'abc123',
voiceAgentId: 'agent_xyz',
});
clientRef.current.on('callConnected', () => setActive(true));
clientRef.current.on('callEnded', () => setActive(false));
return () => { clientRef.current?.destroy(); };
}, []);
const toggle = () => {
if (active) {
clientRef.current?.endCall();
} else {
clientRef.current?.startCall();
}
};
return (
<button onClick={toggle}>
{active ? 'End Call' : 'Start Call'}
</button>
);
}Vue 3
<template>
<button @click="toggle">{{ active ? 'End Call' : 'Start Call' }}</button>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { EkoeWebClient } from 'ekoe-webclient-sdk';
const active = ref(false);
let client;
onMounted(() => {
client = new EkoeWebClient({
backendUrl: 'https://api.ekoe.com',
clientId: 'abc123',
voiceAgentId: 'agent_xyz',
});
client.on('callConnected', () => active.value = true);
client.on('callEnded', () => active.value = false);
});
onUnmounted(() => client?.destroy());
function toggle() {
active.value ? client.endCall() : client.startCall();
}
</script>How it works
Token exchange —
startCall()sends aPOSTrequest to{backendUrl}/retellai/get_standalone_web_call_token/{clientId}/{voiceAgentId}on your Ekoe backend. The backend issues a short-lived access token from Retell without exposing any Retell API keys to the browser.WebRTC connection — The SDK uses
livekit-clientto connect to Retell's hosted LiveKit server (wss://retell-ai-4ihahnq7.livekit.cloud) with the access token. Audio is captured from the microphone with echo cancellation, noise suppression, and auto gain enabled.Events — Transcript updates and agent speech notifications arrive over the LiveKit data channel as JSON messages. The SDK parses them and re-emits them as typed events.
Demo pages
Two HTML examples are included in the examples/ directory:
| File | Description |
|---|---|
| examples/simple-demo.html | Minimal start/end call example — ideal for getting started. Every line is commented. |
| examples/demo.html | Full-featured reference showing transcript rendering, speaking indicator, call duration, and an embed snippet preview. |
To run them locally:
cd ekoe-webclient-sdk
npx serve . # or: python3 -m http.server 8080Then open http://localhost:8080/examples/simple-demo.html.
Building from source
git clone <repo>
cd ekoe-webclient-sdk
npm install
npm test # run unit tests
npm run build # produce dist/Output files:
| File | Format | Use case |
|---|---|---|
| dist/index.js | CommonJS | Node.js / bundlers |
| dist/index.esm.js | ES Module | Modern bundlers (webpack, Vite) |
| dist/index.umd.js | UMD | <script> tags / CDN |
| dist/index.d.ts | TypeScript declarations | TypeScript projects |
Browser support
| Browser | Minimum version | |---|---| | Chrome | 60 | | Firefox | 55 | | Safari | 12 | | Edge | 79 |
WebRTC and microphone access require a secure context (HTTPS or localhost).
