> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wave.sa/llms.txt
> Use this file to discover all available pages before exploring further.

# Web Callback Widget

> Embed a drop-in 'request a callback' button on any website — no API key in the browser.

The Web Callback widget is a drop-in button you paste onto any website. A visitor clicks it, enters their number, confirms a one-time code, and Wave calls them back — all without an API key ever touching the browser.

It runs on top of [Web Callback](/web-callback): the widget places the same click-to-call, it just moves the trigger to your visitor's browser instead of your backend.

## Embed

Copy the snippet from your dashboard (**Web Callback** → *Embed snippet*) — your project id is filled in for you — and paste it just before the closing `</body>` tag:

```html theme={null}
<script src="https://wave.sa/widget.js"
        data-project-id="YOUR_PROJECT_ID"
        data-api-url="https://api.wave.sa"
        data-lang="en"
        data-accent="#6b12a8"></script>
```

| Attribute                 | Required | Description                                                     |
| ------------------------- | -------- | --------------------------------------------------------------- |
| `data-project-id`         | ✅        | Your project id. Public — it only works from an allowed origin. |
| `data-api-url`            | —        | Wave API base URL. Defaults to `https://api.wave.sa`.           |
| `data-recaptcha-site-key` | —        | reCAPTCHA v3 site key. Enables bot protection (see below).      |
| `data-lang`               | —        | `en` or `ar`. Arabic renders right-to-left. Defaults to `en`.   |
| `data-accent`             | —        | Button/panel accent colour (any CSS colour).                    |
| `data-button-text`        | —        | Override the launcher label (default: "Call me back").          |

## How it works

<Steps>
  <Step title="Visitor requests a callback">
    They click the button and enter their Saudi mobile number.
  </Step>

  <Step title="They confirm a one-time code">
    Wave sends an SMS code; the visitor types it back. This proves they own
    the number, so the widget can never be used to dial a stranger.
  </Step>

  <Step title="Wave calls them">
    On a valid code, Wave places the callback. The call shows up in your
    **Call Logs** and fires your [webhooks](/webhooks) like any other call.
  </Step>
</Steps>

<Note>
  **No API key in the browser.** The widget only talks to the public
  `/v1/public/widget/*` endpoints, scoped by your public `project_id`. Your
  secret API keys stay on your servers.
</Note>

## Allowed origins

The widget is refused everywhere until you allowlist the sites that embed it.
Add each site's origin in the dashboard under **Web Callback** → *Allowed
origins* (e.g. `https://www.your-site.com`). A request from any other origin is
rejected with `403 WIDGET_ORIGIN_NOT_ALLOWED`.

<Warning>
  An origin is scheme + host + optional port, with **no path** — e.g.
  `https://shop.example.com`, not `https://shop.example.com/contact`. Only
  HTTPS is accepted (except `http://localhost` for local development).
</Warning>

## reCAPTCHA (recommended for production)

Because the widget can spend money (each callback is a real call), we recommend
adding Google reCAPTCHA v3. Create a v3 key pair, then add the site key to the
snippet:

```html theme={null}
<script src="https://wave.sa/widget.js"
        data-project-id="YOUR_PROJECT_ID"
        data-recaptcha-site-key="YOUR_RECAPTCHA_SITE_KEY"></script>
```

Wave verifies the token server-side on the first code request. Combined with the
one-time code and the origin allowlist, this keeps automated abuse out.

## Customisation

* **Language & direction** — `data-lang="ar"` switches the widget to Arabic and
  flips it to right-to-left.
* **Colour** — `data-accent` themes the launcher and panel.
* **Label** — `data-button-text` overrides the launcher text.

The widget is a single self-contained script with no external dependencies and
prefixed styles, so it won't clash with your site's CSS.
