Quickstart
This guide walks you from zero to a working browser call. You'll:
- Install
@2chat/voice-sdk(or drop in the CDN script tag). - Mint a short-lived SDK JWT on your backend.
- Register a
Devicein the browser and place an outbound call.
1. Install
- npm
- pnpm
- yarn
- CDN (no bundler)
npm install @2chat/voice-sdk
pnpm add @2chat/voice-sdk
yarn add @2chat/voice-sdk
<script src="https://cdn.jsdelivr.net/npm/@2chat/voice-sdk/dist/index.global.js"></script>
<script>
const { Device } = TwoChatVoice;
</script>
2. Mint a JWT on your backend
Your backend holds the long-lived X-User-API-Key. It exchanges it for a short-lived JWT scoped to a single 2Chat User — that JWT is what the browser sees.
- cURL
- Node.js
- Python
curl --location --request POST 'https://api.p.2chat.io/sdk/voip/access-token' \
--header 'X-User-API-Key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data-raw '{
"user_uuid": "USR48a1c2f0-9d6b-4c2a-8e3f-1a7b9d0c4e22",
"label": "agent-ada"
}'
// Pseudo-Express route: GET /my/backend/voice-token
app.get("/my/backend/voice-token", async (req, res) => {
const r = await fetch("https://api.p.2chat.io/sdk/voip/access-token", {
method: "POST",
headers: {
"X-User-API-Key": process.env.TWO_CHAT_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
user_uuid: req.user.twoChatUserUuid,
label: req.user.email,
}),
});
const { token } = await r.json();
res.json({ token });
});
import os, requests
from fastapi import FastAPI
app = FastAPI()
@app.get("/my/backend/voice-token")
def mint_voice_token(user_uuid: str):
r = requests.post(
"https://api.p.2chat.io/sdk/voip/access-token",
headers={
"X-User-API-Key": os.environ["TWO_CHAT_API_KEY"],
"Content-Type": "application/json",
},
json={"user_uuid": user_uuid, "label": "voice-sdk"},
timeout=10,
)
r.raise_for_status()
return {"token": r.json()["token"]}
Need the user UUID?
List the users on your account via GET /open/users or the list_account_users MCP tool.
Full endpoint reference: Authentication.
3. Register a Device and place a call
import { Device } from "@2chat/voice-sdk";
// Fetch a token from the backend route you just built.
const { token } = await fetch("/my/backend/voice-token").then((r) => r.json());
const device = new Device({
token,
logLevel: "info",
});
device.on("registered", () => console.log("ready"));
device.on("incoming", (call) => call.accept());
device.on("error", (err) => console.warn(err.code, err.message));
// Re-mint the token before it expires.
device.on("tokenWillExpire", async () => {
const fresh = await fetch("/my/backend/voice-token").then((r) => r.json());
await device.updateToken(fresh.token);
});
await device.register();
const call = await device.connect({
to: "+15551234567",
from: "+15550000000", // caller ID (E.164)
});
call.on("ringing", () => console.log("ringing"));
call.on("accepted", () => console.log("connected"));
call.on("disconnect", ({ reason }) => console.log("ended:", reason));
4. Mid-call controls
call.mute(true); // mute the local mic
call.hold(true); // put the call on hold
call.sendDigits("1"); // send a DTMF tone
await call.disconnect(); // hang up
What's next
- Authentication — the JWT endpoint, refresh strategy, and token claims.
- Device API — every option, method, and event on the
Deviceclass. - Call API — accept, reject, mute, hold, send DTMF, change audio devices mid-call.
- Errors — the
VoiceSDKErrorcodes you can switch on.