Recibir una llamada entrante
Las llamadas entrantes se transfieren a los clientes con sesiones activas una vez que la API de Vonage ejecuta el flujo de tu webhook de respuesta.
Ruta de entrada
Advertencia: Registra primero los oyentes Registrar llamadas, invitar a oyentes antes de crear o restablecer una sesión, para no perder ninguna invitación entrante.
Gestión de las invitaciones de llamadas entrantes
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;
}
}
Comportamiento de «Invitar» y «Cancelar»
Las invitaciones pueden cancelarse o caducar antes de que el usuario realice ninguna acción. Tu aplicación recibe una llamada de retorno de cancelación con uno de los siguientes motivos:
| Motivo | Descripción |
|---|---|
RemoteCancel |
La persona que llamó colgó antes de que se contestara la llamada. |
RemoteTimeout |
La invitación ha caducado en la API de Vonage sin que el usuario haya realizado ninguna acción. |
AnsweredElsewhere |
El mismo usuario respondió a la llamada en otro dispositivo o en otra instancia del cliente. |
RejectedElsewhere |
El mismo usuario rechazó la llamada en otro dispositivo o instancia del cliente. |
Directrices de experiencia de usuario
- Cerrar inmediatamente la interfaz de usuario de la llamada entrante cuando se produzca cualquier motivo de cancelación.
- No intentes llamar
answer()oreject()Tras recibir una notificación de cancelación, la invitación ya no es válida. - En casos en los que se utilizan varios dispositivos (
AnsweredElsewhere/RejectedElsewhere), plantéate mostrar un breve mensaje o un indicador visual para que el usuario comprenda por qué ha dejado de sonar.
Gestión de errores
answer() y reject() Puede fallar si la sesión ha caducado, la invitación ya no es válida (se ha cancelado) o hay un problema de red. Gestiona siempre la llamada de retorno de error para actualizar la interfaz de usuario en consecuencia:
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 falla debido a que la sesión ha caducado, vuelve a crear la sesión con un JWT nuevo e intenta responder de nuevo. Si la invitación ya se ha cancelado (la persona que llama ha colgado), no hay forma de recuperarla: cierra la interfaz de usuario y espera a la siguiente invitación.
Lecturas complementarias
- Referencia OCNC — documentación completa sobre la estructura JSON que devuelve el webhook de tu respuesta