localStorage under the widget’s configured key.
- Normal (visible) mode: the widget shows its built-in consent popup; there is nothing for you to do.
- Headless Mode: the widget renders nothing, so it cannot show its popup. Instead, it hands the consent step to you.
The flow
1
The action pauses
The visitor triggers
startCall() or sendChatMessage(). Because consent hasn’t been given yet, the widget pauses the action instead of executing it.2
widget.consent-required fires
The widget dispatches
widget.consent-required with { pendingAction: 'voice' | 'chat' } so you know what the visitor was trying to do.3
You show your own consent UI
A modal, a checkbox, an inline banner - whatever fits your design.
4
Accept or decline
On accept, call
widget.acceptConsent(): consent is persisted and the paused action resumes automatically - the call starts, or the queued message sends. On decline, do nothing; the action is simply dropped.Example
Good to know
- Asked once per browser. Consent is persisted in
localStorage, so subsequent visits skip straight to the action. - One flow covers both surfaces. A single consent acceptance covers both voice and chat - you don’t ask twice.
acceptConsent()is always safe. If consent wasn’t requested, the call is a no-op.
The voice-and-chat example includes a complete consent modal serving both surfaces.

