@voicebip/web — Browser SDK

The @voicebip/web package handles WebRTC signaling, mic access, SSE transcript streaming, and call lifecycle so you don’t have to. The package is not yet published to npm — this page previews the planned API.

Installation (Coming Soon)

$npm install @voicebip/web

No build-time native dependencies. ESM-only; requires a bundler (Vite, webpack, esbuild, Next.js, etc.) or a browser that supports ES modules natively.

In the Meantime

Use the Browser Calls (WebRTC) guide to wire WebRTC directly — the guide includes a complete minimal JavaScript example that works today without any SDK dependency.

Prerequisites

You need a scoped token — a short-lived JWT that authorises one call to one agent. Mint it server-side via @voicebip/sdk and pass it to the browser through your own API route.

See Browser Calls (WebRTC) for the full server + browser walkthrough.


Quick start

1import { VoicebipCall } from '@voicebip/web';
2
3// 1. Get a scoped token from your server
4const { token, ice_servers, agent_id } = await fetch('/api/voice-token', {
5 method: 'POST',
6 body: JSON.stringify({ agent_id: 'agt_WX1Y_abcdef123456' }),
7 headers: { 'Content-Type': 'application/json' },
8}).then((r) => r.json());
9
10// 2. Create and start the call
11const call = new VoicebipCall({ token, agentId: agent_id, iceServers: ice_servers });
12
13call.on('state', (s) => console.log('state →', s));
14call.on('transcript', (t) => console.log(`[${t.role}] ${t.text}`));
15call.on('ended', (ms) => console.log('ended after', ms, 'ms'));
16call.on('error', (e) => console.error(e));
17
18await call.start(); // resolves once connected; rejects on setup error
19
20// Later:
21call.setMuted(true);
22await call.hangup();

API Reference

new VoicebipCall(opts)

OptionTypeDefaultNotes
tokenstringRequired. Scoped JWT from POST /v1/webrtc/token.
agentIdstringRequired. Must match the token’s webrtc_agent_id claim.
iceServersIceServer[]Google STUNPass the ice_servers array from the token response unchanged.
audioElementHTMLAudioElementauto-createdWhere the agent’s audio plays. The SDK appends a hidden <audio autoplay> if omitted.
apiBasestringhttps://api.voicebip.comOverride for sandbox or self-hosted.
sourcestringweb_sdkHint attached to the call.initiated webhook payload.

call.start(): Promise<void>

Requests mic access, creates the WebRTC peer connection, POSTs the SDP offer to /v1/webrtc/offer, applies the answer, and opens the transcript SSE stream.

  • Resolves once connectionState === 'connected'.
  • Rejects (and emits error) on any setup failure.
  • Throws Error if called from any state other than idle.

call.hangup(): Promise<void>

Closes the peer connection, stops all mic tracks, and closes the SSE stream. Idempotent — safe to call multiple times.

call.setMuted(muted: boolean): void

Enables or disables the local microphone track. The remote peer hears silence; no signaling event is sent.

call.state: CallState

Read-only. One of 'idle' | 'connecting' | 'connected' | 'ended' | 'error'.

call.on(event, handler): () => void

Registers an event handler. Returns an unsubscribe function.

1const unsub = call.on('transcript', (t) => console.log(t));
2// later:
3unsub();

Events

EventPayloadWhen
stateCallStateEvery state transition.
callstring (call_id)Once the SDP exchange completes and a call_id is assigned.
transcriptTranscriptEventEach transcript line from the SSE stream.
endednumber (duration ms)Exactly once when the call ends, from any cause.
errorErrorOn setup or runtime error, before the state moves to error.

TranscriptEvent

1interface TranscriptEvent {
2 role: 'agent' | 'caller';
3 text: string;
4 timestamp: string; // ISO 8601 UTC
5 is_final: boolean; // true = committed line; false = streaming partial
6}

Types

1type CallState = 'idle' | 'connecting' | 'connected' | 'ended' | 'error';
2
3interface IceServer {
4 urls: string[];
5 username?: string;
6 credential?: string;
7}
8
9interface VoicebipCallOptions {
10 token: string;
11 agentId: string;
12 iceServers?: IceServer[];
13 audioElement?: HTMLAudioElement;
14 apiBase?: string;
15 source?: string;
16}

State machine

idle
└─ start() ──→ connecting
└─ connected ──→ ended
└─ error ──→ ended

call.on('ended', ms) fires exactly once regardless of how the call ends (remote hang-up, network drop, hangup(), or an error in start()).


Security

  • The SDK refuses to operate without a scoped token. Passing a pk_live_* workspace key is rejected at the API gateway with 401 UNAUTHENTICATED.
  • The token authorises one POST /v1/webrtc/offer for one agent. A mismatch between agentId and the token’s webrtc_agent_id claim returns 403 PERMISSION_DENIED.
  • Tokens expire in ≤10 minutes. Mint a fresh token for each call session — do not cache them.
  • TURN relay endpoints are embedded in the ice_servers response — pass them through to the SDK unchanged.

What this SDK does not do

  • No UI. No call buttons, no transcript widget, no audio visualisations. Build your own using the events above.
  • No React-specific helpers. A useVoicebipCall() hook will ship in @voicebip/web-react.
  • No call control beyond mute + hangup. Hold, transfer, and DTMF are server-side operations — use @voicebip/sdk for those.
  • No IE or Safari < 14. WebRTC + EventSource required.

Changelog

VersionNotes
0.1.0Initial release — VoicebipCall, start, hangup, setMuted, on, SSE transcript stream.