Recevoir un appel entrant

Les appels entrants sont acheminés vers les clients dont la session est active une fois que l'API Vonage a exécuté le flux de votre webhook de réponse.

Chemin entrant

Avertissement : enregistrez d'abord les écouteurs Enregistrer les auditeurs invités à l'appel avant créer ou rétablir une session, afin de ne manquer aucune invitation.

Traitement des invitations d'appels entrants

client.on('callInvite', async (callId, from, channelType) => {
  console.log(`Received call invite from ${from} on ${channelType}`);

  // To accept the call:
  await client.answer(callId);

  // Or to reject the call:
  // await client.reject(callId);
});

client.on('callInviteCancel', (callId, reason) => {
  if (reason === CancelReason.AnsweredElsewhere) {
    console.log(`Call ${callId} was answered elsewhere`);
  } else if (reason === CancelReason.RejectedElsewhere) {
    console.log(`Call ${callId} was rejected elsewhere`);
  } else if (reason === CancelReason.RemoteCancel) {
    console.log(`Call ${callId} was cancelled by the caller`);
  } else if (reason === CancelReason.RemoteTimeout) {
    console.log(`Call ${callId} timed out`);
  }
});

 // Answer Call
 client.setCallInviteListener {callId, from, channelType ->
     println("Incoming Call from $from by channel $channelType")
     client.answer(callId) {
         it?.let {err ->
             // Handle Error in answering call
             println("Error in answering call: $err")
         }
     }
 }

 // Or reject the call:
 // client.setCallInviteListener { callId, from, channelType ->
 //     client.reject(callId) {
 //         it?.let {err ->
 //             println("Error in rejecting call: $err")
 //         }
 //     }
 // }

 // Invite Cancelled
 client.setCallInviteCancelListener { callId, reason ->
         when (reason) {
             VoiceInviteCancelReason.RemoteCancel -> println("Call Invite for call $callId was cancelled by the caller")
             VoiceInviteCancelReason.AnsweredElsewhere -> println("Call Invite for call $callId was answered on another device")
             VoiceInviteCancelReason.RejectedElsewhere -> println("Call Invite for call $callId was rejected on another device")
             VoiceInviteCancelReason.RemoteTimeout -> println("Call Invite for call $callId timed out")
         }
     }

 // Answer Call
 client.setCallInviteListener((callId, from, channelType) -> {
     System.out.println("Incoming Call from " + from + " by channel " + channelType);
     client.answer(callId, it -> {
         if (it != null) {
             // Handle Error in answering call
             System.out.println("Error in answering call: " + it);
         }
         return null;
     });
     return null;
 });

 // Or reject the call:
 // client.setCallInviteListener((callId, from, channelType) -> {
 //     client.reject(callId, it -> {
 //         if (it != null) {
 //             System.out.println("Error in rejecting call: " + it);
 //         }
 //         return null;
 //     });
 //     return null;
 // });

 // Invite Cancelled
 client.setCallInviteCancelListener((callId, reason) -> {
     switch (reason) {
         case RemoteCancel:
             System.out.println("Call Invite for call " + callId + " was cancelled by the caller");
             break;
         case AnsweredElsewhere:
             System.out.println("Call Invite for call " + callId + " was answered on another device");
             break;
         case RejectedElsewhere:
             System.out.println("Call Invite for call " + callId + " was rejected on another device");
             break;
         case RemoteTimeout:
             System.out.println("Call Invite for call " + callId + " timed out");
             break;
     }
     return null;
 });
 
 class inboundCallExampleDelegate: NSObject, VGVoiceClientDelegate {
     
     
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: VGCallId, from caller: String, with type: VGVoiceChannelType) {
         print(caller)
         // Answer Invite
         client.answer(callId) { error in
             guard error != nil else {
                 print("Failed to answer \(error?.localizedDescription ?? "")")
                 return
             }
         }
         // Or reject the invite:
         // client.reject(callId) { error in
         //     guard error != nil else {
         //         print("Failed to reject \(error?.localizedDescription ?? "")")
         //         return
         //     }
         // }
     }
     func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: VGCallId, with reason: VGVoiceInviteCancelReason) {
         guard callId == "currentCallId" else {
             return
         }
         
         switch reason {
         case .answeredElsewhere:
             print("Call answered elsewhere")
         case .remoteCancel:
             print("Call cancelled by remote")
         case .rejectedElsewhere:
             print("Call rejected elsewhere")
         case .remoteTimeout:
             print("Call timed out")
         default:
             print("reason unknown")
         }
     }
     
     func client(_ client: VGBaseClient, didReceiveSessionErrorWith reason: VGSessionErrorReason) {}
     func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {}
 }
 

 - (void) voiceClient:(VGVoiceClient *)client didReceiveInviteForCall:(VGCallId)callId from:(NSString *)caller withChannelType:(VGVoiceChannelType)type {
     NSLog(@"Caller: %@", caller);
     // Answer Invite
     [client answer:callId callback:^(NSError * _Nullable error) {
         if(error){
             NSLog(@"Failed to answer: %@", error);
         }
     }];
     // Or reject the invite:
     // [client reject:callId callback:^(NSError * _Nullable error) {
     //     if(error){
     //         NSLog(@"Failed to reject: %@", error);
     //     }
     // }];
 }

 - (void) voiceClient:(VGVoiceClient *)client didReceiveInviteCancelForCall:(VGCallId)callId withReason:(VGVoiceInviteCancelReason)reason {
     if(![callId isEqualToString:@"currentCallId"]){
         return;
     }
     switch(reason) {
         case VGVoiceInviteCancelReasonAnsweredElsewhere:
             NSLog(@"Call answered elsewhere");
             break;
         case VGVoiceInviteCancelReasonRemoteCancel:
             NSLog(@"Call canceled by remote");
             break;
         case VGVoiceInviteCancelReasonRejectedElsewhere:
             NSLog(@"Call rejected elsewhere");
             break;
         case VGVoiceInviteCancelReasonRemoteTimeout:
             NSLog(@"Call timed out");
             break;
         case VGVoiceInviteCancelReasonUnknown:
             NSLog(@"Reason Unknown");
             break;
     }
 }

Comportement en cas d'invitation ou d'annulation

Les invitations peuvent être annulées ou expirer avant que l'utilisateur n'ait eu le temps d'agir. Votre application reçoit alors un rappel d'annulation indiquant l'une des raisons suivantes :

Raison Description
RemoteCancel L'appelant a raccroché avant que l'on réponde à l'invitation.
RemoteTimeout L'invitation a expiré sur l'API Vonage sans intervention de l'utilisateur.
AnsweredElsewhere Ce même utilisateur a répondu à l'appel sur un autre appareil ou une autre instance client.
RejectedElsewhere Ce même utilisateur a rejeté l'appel sur un autre appareil ou une autre instance du client.

Conseils en matière d'expérience utilisateur

  • Fermer immédiatement l'interface utilisateur de l'appel entrant dès qu'un motif d'annulation est détecté.
  • N'essayez pas d'appeler answer() ou reject() après réception d'une notification d'annulation, l'invitation n'est plus valable.
  • Dans le cas d'une utilisation sur plusieurs appareils (AnsweredElsewhere / RejectedElsewhere), pensez à afficher un bref message ou un indicateur visuel afin que l'utilisateur comprenne pourquoi la sonnerie s'est arrêtée.

Traitement des erreurs

answer() et reject() L'opération peut échouer si la session a expiré, si l'invitation n'est plus valide (déjà annulée) ou en cas de problème réseau. Veillez à toujours gérer la fonction de rappel d'erreur afin de mettre à jour votre interface utilisateur en conséquence :

try {
  await client.answer(callId);
  showActiveCallUI();
} catch (error) {
  console.error("Answer failed:", error.type, error.message);
  dismissIncomingCallUI();
}

try {
  await client.reject(callId);
} catch (error) {
  console.error("Reject failed:", error.type, error.message);
} finally {
  dismissIncomingCallUI();
}
client.answer(callId) { error ->
    if (error != null) {
        // Answer failed — dismiss call UI and inform the user
        println("Answer failed: ${error.message}")
        // Common causes:
        // - Session expired (re-create session and retry)
        // - Invite already cancelled by caller
        dismissIncomingCallUI()
        return@answer
    }
    // Success — transition to active call UI
    showActiveCallUI()
}

client.reject(callId) { error ->
    if (error != null) {
        // Reject failed — the invite may have already been cancelled
        println("Reject failed: ${error.message}")
    }
    // In both success and failure, dismiss the incoming call UI
    dismissIncomingCallUI()
}
client.answer(callId) { error in
    if let error = error {
        print("Answer failed: \(error.localizedDescription)")
        self.dismissIncomingCallUI()
        return
    }
    self.showActiveCallUI()
}

client.reject(callId) { error in
    if let error = error {
        print("Reject failed: \(error.localizedDescription)")
    }
    self.dismissIncomingCallUI()
}

Si answer() Si l'opération échoue en raison d'une session expirée, recréez la session à l'aide d'un nouveau JWT et réessayez de répondre. Si l'invitation a déjà été annulée (l'appelant a raccroché), aucune récupération n'est possible : fermez l'interface utilisateur et attendez la prochaine invitation.

Pour en savoir plus

  • Référence NCCO — la documentation complète relative à la structure JSON renvoyée par le webhook de réponse