---
title: "Create a web widget in the dashboard"
description: "Set up a click-to-talk widget for your site from the Web Widgets page — no API calls required."
---

The **Web Widgets** page (`/dashboard/web-widgets`) creates and manages
the publishable keys that power ThunderPhone's embeddable voice widget.
Everything the [API version of this guide](/guides/embed-web-widget)
does with cURL, this page does with a form — plus it generates the embed
code for you.

## Create a widget

<Steps>
  <Step title="Click New widget and pick a mode">
    - **Agent mode** — the widget always runs one agent you pick from
      the dropdown. The simplest setup.
    - **Webhook mode** — on every widget session, ThunderPhone calls
      your webhook URL and your server returns the agent configuration
      for that visitor (per-page routing, logged-in users, rollouts).
      Leave the URL blank to use your organization's default webhook;
      when you enter a new URL you can also set it as the org default.
  </Step>
  <Step title="List the allowed domains">
    At least one domain is required, and browsers can only start
    sessions from the hostnames you list. Subdomain wildcards like
    `*.example.com` are allowed (they match subdomains, not the bare
    domain); bare wildcards like `*` are rejected. The list stops casual
    reuse of your key on other sites but is not authentication: a script
    outside a browser can send any `Origin` header. `localhost` is not
    allowed automatically in production; add it to the list to test
    locally against the production API.
  </Step>
  <Step title="Choose the browser overrides">
    Tick each session setting your page sends: **Page context**,
    **Variables**, **Voice**, **Language**. New widgets accept none, and
    a session request carrying a setting that is off fails with `400`.
    Your key is public, so allow only what you use. See
    [Browser overrides](/guides/embed-web-widget#browser-overrides).
  </Step>
  <Step title="Name it, enable it, create it">
    The **Enable widget** toggle controls whether the key accepts
    sessions — you can create a widget disabled and flip it on at launch.
  </Step>
</Steps>

## Grab the embed code

Right after creation (or via each row's **Embed** button), an embed
dialog generates ready-to-paste code with your publishable key filled
in:

- **React** — a `<ThunderPhoneWidget />` snippet for bundler apps.
- **Script tag** — a CDN `<script>` + stylesheet pair for any site.

The dialog also previews basic appearance options — title, primary
color, corner position, and light/dark theme — and bakes your choices
into the snippet. For the full prop reference, styling hooks, and the
headless option, see the [widget SDK docs](/widget/overview).

<Note>
  Publishable keys (`pk_live_…`) are public by design — safe to ship in
  front-end code. Copy one anytime with the copy button on its row.
</Note>

## Manage widgets from the list

Each row edits in place:

- **Mode / Agent** — switch between agent and webhook mode, or repoint
  the agent, with inline selects.
- **Domains** — add or remove allowed domains as chips.
- **Enable/disable** — a switch per widget; disabled widgets stop
  accepting sessions immediately.
- **Transfer to org** — copy or move the widget to another organization
  you belong to (moving removes it here).
- **Delete** — revokes the publishable key; the widget stops working
  immediately on all domains. This can't be undone.

Widget sessions appear in [call history](/guides/review-calls) with the
same transcript, recording, and grading as phone calls.

## Next steps

<CardGroup cols={2}>
  <Card title="Widget SDK reference" icon="window-maximize" href="/widget/overview">
    React component, headless hook, and CDN script details.
  </Card>
  <Card title="Webhook-mode routing" icon="bolt" href="/guides/dynamic-call-config">
    Return per-visitor agent config from your server.
  </Card>
</CardGroup>
