Recover Pending Call Invites
When a user receives an inbound call invite and then refreshes the page (or restarts the app) before answering, the Client SDK can automatically re-deliver the invite — no extra API call required.
This works because the Conversation Service stores pending events for approximately 30 seconds. When you pass the previous session ID to createSession, the server replays those events into the new session and the callInvite event (or onCallInvite delegate) fires automatically.
How It Works
- Persist the session ID returned by
createSession(e.g. insessionStorageon web, orUserDefaults/SharedPreferenceson mobile). - On the next load, retrieve the saved session ID and pass it back to
createSession. - Register the invite listener before calling
createSessionso it is already in place when pending events are dispatched.
Warning: The Conversation Service retains pending events for approximately 30 seconds. If more time has passed since the original invite arrived, it will not be re-delivered.
Edge Cases
- Window expired: If the 30-second retention window has passed,
createSessionstill succeeds — you get a valid session, but the pending invite is not re-delivered. The caller will have timed out or cancelled by then. - Invalid session ID: If the stored session ID is invalid or belongs to a different user, the SDK ignores it and creates a fresh session. No pending events are replayed.
- App killed vs page refresh: On web,
sessionStorageis cleared when the tab closes, so this pattern works for in-tab refreshes. For mobile app restarts, persist the session ID in durable storage (UserDefaults/SharedPreferences).
Example
Copy
client.on('callInvite', (callId, from) => {
console.log(`Received call invite ${callId} from ${from}`);
});
const previousSessionId = store.getSessionId();
try {
const sessionId = previousSessionId
? await client.createSession(jwt, previousSessionId)
: await client.createSession(jwt);
store.saveSessionId(sessionId);
console.log(`Session ready: ${sessionId}`);
} catch (error) {
console.error('Failed to create/reconnect session', error);
}
Copy
client.setCallInviteListener { callId, from, channelType ->
println("Received call invite $callId from $from on $channelType")
}
val previousSessionId = store.getSessionId()
if (previousSessionId != null) {
client.createSession(jwt, previousSessionId) { error, sessionId ->
if (error != null) {
println("Failed to reconnect session: $error")
return@createSession
}
sessionId?.let(store::saveSessionId)
println("Reconnected session: $sessionId")
}
} else {
client.createSession(jwt) { error, sessionId ->
if (error != null) {
println("Failed to create session: $error")
return@createSession
}
sessionId?.let(store::saveSessionId)
println("Session created: $sessionId")
}
}
Copy
client.setCallInviteListener((callId, from, channelType) -> {
System.out.println(
"Received call invite " + callId + " from " + from + " on " + channelType
);
return null;
});
String previousSessionId = store.getSessionId();
if (previousSessionId != null) {
client.createSession(jwt, previousSessionId, (error, sessionId) -> {
if (error != null) {
System.out.println("Failed to reconnect session: " + error);
return null;
}
if (sessionId != null) {
store.saveSessionId(sessionId);
}
System.out.println("Reconnected session: " + sessionId);
return null;
});
} else {
client.createSession(jwt, null, (error, sessionId) -> {
if (error != null) {
System.out.println("Failed to create session: " + error);
return null;
}
if (sessionId != null) {
store.saveSessionId(sessionId);
}
System.out.println("Session created: " + sessionId);
return null;
});
}
Copy
class InviteDelegate: NSObject, VGVoiceClientDelegate {
func voiceClient(_ client: VGVoiceClient, didReceiveInviteForCall callId: VGCallId, from caller: String, with type: VGVoiceChannelType) {
print("Received call invite \(callId) from \(caller)")
}
func voiceClient(_ client: VGVoiceClient, didReceiveHangupForCall callId: VGCallId, withQuality callQuality: VGRTCQuality, reason: VGHangupReason) {}
func voiceClient(_ client: VGVoiceClient, didReceiveInviteCancelForCall callId: VGCallId, with reason: VGVoiceInviteCancelReason) {}
func client(_ client: VGBaseClient, didReceiveSessionErrorWith reason: VGSessionErrorReason) {}
}
let delegate = InviteDelegate()
client.delegate = delegate
if let previousSessionId = store.getSessionId() {
client.createSession(jwt, sessionId: previousSessionId) { error, sessionId in
guard error == nil, let sessionId = sessionId else {
print("Failed to reconnect session: \(error?.localizedDescription ?? "unknown")")
return
}
store.saveSessionId(sessionId)
print("Reconnected session: \(sessionId)")
}
} else {
client.createSession(jwt) { error, sessionId in
guard error == nil, let sessionId = sessionId else {
print("Failed to create session: \(error?.localizedDescription ?? "unknown")")
return
}
store.saveSessionId(sessionId)
print("Session created: \(sessionId)")
}
}
Copy
self.store = store;
client.delegate = self;
NSString *previousSessionId = [store getSessionId];
if (previousSessionId != nil) {
[client createSession:jwt sessionId:previousSessionId callback:^(NSError * _Nullable error, NSString * _Nullable sessionId) {
if (error != nil || sessionId == nil) {
NSLog(@"Failed to reconnect session: %@", error);
return;
}
[store saveSessionId:sessionId];
NSLog(@"Reconnected session: %@", sessionId);
}];
} else {
[client createSession:jwt callback:^(NSError * _Nullable error, NSString * _Nullable sessionId) {
if (error != nil || sessionId == nil) {
NSLog(@"Failed to create session: %@", error);
return;
}
[store saveSessionId:sessionId];
NSLog(@"Session created: %@", sessionId);
}];
}