> ## Documentation Index
> Fetch the complete documentation index at: https://docs.withperf.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> PerfVoice SDK reference — drop-in voice agent integration for web apps

# JavaScript SDK

The PerfVoice SDK is a single JavaScript file that handles the entire voice agent integration — WebSocket connection, microphone capture, audio encoding, playback, interruptions, and keepalive. No dependencies, no build step.

## Installation

Add the SDK via a script tag:

```html theme={null}
<script src="https://api.withperf.pro/v1/voice/sdk.js"></script>
```

Or install via npm (coming soon):

```bash theme={null}
npm install @perf-ai/voice
```

The SDK exports a `PerfVoice` class globally (or as a UMD/CommonJS module).

## Quick Start

```html theme={null}
<button id="startBtn">Start</button>
<button id="stopBtn">Stop</button>

<script src="https://api.withperf.pro/v1/voice/sdk.js"></script>
<script>
  const voice = new PerfVoice({
    apiKey: 'YOUR_API_KEY',
    agentId: 'YOUR_AGENT_ID',
  });

  voice.on('transcript', (role, text) => {
    console.log(role + ': ' + text);
  });

  voice.on('error', (err) => console.error(err));

  document.getElementById('startBtn').onclick = () => voice.start();
  document.getElementById('stopBtn').onclick = () => voice.stop();
</script>
```

## Constructor

```javascript theme={null}
const voice = new PerfVoice(options);
```

| Parameter | Type   | Required | Default                                        | Description                          |
| --------- | ------ | -------- | ---------------------------------------------- | ------------------------------------ |
| `apiKey`  | string | Yes      | —                                              | Your project API key (`pk_live_...`) |
| `agentId` | string | Yes      | —                                              | Voice agent ID from the dashboard    |
| `wsUrl`   | string | No       | `wss://api.withperf.pro/v1/voice/conversation` | WebSocket endpoint URL               |

## Methods

### `voice.start()`

Start a voice conversation. Requests microphone permission, opens a WebSocket connection, and begins streaming audio.

```javascript theme={null}
voice.start()
  .then(() => console.log('Conversation started'))
  .catch((err) => console.error('Failed to start:', err));
```

Returns a `Promise<void>` that resolves when the connection is established and the agent is ready. Rejects if microphone access is denied or the WebSocket connection fails.

> **Note:** Browsers require a user gesture (click/tap) before allowing microphone access. Always call `start()` from a button click handler.

### `voice.stop()`

End the conversation immediately. Stops all audio playback, releases the microphone, and closes the WebSocket.

```javascript theme={null}
voice.stop();
```

This method is synchronous and safe to call at any time, even if the conversation hasn't started.

## Events

Subscribe to events with `voice.on(event, callback)` and unsubscribe with `voice.off(event, callback)`.

### `transcript`

Fired when a transcript is available for either the agent or the user.

```javascript theme={null}
voice.on('transcript', (role, text) => {
  // role: 'agent' or 'user'
  console.log(role + ': ' + text);
});
```

| Argument | Type                  | Description         |
| -------- | --------------------- | ------------------- |
| `role`   | `'agent'` \| `'user'` | Who said it         |
| `text`   | string                | The transcript text |

### `connected`

Fired when the voice session is established and the agent is ready.

```javascript theme={null}
voice.on('connected', (conversationId) => {
  console.log('Session:', conversationId);
});
```

| Argument         | Type   | Description               |
| ---------------- | ------ | ------------------------- |
| `conversationId` | string | Unique session identifier |

### `disconnected`

Fired when the WebSocket connection closes (either by calling `stop()` or due to a server-side close).

```javascript theme={null}
voice.on('disconnected', (code, reason) => {
  console.log('Disconnected:', code, reason);
});
```

| Argument | Type   | Description                 |
| -------- | ------ | --------------------------- |
| `code`   | number | WebSocket close code        |
| `reason` | string | Close reason (may be empty) |

### `status`

Fired whenever the connection status changes.

```javascript theme={null}
voice.on('status', (status) => {
  // status: 'disconnected', 'connecting', or 'connected'
  updateUI(status);
});
```

| Argument | Type                                                | Description   |
| -------- | --------------------------------------------------- | ------------- |
| `status` | `'disconnected'` \| `'connecting'` \| `'connected'` | Current state |

### `error`

Fired when an error occurs (microphone denied, WebSocket failure, etc.).

```javascript theme={null}
voice.on('error', (err) => {
  console.error('Voice error:', err.message);
});
```

| Argument | Type  | Description      |
| -------- | ----- | ---------------- |
| `err`    | Error | The error object |

### `interruption`

Fired when the user interrupts the agent (starts speaking while the agent is talking). The SDK automatically stops agent audio playback.

```javascript theme={null}
voice.on('interruption', () => {
  console.log('User interrupted the agent');
});
```

### `message`

Fired for any unhandled message type from the server. Useful for debugging.

```javascript theme={null}
voice.on('message', (data) => {
  console.log('Raw message:', data);
});
```

## Properties

| Property               | Type           | Description                                                        |
| ---------------------- | -------------- | ------------------------------------------------------------------ |
| `voice.status`         | string         | Current status: `'disconnected'`, `'connecting'`, or `'connected'` |
| `voice.conversationId` | string \| null | Current session ID (null when disconnected)                        |

## Full Example

A complete example with status indicator, transcript display, and error handling:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <title>Voice Agent</title>
  <style>
    .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
    .dot.disconnected { background: #ef4444; }
    .dot.connecting { background: #eab308; }
    .dot.connected { background: #22c55e; }
  </style>
</head>
<body>
  <span class="dot disconnected" id="dot"></span>
  <span id="statusText">Disconnected</span>
  <br><br>
  <button id="startBtn">Start Conversation</button>
  <button id="stopBtn" disabled>Stop</button>
  <div id="transcript"></div>

  <script src="https://api.withperf.pro/v1/voice/sdk.js"></script>
  <script>
    const voice = new PerfVoice({
      apiKey: 'YOUR_API_KEY',
      agentId: 'YOUR_AGENT_ID',
    });

    voice.on('status', (status) => {
      document.getElementById('dot').className = 'dot ' + status;
      document.getElementById('statusText').textContent =
        status.charAt(0).toUpperCase() + status.slice(1);
      document.getElementById('startBtn').disabled = status !== 'disconnected';
      document.getElementById('stopBtn').disabled = status === 'disconnected';
    });

    voice.on('connected', (id) => {
      document.getElementById('transcript').innerHTML = '';
      addLine('System', 'Connected — session ' + id);
    });

    voice.on('transcript', (role, text) => {
      addLine(role === 'agent' ? 'Agent' : 'You', text);
    });

    voice.on('error', (err) => {
      addLine('Error', err.message);
    });

    document.getElementById('startBtn').onclick = () => voice.start();
    document.getElementById('stopBtn').onclick = () => voice.stop();

    function addLine(label, text) {
      const el = document.getElementById('transcript');
      el.innerHTML += '<p><strong>' + label + ':</strong> ' + text + '</p>';
      el.scrollTop = el.scrollHeight;
    }
  </script>
</body>
</html>
```

## React Integration

```jsx theme={null}
import { useEffect, useRef, useState } from 'react';

function VoiceAgent({ apiKey, agentId }) {
  const voiceRef = useRef(null);
  const [status, setStatus] = useState('disconnected');
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const voice = new PerfVoice({ apiKey, agentId });
    voiceRef.current = voice;

    voice.on('status', setStatus);
    voice.on('transcript', (role, text) => {
      setMessages((prev) => [...prev, { role, text }]);
    });
    voice.on('error', (err) => console.error(err));

    return () => voice.stop();
  }, [apiKey, agentId]);

  return (
    <div>
      <p>Status: {status}</p>
      <button
        onClick={() => voiceRef.current?.start()}
        disabled={status !== 'disconnected'}
      >
        Start
      </button>
      <button
        onClick={() => voiceRef.current?.stop()}
        disabled={status === 'disconnected'}
      >
        Stop
      </button>
      <div>
        {messages.map((m, i) => (
          <p key={i}><strong>{m.role}:</strong> {m.text}</p>
        ))}
      </div>
    </div>
  );
}
```

## Error Handling

| Error                                      | Cause                                 | Resolution                                          |
| ------------------------------------------ | ------------------------------------- | --------------------------------------------------- |
| `NotAllowedError`                          | Microphone permission denied          | Prompt user to allow microphone in browser settings |
| `WebSocket connection failed`              | Network issue or invalid credentials  | Check API key and network connectivity              |
| `Already connecting` / `Already connected` | `start()` called while already active | Check `voice.status` before calling `start()`       |

## Browser Support

The SDK requires:

* `navigator.mediaDevices.getUserMedia` (microphone access)
* `AudioContext` (audio playback)
* `WebSocket` (real-time communication)

Supported in all modern browsers: Chrome 60+, Firefox 55+, Safari 14+, Edge 79+.

## Related

* [Voice Agents Overview](./overview) — Architecture and features
* [WebSocket Protocol](./websocket) — Raw WebSocket integration
* [Python Integration](./python) — Server-side Python integration
