This guide covers how supervisors observe live interactions - browsing active interactions across the system, joining as a silent or visible viewer, joining into the live call audio, reacting to viewer lifecycle events, managing viewers as an owner, and claiming or handing off interaction ownership. It also covers the finer audio interventions available to Elite (hybrid) supervisors - barging in so all parties hear them, or privately coaching the agent.
Personas: Supervisor - browses, monitors, and joins live interactions, manages viewers, claims or hands off ownership, and (Elite) coaches or barges into calls; Agent - owns the interaction being monitored.
Supervisors join active interactions as viewers without interrupting the agent-customer conversation. Two viewing modes are available:
| Mode | Visible to owner | Owner can mute/remove |
|---|---|---|
| Private | No - silent monitor | No |
| Public | Yes | Yes |
User.canViewInteractionPublicly, User.canViewInteractionPrivately, and User.canClaimInteractionOwnership expose which modes the supervisor is permitted to use. Start monitoring with User.viewInteraction, passing an interaction ID sourced from an InteractionView (see User.getInteractionViews).
user.canViewInteractionPublicly or user.canViewInteractionPrivately is true.Read the capability flags once after init to determine which modes are available:
const canPublic = user.canViewInteractionPublicly;
const canPrivate = user.canViewInteractionPrivately;
const canOwn = user.canClaimInteractionOwnership;
if (!canPublic && !canPrivate) {
hideMonitoringUI();
}
Use User.getInteractionViews to fetch server-side view presets, then call InteractionView.getInteractions on the chosen view for a paginated dashboard:
const views = await user.getInteractionViews();
const defaultView = views.find(v => v.isDefault);
const page = await defaultView.getInteractions({
page: 1,
pageSize: 50,
});
page.interactions.forEach(i => renderRow(i)); // i.interactionId, i.user, i.queueDetails, i.currentStatus, i.communicationType
Each InteractionSummary in the page carries a viewerCount you can surface before joining.
Use User.viewInteraction to start monitoring - it resolves to an Interaction you can immediately subscribe to:
const interaction = await user.viewInteraction(interactionId, { viewPrivately: true });
// viewPrivately: true = silent monitor (invisible to owner)
// viewPrivately: false = public viewer (owner can see, mute, remove)
Read back the current viewing mode via Interaction.currentViewingState:
const state = interaction.currentViewingState;
if (state) {
// state.isViewingPrivately === true → private (silent) monitor
// state.isViewingPrivately === false → public viewer
}
Viewing an interaction gives you its data; to hear the live call you join the call's audio leg as a viewer with Interaction.joinCall. Drop the audio again with Interaction.leaveCall while remaining a viewer. Gate each on Interaction.canJoinCall / Interaction.canLeaveCall:
if (interaction.canJoinCall()) {
await interaction.joinCall();
}
// Later - stop listening but stay a viewer
if (interaction.canLeaveCall()) {
await interaction.leaveCall();
}
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_JOINED_CALL, (event) => {
markViewerOnAudio(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_LEFT_CALL, (event) => {
markViewerOffAudio(event.payload.viewer);
});
You join the call audio muted by default, so joining is non-intrusive - you are a silent participant on the conference. To speak to everyone on the call, unmute at any time with Interaction.unmute (gate on Interaction.canUnmute); mute yourself again with Interaction.mute. A viewer on the call therefore behaves like a muted conference participant who can unmute at will.
if (interaction.canUnmute()) {
await interaction.unmute(); // now audible to all parties on the call
}
To fully exit as a viewer - not just leave the audio - use Interaction.leave (gate on Interaction.canLeave).
Elite supervisors: the single unmute-to-talk step above is replaced by two finer modes - privately coach the agent (heard only by the agent), or barge in (heard by everyone). An Elite supervisor on a monitored call's audio leg occupies one of three modes, described by EliteSupervisorMode and exposed on Viewer.eliteSupervisorMode:
Mode Meaning Who hears the supervisor LISTEN_ONLYSilent monitoring (mic muted) Nobody COACHWhisper coaching The agent only LISTEN_TALKBarge-in The agent and the customer The supervisor starts in
LISTEN_ONLYafter joining, then transitions into coaching or barge-in and back (see Barge In and Coach the Agent below). This requires the agent to be licensed for Elite Voice ( User.isEliteVoiceEnabled istrue) and the viewer to be an Elite supervisor on the call.
Subscribe to keep the supervisor UI in sync with changes happening on the interaction:
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_ADDED, (event) => {
addViewerBadge(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_REMOVED, (event) => {
removeViewerBadge(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_OWNERSHIP_CHANGED, (event) => {
updateOwnerDisplay(event.payload.newOwner);
});
The interaction owner can mute or remove any public viewer:
const viewers = interaction.getViewers(); // public viewers only
const viewer = viewers[0];
if (viewer.canMute()) {
await viewer.mute();
}
if (viewer.canRemove()) {
await viewer.remove();
}
The owner can also hand the interaction off to a specific viewer, promoting them to owner with Viewer.assignOwner (gate on Viewer.canAssignOwner):
if (viewer.canAssignOwner()) {
await viewer.assignOwner();
}
A viewer with canOwn === true can promote themselves to interaction owner. Gate on Interaction.canClaimOwnership:
if (interaction.canClaimOwnership()) {
await interaction.claimOwnership();
}
Barging moves an Elite supervisor to LISTEN_TALK so both the agent and the customer hear them. Gate on Interaction.canBarge to start and Interaction.canUnbarge to return to silent listening:
if (interaction.canBarge()) {
await interaction.barge();
}
// Step back out of the conversation, returning to silent listening
if (interaction.canUnbarge()) {
await interaction.unbarge();
}
Coaching moves an Elite supervisor to COACH so only the agent hears them - the customer does not. Gate on Interaction.canCoach:
if (interaction.canCoach()) {
await interaction.coach();
}
There is no explicit "uncoach" call. To stop coaching, leave the call audio with Interaction.leaveCall. Both Interaction.barge and Interaction.coach transition from LISTEN_ONLY only, so switching straight from coaching to barge-in is not supported - leave and rejoin the audio to return to LISTEN_ONLY first.
The supervisor's mode can change from any surface, so drive UI state from events rather than assuming the local call succeeded. Subscribe on the interaction:
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_BARGE_STARTED, (event) => {
showBargeActiveIndicator(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_BARGE_ENDED, (event) => {
hideBargeActiveIndicator(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_COACH_STARTED, (event) => {
showCoachActiveIndicator(event.payload.viewer);
});
interaction.subscribe(InteractionEventType.INTERACTION_VIEWER_COACH_ENDED, (event) => {
hideCoachActiveIndicator(event.payload.viewer);
});
The current mode is always readable from Viewer.eliteSupervisorMode on the supervisor's own Viewer entry.
Failures surface as typed AvayaInfinityAgentSdkErrors with stable codes from AvayaInfinityAgentSdkErrorCodes. Errors on the monitoring path span the ASE_2xxx and ASE_4xxx families; barge and coach failures belong to the ASE_4xxx family - match error.code against AvayaInfinityAgentSdkErrorCodes to identify the specific failure.
A barge or coach request that the server does not confirm within 30 seconds rejects rather than hanging, so always gate on the corresponding can*() guard and surface a retry path when the promise rejects.