# Web calls

> Let visitors on your site talk to an inbound agent from the browser, with a publishable key.


# Web calls

A web call is a conversation with an **inbound agent** started from a browser on your own site: the visitor's microphone is the phone. It runs on the same voice models, tools, extraction and delivery as a phone call, and it uses minutes like one (without the telephony part of the cost).

## Publishable keys

A web call opens with a **publishable key** (`alt_pub_…`), created in the console under _Integrations → Keys → Site keys_. A publishable key:

- names **one** published inbound agent;
- is accepted by browsers only from the **origins** you list (`https://www.example.com`, up to 10);
- opens at most **2 calls at a time** and **100 a day**, one waiting call and a few per minute per visitor.

It is meant to sit in your site's HTML: it cannot read data, change agents or place outbound calls. The origin check stops other sites' pages, not a script that forges the header, so the limits are what bound the minutes a key can use. Revoke it from the console to stop it at once.

## The widget

The quickest way: copy the snippet of a key from the console (_Copy snippet_) and paste it into your site's pages, before `</body>`:

```html
<script
  src="https://api.allerto.cloud/widget.js"
  data-key="alt_pub_…"
  async
></script>
```

A button appears in the bottom-right corner; the visitor taps it, allows the microphone and talks to the agent, with live captions. The widget has Allerto's look on every site, in black or, with `data-theme="light"` on the script tag, in cream; its button text is set per key in the console and reaches every site on the next page load, with no change to the snippet. It tells the visitor they are talking to an AI assistant.

If your site sends a Content Security Policy, allow `https://api.allerto.cloud` in `script-src` and `https://api.allerto.cloud wss://api.allerto.cloud wss://voice.allerto.cloud` in `connect-src`.

To build your own interface instead, use the endpoint and the stream below.

## Starting a call

```http
POST https://api.allerto.cloud/v1/web-calls
Authorization: Bearer alt_pub_…
```

```json
{
  "call_id": "6f2c…",
  "stream": "wss://…/voice/web?token=…",
  "expires_at": "2026-10-20T12:01:00.000Z"
}
```

The browser must open `stream` within 20 seconds. The request is answered with CORS headers for your origin.

If your page asks the visitor for their number before the call, send it in the body. It becomes the call's `from`, the agent does not ask for it again, and the result's `callback_phone` gets it when the agent defines that field. It is not verified: treat it like a caller ID.

```http
POST https://api.allerto.cloud/v1/web-calls
Authorization: Bearer alt_pub_…
Content-Type: application/json

{ "phone": "+393331234567" }
```

| Error                     | Status | Meaning                                           |
| ------------------------- | ------ | ------------------------------------------------- |
| `missing_publishable_key` | 401    | No `Authorization: Bearer alt_pub_…`              |
| `invalid_publishable_key` | 401    | Unknown or revoked key                            |
| `origin_not_allowed`      | 403    | The page's origin is not in the key's list        |
| `invalid_phone`           | 400    | `phone` is not in international format (`+39…`)   |
| `rate_limited`            | 429    | Too many calls from this visitor                  |
| `agent_busy`              | 429    | The key already has its maximum of calls open     |
| `agent_unavailable`       | 503    | The agent is not published, archived or suspended |

## The stream

One WebSocket carries audio both ways:

- **You send** binary frames of 16-bit little-endian mono PCM at **16 kHz** (20 ms frames work well), and `{"type":"stop"}` to hang up.
- **You receive** binary frames of 16-bit little-endian mono PCM at **24 kHz** to play in order, and JSON text events (tool calls are never sent to a site's visitor):

| Event                                   | When                                                               |
| --------------------------------------- | ------------------------------------------------------------------ |
| `{"type":"started","call_id","model"}`  | The agent is connected and about to greet                          |
| `{"type":"state","transcript","tools"}` | The transcript or tool calls changed (`transcript: [{role,text}]`) |
| `{"type":"interrupted"}`                | The visitor spoke over the agent: drop the audio not yet played    |
| `{"type":"ended","call_id","error"}`    | The call is over                                                   |

A call lasts at most 6 minutes. When it ends it is processed like a phone call: the result is extracted and delivered to the agent's `delivery` (email and/or `call.completed` webhook, with `livemode: true`). The call's `from` is the visitor's `phone` when sent, otherwise the page's origin.
