{"id":"1d401c5c-1891-42b3-bf40-74db9f11bae9","shortId":"KywNTj","kind":"skill","title":"telnyx-webrtc-client-android","tagline":">-","description":"# Telnyx WebRTC - Android SDK\n\nBuild real-time voice communication into Android applications using Telnyx WebRTC.\n\n> **Prerequisites**: Create WebRTC credentials and generate a login token using the Telnyx server-side SDK. See the `telnyx-webrtc-*` skill in your server language plugin (e.g., `telnyx-python`, `telnyx-javascript`).\n\n## Installation\n\nAdd JitPack repository to your project's `build.gradle`:\n\n```gradle\nallprojects {\n    repositories {\n        maven { url 'https://jitpack.io' }\n    }\n}\n```\n\nAdd the dependency:\n\n```gradle\ndependencies {\n    implementation 'com.github.team-telnyx:telnyx-webrtc-android:latest-version'\n}\n```\n\n## Required Permissions\n\nAdd to `AndroidManifest.xml`:\n\n```xml\n<uses-permission android:name=\"android.permission.INTERNET\"/>\n<uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n<!-- For push notifications (Android 14+) -->\n<uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\"/>\n<uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_PHONE_CALL\"/>\n```\n\n---\n\n## Authentication\n\n### Option 1: Credential-Based Login\n\n```kotlin\nval telnyxClient = TelnyxClient(context)\ntelnyxClient.connect()\n\nval credentialConfig = CredentialConfig(\n    sipUser = \"your_sip_username\",\n    sipPassword = \"your_sip_password\",\n    sipCallerIDName = \"Display Name\",\n    sipCallerIDNumber = \"+15551234567\",\n    fcmToken = fcmToken,  // Optional: for push notifications\n    logLevel = LogLevel.DEBUG,\n    autoReconnect = true\n)\n\ntelnyxClient.credentialLogin(credentialConfig)\n```\n\n### Option 2: Token-Based Login (JWT)\n\n```kotlin\nval tokenConfig = TokenConfig(\n    sipToken = \"your_jwt_token\",\n    sipCallerIDName = \"Display Name\",\n    sipCallerIDNumber = \"+15551234567\",\n    fcmToken = fcmToken,\n    logLevel = LogLevel.DEBUG,\n    autoReconnect = true\n)\n\ntelnyxClient.tokenLogin(tokenConfig)\n```\n\n### Configuration Options\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `sipUser` / `sipToken` | String | Credentials from Telnyx Portal |\n| `sipCallerIDName` | String? | Caller ID name displayed to recipients |\n| `sipCallerIDNumber` | String? | Caller ID number |\n| `fcmToken` | String? | Firebase Cloud Messaging token for push |\n| `ringtone` | Any? | Raw resource ID or URI for ringtone |\n| `ringBackTone` | Int? | Raw resource ID for ringback tone |\n| `logLevel` | LogLevel | NONE, ERROR, WARNING, DEBUG, INFO, ALL |\n| `autoReconnect` | Boolean | Auto-retry login on failure (3 attempts) |\n| `region` | Region | AUTO, US_EAST, US_WEST, EU_WEST |\n\n---\n\n## Making Outbound Calls\n\n```kotlin\n// Create a new outbound call\ntelnyxClient.call.newInvite(\n    callerName = \"John Doe\",\n    callerNumber = \"+15551234567\",\n    destinationNumber = \"+15559876543\",\n    clientState = \"my-custom-state\"\n)\n```\n\n---\n\n## Receiving Inbound Calls\n\nListen for socket events using SharedFlow (recommended):\n\n```kotlin\nlifecycleScope.launch {\n    telnyxClient.socketResponseFlow.collect { response ->\n        when (response.status) {\n            SocketStatus.ESTABLISHED -> {\n                // Socket connected\n            }\n            SocketStatus.MESSAGERECEIVED -> {\n                response.data?.let { data ->\n                    when (data.method) {\n                        SocketMethod.CLIENT_READY.methodName -> {\n                            // Ready to make/receive calls\n                        }\n                        SocketMethod.LOGIN.methodName -> {\n                            // Successfully logged in\n                        }\n                        SocketMethod.INVITE.methodName -> {\n                            // Incoming call!\n                            val invite = data.result as InviteResponse\n                            // Show incoming call UI, then accept:\n                            telnyxClient.acceptCall(\n                                invite.callId,\n                                invite.callerIdNumber\n                            )\n                        }\n                        SocketMethod.ANSWER.methodName -> {\n                            // Call was answered\n                        }\n                        SocketMethod.BYE.methodName -> {\n                            // Call ended\n                        }\n                        SocketMethod.RINGING.methodName -> {\n                            // Remote party is ringing\n                        }\n                    }\n                }\n            }\n            SocketStatus.ERROR -> {\n                // Handle error: response.errorCode\n            }\n            SocketStatus.DISCONNECT -> {\n                // Socket disconnected\n            }\n        }\n    }\n}\n```\n\n---\n\n## Call Controls\n\n```kotlin\n// Get current call\nval currentCall: Call? = telnyxClient.calls[callId]\n\n// End call\ncurrentCall?.endCall(callId)\n\n// Mute/Unmute\ncurrentCall?.onMuteUnmutePressed()\n\n// Hold/Unhold\ncurrentCall?.onHoldUnholdPressed(callId)\n\n// Send DTMF tone\ncurrentCall?.dtmf(callId, \"1\")\n```\n\n### Handling Multiple Calls\n\n```kotlin\n// Get all active calls\nval calls: Map<UUID, Call> = telnyxClient.calls\n\n// Iterate through calls\ncalls.forEach { (callId, call) ->\n    // Handle each call\n}\n```\n\n---\n\n## Push Notifications (FCM)\n\n### 1. Setup Firebase\n\nAdd Firebase to your project and get an FCM token:\n\n```kotlin\nFirebaseMessaging.getInstance().token.addOnCompleteListener { task ->\n    if (task.isSuccessful) {\n        val fcmToken = task.result\n        // Use this token in your login config\n    }\n}\n```\n\n### 2. Handle Incoming Push\n\nIn your `FirebaseMessagingService`:\n\n```kotlin\nclass MyFirebaseService : FirebaseMessagingService() {\n    override fun onMessageReceived(remoteMessage: RemoteMessage) {\n        val params = remoteMessage.data\n        val metadata = JSONObject(params as Map<*, *>).getString(\"metadata\")\n        \n        // Check for missed call\n        if (params[\"message\"] == \"Missed call!\") {\n            // Show missed call notification\n            return\n        }\n        \n        // Show incoming call notification (use Foreground Service)\n        showIncomingCallNotification(metadata)\n    }\n}\n```\n\n### 3. Decline Push Call (Simplified)\n\n```kotlin\n// The SDK now handles decline automatically\ntelnyxClient.connectWithDeclinePush(\n    txPushMetaData = pushMetaData,\n    credentialConfig = credentialConfig\n)\n// SDK connects, sends decline, and disconnects automatically\n```\n\n### Android 14+ Requirements\n\n```xml\n<service\n    android:name=\".YourForegroundService\"\n    android:foregroundServiceType=\"phoneCall\"\n    android:exported=\"true\" />\n```\n\n---\n\n## Call Quality Metrics\n\nEnable metrics to monitor call quality in real-time:\n\n```kotlin\nval credentialConfig = CredentialConfig(\n    // ... other config\n    debug = true  // Enables call quality metrics\n)\n\n// Listen for quality updates\nlifecycleScope.launch {\n    currentCall?.callQualityFlow?.collect { metrics ->\n        println(\"MOS: ${metrics.mos}\")\n        println(\"Jitter: ${metrics.jitter * 1000} ms\")\n        println(\"RTT: ${metrics.rtt * 1000} ms\")\n        println(\"Quality: ${metrics.quality}\")  // EXCELLENT, GOOD, FAIR, POOR, BAD\n    }\n}\n```\n\n| Quality Level | MOS Range |\n|---------------|-----------|\n| EXCELLENT | > 4.2 |\n| GOOD | 4.1 - 4.2 |\n| FAIR | 3.7 - 4.0 |\n| POOR | 3.1 - 3.6 |\n| BAD | ≤ 3.0 |\n\n---\n\n## AI Agent Integration\n\nConnect to a Telnyx Voice AI Agent without traditional SIP credentials:\n\n### 1. Anonymous Login\n\n```kotlin\ntelnyxClient.connectAnonymously(\n    targetId = \"your_ai_assistant_id\",\n    targetType = \"ai_assistant\",  // Default\n    targetVersionId = \"optional_version_id\",\n    userVariables = mapOf(\"user_id\" to \"12345\")\n)\n```\n\n### 2. Start Conversation\n\n```kotlin\n// After anonymous login, call the AI Agent\ntelnyxClient.newInvite(\n    callerName = \"User Name\",\n    callerNumber = \"+15551234567\",\n    destinationNumber = \"\",  // Ignored for AI Agent\n    clientState = \"state\",\n    customHeaders = mapOf(\n        \"X-Account-Number\" to \"123\",  // Maps to {{account_number}}\n        \"X-User-Tier\" to \"premium\"    // Maps to {{user_tier}}\n    )\n)\n```\n\n### 3. Receive Transcripts\n\n```kotlin\nlifecycleScope.launch {\n    telnyxClient.transcriptUpdateFlow.collect { transcript ->\n        transcript.forEach { item ->\n            println(\"${item.role}: ${item.content}\")\n            // role: \"user\" or \"assistant\"\n        }\n    }\n}\n```\n\n### 4. Send Text to AI Agent\n\n```kotlin\n// Send text message during active call\ntelnyxClient.sendAIAssistantMessage(\"Hello, I need help with my account\")\n```\n\n---\n\n## Custom Logging\n\nImplement your own logger:\n\n```kotlin\nclass MyLogger : TxLogger {\n    override fun log(level: LogLevel, tag: String?, message: String, throwable: Throwable?) {\n        // Send to your logging service\n        MyAnalytics.log(level.name, tag ?: \"Telnyx\", message)\n    }\n}\n\nval config = CredentialConfig(\n    // ... other config\n    logLevel = LogLevel.ALL,\n    customLogger = MyLogger()\n)\n```\n\n---\n\n## ProGuard Rules\n\nIf using code obfuscation, add to `proguard-rules.pro`:\n\n```proguard\n-keep class com.telnyx.webrtc.** { *; }\n-dontwarn kotlin.Experimental$Level\n-dontwarn kotlin.Experimental\n-dontwarn kotlinx.coroutines.scheduling.ExperimentalCoroutineDispatcher\n```\n\n---\n\n## Troubleshooting\n\n| Issue | Solution |\n|-------|----------|\n| No audio | Check RECORD_AUDIO permission is granted |\n| Push not received | Verify FCM token is passed in config |\n| Login fails | Verify SIP credentials in Telnyx Portal |\n| Call drops | Check network stability, enable `autoReconnect` |\n| sender_id_mismatch (push) | FCM project mismatch - ensure app's `google-services.json` matches server credentials |\n\n<!-- BEGIN AUTO-GENERATED API REFERENCE -- do not edit below this line -->\n\n**[references/webrtc-server-api.md](references/webrtc-server-api.md) has the server-side WebRTC API — credential creation, token generation, and push notification setup. You MUST read it when setting up authentication or push notifications.**\n\n## API Reference\n\n\n### TelnyxClient\n\n`TelnyxClient` is the main entry point for interacting with the Telnyx WebRTC SDK. It handles connection management, call creation, and responses from the Telnyx platform.\n\n### Core Functionalities\n\n- **Connection Management**: Establishes and maintains a WebSocket connection to the Telnyx RTC platform.\n- **Authentication**: Supports authentication via SIP credentials or tokens.\n- **Call Control**: Provides methods to initiate (`newInvite`), accept (`acceptCall`), and end (`endCall`) calls.\n- **Event Handling**: Uses `TxSocketListener` to process events from the socket, such as incoming calls (`onOfferReceived`), call answers (`onAnswerReceived`), call termination (`onByeReceived`), and errors (`onErrorReceived`).\n- **State Exposure**: Exposes connection status, session information, and call events via `SharedFlow` (recommended: `socketResponseFlow`) and deprecated `LiveData` (e.g., `socketResponseLiveData`) for UI consumption.\n\n### Key Components and Interactions\n\n- **`TxSocket`**: Manages the underlying WebSocket communication.\n- **`TxSocketListener`**: An interface implemented by `TelnyxClient` to receive and process socket events. Notably:\n    - `onOfferReceived(jsonObject: JsonObject)`: Handles incoming call invitations.\n    - `onAnswerReceived(jsonObject: JsonObject)`: Processes answers to outgoing calls.\n    - `onByeReceived(jsonObject: JsonObject)`: Handles call termination notifications. The `jsonObject` now contains richer details including `cause`, `causeCode`, `sipCode`, and `sipReason`, allowing the client to populate `CallState.DONE` with a detailed `CallTerminationReason`.\n    - `onErrorReceived(jsonObject: JsonObject)`: Manages errors reported by the socket or platform.\n    - `onClientReady(jsonObject: JsonObject)`: Indicates the client is ready for operations after connection and initial setup.\n    - `onGatewayStateReceived(gatewayState: String, receivedSessionId: String?)`: Provides updates on the registration status with the Telnyx gateway.\n- **`Call` Class**: Represents individual call sessions. `TelnyxClient` creates and manages instances of `Call`.\n- **`CallState`**: The client updates the `CallState` of individual `Call` objects based on socket events and network conditions. This includes states like `DROPPED(reason: CallNetworkChangeReason)`, `RECONNECTING(reason: CallNetworkChangeReason)`, and `DONE(reason: CallTerminationReason?)` which now provide more context.\n- **`socketResponseFlow: SharedFlow<SocketResponse<ReceivedMessageBody>>`**: This SharedFlow stream is the recommended approach for applications. It emits `SocketResponse` objects that wrap messages received from the Telnyx platform. For `BYE` messages, the `ReceivedMessageBody` will contain a `com.telnyx.webrtc.sdk.verto.receive.ByeResponse` which is now enriched with termination cause details.\n- **`socketResponseLiveData: LiveData<SocketResponse<ReceivedMessageBody>>`**: **[DEPRECATED]** This LiveData stream is deprecated in favor of `socketResponseFlow`. It's maintained for backward compatibility but new implementations should use SharedFlow.\n\n### Usage Example\n\n**Recommended approach using SharedFlow:**\n\n```kotlin\n// Initializing the client\nval telnyxClient = TelnyxClient(context)\n\n// Observing responses using SharedFlow (Recommended)\nlifecycleScope.launch {\n    telnyxClient.socketResponseFlow.collect { response ->\n        when (response.status) {\n            SocketStatus.MESSAGERECEIVED -> {\n                response.data?.let {\n                    when (it.method) {\n                        SocketMethod.INVITE.methodName -> {\n                            val invite = it.result as InviteResponse\n                            // Handle incoming call invitation\n                        }\n                        SocketMethod.BYE.methodName -> {\n                            val bye = it.result as com.telnyx.webrtc.sdk.verto.receive.ByeResponse\n                            // Call ended by remote party, bye.cause, bye.sipCode etc. are available\n                            Log.d(\"TelnyxClient\", \"Call ended: ${bye.callId}, Reason: ${bye.cause}\")\n                        }\n                        // Handle other methods like ANSWER, RINGING, etc.\n                    }\n                }\n            }\n            SocketStatus.ERROR -> {\n                // Handle errors\n                Log.e(\"TelnyxClient\", \"Error: ${response.errorMessage}\")\n            }\n            // Handle other statuses: ESTABLISHED, LOADING, DISCONNECT\n        }\n    }\n}\n```\n\n**Deprecated approach using LiveData:**\n\n```kotlin\n@Deprecated(\"Use socketResponseFlow instead. LiveData is deprecated in favor of Kotlin Flows.\")\n// Observing responses (including errors and BYE messages)\ntelnyxClient.socketResponseLiveData.observe(lifecycleOwner, Observer { response ->\n    when (response.status) {\n        SocketStatus.MESSAGERECEIVED -> {\n            response.data?.let {\n                when (it.method) {\n                    SocketMethod.INVITE.methodName -> {\n                        val invite = it.result as InviteResponse\n                        // Handle incoming call invitation\n                    }\n                    SocketMethod.BYE.methodName -> {\n                        val bye = it.result as com.telnyx.webrtc.sdk.verto.receive.ByeResponse\n                        // Call ended by remote party, bye.cause, bye.sipCode etc. are available\n                        Log.d(\"TelnyxClient\", \"Call ended: ${bye.callId}, Reason: ${bye.cause}\")\n                    }\n                    // Handle other methods like ANSWER, RINGING, etc.\n                }\n            }\n        }\n        SocketStatus.ERROR -> {\n            // Handle errors\n            Log.e(\"TelnyxClient\", \"Error: ${response.errorMessage}\")\n        }\n        // Handle other statuses: ESTABLISHED, LOADING, DISCONNECT\n    }\n})\n\n// Connecting and Logging In (example with credentials)\ntelnyxClient.connect(\n    credentialConfig = CredentialConfig(\n        sipUser = \"your_sip_username\",\n        sipPassword = \"your_sip_password\",\n        // ... other config ...\n    )\n)\n\n// Making a call\nval outgoingCall = telnyxClient.newInvite(\n    callerName = \"My App\",\n    callerNumber = \"+11234567890\",\n    destinationNumber = \"+10987654321\",\n    clientState = \"some_state\"\n)\n\n// Observing the specific call's state\noutgoingCall.callStateFlow.collect { state ->\n    if (state is CallState.DONE) {\n        Log.d(\"TelnyxClient\", \"Outgoing call ended. Reason: ${state.reason?.cause}\")\n    }\n    // Handle other states\n}\n```\n\nRefer to the SDK's implementation and specific method documentation for detailed usage patterns and configuration options.\n\n### Telnyx Client\nNOTE:\nRemember to add and handle INTERNET, RECORD_AUDIO and ACCESS_NETWORK_STATE permissions\n\n   <p align=\"center\">\n               <img align=\"center\" src=\"https://user-images.githubusercontent.com/9112652/117322479-f4731c00-ae85-11eb-9259-6333fc20b629.png\" />\n            </p>\n\n### Initialize\n\nTo initialize the TelnyxClient you will have to provide the application context.\n\n```kotlin\n  telnyxClient = TelnyxClient(context)\n```\n### Connect\n\nOnce an instance is created, you can call the one of two available .connect(....) method to connect to the socket.\n\n```kotlin\nfun connect(\n    providedServerConfig: TxServerConfiguration = TxServerConfiguration(),\n    credentialConfig: CredentialConfig,\n    txPushMetaData: String? = null,\n    autoLogin: Boolean = true,\n)\n```\n### Listening for events and reacting\n\nWe need to react for a socket connection state or incoming calls. We do this by getting the Telnyx Socket Response callbacks from our TelnyxClient.\n\n```kotlin\nval socketResponseFlow: SharedFlow<SocketResponse<ReceivedMessageBody>>\n```\n\n\n### Call\n\n### Telnyx Call\n\nClass that represents a Call and handles all call related actions, including answering and ending a call.\n### Creating a call invitation\n\nIn order to make a call invitation, you need to provide your callerName, callerNumber, the destinationNumber (or SIP credential), and your clientState (any String value).\n\n```kotlin\n   telnyxClient.call.newInvite(callerName, callerNumber, destinationNumber, clientState)\n```\n### Accepting a call\n\nIn order to be able to accept a call, we first need to listen for invitations. We do this by getting the Telnyx Socket Response as LiveData:\n\n```kotlin\n  fun getSocketResponse(): LiveData<SocketResponse<ReceivedMessageBody>>? =\n        telnyxClient.getSocketResponse()\n```\n### Handling Multiple Calls\n\nThe Telnyx WebRTC SDK allows for multiple calls to be handled at once. You can use the callId to differentiate the calls..\n### Key Properties\n\n- **`callId: UUID`**: A unique identifier for the call.\n- **`sessionId: String`**: The session ID associated with the Telnyx connection.\n- **`callStateFlow: StateFlow<CallState>`**: A Kotlin Flow that emits updates to the call's current state. This is the primary way to observe real-time changes to the call. States include:\n    - `CallState.NEW`: The call has been locally initiated but not yet sent.\n    - `CallState.CONNECTING`: The call is in the process of connecting.\n    - `CallState.RINGING`: The call invitation has been sent, and the remote party is being alerted.\n    - `CallState.ACTIVE`: The call is established and active.\n    - `CallState.HELD`: The call is on hold.\n    - `CallState.DONE(reason: CallTerminationReason?)`: The call has ended. The optional `reason` parameter provides details about why the call terminated (e.g., normal hangup, call rejected, busy, SIP error). `CallTerminationReason` contains `cause`, `causeCode`, `sipCode`, and `sipReason`.\n    - `CallState.ERROR`: An error occurred related to this call.\n    - `CallState.DROPPED(reason: CallNetworkChangeReason)`: The call was dropped, typically due to network issues. The `reason` (`CallNetworkChangeReason.NETWORK_LOST` or `CallNetworkChangeReason.NETWORK_SWITCH`) provides context.\n    - `CallState.RECONNECTING(reason: CallNetworkChangeReason)`: The SDK is attempting to reconnect the call after a network disruption. The `reason` provides context.\n- **`onCallQualityChange: ((CallQualityMetrics) -> Unit)?`**: A callback for real-time call quality metrics.\n- **`audioManager: AudioManager`**: Reference to the Android `AudioManager` for controlling audio settings.\n- **`peerConnection: Peer?`**: Represents the underlying WebRTC peer connection.\n\n### Key Methods\n\n- **`newInvite(...)`**: (Typically initiated via `TelnyxClient`) Initiates a new outgoing call.\n- **`acceptCall(...)`**: (Typically initiated via `TelnyxClient`) Accepts an incoming call.\n- **`endCall(callId: UUID)`**: Terminates the call. This is usually called on the `TelnyxClient` which then manages the specific `Call` object.\n- **`onMuteUnmutePressed()`**: Toggles the microphone mute state.\n- **`onLoudSpeakerPressed()`**: Toggles the loudspeaker state.\n- **`onHoldUnholdPressed(callId: UUID)`**: Toggles the hold state for the call.\n- **`dtmf(callId: UUID, tone: String)`**: Sends DTMF tones.\n\n### Observing Call State\n\nApplications should observe the `callStateFlow` to react to changes in the call's status and update the UI accordingly. For example, displaying call duration when `ACTIVE`, showing a \"reconnecting\" indicator when `RECONNECTING`, or presenting termination reasons when `DONE`.\n\n```kotlin\n// Example: Observing call state in a ViewModel or Composable\nviewModelScope.launch {\n    myCall.callStateFlow.collect { state ->\n        when (state) {\n            is CallState.ACTIVE -> {\n                // Update UI to show active call controls\n            }\n            is CallState.DONE -> {\n                // Call has ended, update UI\n                // Access state.reason for termination details\n                val reasonDetails = state.reason?.let {\n                    \"Cause: ${it.cause}, SIP Code: ${it.sipCode}\"\n                } ?: \"No specific reason provided.\"\n                Log.d(\"Call Ended\", \"Reason: $reasonDetails\")\n            }\n            is CallState.DROPPED -> {\n                // Call dropped, possibly show a message with state.reason.description\n                Log.d(\"Call Dropped\", \"Reason: ${state.callNetworkChangeReason.description}\")\n            }\n            is CallState.RECONNECTING -> {\n                // Call is reconnecting, update UI\n                Log.d(\"Call Reconnecting\", \"Reason: ${state.callNetworkChangeReason.description}\")\n            }\n            // Handle other states like NEW, CONNECTING, RINGING, HELD, ERROR\n            else -> { /* ... */ }\n        }\n    }\n}\n```\n\nFor more details on specific parameters and advanced usage, refer to the SDK's source code and the main `TelnyxClient` documentation.\n\n\n### ReceivedMessageBody\n\n### ReceivedMessageBody\n\nA data class the represents the structure of every message received via the socket connection\n\n```kotlin\ndata class ReceivedMessageBody(val method: String, val result: ReceivedResult?)\n```\n\nWhere the params are:\n* method the Telnyx Message Method - ie. INVITE, BYE, MODIFY, etc. @see [SocketMethod]\n* result the content of the actual message in the structure provided via `ReceivedResult`\n\n### SocketMethod\n\nEnum class to detail the Method property of the response from the Telnyx WEBRTC client with the given [methodName]\n### Structure\n\n```kotlin\ndata class ReceivedMessageBody(\n    val method: String,      // The Telnyx Message Method (e.g., \"telnyx_rtc.invite\", \"telnyx_rtc.bye\")\n    val result: ReceivedResult? // The content of the actual message\n)\n```\n\n- **`method: String`**: This field indicates the type of message received. It corresponds to one of the `SocketMethod` enums (e.g., `SocketMethod.INVITE`, `SocketMethod.ANSWER`, `SocketMethod.BYE`). Your application will typically use this field in a `when` statement to determine how to process the `result`.\n\n- **`result: ReceivedResult?`**: This field holds the actual payload of the message. `ReceivedResult` is a sealed class, and the concrete type of `result` will depend on the `method`. For example:\n    - If `method` is `SocketMethod.LOGIN.methodName`, `result` will be a `LoginResponse`.\n    - If `method` is `SocketMethod.INVITE.methodName`, `result` will be an `InviteResponse`.\n    - If `method` is `SocketMethod.ANSWER.methodName`, `result` will be an `AnswerResponse`.\n    - If `method` is `SocketMethod.BYE.methodName`, `result` will be a `com.telnyx.webrtc.sdk.verto.receive.ByeResponse`. Importantly, this `ByeResponse` now includes detailed termination information such as `cause`, `causeCode`, `sipCode`, and `sipReason`, in addition to the `callId`.\n    - Other `ReceivedResult` subtypes include `RingingResponse`, `MediaResponse`, and `DisablePushResponse`.\n\n### Usage\n\nWhen you observe `TelnyxClient.socketResponseLiveData`, you receive a `SocketResponse<ReceivedMessageBody>`. If the status is `SocketStatus.MESSAGERECEIVED`, the `data` field of `SocketResponse` will contain the `ReceivedMessageBody`.\n\n```kotlin\ntelnyxClient.socketResponseLiveData.observe(this, Observer { response ->\n    if (response.status == SocketStatus.MESSAGERECEIVED) {\n        response.data?.let { receivedMessageBody ->\n            Log.d(\"SDK_APP\", \"Method: ${receivedMessageBody.method}\")\n            when (receivedMessageBody.method) {\n                SocketMethod.LOGIN.methodName -> {\n                    val loginResponse = receivedMessageBody.result as? LoginResponse\n                    // Process login response\n                }\n                SocketMethod.INVITE.methodName -> {\n                    val inviteResponse = receivedMessageBody.result as? InviteResponse\n                    // Process incoming call invitation\n                }\n                SocketMethod.BYE.methodName -> {\n                    val byeResponse = receivedMessageBody.result as? com.telnyx.webrtc.sdk.verto.receive.ByeResponse\n                    byeResponse?.let {\n                        // Process call termination, access it.cause, it.sipCode, etc.\n                        Log.i(\"SDK_APP\", \"Call ${it.callId} ended. Reason: ${it.cause}, SIP Code: ${it.sipCode}\")\n                    }\n                }\n                // Handle other methods...\n            }\n        }\n    }\n})\n```\n\nBy checking the `method` and casting the `result` to its expected type, your application can effectively handle the diverse messages sent by the Telnyx platform.\n\n<!-- END AUTO-GENERATED API REFERENCE -->","tags":["telnyx","webrtc","client","android","team-telnyx","agent-skills","ai-coding-agent","claude-code","cpaas","cursor","iot","llm"],"capabilities":["skill","source-team-telnyx","skill-telnyx-webrtc-client-android","topic-agent-skills","topic-ai-coding-agent","topic-claude-code","topic-cpaas","topic-cursor","topic-iot","topic-llm","topic-sdk","topic-sip","topic-sms","topic-speech-to-text","topic-telephony"],"categories":["ai"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/team-telnyx/ai/telnyx-webrtc-client-android","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add team-telnyx/ai","source_repo":"https://github.com/team-telnyx/ai","install_from":"skills.sh"}},"qualityScore":"0.533","qualityRationale":"deterministic score 0.53 from registry signals: · indexed on github topic:agent-skills · 167 github stars · SKILL.md body (26,426 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:54:54.972Z","embedding":null,"createdAt":"2026-04-18T22:08:42.376Z","updatedAt":"2026-04-22T00:54:54.972Z","lastSeenAt":"2026-04-22T00:54:54.972Z","tsv":"'+10987654321':1359 '+11234567890':1357 '+15551234567':120,152,252,619 '+15559876543':254 '..':1622 '1':94,359,386,579 '1000':533,538 '123':634 '12345':602 '14':490 '2':134,415,603 '3':227,465,649 '3.0':564 '3.1':561 '3.6':562 '3.7':558 '4':665 '4.0':559 '4.1':555 '4.2':553,556 'abl':1568 'accept':307,882,1561,1570,1849 'acceptcal':883,1844 'access':1415,1974,2359 'accord':1923 'account':631,637,685 'action':1519 'activ':366,676,1713,1930,1964 'actual':2103,2153,2201 'add':57,71,88,389,732,1408 'addit':2276 'advanc':2041 'agent':566,574,613,624,670 'ai':565,573,586,590,612,623,669 'alert':1706 'allow':991,1604 'allproject':66 'android':5,8,17,82,489,1818 'androidmanifest.xml':90 'anonym':580,608 'answer':314,904,968,1223,1311,1521 'answerrespons':2250 'api':804,824 'app':790,1355,2324,2365 'applic':18,1102,1430,1905,2178,2390 'approach':1100,1160,1240 'assist':587,591,664 'associ':1638 'attempt':228,1788 'audio':750,753,1413,1822 'audiomanag':1813,1814,1819 'authent':92,820,867,869 'auto':222,231 'auto-retri':221 'autologin':1468 'automat':476,488 'autoreconnect':129,157,219,781 'avail':1211,1299,1449 'backward':1149 'bad':547,563 'base':97,137,1065 'boolean':220,1469 'build':10 'build.gradle':64 'busi':1743 'bye':1116,1198,1261,1286,2093 'bye.callid':1216,1304 'bye.cause':1207,1218,1295,1306 'bye.sipcode':1208,1296 'byerespons':2262,2350,2354 'call':240,246,262,289,296,304,312,316,330,335,338,342,362,367,369,372,376,379,382,445,450,453,458,468,493,500,515,610,677,775,844,875,887,901,903,906,920,962,971,976,1042,1046,1054,1063,1194,1202,1214,1282,1290,1302,1349,1366,1378,1444,1487,1506,1508,1513,1517,1525,1528,1535,1563,1572,1599,1607,1621,1632,1653,1670,1675,1686,1695,1709,1716,1724,1736,1741,1760,1765,1792,1810,1843,1852,1858,1862,1871,1893,1903,1916,1927,1946,1965,1969,1993,1999,2008,2014,2020,2346,2357,2366 'callback':1497,1805 'caller':175,183 'callernam':248,615,1353,1542,1557 'callernumb':251,618,1356,1543,1558 'callid':340,345,352,358,378,1617,1625,1854,1885,1895,2279 'callnetworkchangereason':1078,1081,1763,1784 'callnetworkchangereason.network':1775,1778 'callqualityflow':524 'callqualitymetr':1802 'calls.foreach':377 'callstat':1055,1060 'callstate.active':1707,1959 'callstate.connecting':1684 'callstate.done':996,1374,1720,1968 'callstate.dropped':1761,1998 'callstate.error':1753 'callstate.held':1714 'callstate.new':1673 'callstate.reconnecting':1782,2013 'callstate.ringing':1693 'callstateflow':1643,1909 'callterminationreason':1000,1085,1722,1746 'cast':2382 'caus':986,1130,1382,1748,1983,2270 'causecod':987,1749,2271 'chang':1667,1913 'check':442,751,777,2378 'class':423,693,737,1043,1509,2059,2074,2113,2134,2210 'client':4,993,1017,1057,1166,1404,2126 'clientstat':255,625,1360,1551,1560 'cloud':189 'code':730,1986,2049,2372 'collect':525 'com.github.team':77 'com.telnyx.webrtc':738 'com.telnyx.webrtc.sdk.verto.receive.byeresponse':1123,1201,1289,2259,2353 'communic':15,943 'compat':1150 'compon':935 'compos':1952 'concret':2213 'condit':1071 'config':414,511,718,721,766,1346 'configur':161,1401 'connect':278,483,568,842,854,861,915,1023,1327,1436,1450,1453,1459,1483,1642,1692,1831,2029,2071 'consumpt':933 'contain':982,1121,1747,2308 'content':2100,2150 'context':103,1090,1170,1431,1435,1781,1800 'control':331,876,1821,1966 'convers':605 'core':852 'correspond':2166 'creat':23,242,1049,1441,1526 'creation':806,845 'credenti':25,96,169,578,771,795,805,872,1333,1548 'credential-bas':95 'credentialconfig':106,107,132,480,481,508,509,719,1335,1336,1463,1464 'current':334,1655 'currentcal':337,343,347,350,356,523 'custom':258,686 'customhead':627 'customlogg':724 'data':282,2058,2073,2133,2303 'data.method':284 'data.result':299 'debug':216,512 'declin':466,475,485 'default':592 'depend':73,75,2218 'deprec':927,1135,1140,1239,1244,1250 'descript':165 'destinationnumb':253,620,1358,1545,1559 'detail':984,999,1131,1397,1732,1978,2036,2115,2265 'determin':2189 'differenti':1619 'disablepushrespons':2287 'disconnect':329,487,1238,1326 'display':117,149,178,1926 'disrupt':1796 'divers':2395 'document':1395,2054 'doe':250 'done':1083,1942 'dontwarn':739,742,744 'drop':776,1076,1767,2000,2009 'dtmf':354,357,1894,1900 'due':1769 'durat':1928 'e.g':49,929,1738,2143,2173 'east':233 'effect':2392 'els':2033 'emit':1104,1649 'enabl':496,514,780 'end':317,341,885,1203,1215,1291,1303,1379,1523,1726,1971,1994,2368 'endcal':344,886,1853 'enrich':1127 'ensur':789 'entri':831 'enum':2112,2172 'error':214,325,910,1005,1228,1231,1259,1316,1319,1745,1755,2032 'establish':856,1236,1324,1711 'etc':1209,1225,1297,1313,2095,2362 'eu':236 'event':266,888,894,921,955,1068,1473 'everi':2065 'exampl':1158,1331,1925,1944,2223 'excel':543,552 'expect':2387 'expos':914 'exposur':913 'fail':768 'failur':226 'fair':545,557 'favor':1142,1252 'fcm':385,397,761,786 'fcmtoken':121,122,153,154,186,406 'field':2158,2183,2198,2304 'firebas':188,388,390 'firebasemessaging.getinstance':400 'firebasemessagingservic':421,425 'first':1574 'flow':1255,1647 'foreground':461 'fun':427,697,1458,1592 'function':853 'gateway':1041 'gatewayst':1028 'generat':27,808 'get':333,364,395,1492,1584 'getsocketrespons':1593 'getstr':440 'given':2129 'good':544,554 'google-services.json':792 'gradl':65,74 'grant':756 'handl':324,360,380,416,474,841,889,960,975,1192,1219,1227,1233,1280,1307,1315,1321,1383,1410,1515,1597,1610,2024,2374,2393 'hangup':1740 'held':2031 'hello':679 'help':682 'hold':1719,1889,2199 'hold/unhold':349 'id':176,184,198,207,588,596,600,783,1637 'identifi':1629 'ie':2091 'ignor':621 'implement':76,688,947,1153,1391 'import':2260 'inbound':261 'includ':985,1073,1258,1520,1672,2264,2283 'incom':295,303,417,457,900,961,1193,1281,1486,1851,2345 'indic':1015,1934,2159 'individu':1045,1062 'info':217 'inform':918,2267 'initi':880,1025,1164,1419,1421,1679,1836,1839,1846 'instal':56 'instanc':1052,1439 'instead':1247 'int':204 'integr':567 'interact':834,937 'interfac':946 'internet':1411 'invit':298,963,1188,1195,1276,1283,1529,1536,1579,1696,2092,2347 'invite.calleridnumber':310 'invite.callid':309 'inviterespons':301,1191,1279,2241,2340,2343 'issu':747,1772 'it.callid':2367 'it.cause':1984,2360,2370 'it.method':1185,1273 'it.result':1189,1199,1277,1287 'it.sipcode':1987,2361,2373 'item':657 'item.content':660 'item.role':659 'iter':374 'javascript':55 'jitpack':58 'jitpack.io':70 'jitter':531 'john':249 'jsonobject':436,958,959,965,966,973,974,980,1002,1003,1013,1014 'jwt':139,146 'keep':736 'key':934,1623,1832 'kotlin':99,140,241,270,332,363,399,422,470,506,582,606,652,671,692,1163,1243,1254,1432,1457,1501,1555,1591,1646,1943,2072,2132,2311 'kotlin.experimental':740,743 'kotlinx.coroutines.scheduling.experimentalcoroutinedispatcher':745 'languag':47 'latest':84 'latest-vers':83 'let':281,1183,1271,1982,2320,2355 'level':549,699,741 'level.name':713 'lifecycleown':1264 'lifecyclescope.launch':271,522,653,1176 'like':1075,1222,1310,2027 'listen':263,518,1471,1577 'livedata':928,1133,1137,1242,1248,1590,1594 'load':1237,1325 'local':1678 'log':292,687,698,710,1329 'log.d':1212,1300,1375,1992,2007,2019,2322 'log.e':1229,1317 'log.i':2363 'logger':691 'login':29,98,138,224,413,581,609,767,2336 'loginrespons':2232,2331,2334 'loglevel':127,155,211,212,700,722 'loglevel.all':723 'loglevel.debug':128,156 'lost':1776 'loudspeak':1882 'main':830,2052 'maintain':858,1147 'make':238,1347,1533 'make/receive':288 'manag':843,855,939,1004,1051,1868 'map':370,439,635,645 'mapof':598,628 'match':793 'maven':68 'mediarespons':2285 'messag':190,448,674,703,716,1109,1117,1262,2004,2066,2089,2104,2141,2154,2163,2205,2396 'metadata':435,441,464 'method':878,1221,1309,1394,1451,1833,2077,2086,2090,2117,2137,2142,2155,2221,2225,2234,2243,2252,2325,2376,2380 'methodnam':2130 'metric':495,497,517,526,1812 'metrics.jitter':532 'metrics.mos':529 'metrics.quality':542 'metrics.rtt':537 'microphon':1876 'mismatch':784,788 'miss':444,449,452 'modifi':2094 'monitor':499 'mos':528,550 'ms':534,539 'multipl':361,1598,1606 'must':814 'mute':1877 'mute/unmute':346 'my-custom-st':256 'myanalytics.log':712 'mycall.callstateflow.collect':1954 'myfirebaseservic':424 'mylogg':694,725 'name':118,150,177,617 'need':681,1477,1538,1575 'network':778,1070,1416,1771,1795 'new':244,1152,1841,2028 'newinvit':881,1834 'none':213 'normal':1739 'notabl':956 'note':1405 'notif':126,384,454,459,811,823,978 'null':1467 'number':185,632,638 'obfusc':731 'object':1064,1106,1872 'observ':1171,1256,1265,1363,1663,1902,1907,1945,2291,2314 'occur':1756 'onanswerreceiv':905,964 'onbyereceiv':908,972 'oncallqualitychang':1801 'onclientreadi':1012 'one':1446,2168 'onerrorreceiv':911,1001 'ongatewaystatereceiv':1027 'onholdunholdpress':351,1884 'onloudspeakerpress':1879 'onmessagereceiv':428 'onmuteunmutepress':348,1873 'onofferreceiv':902,957 'oper':1021 'option':93,123,133,162,594,1402,1728 'order':1531,1565 'outbound':239,245 'outgo':970,1377,1842 'outgoingcal':1351 'outgoingcall.callstateflow.collect':1369 'overrid':426,696 'param':432,437,447,2084 'paramet':163,1730,2039 'parti':320,1206,1294,1703 'pass':764 'password':115,1344 'pattern':1399 'payload':2202 'peer':1825,1830 'peerconnect':1824 'permiss':87,754,1418 'platform':851,866,1011,1114,2401 'plugin':48 'point':832 'poor':546,560 'popul':995 'portal':172,774 'possibl':2001 'premium':644 'prerequisit':22 'present':1938 'primari':1660 'println':527,530,535,540,658 'process':893,953,967,1690,2192,2335,2344,2356 'proguard':726,735 'proguard-rules.pro':734 'project':62,393,787 'properti':1624,2118 'provid':877,1032,1088,1428,1540,1731,1780,1799,1991,2108 'providedserverconfig':1460 'push':125,193,383,418,467,757,785,810,822 'pushmetadata':479 'python':52 'qualiti':494,501,516,520,541,548,1811 'rang':551 'raw':196,205 'react':1475,1479,1911 'read':815 'readi':286,1019 'real':12,504,1665,1808 'real-tim':11,503,1664,1807 'reason':1077,1080,1084,1217,1305,1380,1721,1729,1762,1774,1783,1798,1940,1990,1995,2010,2022,2369 'reasondetail':1980,1996 'receiv':260,650,759,951,1110,2067,2164,2294 'receivedmessagebodi':1119,2055,2056,2075,2135,2310,2321 'receivedmessagebody.method':2326,2328 'receivedmessagebody.result':2332,2341,2351 'receivedresult':2081,2110,2148,2196,2206,2281 'receivedsessionid':1030 'recipi':180 'recommend':269,924,1099,1159,1175 'reconnect':1079,1790,1933,1936,2016,2021 'record':752,1412 'refer':825,1386,1815,2043 'references/webrtc-server-api.md':796,797 'region':229,230 'registr':1036 'reject':1742 'relat':1518,1757 'rememb':1406 'remot':319,1205,1293,1702 'remotemessag':429,430 'remotemessage.data':433 'report':1006 'repositori':59,67 'repres':1044,1511,1826,2061 'requir':86,491 'resourc':197,206 'respons':273,847,1172,1178,1257,1266,1496,1588,2121,2315,2337 'response.data':280,1182,1270,2319 'response.errorcode':326 'response.errormessage':1232,1320 'response.status':275,1180,1268,2317 'result':2080,2098,2147,2194,2195,2216,2228,2237,2246,2255,2384 'retri':223 'return':455 'richer':983 'ring':322,1224,1312,2030 'ringback':209 'ringbackton':203 'ringingrespons':2284 'rington':194,202 'role':661 'rtc':865 'rtt':536 'rule':727 'sdk':9,37,472,482,839,1389,1603,1786,2046,2323,2364 'seal':2209 'see':38,2096 'send':353,484,666,672,707,1899 'sender':782 'sent':1683,1699,2397 'server':35,46,794,801 'server-sid':34,800 'servic':462,711 'session':917,1047,1636 'sessionid':1633 'set':818,1823 'setup':387,812,1026 'sharedflow':268,923,1092,1095,1156,1162,1174,1504 'show':302,451,456,1931,1963,2002 'showincomingcallnotif':463 'side':36,802 'simplifi':469 'sip':110,114,577,770,871,1339,1343,1547,1744,1985,2371 'sipcalleridnam':116,148,173 'sipcalleridnumb':119,151,181 'sipcod':988,1750,2272 'sippassword':112,1341 'sipreason':990,1752,2274 'siptoken':144,167 'sipus':108,166,1337 'skill':43 'skill-telnyx-webrtc-client-android' 'socket':265,277,328,897,954,1009,1067,1456,1482,1495,1587,2070 'socketmethod':2097,2111,2171 'socketmethod.answer':2175 'socketmethod.answer.methodname':311,2245 'socketmethod.bye':2176 'socketmethod.bye.methodname':315,1196,1284,2254,2348 'socketmethod.client_ready.methodname':285 'socketmethod.invite':2174 'socketmethod.invite.methodname':294,1186,1274,2236,2338 'socketmethod.login.methodname':290,2227,2329 'socketmethod.ringing.methodname':318 'socketrespons':1093,1105,1134,1505,1595,2296,2306 'socketresponseflow':925,1091,1144,1246,1503 'socketresponselivedata':930,1132 'socketstatus.disconnect':327 'socketstatus.error':323,1226,1314 'socketstatus.established':276 'socketstatus.messagereceived':279,1181,1269,2301,2318 'solut':748 'sourc':2048 'source-team-telnyx' 'specif':1365,1393,1870,1989,2038 'stabil':779 'start':604 'state':259,626,912,1074,1362,1368,1370,1372,1385,1417,1484,1656,1671,1878,1883,1890,1904,1947,1955,1957,2026 'state.callnetworkchangereason.description':2011,2023 'state.reason':1381,1975,1981 'state.reason.description':2006 'stateflow':1644 'statement':2187 'status':916,1037,1235,1323,1918,2299 'stream':1096,1138 'string':168,174,182,187,702,704,1029,1031,1466,1553,1634,1898,2078,2138,2156 'structur':2063,2107,2131 'subtyp':2282 'success':291 'support':868 'switch':1779 'tag':701,714 'targetid':584 'targettyp':589 'targetversionid':593 'task':402 'task.issuccessful':404 'task.result':407 'telnyx':2,6,20,33,41,51,54,78,80,171,571,715,773,837,850,864,1040,1113,1403,1494,1507,1586,1601,1641,2088,2124,2140,2400 'telnyx-javascript':53 'telnyx-python':50 'telnyx-webrtc':40 'telnyx-webrtc-android':79 'telnyx-webrtc-client-android':1 'telnyx_rtc.bye':2145 'telnyx_rtc.invite':2144 'telnyxcli':101,102,826,827,949,1048,1168,1169,1213,1230,1301,1318,1376,1423,1433,1434,1500,1838,1848,1865,2053 'telnyxclient.acceptcall':308 'telnyxclient.call.newinvite':247,1556 'telnyxclient.calls':339,373 'telnyxclient.connect':104,1334 'telnyxclient.connectanonymously':583 'telnyxclient.connectwithdeclinepush':477 'telnyxclient.credentiallogin':131 'telnyxclient.getsocketresponse':1596 'telnyxclient.newinvite':614,1352 'telnyxclient.sendaiassistantmessage':678 'telnyxclient.socketresponseflow.collect':272,1177 'telnyxclient.socketresponselivedata':2292 'telnyxclient.socketresponselivedata.observe':1263,2312 'telnyxclient.tokenlogin':159 'telnyxclient.transcriptupdateflow.collect':654 'termin':907,977,1129,1737,1856,1939,1977,2266,2358 'text':667,673 'throwabl':705,706 'tier':642,648 'time':13,505,1666,1809 'toggl':1874,1880,1887 'token':30,136,147,191,398,410,762,807,874 'token-bas':135 'token.addoncompletelistener':401 'tokenconfig':142,143,160 'tone':210,355,1897,1901 'topic-agent-skills' 'topic-ai-coding-agent' 'topic-claude-code' 'topic-cpaas' 'topic-cursor' 'topic-iot' 'topic-llm' 'topic-sdk' 'topic-sip' 'topic-sms' 'topic-speech-to-text' 'topic-telephony' 'tradit':576 'transcript':651,655 'transcript.foreach':656 'troubleshoot':746 'true':130,158,513,1470 'two':1448 'txlogger':695 'txpushmetadata':478,1465 'txserverconfigur':1461,1462 'txsocket':938 'txsocketlisten':891,944 'type':164,2161,2214,2388 'typic':1768,1835,1845,2180 'ui':305,932,1922,1961,1973,2018 'under':941,1828 'uniqu':1628 'unit':1803 'updat':521,1033,1058,1650,1920,1960,1972,2017 'uri':200 'url':69 'us':232,234 'usag':1157,1398,2042,2288 'use':19,31,267,408,460,729,890,1155,1161,1173,1241,1245,1615,2181 'user':599,616,641,647,662 'usernam':111,1340 'uservari':597 'usual':1861 'uuid':371,1626,1855,1886,1896 'val':100,105,141,297,336,368,405,431,434,507,717,1167,1187,1197,1275,1285,1350,1502,1979,2076,2079,2136,2146,2330,2339,2349 'valu':1554 'verifi':760,769 'version':85,595 'via':870,922,1837,1847,2068,2109 'viewmodel':1950 'viewmodelscope.launch':1953 'voic':14,572 'warn':215 'way':1661 'webrtc':3,7,21,24,42,81,803,838,1602,1829,2125 'websocket':860,942 'west':235,237 'without':575 'wrap':1108 'x':630,640 'x-account-numb':629 'x-user-ti':639 'xml':91,492 'yet':1682","prices":[{"id":"70d9bc3d-7df9-45da-ac70-da7ba74c21f9","listingId":"1d401c5c-1891-42b3-bf40-74db9f11bae9","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"team-telnyx","category":"ai","install_from":"skills.sh"},"createdAt":"2026-04-18T22:08:42.376Z"}],"sources":[{"listingId":"1d401c5c-1891-42b3-bf40-74db9f11bae9","source":"github","sourceId":"team-telnyx/ai/telnyx-webrtc-client-android","sourceUrl":"https://github.com/team-telnyx/ai/tree/main/skills/telnyx-webrtc-client-android","isPrimary":false,"firstSeenAt":"2026-04-18T22:08:42.376Z","lastSeenAt":"2026-04-22T00:54:54.972Z"}],"details":{"listingId":"1d401c5c-1891-42b3-bf40-74db9f11bae9","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"team-telnyx","slug":"telnyx-webrtc-client-android","github":{"repo":"team-telnyx/ai","stars":167,"topics":["agent-skills","ai","ai-coding-agent","claude-code","cpaas","cursor","iot","llm","sdk","sip","sms","speech-to-text","telephony","telnyx","tts","twilio-migration","voice-agents","voice-ai","webrtc","windsurf"],"license":"mit","html_url":"https://github.com/team-telnyx/ai","pushed_at":"2026-04-21T22:09:49Z","description":"Official one-stop shop for AI Agents and developers building with Telnyx.","skill_md_sha":"1d2b6296fe1e75ac02e8113fbee032f43fe1e053","skill_md_path":"skills/telnyx-webrtc-client-android/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/team-telnyx/ai/tree/main/skills/telnyx-webrtc-client-android"},"layout":"multi","source":"github","category":"ai","frontmatter":{"name":"telnyx-webrtc-client-android","description":">-"},"skills_sh_url":"https://skills.sh/team-telnyx/ai/telnyx-webrtc-client-android"},"updatedAt":"2026-04-22T00:54:54.972Z"}}