API reference · Guides
Embed chat and voice
Put your agent on any website or in any app: a chat bubble with one script tag, a Talk button that lets visitors speak to your phone agent in the browser, a small JavaScript SDK for your own UI, and a REST call for apps and backends that want the agent to ring someone.
1. The chat widget
In the dashboard open the agent, then Channels, Website chat. List the domains the widget may run on, save, and paste the snippet into your site's HTML, just before </body>.
<script src="https://icebot-webhooks.vercel.app/api/widget/embed?bot=<your agent id>" async></script>- Allowed domains are required. An empty list disables the widget everywhere. Entries are host names (
shop.example.com) or a wildcard for subdomains (*.example.com, which does not matchexample.comitself). Include the port for local testing (localhost:5173). - The script carries no credential. Each visitor gets a short-lived session token that only works from the site it was issued on.
- The widget draws inside a Shadow DOM, so your CSS does not leak into it or out of it. It respects the visitor's reduced-motion setting.
2. The Talk button (voice)
Turn on voice in the same Website chat settings and the widget gains a Talk button. What it does depends on the workspace's plan:
| Plan | What the visitor gets | Billing |
|---|---|---|
| Paid plans with voice | A live, two-way voice conversation with your phone agent in the browser: same voice, same knowledge, same actions. The transcript appears as they speak. | Voice minutes, 400 credits a minute, metered after the call exactly like a phone call. Refused with a clear message when credits run out. |
| Free (web-only) | Type-to-talk: the visitor types or records a voice note (transcribed), and each reply is read aloud. No live call. | Messages, as for chat. |
- The browser asks for the microphone the first time the visitor taps Talk. If they refuse, the widget says how to allow it and chat keeps working.
- Works in current Chrome, Edge, Firefox and Safari, including Safari and Chrome on iPhone and Android. The page must be served over HTTPS (or
localhost). - Each session is limited per visitor and per site, and the voice provider’s credentials never reach the browser: our server mints a single-use signed URL for each conversation.
3. The JavaScript SDK
For your own buttons and UI. One file, no dependencies, served from https://icebot.icebergaisolutions.com/sdk/icebot.js (UMD, global Floee) and https://icebot.icebergaisolutions.com/sdk/icebot.mjs (ES module). The site still has to be one of the widget's allowed domains.
<script src="https://icebot.icebergaisolutions.com/sdk/icebot.js"></script>
<a href="#" id="chat-link">Chat with us</a>
<button id="talk">Talk to us</button>
<script>
IceBot.chat(document.getElementById('chat-link'), { bot: '<your agent id>' });
document.getElementById('talk').addEventListener('click', async () => {
const session = await IceBot.voice({
bot: '<your agent id>',
onTranscript: (t) => console.log(t.role, t.text, t.final),
onState: (s) => console.log('state', s),
onError: (e) => console.error(e),
});
// later: session.end();
});
</script>import Floee from 'https://icebot.icebergaisolutions.com/sdk/icebot.mjs';
const session = await IceBot.voice({ bot: '<your agent id>', onState: render });
if (session.mode === 'ilmu') session.send('What time do you open on Sunday?');IceBot.chat(el?, options)
Loads the chat widget for options.bot. When el is given, clicking it opens the widget, so any link or button on your page can start a chat.
IceBot.voice(options)
| Option | Type | Description |
|---|---|---|
bot (required) | string | Your agent id. |
onTranscript | (t: { role: "user" | "agent"; text: string; final: boolean }) => void | Each line of the conversation as it is heard or spoken. |
onState | (s: "connecting" | "listening" | "speaking" | "ended" | "error") => void | Drive your button or animation from this. |
onError | (e: Error) => void | Microphone refused, credits used up, site not allowed, network lost. |
It resolves to a session:
interface VoiceSession {
mode: 'elevenlabs' | 'ilmu'; // live call, or type-to-talk on the free plan
end(): void; // hang up and release the microphone
setMuted(muted: boolean): void;
send(text: string): void; // ilmu mode: send a typed message; the reply is spoken
}Call it from a tap or click. Browsers, iOS Safari above all, only allow audio to start inside a user gesture. Calling IceBot.voice on page load will not play sound.
If your site sends a Content-Security-Policy, allow https://icebot-webhooks.vercel.app and wss://api.elevenlabs.io in connect-src, https://icebot.icebergaisolutions.com and https://icebot-webhooks.vercel.app in script-src, and blob: in media-src.
What the SDK calls
These are served by the widget host, https://icebot-webhooks.vercel.app, and only answer requests whose Origin is an allowed domain. You do not need them if you use the SDK; they are listed so you know what leaves the page.
POST /api/widget/session?bot=<agent id>. Mints a widget session for a visitor. Only from an allowed domain. Returns a bearer token for the calls below.
{
"sessionToken": "<64 hex characters>",
"externalUserId": "widget:<uuid>",
"expiresAt": "2026-09-28T04:00:00.000Z",
"config": {
"welcomeMessage": "Hi! How can we help today?"
}
}POST /api/widget/voice-session?bot=<agent id>. Starts a voice session (Authorization: Bearer <sessionToken>). Paid plans get a live voice session; the free plan gets type-to-talk.
{
"mode": "elevenlabs",
"signedUrl": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=...&conversation_signature=...",
"sessionId": "<uuid>",
"dynamicVariables": {
"icebot_web_session": "<uuid>"
}
}GET /api/widget/speak?bot=<agent id>&m=<message id>. An assistant reply in this visitor's conversation, read aloud (audio/mpeg). Only the session's own messages.
The free plan's voice-session response is simply:
{
"mode": "ilmu"
}4. React Native and other apps
In-app voice or chat: a WebView
Host a small page on one of your allowed domains that loads the SDK, and open it in react-native-webview. The page's origin is what the widget checks, so it must be an allowed domain; a local file:// page will be refused.
import { WebView } from 'react-native-webview';
export function TalkToUs() {
return (
<WebView
source={{ uri: 'https://www.example.com/talk' }} // loads https://icebot.icebergaisolutions.com/sdk/icebot.js
mediaCapturePermissionGrantType="grant" // iOS 15+: allow the mic for this page
allowsInlineMediaPlayback
mediaPlaybackRequiresUserAction={false}
onPermissionRequest={(req) => req.grant(req.resources)} // Android
/>
);
}Your app also needs the platform microphone permission: NSMicrophoneUsageDescription in Info.plist on iOS, RECORD_AUDIO and MODIFY_AUDIO_SETTINGS on Android.
Ring the customer: the REST API
To have the agent phone a customer (a booking confirmation, a delivery call-back), call POST /api/v1/calls from your backend. Never put an API key in an app bundle: anyone can extract it. Your app asks your server, your server calls us with a key that has the calls:create scope.
await fetch('https://icebot.icebergaisolutions.com/api/v1/calls', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ICEBOT_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ to: '+60123456789', variables: { customer_name: 'Aisyah' } }),
});