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

# Embedded Dialer

> Embed the Allo dialer inside your own web app and control it with postMessage

The embedded dialer lets you place a working Allo dialer inside your own web product with a single iframe. Your team makes and receives calls without leaving your app, and your page is notified of every call event over `window.postMessage`.

This is the same mechanism behind the HubSpot and Salesforce panels, opened up so any product can use it.

<Note>
  Access is granted per website. Email [support@withallo.com](mailto:support@withallo.com) with the domains you want to embed from, and we activate them. Until a domain is activated, the iframe shows a "dialer not activated" screen.
</Note>

## What you can do

* Add a click-to-call button anywhere in your app that opens the Allo dialer with a number prefilled
* React to calls in your own interface: log them, open the matching record, start a timer
* Keep your users in one tab instead of switching to a separate calling app

## How to embed the dialer

<Steps>
  <Step title="Add the iframe">
    ```html theme={null}
    <iframe
      id="allo-dialer"
      src="https://web.withallo.com?variant=dialer"
      allow="microphone; autoplay; clipboard-write"
      style="width: 376px; height: 640px; border: 0;"
    ></iframe>
    ```

    The `allow="microphone; autoplay"` attribute is required. Calling needs microphone access, and the browser only grants it to an iframe whose parent page allows it. If your page is itself inside another iframe, every ancestor has to pass the permission down.
  </Step>

  <Step title="Sign in">
    Your teammate signs in to Allo inside the iframe on first use, or reuses an existing `web.withallo.com` session. The dialer emits a `login` event once a user is authenticated.
  </Step>

  <Step title="Listen for events">
    Add a `message` listener to react to calls (see below).
  </Step>

  <Step title="Send a number to dial">
    Post a `dial` command to prefill the keypad from your own click-to-call button (see below).
  </Step>
</Steps>

## Listen for events from the dialer

Every message from the dialer has the shape `{ source: "allo-dialer", type, payload }`.

```js theme={null}
window.addEventListener("message", (event) => {
  if (event.origin !== "https://web.withallo.com") return;
  const { source, type, payload } = event.data ?? {};
  if (source !== "allo-dialer") return;

  switch (type) {
    case "ready":         /* dialer loaded */ break;
    case "login":         /* payload: { user_id, team_id } */ break;
    case "outgoing_call": /* payload: { to, call_id } */ break;
    case "incoming_call": /* payload: { from, to, call_id } */ break;
    case "call_answered": /* payload: { call_id } */ break;
    case "call_ended":    /* payload: { call_id, duration } */ break;
  }
});
```

### Events the dialer emits

| Event           | Payload                 | When                                                                                   |
| --------------- | ----------------------- | -------------------------------------------------------------------------------------- |
| `ready`         | none                    | Dialer loaded and activated for your domain                                            |
| `login`         | `{ user_id, team_id }`  | An Allo user is authenticated                                                          |
| `outgoing_call` | `{ to, call_id }`       | An outbound call was started                                                           |
| `incoming_call` | `{ from, to, call_id }` | An inbound call is ringing                                                             |
| `call_answered` | `{ call_id }`           | The active call connected                                                              |
| `call_ended`    | `{ call_id, duration }` | The call ended. `duration` is talk time in seconds, `0` if the call was never answered |

## Send commands to the dialer

Post a `{ source: "allo-host", type, payload }` message to the iframe. Always target `https://web.withallo.com`, never `*`.

```js theme={null}
const iframe = document.getElementById("allo-dialer");

const send = (type, payload) =>
  iframe.contentWindow.postMessage(
    { source: "allo-host", type, payload },
    "https://web.withallo.com",
  );

// Optional handshake. Makes the dialer re-announce `ready`, and on browsers that
// strip the referrer it is what proves your (activated) origin.
send("init");

// Click to dial: prefills the keypad. Your teammate presses call.
send("dial", { phone_number: "+15551234567" });
```

### Commands the dialer accepts

| Command | Payload            | Effect                                     |
| ------- | ------------------ | ------------------------------------------ |
| `init`  | none               | Handshake. The dialer replies with `ready` |
| `dial`  | `{ phone_number }` | Prefills the keypad with a number to call  |

<Note>
  `dial` prefills the keypad only, it never starts a call on its own. The browser requires a click from your teammate to unlock the microphone, so the call starts when they press the call button.
</Note>

## Example

```html theme={null}
<iframe
  id="allo-dialer"
  src="https://web.withallo.com?variant=dialer"
  allow="microphone; autoplay; clipboard-write"
  style="width: 376px; height: 640px; border: 0;"
></iframe>

<button id="call-lead">Call this lead</button>

<script>
  const iframe = document.getElementById("allo-dialer");
  const ALLO = "https://web.withallo.com";

  document.getElementById("call-lead").addEventListener("click", () => {
    iframe.contentWindow.postMessage(
      { source: "allo-host", type: "dial", payload: { phone_number: "+15551234567" } },
      ALLO,
    );
  });

  window.addEventListener("message", (event) => {
    if (event.origin !== ALLO) return;
    const { source, type, payload } = event.data ?? {};
    if (source !== "allo-dialer") return;

    if (type === "call_ended") {
      console.log("Call finished:", payload.call_id, payload.duration, "seconds");
    }
  });
</script>
```

## Security

* Give the iframe at least about 600px of height so the full keypad and in-call screen fit. The panel is a fixed width of about 376px.
* The dialer only accepts commands from activated domains, and only posts events back to your verified origin.
* `event.origin` is set by the browser and cannot be forged. Always check it on your side, as shown in the examples.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The iframe shows a 'dialer not activated' screen">
    Your domain is not activated yet. Email [support@withallo.com](mailto:support@withallo.com) with the exact origins you embed from (for example `https://app.yourcompany.com`) and we activate them.
  </Accordion>

  <Accordion title="Calls fail or the microphone does not work">
    Make sure the iframe has `allow="microphone; autoplay"`. If your page is itself nested inside another iframe, every parent frame has to pass the same permission down.
  </Accordion>

  <Accordion title="I sent a dial command but nothing happens">
    Check that you target `https://web.withallo.com` (not `*`) and that the message shape is `{ source: "allo-host", type: "dial", payload: { phone_number } }`. Send an `init` first if you added your listener after the iframe loaded.
  </Accordion>

  <Accordion title="I am not receiving events">
    Confirm your `message` listener checks `event.origin === "https://web.withallo.com"` and `source === "allo-dialer"`. Events only flow once your domain is activated.
  </Accordion>
</AccordionGroup>

## Need help?

<CardGroup cols={2}>
  <Card title="Activate your domain" icon="headset" href="mailto:support@withallo.com">
    Ask the Allo team to activate your embedding domains
  </Card>

  <Card title="Webhooks" icon="bolt" href="/en/integrations/webhooks">
    Get server-side notifications for calls and messages
  </Card>
</CardGroup>
