Build with DelegateWorker
Session lifecycle and events
What a session reports from the first click to the record it leaves behind.
A session is short-lived and self-contained: it starts on a click, runs a conversation, and ends. Between those points the SDK reports everything through callbacks, and your interface follows along. This page covers each status, the events that arrive during a session, what happens when one ends, and what is left afterwards.
Status values
Five statuses, reported by onStatusChange and readable at any time with session.getStatus(). Treat them as the single source of truth for what your interface shows.
| Status | What it means | What to render |
|---|---|---|
connecting | The socket is open or opening and the session has not been accepted yet. | A connecting state. The visitor cannot speak yet. |
connected | The session is ready, the microphone is live, and audio is playing back. | The live call surface: mute, end, and whatever your worker drives. |
reconnecting | The socket closed unexpectedly and the SDK is making its one retry. | Keep the call surface up with a reconnecting note. Do not tear down. |
ended | The session finished — either your end() call or a close from our side. | Your post-session state: summary, feedback prompt, or a start-again button. |
error | The session could not start, or the retry failed. onError carries the code. | A plain failure state with a way to try again. |
The normal path
A session starts at connecting. When our side accepts it, microphone capture begins, the status becomes connected, and the promise returned by DWVoiceSession.start() resolves. It stays connected for the whole conversation — tool calls, transcripts, and interruptions all happen without a status change. It reaches ended once, at the end.
Reconnection
If the socket closes unexpectedly mid-session, the SDK reconnects once on its own. The status goes to reconnecting while it tries, then back to connected if it succeeds. If the second attempt also fails, onError fires with DW_VOICE_CONNECTION_LOST and the session ends in error.
Keep your call surface up during reconnecting — the visitor has not left, and tearing the interface down turns a two-second network blip into a lost conversation. A quiet line saying the connection dropped is enough.
Events during a session
onTranscript fires as the conversation goes, with role set to worker or visitor and the text of the line. When a worker line is revised, the callback fires again with the corrected text and corrected: true — replace the previous worker line rather than appending, or the transcript will read as if the worker said it twice.
onAgentSpeaking fires with true while the worker is audibly speaking and false when playback drains. It follows what the visitor actually hears, so it is the honest signal for a speaking indicator. If the visitor talks over the worker, playback is dropped and this goes false straight away.
onClientTool fires for every tool call with (toolName, parameters). It is a notification — the answer comes from your clientTools handler. See the tools guide.
onError fires with a code and an optional message. Some errors end the session and some do not, so read the status alongside the code rather than assuming.
| Code | What happened |
|---|---|
DW_VOICE_UNAUTHORIZED | The embed key is wrong, or the page origin is not on its allowed-domains list. |
DW_VOICE_SESSION_EXPIRED | The session token was no longer valid when the socket opened. Start a new session. |
DW_VOICE_BUSY | The worker is at its concurrent-session limit. Ask the visitor to try again shortly. |
DW_VOICE_MAX_DURATION | The session hit the maximum length configured for the worker and was closed. |
DW_VOICE_UPSTREAM_ERROR | The relay could not keep the session running. Offer a retry. |
DW_VOICE_MIC_UNAVAILABLE | Microphone permission was denied or no input device was available. The SDK ends the session. |
DW_VOICE_CONNECTION_LOST | The connection dropped and the retry did not recover it. The session ends in error. |
Transcripts, during and after
onTranscript is live only. The session object does not keep a transcript, and once you call end() there is nothing to read back from it. If your page needs the conversation after it finishes — to show a summary, to attach it to a record — collect the lines as they arrive.
const lines = [];
const session = await DWVoiceSession.start({
embedKey: 'YOUR_EMBED_KEY',
onTranscript: (event) => {
if (event.corrected) {
// A revision of the worker's last line. Replace rather than append.
const last = lines[lines.length - 1];
if (last && last.role === 'worker') {
last.text = event.text;
return;
}
}
lines.push({ role: event.role, text: event.text });
},
onStatusChange: (status) => {
if (status === 'ended') showSummary(lines);
},
});Separately, every session is recorded against your account. The transcript and the structured outcome the worker produced are available in the console at app.delegateworker.com under the worker session history, and through the same output routes as meeting sessions. Your page does not have to store anything to keep a record; collect the transcript in the browser only when your own interface needs it.
The feedback prompt
When a session ends in the embed widget, the widget shows a short feedback prompt — a single question about how the conversation went. The answer is stored with the session record, so the sessions that went badly are easy to find and the brief can be fixed rather than guessed at.
The headless SDK renders nothing, the feedback prompt included. If you want the same signal, build your own on the ended status. Keep it to one question, ask it once, and let the visitor skip it.
Ending a session
A session ends in one of three ways, and all three land in the same place.
- You call
end(). The SDK tells our side, stops the microphone, stops playback, closes the socket, and reportsended. - Our side ends it. The worker finished its brief, or the session hit the maximum length configured for it. The SDK tears down the same way and reports
ended. You do not need to callend()as well. - It fails. The connection could not be recovered, or the microphone became unavailable.
onErrorfires and the status becomeserror.
Teardown always releases the microphone. The browser recording indicator going out is the visitor seeing that for themselves, which is worth getting right: end the session as soon as it is over, not when the page happens to unload.
end() is safe to call more than once and safe to call on a session that has already ended. Call it from every path that leaves the conversation — a component unmount, a route change, and pagehide.
useEffect(() => {
return () => {
void sessionRef.current?.end();
sessionRef.current = null;
};
}, []);After ended, the session object is spent. To start again, call DWVoiceSession.start() for a new one.
Next
- Headless SDK quickstart — the full session surface with a working example.
- Tools guide — what the worker can do during a session.
- Zoom documentation — the same worker in meetings.