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
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 | Origin of the parent page (used for message validation) |
| 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
Always validate the message origin:
function handler(event) { if (event.origin !== 'https://app.brightcall.ai') return; const message = event.data; } Failure to validate origin introduces severe security risks.
4.3 Message Structure
type BroadcastMessage = { type: BroadcastMessageType; callData: WebphoneCallInfo | null; agent?: IpmaxiDialerAppLoginResponseDto; inputPhone?: string; broadcastId?: number; }; 4.4 Message Fields
type (required)
Event identifier:
enum BroadcastMessageType { DESTROYED = 'destroyed', 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', ANSWER = 'answer', OUTGOING_CALL = 'outgoing_call', UPDATE_INPUT_PHONE = 'update_input_phone' }
callData: WebphoneCallInfo | null
Present for:
CALL_STATUS_UPDATECALL_STARTEDEND_CALLANSWERSHOW_SUMMARY
Contains:
Call status (Connecting, Answered, Completed, etc.)
Duration
Direction (incoming / outgoing)
Phone number
Call outcome & feedback
agent?: IpmaxiDialerAppLoginResponseDto
Present for:
LOG_INLOG_OUTSometimes
READY
Contains:
Agent name
Extension
SIP info
Devices
Internal identifiers
inputPhone?: string
Present for:
UPDATE_INPUT_PHONEOUTGOING_CALL
Represents the phone number entered by the agent.
broadcastId?: number
Optional correlation ID.
4.5 Message Type Behaviour Reference
Type | When Sent | Expected Host Action |
READY | iFrame initialized | Mark integration as ready |
DESTROYED | iFrame unloaded | Cleanup state |
LOG_IN | Agent logged in | Store agent info |
LOG_OUT | Agent logged out | Clear session |
OUTGOING_CALL | Call initiated | Track call |
CALL_STARTED | Call connected | Update call UI |
CALL_STATUS_UPDATE | Status change | Refresh call state |
ANSWER | Call answered | Mark as answered |
END_CALL | Call finished | Finalize call |
SHOW_SUMMARY | Summary available | Show results |
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
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_inOptionally send
update_nameSend
dial_numberTrack 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
<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 = document.getElementById('webphone'); startCall = () => { const phone = document.getElementById('phone').value; const name = document.getElementById('name').value; if (phone) { iframe.contentWindow.postMessage( { type: "dial_number", phone: phone, name: name }, '*' ); } }; updateName = () => { const name = document.getElementById('name').value; if (name) { iframe.contentWindow.postMessage( { type: "update_name", name: name }, '*' ); } }; logIn = () => { const hash = document.getElementById('hash').value; if (hash) { iframe.contentWindow.postMessage( { type: "log_in", hash: hash }, '*' ); } }; logOut = () => { iframe.contentWindow.postMessage( { type: "log_out" }, '*' ); }; window.addEventListener('message', (message) => { const data = message.data; switch (data.type) { case 'ready': { document.getElementById('webphone_status').innerHTML = 'ready'; break; } case 'log_in': { document.getElementById('login_status').innerHTML = `Logged in as ${data.agent.name}`; break; } case 'log_out': { document.getElementById('login_status').innerHTML = 'Logged out'; break; } case 'call_started': { document.getElementById('call_info').innerHTML = `New call ${data.callData.isIncoming ? "from" : "to"} ${data.callData.callerDisplayName ?? data.callData.phoneNumber}`; break; } case 'end_call': { document.getElementById('call_info').innerHTML = `Call ended. ${data.callData.outcome ? `Call outcome: ${data.callData.outcome.outcome}` : ""}`; break; } case 'call_status_update': { document.getElementById('call_status').innerHTML = data.callData.callStatus; break; } } }); </script> </body> </html>
