Getting Started
Copy a setup prompt with the install steps and the full markdown guide for this plugin.
Set up this Capacitor plugin in the project.
Use the package manager already used by the project.
Install these package(s): `@capgo/capacitor-incoming-call-kit`
Run the required Capacitor sync/update step after installation.
Read this markdown guide for the full setup steps: https://raw.githubusercontent.com/Cap-go/website/refs/heads/main/apps/docs/src/content/docs/docs/plugins/incoming-call-kit/getting-started.mdx
Use that guide for platform-specific steps, native file edits, permissions, config changes, imports, and usage setup.
If that guide references other docs pages, read them too.
-
Install the package
Terminal window bun add @capgo/capacitor-incoming-call-kit -
Sync native projects
Terminal window bunx cap sync -
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.
How the integration fits together
Section titled “How the integration fits together”This plugin only owns native incoming-call presentation. Your app still owns transport, authentication, and the actual media session.
The common production pattern is:
- Your backend or calling SDK emits a ring event.
- Your app calls
showIncomingCall(). - The plugin presents native incoming-call UI.
callAcceptedtells your app to join the actual room or VoIP session.callDeclined,callEnded, orcallTimedOuttells your app to clean up remote state.
Minimal integration
Section titled “Minimal integration”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', },});Important options
Section titled “Important options”callId: stable identifier reused later withendCall()timeoutMs: best-effort unanswered timeoutextra: arbitrary JSON echoed back in listener payloadsandroid.channelIdandandroid.channelName: Android notification channel tuningandroid.showFullScreen: requests the Android full-screen incoming-call activityios.handleType: choosegeneric,phoneNumber, oremailAddressfor CallKit
Managing active calls
Section titled “Managing active calls”const { calls } = await IncomingCallKit.getActiveCalls();
await IncomingCallKit.endCall({ callId: 'call-42', reason: 'remote-ended',});
await IncomingCallKit.endAllCalls({ reason: 'session-reset',});Event model
Section titled “Event model”incomingCallDisplayed: native UI was shown successfullycallAccepted: user accepted from the native UIcallDeclined: user declined before joiningcallEnded: your app or the platform ended the tracked callcallTimedOut: the call stayed unanswered untiltimeoutMs
Each event carries the normalized call payload and your original extra object.
Platform notes
Section titled “Platform notes”- 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.