Skip to content

Getting Started

  1. Install the package

    Terminal window
    npm i @capgo/capacitor-incoming-call-kit
  2. Sync native projects

    Terminal window
    npx cap sync
  3. Choose your ring source Decide whether the incoming-call event comes from your backend, an SDK such as Twilio or Stream, or a native push path such as FCM or PushKit.

This plugin only owns native incoming-call presentation. Your app still owns transport, authentication, and the actual media session.

The common production pattern is:

  1. Your backend or calling SDK emits a ring event.
  2. Your app calls showIncomingCall().
  3. The plugin presents native incoming-call UI.
  4. callAccepted tells your app to join the actual room or VoIP session.
  5. callDeclined, callEnded, or callTimedOut tells your app to clean up remote state.
import { IncomingCallKit } from '@capgo/capacitor-incoming-call-kit';
await IncomingCallKit.requestPermissions();
await IncomingCallKit.requestFullScreenIntentPermission();
await IncomingCallKit.addListener('callAccepted', async ({ call }) => {
console.log('Accepted', call.callId, call.extra);
// Start or join your real call session here.
});
await IncomingCallKit.addListener('callDeclined', ({ call }) => {
console.log('Declined', call.callId);
// Tell your backend or SDK that the user declined.
});
await IncomingCallKit.addListener('callTimedOut', ({ call }) => {
console.log('Timed out', call.callId);
// Clear ringing state in your backend or SDK.
});
await IncomingCallKit.showIncomingCall({
callId: 'call-42',
callerName: 'Ada Lovelace',
handle: '+39 555 010 020',
appName: 'Capgo Phone',
hasVideo: true,
timeoutMs: 45_000,
extra: {
roomId: 'room-42',
callerUserId: 'user_ada',
},
android: {
channelId: 'calls',
channelName: 'Incoming Calls',
showFullScreen: true,
},
ios: {
handleType: 'phoneNumber',
},
});
  • callId: stable identifier reused later with endCall()
  • timeoutMs: best-effort unanswered timeout
  • extra: arbitrary JSON echoed back in listener payloads
  • android.channelId and android.channelName: Android notification channel tuning
  • android.showFullScreen: requests the Android full-screen incoming-call activity
  • ios.handleType: choose generic, phoneNumber, or emailAddress for CallKit
const { calls } = await IncomingCallKit.getActiveCalls();
await IncomingCallKit.endCall({
callId: 'call-42',
reason: 'remote-ended',
});
await IncomingCallKit.endAllCalls({
reason: 'session-reset',
});
  • incomingCallDisplayed: native UI was shown successfully
  • callAccepted: user accepted from the native UI
  • callDeclined: user declined before joining
  • callEnded: your app or the platform ended the tracked call
  • callTimedOut: the call stayed unanswered until timeoutMs

Each event carries the normalized call payload and your original extra object.

  • Read the iOS guide before wiring CallKit into a PushKit or APNs flow.
  • Read the Android guide before relying on full-screen intents on Android 14 and later.
  • Web is not supported.