You can obtain an agent instance using either the connect.agent(callback)
API or the new connect.Agent()
API.
connect.agent(callback)
: Can be called at any time after invokingconnect.core.initCCP()
. The callback will be triggered once the agent is initialized or immediately if already initialized.new connect.Agent()
: Preferred when you want fewer callbacks. However, ensure the agent is initialized before calling this, as it will throw an error otherwise.
An agent is considered initialized after the first agent data is retrieved from the backend and connect.agent()
is triggered. Logging out of Connect uninitializes the agent.
// Using connect.agent(callback)
connect.agent((agent) => {
const state = agent.getState();
});
// Using new connect.Agent()
// Make sure to use this approach only after agent is initialized.
const agent = new connect.Agent();
const state = agent.getState();
Get the current state of the agent:
const state = agent.getState();
console.log("Agent's current state", state);
Get all possible agent states:
const states = agent.getAgentStates();
console.log("All agent states:", states);
Set the agent state to OFFLINE:
const offlineState = agent.getAgentStates().find(
(state) => state.type === connect.AgentStateType.OFFLINE
);
agent.setState(offlineState, {
success: () => console.log("Agent state set to OFFLINE"),
failure: () => console.log("Failed to set agent state to OFFLINE""),
}, { enqueueNextState: true });
Set the agent state to ROUTABLE: (The defalt routable state name is "Available" but you can edit it in the Admin Website)
const routableState = agent.getAgentStates().find(
(state) => state.type === connect.AgentStateType.ROUTABLE
);
agent.setState(routableState, {
success: () => console.log(`Agent state set to ${routableState.name}`),
failure: () => console.log("Failed to set agent state to ROUTABLE"),
}, { enqueueNextState: true });
Change the agent state to a custom state:
const targetCustomStateName = "your-custom-state";
const customState = agent.getAgentStates().find(
(state) => state.name === targetCustomStateName
);
agent.setState(customState, {
success: () => console.log(`Agent state set to ${routableState.name}`),
failure: () => console.log(`Failed to set agent state to ${targetCustomStateName}`),
}, { enqueueNextState: true });
Listen for a new contact being routed to the agent:
connect.contact((contact) => {
const contactId = contact.getContactId();
const initialContactId = contact.getInitialContactId();
const type = contact.getType();
const subType = contact.getContactSubtype();
console.log("New contact detected:", { contactId, initialContactId, type, subType });
});
Display an “Accept” button when an incoming contact arrives:
connect.contact((contact) => {
contact.onRefresh((_contact) => {
if (shouldShowAcceptButton(_contact)) {
showAcceptButton(); // Your custom function to display the button
} else {
hideAcceptButton(); // Your custom function to hide the button
}
function shouldShowAcceptButton(c) {
const contactStateType = c.getState().type;
const contactType = c.getType();
const agentConnection = c.getAgentConnection();
if (contactType === connect.ContactType.QUEUE_CALLBACK || contactType === connect.ContactType.VOICE || contactType === connect.ContactType.CHAT) {
// Return false if this is a monitoring contact for supervisor.
// Monitoring contact is not supported for task and email channel.
const isMonitoringCall = agentConnection.getType() === connect.ConnectionType.MONITORING || Object.values(connect.MonitoringMode).includes(agentConnection.getMonitorStatus()?.toUpperCase());
if (isMonitoringCall) return false;
}
if (contactType === connect.ContactType.QUEUE_CALLBACK) {
// Queued callback contact comes in as "incoming" state.
return contactStateType === connect.ContactStateType.INCOMING;
} else if (contactType === connect.ContactType.VOICE) {
// Voice contact comes in as "connecting" state. Return true only if the contact is inbound and the agent is configured to use softphone, not deskphone.
return c.isSoftphoneCall() && c.isInbound() && contactStateType === connect.ContactStateType.CONNECTING;
} else if (contactStateType === connect.ContactStateType.CONNECTING) {
return true;
}
return false;
}
})
});
Terminate the agent’s connection to a contact:
const agentConnection = contact.getAgentConnection();
agentConnection.destroy({
success: () => console.log("Disconnected from the contact"),
failure: () => console.log("Failed to disconnect from the contact"),
});
Disconnect and clear the contact when it ends:
const agentConnection = contact.getAgentConnection();
contact.onEnded((_contact) => {
_contact.clear({
success: () => console.log("Contact cleared"),
failure: () => console.log("Failed to clear contact"),
});
});
// Don't call contact.clear in success callback as the backend would not be ready yet. Rather use contact.onEnded().
agentConnection.destroy({
success: () => console.log("Disconnected from the contact"),
failure: () => console.log("Failed to disconnect from the contact"),
});
Fetch Quick Connect endpoints for all queues in the routing profile:
const defaultOutboundQueueARN = agent.getRoutingProfile().defaultOutboundQueue.queueARN;
agent.getEndpoints(defaultOutboundQueueARN, {
success: ({ endpoints }) => console.log("Retrieved Quick Connects", endpoints),
failure: () => console.log("Failed to retrieve quick connects"),
});
Fetch Quick Connect endpoints for all queues in the routing profile:
const routingProfileQueueARNs = agent.getAllQueueARNs();
agent.getEndpoints(routingProfileQueueARNs, {
success: ({ endpoints }) => console.log("Retrieved Quick Connects", endpoints),
failure: () => console.log("Failed to retrieve quick connects"),
});
Fetch Quick Connect endpoints for the queue associated with the current contact:
const currentContactQueueARN = contact.getQueue().queueARN;
agent.getEndpoints(currentContactQueueARN, {
success: ({ endpoints }) => console.log("Retrieved Quick Connects", endpoints),
failure: () => console.log("Failed to retrieve quick connects"),
});
Fetch Quick Connect endpoints for the default outbound queue and all routing profile queues (Native CCP behavior):
const defaultOutboundQueueARN = agent.getRoutingProfile().defaultOutboundQueue.queueARN;
const routingProfileQueueARNs = agent.getAllQueueARNs();
agent.getEndpoints(routingProfileQueueARNs.concat(defaultOutboundQueueARN), {
success: ({ endpoints }) => console.log("Retrieved Quick Connects", endpoints),
failure: () => console.log("Failed to retrieve quick connects"),
});
Initiate an outbound call using a phone number:
const endpoint = connect.Endpoint.byPhoneNumber("+18005550100");
agent.connect(endpoint, {
success: () => console.log("Started an outbound call"),
failure: () => console.log("Failed to start an outbound call"),
});
Filter external Quick Connect endpoints of the default outbound queue and start a call:
const filteredEndpoints = endpoints.filter((endpoint) => endpoint.type === connect.AddressType.PHONE_NUMBER);
// Select an endpoint
const endpoint = filteredEndpoints[0];
agent.connect(endpoint, {
success: () => console.log("Started an outbound call"),
failure: () => console.log("Failed to start an outbound call"),
});
Set the agent state to OFFLINE and log out:
function handleLogoutButtonClick() {
const agent = new connect.Agent();
if (agent.getState().type === connect.AgentStatusType.OFFLINE) {
logout();
} else {
setAgentOffline()
.then(logout)
.catch(console.error);
}
}
function setAgentOffline() {
return new Promise((resolve, reject) => {
const agent = new connect.Agent();
const offlineState = agent.getAgentStates().find(
(state) => state.type === connect.AgentStateType.OFFLINE,
);
agent.setState(offlineState, {
success: resolve,
failure: reject,
}, { enqueueNextState: true });
});
}
function logout() {
const logoutEndpoint = "https://<your-instance-domain>/logout";
fetch(logoutEndpoint, { credentials: 'include', mode: 'no-cors'})
.then(() => {
// Notify all CCPs to terminate
connect.core.getUpstream().sendUpstream(connect.EventType.TERMINATE);
});
}
Enable softphone for the agent:
const config = agent.getConfiguration();
const newConfig = {
...config,
softphoneEnabled: true,
};
agent.setConfiguration(newConfig, {
success: () => console.log("Updated agent configuration"),
failure: () => console.log("Failed to update agent configuration"),
});
Change the agent’s extension number:
const config = agent.getConfiguration();
const newConfig = {
...config,
softphoneEnabled: false,
extension: "+11234567890",
};
agent.setConfiguration(newConfig, {
success: () => console.log("Updated agent configuration"),
failure: () => console.log("Failed to update agent configuration"),
});
Set the agent’s preferred language:
const config = agent.getConfiguration();
const newConfig = {
...config,
agentPreferences: {
locale: "en_US",
},
};
agent.setConfiguration(newConfig, {
success: () => console.log("Updated agent configuration"),
failure: () => console.log("Failed to update agent configuration"),
});
Manage audio devices (microphone, speaker, ringer). The process varies based on the softphone configuration:
Device IDs are unique to their origin, so the method for obtaining microphone and speaker device IDs depends on where the WebRTC communication occurs. If you initialize the CCP with allowFramedSoftphone: true
, use the connect.core.getFrameMediaDevices()
API to retrieve device IDs from the embedded CCP context in the Connect domain. However, ringer device IDs should always be obtained from the embedded CCP context using the same API, as ringtones are played there regardless of the allowFramedSoftphone
parameter.
For those who call initCCP with allowFramedSoftphone: true
:
const deviceList = await connect.core.getFrameMediaDevices();
const audioInputDevices = deviceList.filter(device => device.kind === 'audioinput');
const audioOutputDevices = deviceList.filter(device => device.kind === 'audiooutput');
// Agent-selected devices
const targetMicrophoneDeviceId = audioInputDevices[0].deviceId;
const targetSpeakerDeviceId = audioOutputDevices[0].deviceId;
const targetRingerDeviceId = audioOutputDevices[0].deviceId;
// Setting speaker and ringer devices can work anytime.
agent.setSpeakerDevice(targetSpeakerDeviceId);
agent.setRingerDevice(targetRingerDeviceId);
// The setMicrophoneDevice method can only be used when there is an active softphone contact, so it should be called within the onLocalMediaStreamCreated callback.
// If you need to call agent.onLocalMediaStreamCreated multiple times, ensure you unsubscribe from the previous callback before registering a new one to prevent setMicrophoneDevice from being called redundantly.
agent.onLocalMediaStreamCreated(() => {
agent.setMicrophoneDevice(targetMicrophoneDeviceId);
});
For those who load RTCJS, call initCCP with allowFramedSoftphone: false
, and call connect.core.initSoftphoneManager()
:
const deviceListFromConnectDomain = await connect.core.getFrameMediaDevices();
const audioInputDevicesFromConnectDomain = deviceListFromConnectDomain.filter(device => device.kind === 'audioinput');
const audioOutputDevicesFromConnectDomain = deviceListFromConnectDomain.filter(device => device.kind === 'audiooutput');
const deviceListFromMyDomain = await navigator.mediaDevices.enumerateDevices();
const audioOutputDevicesFromMyDomain = deviceListFromMyDomain.filter(device => device.kind === 'audiooutput');
// Agent-selected devices
const targetMicrophoneDeviceId = audioInputDevicesFromConnectDomain[0].deviceId;
const targetSpeakerDeviceId = audioOutputDevicesFromConnectDomain[0].deviceId;
const targetRingerDeviceId = audioOutputDevicesFromMyDomain[0].deviceId;
// Setting speaker and ringer devices can work anytime.
agent.setSpeakerDevice(targetSpeakerDeviceId);
agent.setRingerDevice(targetRingerDeviceId);
// The setMicrophoneDevice method can only be used when there is an active softphone contact, so it should be called within the onLocalMediaStreamCreated callback.
// If you need to call agent.onLocalMediaStreamCreated multiple times, ensure you unsubscribe from the previous callback before registering a new one to prevent setMicrophoneDevice from being called redundantly.
agent.onLocalMediaStreamCreated(() => {
agent.setMicrophoneDevice(targetMicrophoneDeviceId);
});