1. Overview
The Brightcall Webphone can be embedded into any web application using a secure <iframe>, enabling outbound and inbound calling directly from your product without requiring users to switch context.
The integration is based on the standard browser postMessage API and provides a bidirectional communication channel between the host application and the Brightcall webphone. This allows the host application to:
Initiate outbound calls
Control agent authentication (login / logout)
Update contact metadata (e.g., display name)
Receive real-time call lifecycle events
Track agent session state
React to call status changes and call completion
The iframe operates as an isolated application while exposing a well-defined messaging interface for orchestration and event handling.
Key capabilities
Embedded softphone UI
Fully functional webphone rendered inside your application.Outbound call control
Programmatically trigger calls with phone number and contact context.Inbound call support
Optional ability to receive incoming calls when the iframe remains loaded.Real-time event streaming
Call progress, connection state, completion, and summary events are broadcast to the host.Agent session management
Support for programmatic login and logout using secure agent hashes.
This document describes:
iFrame setup and configuration
URL parameters
Incoming messages (from Brightcall to host application)
Outgoing messages (from host application to Brightcall)
Security requirements
Recommended lifecycle
A complete working HTML example
Supported browsers
The integration relies on:
iframeembeddingwindow.postMessageWebRTC audio APIs
It is therefore supported in all modern browsers, including:
Chrome
Edge
Firefox
Safari (latest versions)
Mobile browsers are not officially supported unless explicitly tested.
2. iFrame Setup
Before signing in, select Webphone (Voximplant) as the agent’s active device.
2.1 Example Markup
<iframe
id="webphone"
allow="microphone; autoplay; local-network-access"
src="https://app.brightcall.ai/webphone/iframe?isReceiveCalls=true&origin=https://example.com"
width="370"
height="520">
</iframe>
2.2 Required Attributes
Attribute | Description |
| Must include |
| Webphone URL with query parameters |
| Recommended: 370 |
| Recommended: 520 |
2.3 Allowed Permissions
microphone; autoplay; local-network-access
3. Webphone URL & Query Parameters
3.1 Base URLs
Environment | URL |
Production |
|
3.2 Supported Query Parameters
Parameter | Required | Description |
| No | If |
| Yes | Exact origin of the parent page (scheme, host and port). Used to send messages back to the parent. |
| No | Agent authentication hash for automatic login |
Example
https://app.brightcall.ai/webphone/iframe?isReceiveCalls=true&origin=https://example.com&hash=AGENT_HASH
4. Receiving Messages from Brightcall (iframe → parent)
4.1 Transport Mechanism
Messages are delivered via:
window.addEventListener('message', handler); 4.2 Mandatory Security Validation
Validate both the message origin and the sending iframe:
const iframe = document.getElementById('webphone');
function handler(event) {
if (event.origin !== 'https://app.brightcall.ai' ||
event.source !== iframe.contentWindow) return;
const message = event.data;
if (!message || typeof message !== 'object') return;
// Handle the message here.
}Failure to validate origin introduces severe security risks.
4.3 Message Structure
type BroadcastMessage = {
type: BroadcastMessageType;
callData: WebphoneCallInfo | null;
agent?: IpmaxiDialerAppLoginResponseDto;
broadcastId?: number;
};4.4 Message Fields
type (required)
Common event identifiers delivered to the parent:
enum BroadcastMessageType {
READY = 'ready',
CALL_STATUS_UPDATE = 'call_status_update',
LOG_IN = 'log_in',
LOG_OUT = 'log_out',
CALL_STARTED = 'call_started',
END_CALL = 'end_call',
SHOW_SUMMARY = 'show_summary'
}callData: WebphoneCallInfo | null
May contain call data for READY and the following events; always check for null:
CALL_STATUS_UPDATECALL_STARTEDEND_CALL
Contains:
Call status (Connecting, Answered, Completed, etc.)
Duration
Direction (incoming / outgoing)
Phone number
Call outcome & feedback, when available
SHOW_SUMMARY carries callData: null; use the latest call status or final call data for call details.
agent?: IpmaxiDialerAppLoginResponseDto
Present for:
LOG_IN
Contains:
Agent name
Extension
SIP info
Devices
Internal identifiers
broadcastId?: number
Optional identifier of the webphone instance.
4.5 Message Type Behaviour Reference
Type | When Sent | Expected Host Action |
READY | iFrame initialized; agent login and telephony connection are separate | Mark integration as ready |
LOG_IN | Agent login information is available | Store agent info |
LOG_OUT | Agent logged out | Clear session |
CALL_STARTED | Call attempt starts (Connecting) | Update call UI |
CALL_STATUS_UPDATE | Status change, including Answered | Refresh call state; detect an answer from callData.callStatus |
END_CALL | Call finalized, normally after the summary step | Finalize call |
SHOW_SUMMARY | Post-call summary form is shown | Use the latest call status for call details |
5. Sending Messages to Brightcall (parent → iframe)
5.1 Transport
const iframe = document.getElementById('webphone'); iframe.contentWindow.postMessage(message, IFRAME_ORIGIN);
5.2 Security Requirement
Always use the exact iframe origin:
const IFRAME_ORIGIN = 'https://app.brightcall.ai';
Never use "*" in production.
6. Supported Outgoing Message Types
6.1 Start Outbound Call
Type
type: "dial_number"
Payload
{ type: "dial_number"; phone: string; name?: string; } Example
iframe.contentWindow.postMessage( { type: 'dial_number', phone: '+14155552671', name: 'John Doe' }, IFRAME_ORIGIN );
6.2 Update Contact / Display Name
Updates the display name of an existing call. To set the initial name, include name in dial_number.
Type
type: "update_name"
Payload
{ type: "update_name"; name: string; } 6.3 Log Agent In
Type
type: "log_in"
Payload
{ type: "log_in"; hash: AgentHash; } 6.4 Log Agent Out
Type
type: "log_out"
Payload
{ type: "log_out"; }
7. Recommended Lifecycle
Load iframe
Wait for
READYmessageSend
log_inand wait for the webphone to connectSend
dial_number, includingnameif neededOptionally send
update_nameduring the callTrack call using broadcast messages
Send
log_outwhen session ends
8. Error Handling
Invalid messages are silently ignored by the iframe
No error responses are guaranteed
Use broadcast messages (
CALL_STATUS_UPDATE,END_CALL, etc.) as the source of truth
HTML example
Replace https://example.com in the origin parameter with the actual origin of your parent page.
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webphone Demo</title>
</head>
<body>
<table>
<tr>
<td>
<button onclick="startCall()">Start Call</button>
</td>
<td>
<input id="phone" placeholder="phone number">
</td>
</tr>
<tr>
<td>
<button onclick="updateName()">Update Name</button>
</td>
<td>
<input id="name" placeholder="name">
</td>
</tr>
<tr>
<td>
<button onclick="logIn()">Log In</button>
</td>
<td>
<input id="hash" placeholder="hash">
</td>
</tr>
<tr>
<td>
<button onclick="logOut()">Log out</button>
</td>
<td></td>
</tr>
</table>
<table>
<tr>
<td> Webphone status:
</td>
<td>
<div id="webphone_status">not ready</div>
</td>
</tr>
<tr>
<td> Login status:
</td>
<td>
<div id="login_status"></div>
</td>
</tr>
<tr>
<td> Call Info:
</td>
<td>
<div id="call_info"></div>
</td>
</tr>
<tr>
<td> Call status:
</td>
<td>
<div id="call_status"></div>
</td>
</tr>
</table> <br><br>
<iframe id="webphone" allow="microphone; autoplay; local-network-access" src="https://app.brightcall.ai/webphone/iframe?isReceiveCalls=true&origin=https://example.com" width="370" height="520">
</iframe> <script>
const IFRAME_ORIGIN = 'https://app.brightcall.ai';
const iframe = document.getElementById('webphone');
function startCall() {
const phone = document.getElementById('phone').value;
const name = document.getElementById('name').value;
if (phone) {
iframe.contentWindow.postMessage(
{ type: 'dial_number', phone, name }, IFRAME_ORIGIN
);
}
}
function updateName() {
const name = document.getElementById('name').value;
if (name) {
iframe.contentWindow.postMessage(
{ type: 'update_name', name }, IFRAME_ORIGIN
);
}
}
function logIn() {
const hash = document.getElementById('hash').value;
if (hash) {
iframe.contentWindow.postMessage(
{ type: 'log_in', hash }, IFRAME_ORIGIN
);
}
}
function logOut() {
iframe.contentWindow.postMessage(
{ type: 'log_out' }, IFRAME_ORIGIN
);
}
window.addEventListener('message', (event) => {
if (event.origin !== IFRAME_ORIGIN ||
event.source !== iframe.contentWindow) return;
const data = event.data;
if (!data || typeof data !== 'object') return;
switch (data.type) {
case 'ready':
document.getElementById('webphone_status').textContent = 'ready';
break;
case 'log_in':
if (data.agent) {
document.getElementById('login_status').textContent =
`Logged in as ${data.agent.name}`;
}
break;
case 'log_out':
document.getElementById('login_status').textContent = 'Logged out';
break;
case 'call_started':
if (data.callData) {
document.getElementById('call_info').textContent =
`New call ${data.callData.isIncoming ? 'from' : 'to'} ${data.callData.callerDisplayName ?? data.callData.phoneNumber}`;
}
break;
case 'end_call':
document.getElementById('call_info').textContent =
`Call ended. ${data.callData?.outcome ? 'Call outcome: ' + data.callData.outcome.outcome : ''}`;
break;
case 'call_status_update':
if (data.callData) {
document.getElementById('call_status').textContent =
data.callData.callStatus;
}
break;
}
});
</script>
</body>
</html>
