Skip to content
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ node_modules
.serena
.claude
.vscode
.docs-review
4 changes: 3 additions & 1 deletion fern/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ products:
icon: fa-regular fa-browser
subtitle: Build voice, video and chat applications for the browser
versions:
- display-name: v4
path: products/browser-sdk/versions/v4.yml
availability: beta
- display-name: v3
path: products/browser-sdk/versions/latest.yml
availability: stable
Expand Down Expand Up @@ -170,7 +173,6 @@ css:
- brand-overrides.css
- styles.css


redirects:
- source: /docs/agents-sdk
destination: /docs/server-sdks
Expand Down
2 changes: 1 addition & 1 deletion fern/fern.config.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"organization": "signalwire",
"version": "4.67.1"
"version": "4.106.1"
}
170 changes: 170 additions & 0 deletions fern/products/browser-sdk/pages/v4/guides/authentication.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
title: "Authentication"
sidebar-title: "Authentication"
position: 2
slug: /js/v4/guides/authentication
max-toc-depth: 3
---

The SDK supports two authentication methods: **Subscriber Access Tokens (SAT)** for full-featured access, and **Embed Tokens** for guest access. This guide explains when to use each and how to implement them.

## Token types

| Token Type | Use Case | Features | Expires |
| --------------------------------- | ------------------------ | ----------------------------------------------- | ------------------ |
| **Subscriber Access Token (SAT)** | Authenticated users | Full access: directory, inbound calls, presence | Yes (configurable) |
| **Embed Token** | Guests, embedded widgets | Outbound calls only | Yes |

## Subscriber Access Tokens (SAT)

SATs are JWTs that authenticate a specific subscriber. Use these when your users have accounts in your system.

### Getting a SAT

Generate SATs from your backend using the SignalWire REST API:

```bash
curl -X POST "https://yourspace.signalwire.com/api/fabric/subscribers/{subscriber_id}/tokens" \
-H "Authorization: Basic YOUR_API_CREDENTIALS" \
-H "Content-Type: application/json"
```

The response includes a `token` field containing the SAT.

### Using a SAT

```js
import { SignalWire, StaticCredentialProvider } from "@signalwire/js";

const credentials = new StaticCredentialProvider({
token: "eyJhbGciOiJS...",
});

const client = new SignalWire(credentials);
```

### Token refresh

SATs expire. For long-running applications, implement a credential provider that refreshes tokens:

```js
class RefreshingCredentialProvider {
async authenticate(context) {
// context.fingerprint - DPoP key fingerprint for client-bound tokens
const response = await fetch("/api/signalwire-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ fingerprint: context?.fingerprint }),
});
const { token, expiresAt } = await response.json();
return { token, expiry_at: expiresAt };
}

async refresh() {
return this.authenticate();
}
}

const client = new SignalWire(new RefreshingCredentialProvider());
```

The SDK calls `refresh()` automatically before the token expires if the method is defined.

## Embed Tokens

Embed tokens provide guest access without requiring a subscriber account. They're ideal for:

- Public-facing call widgets
- Customer support embeds
- One-time meeting links

### Getting an embed token

```bash
curl -X POST "https://yourspace.signalwire.com/api/fabric/embeds/tokens" \
-H "Authorization: Basic YOUR_API_CREDENTIALS" \
-H "Content-Type: application/json" \
-d '{"resource_id": "YOUR_RESOURCE_ID"}'
```

### Using an embed token

The simplest approach uses `embeddableCall()`:

```js
import { embeddableCall } from "@signalwire/js";

const call = await embeddableCall({
host: "yourspace.signalwire.com",
embedToken: "YOUR_EMBED_TOKEN",
to: "/public/room-name",
});
```

This handles everything: creating credentials, connecting, and dialing.

## Security best practices

### Never expose credentials in client code

```js
// Bad - token hardcoded
const credentials = new StaticCredentialProvider({ token: "eyJhbGciOiJS..." });

// Good - token fetched from your backend
async function getCredentials() {
const response = await fetch("/api/get-signalwire-token", {
headers: { Authorization: `Bearer ${userSessionToken}` },
});
const { token } = await response.json();
return new StaticCredentialProvider({ token });
}
```

### Use short-lived tokens

- Interactive sessions: 1–4 hours
- Embed tokens: 15–60 minutes
- Background services: longer, with refresh

### Validate on your backend

Before issuing tokens, verify the user is authorized in your system, then generate the SAT server-side.

## Connection options

Control automatic behaviors when creating the client:

```js
const client = new SignalWire(credentials, {
skipConnection: true,
skipRegister: true,
skipDeviceMonitoring: true,
reconnectAttachedCalls: true,
savePreferences: true,
persistSession: true,
});

await client.connect();
```

## Debugging authentication

```js
client.isConnected$.subscribe((c) => console.log("Connected:", c));
client.isRegistered$.subscribe((r) => console.log("Registered:", r));
client.ready$.subscribe((r) => console.log("Ready:", r));

client.errors$.subscribe((error) => {
if (error.name === "InvalidCredentialsError") {
// Redirect to login or refresh token
}
});
```

| Symptom | Likely Cause | Solution |
| ----------------------------- | -------------------------------- | -------------------------------- |
| `InvalidCredentialsError` | Token expired or malformed | Generate a new token |
| `WebSocketConnectionError` | Network issue or wrong host | Check host URL and network |
| `NotConnectedError` | Calling methods before connected | Wait for `ready$` to emit `true` |
| Connection closes immediately | Token for wrong environment | Verify token matches host |
119 changes: 119 additions & 0 deletions fern/products/browser-sdk/pages/v4/guides/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
---
title: "Overview"
sidebar-title: "Overview"
position: 0
slug: /js/v4/guides
max-toc-depth: 3
---

The SignalWire Browser SDK is a JavaScript library that enables WebRTC-based voice, video, and chat applications directly in web browsers. Built on a WebSocket architecture, it provides real-time communication capabilities without plugins or downloads.

This guide gets you from zero to a working video call in about five minutes.

## Prerequisites

- A SignalWire account ([sign up here](https://signalwire.com))
- Node.js 18+ (for the npm package) or a modern browser (for CDN usage)
- A Subscriber Access Token (SAT) or Embed Token from your SignalWire dashboard

## Install

<CardGroup cols={2}>
<Card title="npm" icon="brands npm" href="https://www.npmjs.com/package/@signalwire/js">
@signalwire/js
</Card>
<Card title="GitHub" icon="brands github" href="https://github.com/signalwire/signalwire-js">
signalwire-js
</Card>
</CardGroup>

```bash
npm install @signalwire/js rxjs
```

RxJS is a peer dependency — the SDK uses observables for all reactive state. See the [RxJS Primer](/js/v4/guides/rxjs-primer).

### CDN

```html
<script src="https://unpkg.com/@signalwire/js@latest/dist/browser.umd.js"></script>
```

The SDK exposes a global `SignalWire` object.

## Your first call

```js
import { SignalWire, StaticCredentialProvider } from "@signalwire/js";

const credentials = new StaticCredentialProvider({
token: "YOUR_SUBSCRIBER_ACCESS_TOKEN",
});

const client = new SignalWire(credentials);

client.ready$.subscribe(async (ready) => {
if (!ready) return;

const call = await client.dial("/public/test-room", {
audio: true,
video: true,
});

call.localStream$.subscribe((stream) => {
document.getElementById("localVideo").srcObject = stream;
});

call.remoteStream$.subscribe((stream) => {
document.getElementById("remoteVideo").srcObject = stream;
});

call.status$.subscribe((status) => {
console.log("Call status:", status);
});
});
```

```html
<video id="localVideo" autoplay muted playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<button id="hangup" onclick="call.hangup()">Hang Up</button>
```

## Embed tokens (guest access)

For simpler integrations where users don't need accounts, use `embeddableCall`:

```js
import { embeddableCall } from "@signalwire/js";

const call = await embeddableCall({
host: "yourspace.signalwire.com",
embedToken: "YOUR_EMBED_TOKEN",
to: "/public/test-room",
});
```

This handles authentication and dialing in a single call.

## Receiving inbound calls

```js
await client.register();

client.session.incomingCalls$.subscribe((calls) => {
const ringing = calls.find((c) => c.status === "ringing");
if (!ringing) return;

document.getElementById("accept").onclick = () => ringing.answer();
document.getElementById("reject").onclick = () => ringing.reject();
});
```

## Next steps

- [Web Components](/js/v4/guides/web-components) — drop-in HTML elements
- [Authentication](/js/v4/guides/authentication) — token types and refresh
- [RxJS Primer](/js/v4/guides/rxjs-primer) — reactive state in the SDK
- [Troubleshooting](/js/v4/guides/troubleshooting) — common issues
- [API Reference](/js/v4/reference) — complete API docs
Loading
Loading