Embedded UCP¶
In this document you can find the step-by-step explanation of how you can load a embedded version of UCP from your website.
To embed your CRM or external site inside the Admin Portal sidebar instead, see Portal Embedded Site Integration.
The powerful iframe HTML tag!¶
The most easily way to load UCP from your site is by using an iframe HTML tag. Lets go to the explanation of how to use this powerful HTML tag in your site to load the application.
Lets use the following vocabulary to talk about your website and UCP - webParent: The URL of the website that you want to load UCP from - webUCP: The URL of UCP website (Ex: https://qvoice.ca/ucp/login) The URL of UCP FULLSCREEN website (Ex: https://qvoice.ca/ucp/login?fullscreen=true)
Insert the iframe HTML tag in your website (webParent)¶
In the HTML document of your website (webParent) or inside your component if you are working with React you should insert the following:
<iframe title='ucp' src='{webUCP}' allow='notifications; microphone'
/>
Lets talk about the attributes of the iframe
- title: This is the title of your iframe, we suggest to use ucp to easily recognize it
- src: This is the source of the content of the iframe, in other terms it is the URL of the website that you want to load, in this case we are talking about webUCP (URL of UCP website)
Important: Always use the URL of UCP with the /login path at the end, otherwise the content of UCP will not be loaded successfully
Incorrect usage: https://qvoice.ca/ucp
Correct usage: https://qvoice.ca/ucp/login
- allow: This is a list of permissions splited by ; that you grant to the embedded website. UCP uses two of them, notifications in order to have the ability to push notifications and microphone in order to access the audio input that will be used during your calls
Important: If you do not want to grant these two permissions to UCP you should take into account that incoming and outgoing calls are not going to work
Finally you should see UCP embedded inside your website, for sure you can customise the styles of the iframe.
Important: Minimum required size for the iframe is 350 pixels x 550 pixels in order to give the UI enough space to normally display the elements
Enjoy Embedded UCP in your website!
Agent state in Embedded UCP¶
For a contact center agent, the top of Embedded UCP shows the agent's current state (for example Available, Lunch or On chat) and how long the agent has been in it. Clicking it opens the list of states the agent can switch to.
Whether agents can change their state from UCP is set per account in the Portal, under Account → Softphones, in the UCP section, with the Disable Agent State switch:
| Disable Agent State | What the agent sees in UCP |
|---|---|
| Off (default) | The state can be changed from UCP by clicking it. |
| On | The state is still shown and kept up to date, but it cannot be changed from UCP: clicking it does nothing and the pointer shows a "not allowed" cursor. |
The switch applies to all of the account's users, both in Embedded UCP and in the full UCP web application.
Turn it on when another application is the one that decides the agent's state — for example the application UCP is embedded in — so that agents change their state only there. A state changed outside UCP is still shown in UCP. If agents report that they cannot change their state from UCP, check this switch first.
Note
Disable Agent State only locks the agent state. Logging in and out of queues has its own switch in the same section, Disable Queue Login Logout.
Receiving messages in parent window from Embedded UCP¶
Embedded UCP sends call events to the parent window so you can subscribe to these events by opening a listener in the parent window.
Below you can find the examples of the events:
On Set Disposition:
{
"type": "UCP_DISPOSITION",
"payload": {
"call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"disposition": "interested in product",
"disposition_id": "abc123",
"params": {},
"customer_call_id": "1-25426@142.93.151.168",
"primary_call_id": "1-25426@142.93.151.168"
}
}
The payload includes the following fields:
| Field | Always present | Description |
|---|---|---|
call_id |
yes | Call-ID of the agent leg (the same call_id you received on UCP_INCOMING_CALL / UCP_ANSWERED_CALL). |
disposition |
yes | The human-readable label of the disposition selected by the agent. |
disposition_id |
yes (may be empty) | The identifier of the disposition option selected. Returned as an empty string "" when dispositions are not configured for the account. |
params |
yes | Key-value map of additional disposition form fields submitted by the agent. Empty object {} when no additional fields are configured. |
customer_call_id |
yes (may be empty) | Call-ID of the external / customer leg. Returned as an empty string "" for calls that are not queue calls (where there is no separate customer leg in scope). |
primary_call_id |
yes | Call-ID of the call's primary CDR leg. See Correlating a call with its CDR. |
On Set Agent State:
{
"type": "UCP_AGENT_STATE",
"payload": {
"state": "2dc22f830e444cf885e5724a804f51e9" // agent state id
}
}
{
"type": "UCP_INCOMING_CALL",
"payload": {
"call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"primary_call_id": "1-25426@142.93.151.168",
"caller_id_name": "John Doe",
"caller_id_number": "1234567890",
"queue_id": "1234567890",
"queue_name": "Test Queue",
"campaign_name": "Test Campaign",
"lead_id": "1234567890",
"user_id": "1234567890",
"user_ext": "1000"
}
},
{
"type": "UCP_ANSWERED_CALL",
"payload": {
"call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"primary_call_id": "1-25426@142.93.151.168",
"caller_id_name": "John Doe",
"caller_id_number": "1234567890",
"queue_id": "1234567890",
"queue_name": "Test Queue",
"campaign_name": "Test Campaign",
"lead_id": "1234567890",
"user_id": "1234567890",
"user_ext": "1000"
}
},
{
"type": "UCP_HANGUP_CALL",
"payload": {
"call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"primary_call_id": "1-25426@142.93.151.168",
"user_id": "1234567890",
"user_ext": "1000",
"initiated_by": "agent",
"interaction_id": "63947898542-5ee8b08f",
"customer_call_id": "1-25426@142.93.151.168"
}
},
{
"type": "UCP_OUTGOING_CALL",
"payload": {
"call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"primary_call_id": "8a9e7f1e-af22-4257-84d2-043e56dc0d1e",
"callee_id_number": "1237",
"user_id": "1234567890",
"user_ext": "1000"
}
}
The UCP_HANGUP_CALL event in detail¶
The UCP_HANGUP_CALL event is emitted when a call ends. It now fires whichever party ends the call — when the agent hangs up and when the customer / remote party hangs up. Earlier versions only emitted it on agent hang-up.
Important:
UCP_HANGUP_CALLfires for calls that reach the agent's softphone. Calls that abandon in the IVR or queue before reaching an agent do not produce a UCP event — use your CDR data to account for those.
The payload includes the following fields:
| Field | Always present | Description |
|---|---|---|
call_id |
yes | Call-ID of the agent leg (the same call_id you received on UCP_INCOMING_CALL / UCP_ANSWERED_CALL). |
user_id |
yes | Id of the agent. |
user_ext |
yes | Extension of the agent. |
initiated_by |
yes | Which party ended the call: "agent" (the agent pressed hang-up / cancelled) or "customer" (the remote party ended the call, i.e. the agent did not). |
interaction_id |
yes (may be empty) | Stable identifier of the logical call, useful to correlate this event with the CDR and other data sources. Returned as an empty string "" when it is not available (see note below). |
customer_call_id |
yes (may be empty) | Call-ID of the external / customer leg. Returned as an empty string "" for calls that are not queue calls (where there is no separate customer leg in scope). |
primary_call_id |
yes | Call-ID of the call's primary CDR leg. See Correlating a call with its CDR. |
Note:
initiated_byis always populated ("agent"or"customer") because it is determined by UCP itself.interaction_idis provided by the telephony platform; on older platform versions, or before the identifier has propagated to the call, it is returned as an empty string. Wheninteraction_idis empty you can still correlate the call usingcustomer_call_id/call_id.Edge case — transferred / merged calls: in a 3-way / merged conference the agent leg only ends when the agent themselves hangs up, so that case is reported as
initiated_by: "agent".
Correlating a call with its CDR¶
Every call event (UCP_INCOMING_CALL, UCP_ANSWERED_CALL, UCP_OUTGOING_CALL,
UCP_HANGUP_CALL) and UCP_DISPOSITION carry primary_call_id. Use it as the one
key for a call record, whatever route the call took.
- What it is. The Call-ID of the first leg that entered the platform: the carrier leg of an inbound call, the calling agent's leg of an outbound or internal call. Every later leg of the same call carries the same value: ring-group legs, the called extension, queue agent legs and the legs created by a transfer.
- It is the primary CDR leg. Among the legs returned by
GET /v2/accounts/{account_id}/cdrs/legs/{YYYYMM}-{primary_call_id}, the primary leg is the one whosecall_idequalsprimary_call_id. It is also the only leg withcall_direction"inbound", and it holds the callflow, the recording and the call's full duration and billing. - It matches the CDR report.
GET /api/v2/reports/cdrsreturns it asa_leg. A report row is the main call whena_legequals itscall_id, so a record keyed byprimary_call_idand a record created from the report are the same record. call_idstays the agent's own leg. It is still the id to use for anything about that agent's leg (for example the agent's talk time on a transferred call). On an outbound call the agent's leg is the primary leg, socall_idandprimary_call_idare equal.- Queue calls.
primary_call_idequalscustomer_call_id, so an integration that already keys queue calls bycustomer_call_idkeeps the same key.
Note: if the telephony platform did not supply the identifier for a leg,
primary_call_idfalls back tocustomer_call_id(queue calls) and then to the leg's owncall_id. It is therefore never a worse key than the one earlier versions offered: on queue calls it equalscustomer_call_id, and on calls the agent places it equalscall_id.
- Receiving messages in parent window
window.addEventListener('message', function(event) { if (event.data.type === "UCP_INCOMING_CALL") { console.log('INCOMING CALL FROM UCP: ', event.data) } else if (event.data.type === "UCP_ANSWERED_CALL") { console.log('ANSWERED CALL FROM UCP: ', event.data) } else if (event.data.type === "UCP_HANGUP_CALL") { // event.data.payload.initiated_by is "agent" or "customer" console.log('HUNGUP CALL FROM UCP: ', event.data) } else if (event.data.type === "UCP_OUTGOING_CALL") { console.log('OUTGOING CALL FROM UCP: ', event.data) } }, false)
Sending messages from Parent window to Embedded UCP¶
The parent window can also send messages to the embedded UCP to trigger specific actions. This allows you to control UCP functionality directly from your website.
Making calls from Parent window¶
You can initiate calls from the parent window by sending a message to the embedded UCP iframe. Here's how to do it:
document.getElementById('ucp-iframe').contentWindow.postMessage({
type: 'MAKE_CALL',
payload: {
destination: '1234'
}
}, '*')
Important considerations:
-
iframe ID: Make sure your iframe has an
idattribute so you can reference it. For example:<iframe id="ucp-iframe" title='ucp' src='{webUCP}' allow='notifications; microphone' /> -
Message structure: The message must follow the specific format with
typeandpayloadproperties. -
Destination: The
destinationfield should contain the phone number you want to call. -
Origin parameter: The
'*'parameter allows the message to be sent to any origin. For security reasons, you might want to specify the exact UCP domain instead.
Available message types¶
Currently, the following message types are supported:
- MAKE_CALL: Initiates an outgoing call to the specified destination
Note: More message types will be added in future versions to support additional UCP functionality.
Example implementation¶
Here's a complete example showing how to add a button that makes a call from the parent window:
<iframe id="ucp-iframe" title='ucp' src='https://qvoice.ca/ucp/login' allow='notifications; microphone' />
<button onclick="makeCall('1234567890')">Call 123-456-7890</button>
<script>
function makeCall(destination) {
const iframe = document.getElementById('ucp-iframe');
iframe.contentWindow.postMessage({
type: 'MAKE_CALL',
payload: {
destination: destination
}
}, '*');
}
</script>
Now you can control UCP functionality directly from your parent website!