{"id":31433413,"url":"https://github.com/cap-go/capacitor-twilio-voice","last_synced_at":"2026-04-16T22:01:15.582Z","repository":{"id":308732024,"uuid":"1029043384","full_name":"Cap-go/capacitor-twilio-voice","owner":"Cap-go","description":"Capacitor plugin to create and answer calls with twill voice SDK","archived":false,"fork":false,"pushed_at":"2026-04-15T18:29:36.000Z","size":935,"stargazers_count":6,"open_issues_count":6,"forks_count":4,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T18:35:59.319Z","etag":null,"topics":["capacitor","capgo","ionic","sdk","twilio"],"latest_commit_sha":null,"homepage":"https://capgo.app","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cap-go.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"Cap-go","patreon":null,"open_collective":"capgo","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2025-07-30T12:43:47.000Z","updated_at":"2026-04-15T18:29:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"79db768e-157b-4bf3-8908-bcb3be6e8719","html_url":"https://github.com/Cap-go/capacitor-twilio-voice","commit_stats":null,"previous_names":["cap-go/capacitor-twilio-voice"],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/Cap-go/capacitor-twilio-voice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cap-go%2Fcapacitor-twilio-voice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cap-go%2Fcapacitor-twilio-voice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cap-go%2Fcapacitor-twilio-voice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cap-go%2Fcapacitor-twilio-voice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cap-go","download_url":"https://codeload.github.com/Cap-go/capacitor-twilio-voice/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cap-go%2Fcapacitor-twilio-voice/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31905895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-16T18:22:33.417Z","status":"ssl_error","status_checked_at":"2026-04-16T18:21:47.142Z","response_time":69,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["capacitor","capgo","ionic","sdk","twilio"],"created_at":"2025-09-30T11:13:51.436Z","updated_at":"2026-04-16T22:01:15.568Z","avatar_url":"https://github.com/Cap-go.png","language":"Java","funding_links":["https://github.com/sponsors/Cap-go","https://opencollective.com/capgo"],"categories":[],"sub_categories":[],"readme":"## Capacitor Twilio Voice Plugin\n \u003ca href=\"https://capgo.app/\"\u003e\u003cimg src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/\u003e\u003c/a\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ch2\u003e\u003ca href=\"https://capgo.app/?ref=plugin_twilio_voice\"\u003e ➡️ Get Instant updates for your App with Capgo\u003c/a\u003e\u003c/h2\u003e\n  \u003ch2\u003e\u003ca href=\"https://capgo.app/consulting/?ref=plugin_twilio_voice\"\u003e Missing a feature? We’ll build the plugin for you 💪\u003c/a\u003e\u003c/h2\u003e\n\u003c/div\u003e\n\nA Capacitor plugin for integrating Twilio Voice calling functionality into iOS and Android applications.\n\n## Documentation\n\nThe most complete doc is available here: https://capgo.app/docs/plugins/twilio-voice/\n\n## Compatibility\n\n| Plugin version | Capacitor compatibility | Maintained |\n| -------------- | ----------------------- | ---------- |\n| v8.\\*.\\*       | v8.\\*.\\*                | ✅          |\n| v7.\\*.\\*       | v7.\\*.\\*                | On demand   |\n| v6.\\*.\\*       | v6.\\*.\\*                | ❌          |\n| v5.\\*.\\*       | v5.\\*.\\*                | ❌          |\n\n\u003e **Note:** The major version of this plugin follows the major version of Capacitor. Use the version that matches your Capacitor installation (e.g., plugin v8 for Capacitor 8). Only the latest major version is actively maintained.\n\n## Installation\n\n```bash\nnpm install @capgo/capacitor-twilio-voice\nnpx cap sync\n```\n\n## iOS Setup\n\n### 1. Install the plugin\n\n```bash\nnpm install @capgo/capacitor-twilio-voice\nnpx cap sync\n```\n\n### 2. Setup `CustomCapacitorViewController.swift`\n\nCopy the code from `example-app/ios/App/App/CustomCapacitorViewController.swift` to your `ios/App/App/CustomCapacitorViewController.swift` file.\n\n3. Modify `AppDelegate.swift`\n\nAdd the following to `AppDelegate.swift`:\n\n```diff\nimport UIKit\nimport Capacitor\n+ import PushKit\n+ import CapgoCapacitorTwilioVoice\n\n@UIApplicationMain\n- class AppDelegate: UIResponder, UIApplicationDelegate {\n+ class AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {\n\n    var window: UIWindow?\n+     var pushKitEventDelegate: PushKitEventDelegate?\n+     var voipRegistry = PKPushRegistry.init(queue: DispatchQueue.main)\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n        // Override point for customization after application launch.\n+         \n+         /*\n+          * Your app must initialize PKPushRegistry with PushKit push type VoIP at the launch time. As mentioned in the\n+          * [PushKit guidelines](https://developer.apple.com/documentation/pushkit/supporting_pushkit_notifications_in_your_app),\n+          * the system can't deliver push notifications to your app until you create a PKPushRegistry object for\n+          * VoIP push type and set the delegate. If your app delays the initialization of PKPushRegistry, your app may receive outdated\n+          * PushKit push notifications, and if your app decides not to report the received outdated push notifications to CallKit, iOS may\n+          * terminate your app.\n+          */\n+         initializePushKit()\n+         \n+         guard let viewController = UIApplication.shared.windows.first?.rootViewController as? CustomCapacitorViewController else {\n+             fatalError(\"Root view controller is not Capacitor view controller\")\n+         }\n+         \n+         viewController.passPushKitEventDelegate = { delegate in\n+             self.pushKitEventDelegate = delegate\n+         }\n+         \n+         // self.pushKitEventDelegate = viewController\n+         \n        return true\n    }\n+     \n+     func initializePushKit() {\n+         voipRegistry.delegate = self\n+         voipRegistry.desiredPushTypes = Set([PKPushType.voIP])\n+     }\n\n    // ... (existing lifecycle methods remain the same) ...\n\n+     // MARK: PKPushRegistryDelegate\n+     func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {\n+         NSLog(\"pushRegistry:didUpdatePushCredentials:forType:\")\n+         \n+         if let delegate = self.pushKitEventDelegate {\n+             delegate.credentialsUpdated(credentials: credentials)\n+         }\n+     }\n+     \n+     func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {\n+         NSLog(\"pushRegistry:didInvalidatePushTokenForType:\")\n+         \n+         if let delegate = self.pushKitEventDelegate {\n+             delegate.credentialsInvalidated()\n+         }\n+     }\n+ \n+     /**\n+      * Try using the `pushRegistry:didReceiveIncomingPushWithPayload:forType:withCompletionHandler:` method if\n+      * your application is targeting iOS 11. According to the docs, this delegate method is deprecated by Apple.\n+      */\n+     func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType) {\n+         NSLog(\"pushRegistry:didReceiveIncomingPushWithPayload:forType:\")\n+         \n+         if let delegate = self.pushKitEventDelegate {\n+             delegate.incomingPushReceived(payload: payload)\n+         }\n+     }\n+ \n+     /**\n+      * This delegate method is available on iOS 11 and above. Call the completion handler once the\n+      * notification payload is passed to the `TwilioVoiceSDK.handleNotification()` method.\n+      */\n+     func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -\u003e Void) {\n+         NSLog(\"pushRegistry:didReceiveIncomingPushWithPayload:forType:completion:\")\n+ \n+         if let delegate = self.pushKitEventDelegate {\n+             delegate.incomingPushReceived(payload: payload, completion: completion)\n+         }\n+         \n+         if let version = Float(UIDevice.current.systemVersion), version \u003e= 13.0 {\n+             /**\n+              * The Voice SDK processes the call notification and returns the call invite synchronously. Report the incoming call to\n+              * CallKit and fulfill the completion before exiting this callback method.\n+              */\n+             completion()\n+         }\n+     }\n\n}\n```\n\n### 3. Edit `Main.storyboard`\n\nChange the view controller to `CustomCapacitorViewController` in `Main.storyboard`.\n\n```diff\n\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cdocument type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"14111\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" colorMatched=\"YES\" initialViewController=\"BYZ-38-t0r\"\u003e\n    \u003cdevice id=\"retina4_7\" orientation=\"portrait\"\u003e\n        \u003cadaptation id=\"fullscreen\"/\u003e\n    \u003c/device\u003e\n    \u003cdependencies\u003e\n        \u003cdeployment identifier=\"iOS\"/\u003e\n        \u003cplugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"14088\"/\u003e\n    \u003c/dependencies\u003e\n    \u003cscenes\u003e\n        \u003c!--Bridge View Controller--\u003e\n        \u003cscene sceneID=\"the-QT-ifu\"\u003e\n            \u003cobjects\u003e\n-                \u003cviewController id=\"BYZ-38-t0r\" customClass=\"CAPBridgeViewController\" customModule=\"Capacitor\" sceneMemberID=\"viewController\"/\u003e\n+                \u003cviewController id=\"BYZ-38-t0r\" customClass=\"CustomCapacitorViewController\" customModule=\"App\" customModuleProvider=\"target\" sceneMemberID=\"viewController\"/\u003e\n                \u003cplaceholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/\u003e\n            \u003c/objects\u003e\n        \u003c/scene\u003e\n    \u003c/scenes\u003e\n\u003c/document\u003e\n```\n\nLook in the example app for more details.\n\n\n### 4. Setup `Info.plist`\n\nAdd the following to `ios/App/App/Info.plist`:\n\n```xml\n\u003ckey\u003eNSMicrophoneUsageDescription\u003c/key\u003e\n\u003cstring\u003eThis app uses the microphone for voice calls\u003c/string\u003e\n\n\u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n\u003carray\u003e\n    \u003cstring\u003evoip\u003c/string\u003e\n    \u003cstring\u003eaudio\u003c/string\u003e\n\u003c/array\u003e\n```\n\n5. Make sure you have the following capabilities enabled in Xcode:\n\n- Push Notifications\n- Background Modes\n\n6. Generate the certificate for Push Notifications\n\nIn order to generate the certificate for Push Notifications, you need to follow these steps:\n\n1. Generate the signing certificate for your app.\n```bash\nopenssl genrsa -out ALDsigning.key 2048\n```\n\n2. Generate the signing request.\n```bash\nopenssl req -new -key ALDsigning.key -out csr3072ALDSigning.certSigningRequest -subj \"/emailAddress=example@example.com, CN=Example Name, C=IE\"\n```\n\nThen, please upload it to your Apple Developer account [here](https://developer.apple.com/account/resources/certificates/add). Search for `Create a new VoIP Services Certificate`\n\n3. Download the file provided by Apple.\n\n4. Extract the .p12 file from the downloaded file.\n\n```bash\nopenssl pkcs12 -export -out voip_services.p12 -inkey ALDsigning.key -in voip_services.cer\n```\n\n5. Export the `cert.pem` and `key.pem` files from the .p12 file.\n\n```bash\nopenssl pkcs12 -in voip_services.p12 -nokeys -out cert.pem -nodes\nopenssl pkcs12 -in voip_services.p12 -nocerts -out key.pem -nodes\n```\n\n6. Upload the `cert.pem` and `key.pem` files twilio.\n\n```bash\nnpx twilio api:chat:v2:credentials:create --type=apn --sandbox --friendly-name=\"voice-push-credential (sandbox)\" --certificate=\"$(cat /Users/your_username/Documents/twilio-voip/cert.pem)\" --private-key=\"$(cat /Users/your_username/Documents/twilio-voip/key.pem)\"\n```\n\n## Android Setup\n\n### 1. Firebase Setup\nAdd Firebase to your Android project:\n\n1. Add `google-services.json` to `android/app/`\n\n2. Add `CapacitorApplication.java` to `android/app/src/main/java/YOUR_APP_PACKAGE/CapacitorApplication.java`\nCopy the content of `example-app/android/app/src/main/java/com/example/plugin/CapacitorApplication.java` to your `android/app/src/main/java/YOUR_APP_PACKAGE/CapacitorApplication.java` file.\n\n3. Import `androidx.webkit:webkit` in `build.gradle` (module :app)\n\n```diff\ndependencies {\n+     implementation 'androidx.webkit:$androidxWebkitVersion'\n}\n```\n\n4. Modify `MainActivity.java`\n\nAdd the following to `MainActivity.java`:\n\n```diff\npackage com.example.plugin;\n\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.webkit.ValueCallback;\n\nimport androidx.webkit.WebViewCompat;\nimport androidx.webkit.WebViewFeature;\n\nimport com.getcapacitor.BridgeActivity;\nimport com.getcapacitor.JSExport;\nimport com.getcapacitor.Logger;\nimport com.getcapacitor.PluginHandle;\n\n+ import java.util.ArrayList;\n+ import java.util.Collections;\n\nimport ee.forgr.capacitor_twilio_voice.CapacitorTwilioVoicePlugin;\n\npublic class MainActivity extends BridgeActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n+         \n+         this.bridge.registerPluginInstance(CapacitorTwilioVoicePlugin.getInstance());\n+         ArrayList\u003cPluginHandle\u003e pluginHandles = new ArrayList\u003c\u003e();\n+         pluginHandles.add(this.bridge.getPlugin(\"CapacitorTwilioVoice\"));\n+         String pluginJS = JSExport.getPluginJS(pluginHandles);\n+         if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {\n+             String allowedOrigin = Uri.parse(this.bridge.getAppUrl()).buildUpon().path(null).fragment(null).clearQuery().build().toString();\n+             try {\n+                 WebViewCompat.addDocumentStartJavaScript(this.getBridge().getWebView(), pluginJS, Collections.singleton(allowedOrigin));\n+             } catch (IllegalArgumentException ex) {\n+                 Logger.warn(\"Invalid url, using fallback\");\n+             }\n+         }\n    }\n}\n```\n\n5. Register the plugin in JS\n\n```diff\n+ import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';\n+ import { Capacitor } from '@capacitor/core';\n\n+ Capacitor.registerPlugin('CapacitorTwilioVoice');\n```\n\n6. Add `android:name=\"CapacitorApplication\"` to the `application` tag in `android/app/src/main/AndroidManifest.xml`\n\n7. Add the following to `android/app/src/main/AndroidManifest.xml`:\n\n```xml\n    \u003cuses-permission android:name=\"android.permission.WAKE_LOCK\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.TURN_SCREEN_ON\" /\u003e\n    \u003cuses-permission android:name=\"android.permission.SHOW_WHEN_LOCKED\" /\u003e\n```\n\nKeep in mind, this will make it so that you app can be accessed when the screen is locked.\n\n## Twilio Setup\n\n - [iOS Setup](https://www.twilio.com/docs/voice/sdks/ios/get-started)\n - [Android Setup](https://www.twilio.com/docs/voice/sdks/android/get-started)\n\n## Caller Name Display (CapacitorTwilioCallerName)\n\nBy default, incoming calls display the caller's phone number or client ID. You can customize this by passing a `CapacitorTwilioCallerName` parameter from your TwiML backend to display a friendly name instead.\n\n### Backend Setup\n\nWhen generating your TwiML response for the `\u003cClient\u003e` dial, add the `CapacitorTwilioCallerName` parameter:\n\n```java\n// Java example (see exemple-backend for full implementation)\nParameter callerNameParam = new Parameter.Builder()\n    .name(\"CapacitorTwilioCallerName\")\n    .value(\"John Doe\")\n    .build();\n\nClient client = new Client.Builder(identity)\n    .parameter(callerNameParam)\n    .build();\n\nDial dial = new Dial.Builder()\n    .client(client)\n    .build();\n```\n\n```javascript\n// Node.js example\nconst VoiceResponse = require('twilio').twiml.VoiceResponse;\n\nconst response = new VoiceResponse();\nconst dial = response.dial();\ndial.client({\n  name: 'CapacitorTwilioCallerName',\n  value: 'John Doe'\n}, identity);\n```\n\n### How It Works\n\n1. When your backend receives an incoming call, it generates TwiML to route the call\n2. Include the `CapacitorTwilioCallerName` parameter with the caller's display name\n3. The plugin automatically extracts this parameter and uses it for:\n   - iOS CallKit incoming call screen\n   - Android incoming call notification\n   - The `from` field in `callInviteReceived` events\n   - The `pendingInvites` array in `getCallStatus()`\n\nIf `CapacitorTwilioCallerName` is not provided, the plugin falls back to the caller's phone number or client ID.\n\n## Usage\n\n### Authentication\n\n#### `login(options: { accessToken: string })`\nAuthenticates with Twilio using a JWT access token:\n- Validates token expiration automatically\n- Stores token securely for app restarts  \n- Registers for VoIP push notifications\n- **Note**: The plugin will reject expired tokens\n\n#### `logout()`\nLogs out the current user and cleans up all session data:\n- Unregisters from VoIP push notifications\n- Clears stored access tokens\n- Ends any active calls\n- Resets all call state\n\n#### `isLoggedIn()`\nChecks if user is currently logged in with a valid (non-expired) token.\nReturns: `{ isLoggedIn: boolean, hasValidToken: boolean, identity?: string }`\n\nThe `identity` field contains the user identity extracted from the JWT token if logged in.\n\n#### `makeCall(options: { to: string })`\nInitiates an outgoing call. Requires prior authentication via `login()`.\n\n#### `acceptCall(options: { callSid: string })`\nAccepts an incoming call.\n\n#### `rejectCall(options: { callSid: string })`\nRejects an incoming call.\n\n#### `endCall(options?: { callSid?: string })`\nEnds the active call or a specific call.\n\n#### `muteCall(options: { muted: boolean, callSid?: string })`\nMutes or unmutes the microphone.\n\n#### `setSpeaker(options: { enabled: boolean })`\nEnables or disables the speaker. On Android, uses Twilio AudioSwitch to manage audio routing between earpiece, speaker, and connected devices (headsets, Bluetooth, etc.).\n\n#### `getCallStatus()`\nGets the current call status.\n\n#### `checkMicrophonePermission()`\nChecks if microphone permission is granted.\n\n#### `requestMicrophonePermission()`\nRequests microphone permission from the user.\n\n### Event Listeners\n\n```typescript\nimport { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';\n\n// Registration events\nCapacitorTwilioVoice.addListener('registrationSuccess', (data) =\u003e {\n  console.log('Successfully registered:', data);\n});\n\nCapacitorTwilioVoice.addListener('registrationFailure', (data) =\u003e {\n  console.error('Registration failed:', data);\n});\n\n// Call events\nCapacitorTwilioVoice.addListener('callInviteReceived', (data) =\u003e {\n  console.log('Incoming call from:', data.from);\n});\n\nCapacitorTwilioVoice.addListener('callConnected', (data) =\u003e {\n  console.log('Call connected:', data);\n});\n\nCapacitorTwilioVoice.addListener('callDisconnected', (data) =\u003e {\n  console.log('Call ended:', data);\n});\n\nCapacitorTwilioVoice.addListener('callRinging', (data) =\u003e {\n  console.log('Call is ringing:', data);\n});\n\nCapacitorTwilioVoice.addListener('callReconnecting', (data) =\u003e {\n  console.log('Call reconnecting:', data);\n});\n\nCapacitorTwilioVoice.addListener('callReconnected', (data) =\u003e {\n  console.log('Call reconnected:', data);\n});\n\nCapacitorTwilioVoice.addListener('callQualityWarningsChanged', (data) =\u003e {\n  console.log('Quality warnings:', data);\n});\n```\n\n## JWT Token Management\n\n### Token Format\nThe plugin expects Twilio access tokens in JWT format with this structure:\n```json\n{\n  \"iss\": \"your-account-sid\",\n  \"exp\": 1234567890,\n  \"grants\": {\n    \"voice\": {\n      \"outgoing\": {\n        \"application_sid\": \"your-app-sid\"\n      },\n      \"push_credential_sid\": \"your-push-credential-sid\"\n    },\n    \"identity\": \"user-identity\"\n  }\n}\n```\n\n### Token Validation\n- Tokens are automatically validated for expiration\n- Invalid or expired tokens will be rejected\n- Use `isLoggedIn()` to check token status\n\n### Backend Integration\nFetch access tokens from your backend server:\n```typescript\nasync function fetchAccessToken(identity: string): Promise\u003cstring\u003e {\n  const response = await fetch(`/accessToken?identity=${identity}`);\n  return response.text();\n}\n```\n\n## Testing Requirements\n\n### iOS Simulator Limitations\n- VoIP push notifications don't work in the iOS Simulator\n- Use a physical iOS device for testing incoming calls\n- Outgoing calls work in both Simulator and device\n\n### Android Emulator\n- Requires Google Play Services\n- Firebase messaging works in Android Emulator with Google APIs\n\n## Error Handling\n\nThe plugin provides detailed error information:\n```typescript\ntry {\n  await CapacitorTwilioVoice.makeCall({ to: '+1234567890' });\n} catch (error) {\n  console.error('Call failed:', error);\n}\n```\n\nCommon error scenarios:\n- **Invalid token**: Check token format and expiration\n- **No microphone permission**: Call `requestMicrophonePermission()`\n- **Network issues**: Verify internet connectivity\n- **Invalid phone number**: Use E.164 format (+1234567890)\n\n## Security Notes\n\n- Access tokens are stored in secure device storage\n- Tokens are automatically validated before use\n- No sensitive data is logged in production builds\n- Always use HTTPS for token fetching from your backend\n\n## Platform Support\n\n| Platform | Support | Notes |\n|----------|---------|-------|\n| iOS      | ✅      | Requires iOS 13.0+ |\n| Android  | ✅      | Requires API level 23+ |\n| Web      | ❌      | Not supported |\n\n## API\n\n\u003cdocgen-index\u003e\n\n* [`login(...)`](#login)\n* [`logout()`](#logout)\n* [`isLoggedIn()`](#isloggedin)\n* [`makeCall(...)`](#makecall)\n* [`acceptCall(...)`](#acceptcall)\n* [`rejectCall(...)`](#rejectcall)\n* [`endCall(...)`](#endcall)\n* [`muteCall(...)`](#mutecall)\n* [`setSpeaker(...)`](#setspeaker)\n* [`getCallStatus()`](#getcallstatus)\n* [`checkMicrophonePermission()`](#checkmicrophonepermission)\n* [`requestMicrophonePermission()`](#requestmicrophonepermission)\n* [`addListener('callInviteReceived', ...)`](#addlistenercallinvitereceived-)\n* [`addListener('callConnected', ...)`](#addlistenercallconnected-)\n* [`addListener('callInviteCancelled', ...)`](#addlistenercallinvitecancelled-)\n* [`addListener('outgoingCallInitiated', ...)`](#addlisteneroutgoingcallinitiated-)\n* [`addListener('outgoingCallFailed', ...)`](#addlisteneroutgoingcallfailed-)\n* [`addListener('callDisconnected', ...)`](#addlistenercalldisconnected-)\n* [`addListener('callRinging', ...)`](#addlistenercallringing-)\n* [`addListener('callReconnecting', ...)`](#addlistenercallreconnecting-)\n* [`addListener('callReconnected', ...)`](#addlistenercallreconnected-)\n* [`addListener('callQualityWarningsChanged', ...)`](#addlistenercallqualitywarningschanged-)\n* [`addListener('registrationSuccess', ...)`](#addlistenerregistrationsuccess-)\n* [`addListener('registrationFailure', ...)`](#addlistenerregistrationfailure-)\n* [`removeAllListeners()`](#removealllisteners)\n* [`getPluginVersion()`](#getpluginversion)\n* [Interfaces](#interfaces)\n* [Type Aliases](#type-aliases)\n\n\u003c/docgen-index\u003e\n\n\u003cdocgen-api\u003e\n\u003c!--Update the source file JSDoc comments and rerun docgen to update the docs below--\u003e\n\n### login(...)\n\n```typescript\nlogin(options: { accessToken: string; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nAuthenticate the user with Twilio Voice using an access token.\n\nThe access token should be generated on your backend server using your Twilio credentials.\nThis token is required to make and receive calls through Twilio Voice.\n\n| Param         | Type                                  | Description            |\n| ------------- | ------------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ accessToken: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### logout()\n\n```typescript\nlogout() =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nLog out the current user and unregister from Twilio Voice.\n\nThis will disconnect any active calls and stop the device from receiving\nnew incoming call notifications.\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### isLoggedIn()\n\n```typescript\nisLoggedIn() =\u003e Promise\u003c{ isLoggedIn: boolean; hasValidToken: boolean; identity?: string; }\u003e\n```\n\nCheck if the user is currently logged in and has a valid access token.\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ isLoggedIn: boolean; hasValidToken: boolean; identity?: string; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### makeCall(...)\n\n```typescript\nmakeCall(options: { to: string; displayName?: string; callerId?: string; }) =\u003e Promise\u003c{ success: boolean; callSid?: string; }\u003e\n```\n\nInitiate an outgoing call to a phone number or client.\n\nThe user must be logged in before making a call. The call will be routed\nthrough your Twilio backend configuration.\n\n| Param         | Type                                                                  | Description            |\n| ------------- | --------------------------------------------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ to: string; displayName?: string; callerId?: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; callSid?: string; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### acceptCall(...)\n\n```typescript\nacceptCall(options: { callSid: string; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nAccept an incoming call.\n\nThis should be called in response to a 'callInviteReceived' event.\n\n| Param         | Type                              | Description            |\n| ------------- | --------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ callSid: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### rejectCall(...)\n\n```typescript\nrejectCall(options: { callSid: string; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nReject an incoming call.\n\nThis should be called in response to a 'callInviteReceived' event.\nThe caller will hear a busy signal or be directed to voicemail.\n\n| Param         | Type                              | Description            |\n| ------------- | --------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ callSid: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### endCall(...)\n\n```typescript\nendCall(options: { callSid?: string; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nEnd an active call.\n\nIf callSid is not provided, this will end the currently active call.\n\n| Param         | Type                               | Description            |\n| ------------- | ---------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ callSid?: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### muteCall(...)\n\n```typescript\nmuteCall(options: { muted: boolean; callSid?: string; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nMute or unmute the microphone during an active call.\n\nWhen muted, the other party will not hear audio from your microphone.\n\n| Param         | Type                                               | Description            |\n| ------------- | -------------------------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ muted: boolean; callSid?: string; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### setSpeaker(...)\n\n```typescript\nsetSpeaker(options: { enabled: boolean; }) =\u003e Promise\u003c{ success: boolean; }\u003e\n```\n\nEnable or disable speakerphone mode.\n\nWhen enabled, audio will be routed through the device's speaker instead of the earpiece.\n\n| Param         | Type                               | Description            |\n| ------------- | ---------------------------------- | ---------------------- |\n| **`options`** | \u003ccode\u003e{ enabled: boolean; }\u003c/code\u003e | - Configuration object |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ success: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### getCallStatus()\n\n```typescript\ngetCallStatus() =\u003e Promise\u003c{ hasActiveCall: boolean; isOnHold: boolean; isMuted: boolean; callSid?: string; callState?: string; pendingInvites: CallInvite[]; activeCallsCount: number; }\u003e\n```\n\nGet the current status of the active call.\n\nThis provides real-time information about the call state, mute status,\nhold status, and call identifiers.\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ hasActiveCall: boolean; isOnHold: boolean; isMuted: boolean; callSid?: string; callState?: string; pendingInvites: CallInvite[]; activeCallsCount: number; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### checkMicrophonePermission()\n\n```typescript\ncheckMicrophonePermission() =\u003e Promise\u003c{ granted: boolean; }\u003e\n```\n\nCheck if microphone permission has been granted.\n\nThis does not request permission, only checks the current permission status.\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ granted: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### requestMicrophonePermission()\n\n```typescript\nrequestMicrophonePermission() =\u003e Promise\u003c{ granted: boolean; }\u003e\n```\n\nRequest microphone permission from the user.\n\nOn iOS and Android, this will show the system permission dialog if permission\nhas not been granted yet. If permission was previously denied, the user may need\nto grant it in system settings.\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ granted: boolean; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callInviteReceived', ...)\n\n```typescript\naddListener(eventName: 'callInviteReceived', listenerFunc: (data: CallInvite) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for incoming call invitations.\n\nThis event is fired when another user or phone number is calling you.\nYou should call acceptCall() or rejectCall() in response.\n\n| Param              | Type                                                                 | Description                             |\n| ------------------ | -------------------------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callInviteReceived'\u003c/code\u003e                                    | - The event name ('callInviteReceived') |\n| **`listenerFunc`** | \u003ccode\u003e(data: \u003ca href=\"#callinvite\"\u003eCallInvite\u003c/a\u003e) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callConnected', ...)\n\n```typescript\naddListener(eventName: 'callConnected', listenerFunc: (data: { callSid: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call connected events.\n\nThis event is fired when a call (incoming or outgoing) has been successfully\nconnected and audio can be heard.\n\n| Param              | Type                                                 | Description                             |\n| ------------------ | ---------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callConnected'\u003c/code\u003e                         | - The event name ('callConnected')      |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callInviteCancelled', ...)\n\n```typescript\naddListener(eventName: 'callInviteCancelled', listenerFunc: (data: { callSid: string; reason: 'user_declined' | 'remote_cancelled'; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call invite cancellation events.\n\nThis event is fired when an incoming call invitation is cancelled before being\nanswered, either by the caller hanging up or by the user declining.\n\n| Param              | Type                                                                                                | Description                              |\n| ------------------ | --------------------------------------------------------------------------------------------------- | ---------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callInviteCancelled'\u003c/code\u003e                                                                  | - The event name ('callInviteCancelled') |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; reason: 'user_declined' \\| 'remote_cancelled'; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event  |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('outgoingCallInitiated', ...)\n\n```typescript\naddListener(eventName: 'outgoingCallInitiated', listenerFunc: (data: { callSid: string; to: string; source: 'app' | 'system'; displayName?: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for outgoing call initiation events.\n\nThis event is fired when an outgoing call is initiated, either from the app\nor from the system (e.g., CallKit on iOS, Telecom on Android).\n\n| Param              | Type                                                                                                              | Description                                |\n| ------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |\n| **`eventName`**    | \u003ccode\u003e'outgoingCallInitiated'\u003c/code\u003e                                                                              | - The event name ('outgoingCallInitiated') |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; to: string; source: 'app' \\| 'system'; displayName?: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event    |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('outgoingCallFailed', ...)\n\n```typescript\naddListener(eventName: 'outgoingCallFailed', listenerFunc: (data: { callSid: string; to: string; reason: 'missing_access_token' | 'connection_failed' | 'no_call_details' | 'microphone_permission_denied' | 'invalid_contact' | 'callkit_request_failed' | 'unsupported_intent'; displayName?: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for outgoing call failure events.\n\nThis event is fired when an outgoing call fails to connect due to various reasons\nsuch as missing credentials, permission issues, or network problems.\n\n| Param              | Type                                                                                                                                                                                                                                                                          | Description                             |\n| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'outgoingCallFailed'\u003c/code\u003e                                                                                                                                                                                                                                             | - The event name ('outgoingCallFailed') |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; to: string; reason: 'missing_access_token' \\| 'connection_failed' \\| 'no_call_details' \\| 'microphone_permission_denied' \\| 'invalid_contact' \\| 'callkit_request_failed' \\| 'unsupported_intent'; displayName?: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callDisconnected', ...)\n\n```typescript\naddListener(eventName: 'callDisconnected', listenerFunc: (data: { callSid: string; error?: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call disconnection events.\n\nThis event is fired when a call ends, either normally or due to an error.\n\n| Param              | Type                                                                 | Description                             |\n| ------------------ | -------------------------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callDisconnected'\u003c/code\u003e                                      | - The event name ('callDisconnected')   |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; error?: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callRinging', ...)\n\n```typescript\naddListener(eventName: 'callRinging', listenerFunc: (data: { callSid: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call ringing events.\n\nThis event is fired when an outgoing call starts ringing on the other end.\n\n| Param              | Type                                                 | Description                             |\n| ------------------ | ---------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callRinging'\u003c/code\u003e                           | - The event name ('callRinging')        |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callReconnecting', ...)\n\n```typescript\naddListener(eventName: 'callReconnecting', listenerFunc: (data: { callSid: string; error?: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call reconnecting events.\n\nThis event is fired when a call loses connection and Twilio is attempting to\nreconnect. The call is not disconnected yet but audio may be interrupted.\n\n| Param              | Type                                                                 | Description                             |\n| ------------------ | -------------------------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callReconnecting'\u003c/code\u003e                                      | - The event name ('callReconnecting')   |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; error?: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callReconnected', ...)\n\n```typescript\naddListener(eventName: 'callReconnected', listenerFunc: (data: { callSid: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call reconnected events.\n\nThis event is fired when a call successfully reconnects after a connection loss.\nAudio should resume normally after this event.\n\n| Param              | Type                                                 | Description                             |\n| ------------------ | ---------------------------------------------------- | --------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callReconnected'\u003c/code\u003e                       | - The event name ('callReconnected')    |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('callQualityWarningsChanged', ...)\n\n```typescript\naddListener(eventName: 'callQualityWarningsChanged', listenerFunc: (data: { callSid: string; currentWarnings: string[]; previousWarnings: string[]; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for call quality warning events.\n\nThis event is fired when the call quality changes, providing warnings about\npotential issues like high jitter, packet loss, or low audio levels.\n\n| Param              | Type                                                                                                        | Description                                     |\n| ------------------ | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'callQualityWarningsChanged'\u003c/code\u003e                                                                   | - The event name ('callQualityWarningsChanged') |\n| **`listenerFunc`** | \u003ccode\u003e(data: { callSid: string; currentWarnings: string[]; previousWarnings: string[]; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event         |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('registrationSuccess', ...)\n\n```typescript\naddListener(eventName: 'registrationSuccess', listenerFunc: () =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for successful registration events.\n\nThis event is fired when the device successfully registers with Twilio Voice\nand is ready to make and receive calls. This typically occurs after a successful\nlogin with a valid access token.\n\n| Param              | Type                               | Description                              |\n| ------------------ | ---------------------------------- | ---------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'registrationSuccess'\u003c/code\u003e | - The event name ('registrationSuccess') |\n| **`listenerFunc`** | \u003ccode\u003e() =\u0026gt; void\u003c/code\u003e         | - Callback function to handle the event  |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### addListener('registrationFailure', ...)\n\n```typescript\naddListener(eventName: 'registrationFailure', listenerFunc: (data: { error: string; }) =\u003e void) =\u003e Promise\u003cPluginListenerHandle\u003e\n```\n\nListen for registration failure events.\n\nThis event is fired when the device fails to register with Twilio Voice,\ntypically due to an invalid or expired access token, network issues, or\nTwilio service problems.\n\n| Param              | Type                                               | Description                              |\n| ------------------ | -------------------------------------------------- | ---------------------------------------- |\n| **`eventName`**    | \u003ccode\u003e'registrationFailure'\u003c/code\u003e                 | - The event name ('registrationFailure') |\n| **`listenerFunc`** | \u003ccode\u003e(data: { error: string; }) =\u0026gt; void\u003c/code\u003e | - Callback function to handle the event  |\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;\u003ca href=\"#pluginlistenerhandle\"\u003ePluginListenerHandle\u003c/a\u003e\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### removeAllListeners()\n\n```typescript\nremoveAllListeners() =\u003e Promise\u003cvoid\u003e\n```\n\nRemove all registered event listeners.\n\nThis is useful for cleanup when your component unmounts or when you want to\nreset all event handling.\n\n--------------------\n\n\n### getPluginVersion()\n\n```typescript\ngetPluginVersion() =\u003e Promise\u003c{ version: string; }\u003e\n```\n\nGet the native Capacitor plugin version\n\n**Returns:** \u003ccode\u003ePromise\u0026lt;{ version: string; }\u0026gt;\u003c/code\u003e\n\n--------------------\n\n\n### Interfaces\n\n\n#### CallInvite\n\nRepresents a pending incoming call invitation.\n\nThis interface describes the data structure for call invitations that have been received\nbut not yet accepted or rejected. The same structure is used both in the\n`callInviteReceived` event and in the `pendingInvites` array returned by `getCallStatus()`.\n\n| Prop               | Type                                                            | Description                                                                      |\n| ------------------ | --------------------------------------------------------------- | -------------------------------------------------------------------------------- |\n| **`callSid`**      | \u003ccode\u003estring\u003c/code\u003e                                             | Unique identifier for the incoming call invitation                               |\n| **`from`**         | \u003ccode\u003estring\u003c/code\u003e                                             | Phone number or client identifier of the caller (may include custom caller name) |\n| **`to`**           | \u003ccode\u003estring\u003c/code\u003e                                             | Phone number or client identifier being called                                   |\n| **`customParams`** | \u003ccode\u003e\u003ca href=\"#record\"\u003eRecord\u003c/a\u003e\u0026lt;string, string\u0026gt;\u003c/code\u003e | Custom parameters passed with the call invitation                                |\n\n\n#### PluginListenerHandle\n\n| Prop         | Type                                      |\n| ------------ | ----------------------------------------- |\n| **`remove`** | \u003ccode\u003e() =\u0026gt; Promise\u0026lt;void\u0026gt;\u003c/code\u003e |\n\n\n### Type Aliases\n\n\n#### Record\n\nConstruct a type with a set of properties K of type T\n\n\u003ccode\u003e{\r [P in K]: T;\r }\u003c/code\u003e\n\n\u003c/docgen-api\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcap-go%2Fcapacitor-twilio-voice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcap-go%2Fcapacitor-twilio-voice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcap-go%2Fcapacitor-twilio-voice/lists"}