Transfer a call from an application using the Webphone SDK

Request an IllyVoIP SDK call transfer, prevent duplicate submissions and distinguish transfer signaling from the destination’s actual answer.

Use the Webphone SDK transfer method to request transfer of an existing browser call to a validated destination. Keep the original call visible while the request is being handled, and use subsequent call events to update the interface. A resolved transfer promise is not proof that a person answered at the destination.

This guide covers an application built with SDK 1.4.2. For the built-in Portal webphone buttons, use the separate Portal transfer guide. The server-side Voice API has a different transfer workflow and identifiers; its transfer-status and transfer-cancel operations are not SDK call-object methods.

1. Prepare an authorized destination

Start with an active call owned by your existing phone instance. Resolve the destination from an approved directory or a user entry validated by your backend/application policy. Do not accept arbitrary untrusted text as an unrestricted transfer target.

For a telephone destination, use the validated full international number expected by your workflow. For an internal target, follow the current account and SDK routing requirements; do not guess an extension or reuse a database user ID. External destinations can incur calling charges and remain subject to account and destination controls.

Tell the caller what you intend to do before transferring. This method is not, by itself, a consultation or warm-transfer workflow. If you need to speak privately to another party first, confirm a separately supported implementation rather than promising that behavior from one transfer call.

2. Bind the action to the existing call

The call-object form is await call.transfer(target). The phone-object form is await phone.transfer(target, callId). Both require the intended current call. Do not pass a Voice API control-session ID or choose whichever call happens to be returned by a generic current-call lookup.

Store a transfer-pending state in your application and disable repeat submissions. If the network result becomes uncertain, keep that uncertainty visible until the actual call state is understood.

3. Submit once and wait for call evidence

function transferControl(phone, callId, report) {
  let phase = 'ready';
  return {
    async request(approvedTarget) {
      const call = phone.getCall(callId);
      if (phase !== 'ready' || !call || call.state !== 'active') return false;
      // Resolve and authorize this target before calling this helper.
      if (typeof approvedTarget !== 'string' || !approvedTarget.trim()) return false;
      phase = 'pending'; report(phase);
      try {
        await call.transfer(approvedTarget.trim());
        phase = 'submitted'; report(phase);
        return true;
      } catch {
        phase = 'needs-review'; report(phase);
        return false;
      }
    }
  };
}

report is your plain-text UI adapter. In a production application, keep this operation state in the existing phone owner so a component remount cannot reset it to ready while a transfer is pending. Do not automatically reset needs-review and retry. Check the original call and the intended destination first.

The helper intentionally does not hang up after requesting transfer. An immediate hangup can interfere with the workflow before its outcome is known. It also does not create a second outgoing call as a fallback.

4. Interpret events without overstating completion

Subscribe to call:transferred for the matching call ID and continue listening to call:state and call:ended. The transferred event indicates the SDK transfer operation completed; do not translate it into “the destination answered” without appropriate evidence.

If the original call remains active, keep its controls available according to your application’s transfer state. If it ends, clear its interface. A remote hangup can occur while the request is pending, so late promise handlers must not recreate an ended call panel.

This SDK workflow does not expose the server Voice API’s transfer cancellation contract. Do not show a Cancel transfer button wired to an unrelated API endpoint or imply a guaranteed recovery operation that the integration does not implement.

5. Handle a failed or uncertain request

For an invalid target, correct the application’s destination validation. For call_not_found, confirm the original call still exists in this phone instance. For transfer_failed or a lost connection, inspect the latest call state and any available authorized call evidence before taking another action.

Keep a sanitized event timeline with the SDK version, timestamp and local application operation reference. Avoid logging the destination, SIP credentials or full signaling unless an authorized diagnostic workflow specifically needs them. If the result cannot be established, ask the user to review the call rather than reporting either success or failure as certain.

Acceptance checklist

Use fixtures for duplicate clicks, rejection, original-call termination during transfer and late completion after UI disposal. Then, in an authorized live check, test the intended internal or external destination, destination busy/no answer and caller hangup. Confirm actual call behavior and charges separately from the SDK promise result.

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.