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

ekoe-webclient-sdk

v1.0.0

Published

Ekoe-branded SDK for embedding voice AI calls on any website

Downloads

19

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; localhost is also accepted)

Installation

NPM / Yarn

npm install ekoe-webclient-sdk
yarn add ekoe-webclient-sdk

CDN (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

  1. Token exchangestartCall() sends a POST request 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.

  2. WebRTC connection — The SDK uses livekit-client to 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.

  3. 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 8080

Then 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).