Skip to main content

Embed Brightcall Dialer in iframe

Updated this week

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:

  • iframe embedding

  • window.postMessage

  • WebRTC 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

allow

Must include microphone and autoplay

src

Webphone URL with query parameters

width

Recommended: 370

height

Recommended: 520

2.3 Allowed Permissions

microphone; autoplay; local-network-access

3. Webphone URL & Query Parameters

3.1 Base URLs

Environment

URL

Production

https://app.brightcall.ai/webphone/iframe


3.2 Supported Query Parameters

Parameter

Required

Description

isReceiveCalls

No

If true, the webphone can receive incoming calls (iframe must remain loaded)

origin

Yes

Origin of the parent page (used for message validation)

hash

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_UPDATE

  • CALL_STARTED

  • END_CALL

  • ANSWER

  • SHOW_SUMMARY

Contains:

  • Call status (Connecting, Answered, Completed, etc.)

  • Duration

  • Direction (incoming / outgoing)

  • Phone number

  • Call outcome & feedback


agent?: IpmaxiDialerAppLoginResponseDto

Present for:

  • LOG_IN

  • LOG_OUT

  • Sometimes READY

Contains:

  • Agent name

  • Extension

  • SIP info

  • Devices

  • Internal identifiers


inputPhone?: string

Present for:

  • UPDATE_INPUT_PHONE

  • OUTGOING_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

  1. Load iframe

  2. Wait for READY message

  3. Send log_in

  4. Optionally send update_name

  5. Send dial_number

  6. Track call using broadcast messages

  7. Send log_out when 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> 

Did this answer your question?