Add mute, hold, resume and keypad controls to an IllyVoIP browser phone

Add SDK mute, local hold, resume and keypad controls, with accurate state labels and clear limits on waiting music, billing and DTMF confirmation.

The IllyVoIP Webphone SDK exposes mute, hold, resume and DTMF methods on a call object. Bind your controls to that specific call and update their labels from the SDK’s state. Do not treat microphone mute, local hold and server-side waiting music as the same operation.

You need an existing SDK 1.4.2 integration with one phone owner and an active call. The snippets below extend that integration; they do not create a token, register another phone or place a call. In your interface, disable controls while an action is pending and when the selected call has ended.

1. Understand what each control changes

Mute disables sending microphone audio for that call; it does not intentionally silence the remote caller. Unmute enables sending again, subject to other controls such as hold. Always show the actual mute state rather than assuming the last click succeeded.

Hold uses local mode by default. In SDK 1.4.2, local hold disables the sender tracks and mutes remote playback. Resume reverses the local hold while respecting the mute state. Local hold does not ask the server to play waiting music, pause billing or transfer the caller into a queue.

The SDK also exposes a signaling hold mode. Use it only when your application deliberately selects and verifies that behavior. This guide’s example uses the default local mode; it does not promise music or identical hold behavior across external SIP systems.

2. Keep one action in flight for the selected call

function callControls(phone, callId, render, notice) {
  let busy = false;
  const current = () => phone.getCall(callId);
  function paint() {
    const call = current();
    render({
      available: !!call && ['active', 'held'].includes(call.state),
      busy,
      muted: call ? call.isMuted() : false,
      held: call ? call.isHeld() : false
    });
  }
  return {
    paint,
    async run(action, digits = '') {
      const call = current();
      if (busy || !call || !['active', 'held'].includes(call.state)) return false;
      if (!['mute', 'unmute', 'hold', 'resume', 'dtmf'].includes(action)) return false;
      if (action === 'dtmf' && (!/^[0-9*#]+$/.test(digits) || call.isHeld())) {
        notice('Resume the call and enter keypad digits.'); return false;
      }
      busy = true; paint();
      try {
        if (action === 'dtmf') await call.sendDtmf(digits);
        else await call[action]();
        return true;
      } catch {
        notice('Control failed. Check the call before trying again.');
        return false;
      } finally {
        busy = false; paint();
      }
    }
  };
}

render and notice are application functions that update the visible UI using plain text. Call paint after relevant SDK call-state, muted, unmuted, held, resumed and ended events. Remove those listeners when the owner is disposed. If the panel disappears during an action, its render/notice adapters must ignore late updates.

The helper deliberately uses one call ID. It is not a multi-call scheduler, and local playback behavior must be assessed separately before designing simultaneous-call interfaces.

3. Add mute and hold buttons with clear labels

Choose Mute or Unmute from isMuted(), and Hold or Resume from isHeld(). A disabled microphone permission is a different problem from a muted call: toggling mute cannot grant browser permission. Show a device/permission message when that is the actual failure.

After holding and resuming, verify the intended mute state remains visible. Do not label a call “waiting music” merely because hold completed. If your product needs server-controlled music, use an appropriate supported calling workflow and its own guide.

4. Send keypad input deliberately

Use sendDtmf for telephone keypad tones, such as navigating a remote IVR. The example permits digits, star and hash and requires a resumed call. This is the application’s conservative input policy, not a claim that all possible SDK inputs have the same validation.

Send only the keys the user chose. Avoid logging keypad content, which may contain PINs or other sensitive information. A resolved send promise does not prove the remote IVR accepted the entry; listen for or inspect the remote system’s response. Do not automatically resend a sequence after a timeout, because the first one may already have been processed.

5. Verify controls and failure handling

Test muted → held → resumed, unmuted → held → resumed, a remote hangup during an action, duplicate clicks, a missing call ID and an invalid keypad string. Confirm unavailable controls are disabled and the UI follows the final call state.

For an authorized live check, use a controlled destination to confirm microphone mute, local playback hold/resume and keypad reception. Check the supported browsers and audio devices individually. Local fixtures prove control sequencing, not audible media or compatibility with every IVR.

Common questions

Does hold stop charging? No such guarantee follows from local hold; the call remains established. Why is the caller silent while held? Default local hold also mutes remote playback. Why did keypad input fail? Check that the intended call is active and resumed, then inspect the remote IVR and actual call evidence rather than repeating digits blindly.

Related guides and reference

Reviewed 10 October 2026 against the current IllyVoIP reference and implementation. SDK examples target version 1.4.2 where applicable. Local example checks do not certify a completed live call, routing change or third-party integration.