diff --git a/docs/deployment/web-widget.mdx b/docs/deployment/web-widget.mdx
index 60d770f..6059b53 100644
--- a/docs/deployment/web-widget.mdx
+++ b/docs/deployment/web-widget.mdx
@@ -27,48 +27,138 @@ Step 4: Copy the generated embed code and paste it into your web page to test yo
| Mode | What it renders | When to use |
| --------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
-| **Floating Widget** | A circular call button anchored to a corner of the page. | You want a turn-key chat-bubble experience that doesn't disturb your existing layout. |
+| **Floating Widget** | A pill-shaped CTA button anchored to a corner of the page. | You want a turn-key chat-bubble experience that doesn't disturb your existing layout. |
| **Inline Component** | A panel rendered inside a `
` that you place in your page. | You want the agent embedded in a specific section (landing-page hero, support tab, etc.). |
| **Headless** | No UI. Only the audio pipeline plus a JavaScript API on `window.DograhWidget`. | You want full control over the UI — your own buttons, design system, framework state, animations. |
-## Headless mode
+## Prerequisites
+
+These apply to all three modes:
+
+- Serve your page over **HTTPS** or from `http://localhost`. Browsers refuse microphone access on plain HTTP origins or `file://`.
+- If you set **Allowed Domains** in the dashboard, include your test origin (e.g. `localhost`) — otherwise the widget's config and signaling requests are rejected. Leave the list empty to allow all domains.
+- The embed snippet you copy from the dashboard is a single `
```
-#### React
+### React + TypeScript
```tsx
-function TalkButton() {
- const [status, setStatus] = useState('idle');
+import { useEffect, useState } from 'react';
+
+type CallStatus = 'idle' | 'connecting' | 'connected' | 'failed';
+
+declare global {
+ interface Window {
+ DograhWidget: {
+ start: () => void;
+ end: () => void;
+ onStatusChange: (cb: (status: CallStatus, text?: string, subtext?: string) => void) => void;
+ onError: (cb: (err: Error) => void) => void;
+ };
+ }
+}
+
+export function TalkButton() {
+ const [status, setStatus] = useState('idle');
useEffect(() => {
- window.DograhWidget?.onStatusChange(setStatus);
+ window.DograhWidget.onStatusChange((s) => setStatus(s));
+ window.DograhWidget.onError((err) => console.error('Dograh error:', err.message));
}, []);
const isLive = status === 'connected' || status === 'connecting';
@@ -102,3 +208,19 @@ function TalkButton() {
`start()` must run inside a real user-gesture handler (`click`, `touchend`, etc.). Browsers refuse to grant microphone access to scripts that request it outside of one — calling `start()` from a `setTimeout` or on page load will fail with a permission error.
+
+## Lifecycle callbacks (all modes)
+
+The `on*` callbacks in the [Headless JavaScript API](#javascript-api) work in **all three embed modes**, not just Headless. Use them for analytics or to trigger UI in the host page even when the widget is rendering its own UI (Floating or Inline).
+
+```js
+window.DograhWidget.onCallConnected(({ agentId, workflowRunId }) => {
+ analytics.track('voice_call_started', { agentId, workflowRunId });
+});
+
+window.DograhWidget.onCallDisconnected(({ workflowRunId, durationSeconds }) => {
+ analytics.track('voice_call_ended', { workflowRunId, durationSeconds });
+});
+```
+
+`onCallConnected` and `onCallDisconnected` only fire when the call actually establishes a media connection — failed-to-connect attempts (e.g. denied mic, network failure) don't trigger them, so analytics stay clean.
diff --git a/docs/images/copy-deployment-code.png b/docs/images/copy-deployment-code.png
index 5dff882..87c8b0d 100644
Binary files a/docs/images/copy-deployment-code.png and b/docs/images/copy-deployment-code.png differ
diff --git a/docs/images/floating-widget-example.png b/docs/images/floating-widget-example.png
new file mode 100644
index 0000000..d72d5d2
Binary files /dev/null and b/docs/images/floating-widget-example.png differ
diff --git a/docs/images/go-to-deployment.png b/docs/images/go-to-deployment.png
index 25d49b6..3cf692d 100644
Binary files a/docs/images/go-to-deployment.png and b/docs/images/go-to-deployment.png differ
diff --git a/docs/images/headless-widget-example.png b/docs/images/headless-widget-example.png
new file mode 100644
index 0000000..80e1161
Binary files /dev/null and b/docs/images/headless-widget-example.png differ
diff --git a/docs/images/inline-widget-example.png b/docs/images/inline-widget-example.png
new file mode 100644
index 0000000..4dae9bf
Binary files /dev/null and b/docs/images/inline-widget-example.png differ
diff --git a/docs/images/open-settings.png b/docs/images/open-settings.png
index b17e712..0f4a89c 100644
Binary files a/docs/images/open-settings.png and b/docs/images/open-settings.png differ
diff --git a/docs/images/save-configurations.png b/docs/images/save-configurations.png
index 3cc5fe5..6a00589 100644
Binary files a/docs/images/save-configurations.png and b/docs/images/save-configurations.png differ