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.

StatusWhat it meansWhat to render
connectingThe socket is open or opening and the session has not been accepted yet.A connecting state. The visitor cannot speak yet.
connectedThe session is ready, the microphone is live, and audio is playing back.The live call surface: mute, end, and whatever your worker drives.
reconnectingThe socket closed unexpectedly and the SDK is making its one retry.Keep the call surface up with a reconnecting note. Do not tear down.
endedThe session finished — either your end() call or a close from our side.Your post-session state: summary, feedback prompt, or a start-again button.
errorThe 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.

CodeWhat happened
DW_VOICE_UNAUTHORIZEDThe embed key is wrong, or the page origin is not on its allowed-domains list.
DW_VOICE_SESSION_EXPIREDThe session token was no longer valid when the socket opened. Start a new session.
DW_VOICE_BUSYThe worker is at its concurrent-session limit. Ask the visitor to try again shortly.
DW_VOICE_MAX_DURATIONThe session hit the maximum length configured for the worker and was closed.
DW_VOICE_UPSTREAM_ERRORThe relay could not keep the session running. Offer a retry.
DW_VOICE_MIC_UNAVAILABLEMicrophone permission was denied or no input device was available. The SDK ends the session.
DW_VOICE_CONNECTION_LOSTThe 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 reports ended.
  • 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 call end() as well.
  • It fails. The connection could not be recovered, or the microphone became unavailable. onError fires and the status becomes error.

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.

Ending on unmount
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