Skip to content

Getting started ​

expo-callkit-telecom is opinionated about system integration and unopinionated about media. You wire your media library (LiveKit, plain WebRTC, etc.) to the events the module emits.

Tested against

This library has been exercised end-to-end on real devices in the example/ app. See Verified against for the full compatibility matrix (iOS, Android, Expo SDK, React Native, media transport).

Install ​

sh
bun add expo-callkit-telecom

Peer dependencies ​

The module requires two peer dependencies in your app:

sh
bun add @livekit/react-native-webrtc expo-notifications
  • @livekit/react-native-webrtc >= 144 — if your app already uses react-native-webrtc, replace it with this fork; the two cannot be installed side by side.
  • expo-notifications — required on Android, where the module's FCM service builds on it.

Add the config plugin to app.json / app.config.ts. Minimal form:

jsonc
{
  "expo": {
    "plugins": ["expo-callkit-telecom"]
  }
}

With custom sounds and options:

jsonc
{
  "expo": {
    "plugins": [
      [
        "expo-callkit-telecom",
        {
          "sounds": [
            "./assets/sounds/ringtone.wav",
            "./assets/sounds/dialtone.wav"
          ],
          "defaultRingtoneIos": "ringtone.wav",
          "defaultRingtoneAndroid": "ringtone.wav",
          "defaultDialtone": "dialtone.wav",
          "iconTemplateIos": "./assets/callkit-icon.png",
          "incomingCallTimeout": 45,
          "outgoingCallTimeout": 60,
          "fulfillAnswerCallTimeout": 30,
          "includesCallsInRecents": true,
          "microphonePermission": "$(PRODUCT_NAME) needs the microphone to make calls."
        }
      ]
    ]
  }
}

Files in sounds are copied into the iOS bundle and Android raw resources at prebuild time. Set includesCallsInRecents: false to keep calls out of the phone's Recents/call log (iOS — defaults to true; Android self-managed calls are never logged, so no flag is needed there). The full prop type is ExpoCallKitTelecomPluginProps in plugin/src/.

iconTemplateIos sets the app's icon on the iOS CallKit call screen. CallKit renders it as a template — only the alpha channel is used (RGB is ignored) and the system tints it — so provide a ~40×40pt square PNG whose alpha describes the glyph. It's iOS-only; Android brands the incoming-call UI from the launcher icon automatically.

Concepts ​

The TypeScript API is organised into three verbs:

VerbDirectionExamples
RequestApp → SystemstartOutgoingCall, answerCall, endCall, setMuted
ReportApp → System (state)reportIncomingCall, reportOutgoingCallConnected, reportCallEnded
FulfillApp → System (ack)fulfillIncomingCallConnected

Events flow the other way (System → App) via addXxxListener.

Registering for VoIP push ​

ts
import {
  registerVoIPPush,
  useVoIPPushToken,
} from "expo-callkit-telecom";

// Once, early in app lifecycle:
registerVoIPPush();

// In a React component:
function App() {
  const voip = useVoIPPushToken();
  useEffect(() => {
    if (voip) {
      // voip.type is "APNS_VOIP" on iOS, "FCM" on Android.
      sendToBackend(voip.token, voip.type);
    }
  }, [voip]);
}

Example app ​

The repo's example/ contains a runnable Expo app (example/client/) and a zero-dep push-sender script (example/server/). See their READMEs for setup and how to validate VoIP push end-to-end.

Next steps ​