Browser SDK

@dialgem/client: microphone, playback and live events for a web call, with a React hook.

@dialgem/client lets a visitor talk to an agent from a web page. It opens the microphone, streams the voice to the agent, plays the reply as it arrives, and gives you events for your interface. Your API key never reaches the page.

It has no runtime dependencies. It works in Chrome 66+, Edge 79+, Firefox 76+ and Safari 14.1+ (anything with AudioWorklet), over HTTPS or localhost.

Install

bash
npm install @dialgem/client

How a call starts

Your server asks Dialgem for a call with your API key and gives the page a one-use token that lasts 60 seconds. The page connects with that token alone.

ts
// On your server, with @dialgem/sdk
const { access_token } = await dialgem.webCalls.create({ agent_id: "AGENT_ID" })
ts
// On your page
import { DialgemCall } from "@dialgem/client"

const call = new DialgemCall({
  accessToken: () => fetch("/api/start-call").then((r) => r.text()),
})

call.on("transcript", (t) => console.log(t.role, t.text, t.final ? "" : "…"))
call.on("agent_speaking", ({ speaking }) => orb.classList.toggle("talking", speaking))
call.on("ended", ({ reason }) => console.log("call ended:", reason))
call.on("error", (e) => alert(e.message))

button.onclick = () => call.start() // from a click: browsers only allow sound after one
hangUp.onclick = () => call.stop()

Before the first call, allow your page's origin in Settings, Web calls, or with webCalls.updateSettings. See Web calls.

React

tsx
import { useDialgemCall } from "@dialgem/client/react"

function Talk() {
  const call = useDialgemCall({ getAccessToken: () => fetch("/api/start-call").then((r) => r.text()) })
  return (
    <>
      <button onClick={call.start} disabled={call.state === "connecting" || call.state === "active"}>Talk to us</button>
      {call.state === "active" && <button onClick={call.stop}>Hang up</button>}
      {call.transcript.map((l) => <p key={l.id}>{l.role}: {l.text}</p>)}
      {call.error && <p role="alert">{call.error.message}</p>}
    </>
  )
}

React 18 or newer is an optional peer dependency.

DialgemCall

new DialgemCall({ accessToken, microphone? })

  • accessToken is the token, or a function returning it (sync or async). A function is called after the visitor has allowed the microphone, because the token lives 60 seconds and a permission prompt can take longer.
  • microphone is { deviceId, echoCancellation, noiseSuppression, autoGainControl }. The last three default to true. Without echo cancellation the agent's own voice comes back through the microphone and it answers itself.

Methods: start() (resolves when connected), stop(), and setMuted(boolean) (the agent hears silence and the call stays open). Properties: state, callId, muted.

A DialgemCall is used once, because a token is single-use. The React hook makes a new one on every start().

Events

EventPayload
stateidle, connecting, active, ended or error.
started{ callId }. The agent is live.
transcript{ role: "user" or "agent", text, final }. A caller's line arrives as final: false while they speak, then settles.
agent_speaking{ speaking }. True from the first audio until it has played out.
interruptedThe caller talked over the agent.
level{ level }: microphone level from 0 to 1, about ten times a second.
ended{ reason }: hangup, agent_ended or connection_lost.
error{ code, message, reason? }

Error codes

CodeMeaning
unsupportedThe browser lacks AudioWorklet, or your Content-Security-Policy blocks blob:.
mic_denied, mic_unavailableThe microphone was refused or is missing.
token_unavailableYour function that fetches the token failed.
refusedThe token is invalid, expired or used, or the page's origin is not allowed. reason says which.
busyToo many calls are running.
call_refusedThe server declined the call. reason says why, for example insufficient_credit.
server_error, connection_failedA failure on the way.

See Troubleshooting for fixes.

There is no reconnecting

The conversation lives on the server for as long as the connection does, and the token is single-use. A call that drops has ended with reason: "connection_lost". Tell the visitor and start a new one.

Things worth knowing

  • Content-Security-Policy. The audio processor loads from a blob: URL, so script-src needs blob:.
  • Autoplay. Call start() straight from a click or tap handler. Browsers count that as the visitor starting the sound.
  • Billing. Browser calls are billed like any other call. See Billing and pricing.