{"id":"ade15a50-e79d-44e3-bd24-bfe677645944","shortId":"PSrJKE","kind":"skill","title":"telnyx-webrtc-client-ios","tagline":">-","description":"# Telnyx WebRTC - iOS SDK\n\nBuild real-time voice communication into iOS 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\n### CocoaPods\n\n```ruby\npod 'TelnyxRTC', '~> 0.1.0'\n```\n\nThen run:\n\n```bash\npod install --repo-update\n```\n\n### Swift Package Manager\n\n1. In Xcode: File → Add Packages\n2. Enter: `https://github.com/team-telnyx/telnyx-webrtc-ios.git`\n3. Select the `main` branch\n\n## Project Configuration\n\n1. **Disable Bitcode**: Build Settings → \"Bitcode\" → Set to \"NO\"\n\n2. **Enable Background Modes**: Signing & Capabilities → +Capability → Background Modes:\n   - Voice over IP\n   - Audio, AirPlay, and Picture in Picture\n\n3. **Microphone Permission**: Add to `Info.plist`:\n   ```xml\n   <key>NSMicrophoneUsageDescription</key>\n   <string>Microphone access required for VoIP calls</string>\n   ```\n\n---\n\n## Authentication\n\n### Option 1: Credential-Based Login\n\n```swift\nimport TelnyxRTC\n\nlet telnyxClient = TxClient()\ntelnyxClient.delegate = self\n\nlet txConfig = TxConfig(\n    sipUser: \"your_sip_username\",\n    password: \"your_sip_password\",\n    pushDeviceToken: \"DEVICE_APNS_TOKEN\",\n    ringtone: \"incoming_call.mp3\",\n    ringBackTone: \"ringback_tone.mp3\",\n    logLevel: .all\n)\n\ndo {\n    try telnyxClient.connect(txConfig: txConfig)\n} catch {\n    print(\"Connection error: \\(error)\")\n}\n```\n\n### Option 2: Token-Based Login (JWT)\n\n```swift\nlet txConfig = TxConfig(\n    token: \"your_jwt_token\",\n    pushDeviceToken: \"DEVICE_APNS_TOKEN\",\n    ringtone: \"incoming_call.mp3\",\n    ringBackTone: \"ringback_tone.mp3\",\n    logLevel: .all\n)\n\ntry telnyxClient.connect(txConfig: txConfig)\n```\n\n### Configuration Options\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `sipUser` / `token` | String | Credentials from Telnyx Portal |\n| `password` | String | SIP password (credential auth) |\n| `pushDeviceToken` | String? | APNS VoIP push token |\n| `ringtone` | String? | Audio file for incoming calls |\n| `ringBackTone` | String? | Audio file for ringback |\n| `logLevel` | LogLevel | .none, .error, .warning, .debug, .info, .all |\n| `forceRelayCandidate` | Bool | Force TURN relay (avoid local network) |\n\n### Region Selection\n\n```swift\nlet serverConfig = TxServerConfiguration(\n    environment: .production,\n    region: .usEast  // .auto, .usEast, .usCentral, .usWest, .caCentral, .eu, .apac\n)\n\ntry telnyxClient.connect(txConfig: txConfig, serverConfiguration: serverConfig)\n```\n\n---\n\n## Client Delegate\n\nImplement `TxClientDelegate` to receive events:\n\n```swift\nextension ViewController: TxClientDelegate {\n    \n    func onSocketConnected() {\n        // Connected to Telnyx backend\n    }\n    \n    func onSocketDisconnected() {\n        // Disconnected from backend\n    }\n    \n    func onClientReady() {\n        // Ready to make/receive calls\n    }\n    \n    func onClientError(error: Error) {\n        // Handle error\n    }\n    \n    func onIncomingCall(call: Call) {\n        // Incoming call while app is in foreground\n        self.currentCall = call\n    }\n    \n    func onPushCall(call: Call) {\n        // Incoming call from push notification\n        self.currentCall = call\n    }\n    \n    func onCallStateUpdated(callState: CallState, callId: UUID) {\n        switch callState {\n        case .CONNECTING:\n            break\n        case .RINGING:\n            break\n        case .ACTIVE:\n            break\n        case .HELD:\n            break\n        case .DONE(let reason):\n            if let reason = reason {\n                print(\"Call ended: \\(reason.cause ?? \"Unknown\")\")\n                print(\"SIP: \\(reason.sipCode ?? 0) \\(reason.sipReason ?? \"\")\")\n            }\n        case .RECONNECTING(let reason):\n            print(\"Reconnecting: \\(reason.rawValue)\")\n        case .DROPPED(let reason):\n            print(\"Dropped: \\(reason.rawValue)\")\n        }\n    }\n}\n```\n\n---\n\n## Making Outbound Calls\n\n```swift\nlet call = try telnyxClient.newCall(\n    callerName: \"John Doe\",\n    callerNumber: \"+15551234567\",\n    destinationNumber: \"+18004377950\",\n    callId: UUID()\n)\n```\n\n---\n\n## Receiving Inbound Calls\n\n```swift\nfunc onIncomingCall(call: Call) {\n    // Store reference and show UI\n    self.currentCall = call\n    \n    // Answer the call\n    call.answer()\n}\n```\n\n---\n\n## Call Controls\n\n```swift\n// End call\ncall.hangup()\n\n// Mute/Unmute\ncall.muteAudio()\ncall.unmuteAudio()\n\n// Hold/Unhold\ncall.hold()\ncall.unhold()\n\n// Send DTMF\ncall.dtmf(digit: \"1\")\n\n// Toggle speaker\n// (Use AVAudioSession for speaker routing)\n```\n\n---\n\n## Push Notifications (PushKit + CallKit)\n\n### 1. Configure PushKit\n\n```swift\nimport PushKit\n\nclass AppDelegate: UIResponder, UIApplicationDelegate, PKPushRegistryDelegate {\n    \n    private var pushRegistry = PKPushRegistry(queue: .main)\n    \n    func initPushKit() {\n        pushRegistry.delegate = self\n        pushRegistry.desiredPushTypes = [.voIP]\n    }\n    \n    func pushRegistry(_ registry: PKPushRegistry, \n                      didUpdate credentials: PKPushCredentials, \n                      for type: PKPushType) {\n        if type == .voIP {\n            let token = credentials.token.map { String(format: \"%02X\", $0) }.joined()\n            // Save token for use in TxConfig\n        }\n    }\n    \n    func pushRegistry(_ registry: PKPushRegistry, \n                      didReceiveIncomingPushWith payload: PKPushPayload, \n                      for type: PKPushType, \n                      completion: @escaping () -> Void) {\n        if type == .voIP {\n            handleVoIPPush(payload: payload)\n        }\n        completion()\n    }\n}\n```\n\n### 2. Handle VoIP Push\n\n```swift\nfunc handleVoIPPush(payload: PKPushPayload) {\n    guard let metadata = payload.dictionaryPayload[\"metadata\"] as? [String: Any] else { return }\n    \n    let callId = metadata[\"call_id\"] as? String ?? UUID().uuidString\n    let callerName = (metadata[\"caller_name\"] as? String) ?? \"\"\n    let callerNumber = (metadata[\"caller_number\"] as? String) ?? \"\"\n    \n    // Reconnect client and process push\n    let txConfig = TxConfig(sipUser: sipUser, password: password, pushDeviceToken: token)\n    try? telnyxClient.processVoIPNotification(\n        txConfig: txConfig, \n        serverConfiguration: serverConfig,\n        pushMetaData: metadata\n    )\n    \n    // Report to CallKit (REQUIRED on iOS 13+)\n    let callHandle = CXHandle(type: .generic, value: callerNumber)\n    let callUpdate = CXCallUpdate()\n    callUpdate.remoteHandle = callHandle\n    \n    provider.reportNewIncomingCall(with: UUID(uuidString: callId)!, update: callUpdate) { error in\n        if let error = error {\n            print(\"Failed to report call: \\(error)\")\n        }\n    }\n}\n```\n\n### 3. CallKit Integration\n\n```swift\nimport CallKit\n\nclass AppDelegate: CXProviderDelegate {\n    \n    var callKitProvider: CXProvider!\n    \n    func initCallKit() {\n        let config = CXProviderConfiguration(localizedName: \"TelnyxRTC\")\n        config.maximumCallGroups = 1\n        config.maximumCallsPerCallGroup = 1\n        callKitProvider = CXProvider(configuration: config)\n        callKitProvider.setDelegate(self, queue: nil)\n    }\n    \n    // CRITICAL: Audio session handling for WebRTC + CallKit\n    func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {\n        telnyxClient.enableAudioSession(audioSession: audioSession)\n    }\n    \n    func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {\n        telnyxClient.disableAudioSession(audioSession: audioSession)\n    }\n    \n    func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {\n        // Use SDK method to handle race conditions\n        telnyxClient.answerFromCallkit(answerAction: action)\n    }\n    \n    func provider(_ provider: CXProvider, perform action: CXEndCallAction) {\n        telnyxClient.endCallFromCallkit(endAction: action)\n    }\n}\n```\n\n---\n\n## Call Quality Metrics\n\nEnable with `debug: true`:\n\n```swift\nlet call = try telnyxClient.newCall(\n    callerName: \"John\",\n    callerNumber: \"+15551234567\",\n    destinationNumber: \"+18004377950\",\n    callId: UUID(),\n    debug: true\n)\n\ncall.onCallQualityChange = { metrics in\n    print(\"MOS: \\(metrics.mos)\")\n    print(\"Jitter: \\(metrics.jitter * 1000) ms\")\n    print(\"RTT: \\(metrics.rtt * 1000) ms\")\n    print(\"Quality: \\(metrics.quality.rawValue)\")\n    \n    switch metrics.quality {\n    case .excellent, .good:\n        // Green indicator\n    case .fair:\n        // Yellow indicator\n    case .poor, .bad:\n        // Red indicator\n    case .unknown:\n        // Gray indicator\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\n### 1. Anonymous Login\n\n```swift\nclient.anonymousLogin(\n    targetId: \"your-ai-assistant-id\",\n    targetType: \"ai_assistant\"\n)\n```\n\n### 2. Start Conversation\n\n```swift\n// After anonymous login, destination is ignored\nlet call = client.newInvite(\n    callerName: \"User\",\n    callerNumber: \"user\",\n    destinationNumber: \"ai-assistant\",  // Ignored\n    callId: UUID()\n)\n```\n\n### 3. Receive Transcripts\n\n```swift\nlet cancellable = client.aiAssistantManager.subscribeToTranscriptUpdates { transcripts in\n    for item in transcripts {\n        print(\"\\(item.role): \\(item.content)\")\n        // role: \"user\" or \"assistant\"\n    }\n}\n```\n\n### 4. Send Text Message\n\n```swift\nlet success = client.sendAIAssistantMessage(\"Hello, can you help me?\")\n```\n\n---\n\n## Custom Logging\n\n```swift\nclass MyLogger: TxLogger {\n    func log(level: LogLevel, message: String) {\n        // Send to your logging service\n        MyAnalytics.log(level: level, message: message)\n    }\n}\n\nlet txConfig = TxConfig(\n    sipUser: sipUser,\n    password: password,\n    logLevel: .all,\n    customLogger: MyLogger()\n)\n```\n\n---\n\n## Troubleshooting\n\n| Issue | Solution |\n|-------|----------|\n| No audio | Ensure microphone permission granted |\n| Push not working | Verify APNS certificate in Telnyx Portal |\n| CallKit crash on iOS 13+ | Must report incoming call to CallKit |\n| Audio routing issues | Use `enableAudioSession`/`disableAudioSession` in CXProviderDelegate |\n| Login fails | Verify SIP credentials in Telnyx Portal |\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### TxClient\n\n**CLASS**\n\n# `TxClient`\n\n```swift\npublic class TxClient\n```\n\nThe `TelnyxRTC` client connects your application to the Telnyx backend,\nenabling you to make outgoing calls and handle incoming calls.\n\n### Examples\n### Connect and login:\n\n// Initialize the client\n### Listen TxClient delegate events.\n\nextension ViewController: TxClientDelegate {\n### Methods\n### `enableAudioSession(audioSession:)`\n\n```swift\npublic func enableAudioSession(audioSession: AVAudioSession)\n```\n\nEnables and configures the audio session for a call.\nThis method sets up the appropriate audio configuration and activates the session.\n\n- Parameter audioSession: The AVAudioSession instance to configure\n- Important: This method MUST be called from the CXProviderDelegate's `provider(_:didActivate:)` callback\n            to properly handle audio routing when using CallKit integration.\n\nExample usage:\n```swift\nfunc provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {\n    print(\"provider:didActivateAudioSession:\")\n    self.telnyxClient.enableAudioSession(audioSession: audioSession)\n}\n```\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| audioSession | The AVAudioSession instance to configure |\n\n### `disableAudioSession(audioSession:)`\n\n```swift\npublic func disableAudioSession(audioSession: AVAudioSession)\n```\n\nDisables and resets the audio session.\nThis method cleans up the audio configuration and deactivates the session.\n\n- Parameter audioSession: The AVAudioSession instance to reset\n- Important: This method MUST be called from the CXProviderDelegate's `provider(_:didDeactivate:)` callback\n            to properly clean up audio resources when using CallKit integration.\n\nExample usage:\n```swift\nfunc provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {\n    print(\"provider:didDeactivateAudioSession:\")\n    self.telnyxClient.disableAudioSession(audioSession: audioSession)\n}\n```\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| audioSession | The AVAudioSession instance to reset |\n\n### `init()`\n\n```swift\npublic init()\n```\n\nTxClient has to be instantiated.\n\n### `deinit`\n\n```swift\ndeinit\n```\n\nDeinitializer to ensure proper cleanup of resources\n\n### `connect(txConfig:serverConfiguration:)`\n\n```swift\npublic func connect(txConfig: TxConfig,\n                    serverConfiguration: TxServerConfiguration = TxServerConfiguration()) throws\n```\n\nConnects to the iOS cloglient to the Telnyx signaling server using the desired login credentials.\n- Parameters:\n  - txConfig: The desired login credentials. See TxConfig docummentation for more information.\n  - serverConfiguration: (Optional) To define a custom `signaling server` and `TURN/ STUN servers`. As default we use the internal Telnyx Production servers.\n- Throws: TxConfig parameters errors\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| txConfig | The desired login credentials. See TxConfig docummentation for more information. |\n| serverConfiguration | (Optional) To define a custom `signaling server` and `TURN/ STUN servers`. As default we use the internal Telnyx Production servers. |\n\n### `disconnect()`\n\n```swift\npublic func disconnect()\n```\n\nDisconnects the TxClient from the Telnyx signaling server.\n\n### `isConnected()`\n\n```swift\npublic func isConnected() -> Bool\n```\n\nTo check if TxClient is connected to Telnyx server.\n- Returns: `true` if TxClient socket is connected, `false` otherwise.\n\n### `answerFromCallkit(answerAction:customHeaders:debug:)`\n\n```swift\npublic func answerFromCallkit(answerAction: CXAnswerCallAction,\n                              customHeaders: [String:String] = [:],\n                              debug: Bool = false)\n```\n\nAnswers an incoming call from CallKit and manages the active call flow.\n\nThis method should be called from the CXProviderDelegate's `provider(_:perform:)` method\nwhen handling a `CXAnswerCallAction`. It properly integrates with CallKit to answer incoming calls.\n\n### Examples:\n\nextension CallKitProvider: CXProviderDelegate {\n### `endCallFromCallkit(endAction:callId:)`\n\n```swift\npublic func endCallFromCallkit(endAction: CXEndCallAction,\n                               callId: UUID? = nil)\n```\n\nTo end and control callKit active and conn\n\n### `disablePushNotifications()`\n\n```swift\npublic func disablePushNotifications()\n```\n\nTo disable push notifications for the current user\n\n### `getSessionId()`\n\n```swift\npublic func getSessionId() -> String\n```\n\nGet the current session ID after logging into Telnyx Backend.\n- Returns: The current sessionId. If this value is empty, that means that the client is not connected to Telnyx server.\n\n### `anonymousLogin(targetId:targetType:targetVersionId:userVariables:reconnection:serverConfiguration:)`\n\n```swift\npublic func anonymousLogin(\n    targetId: String, \n    targetType: String = \"ai_assistant\", \n    targetVersionId: String? = nil,\n    userVariables: [String: Any] = [:],\n    reconnection: Bool = false,\n    serverConfiguration: TxServerConfiguration = TxServerConfiguration()\n)\n```\n\nPerforms an anonymous login to the Telnyx backend for AI assistant connections.\nThis method allows connecting to AI assistants without traditional authentication.\n\nIf the socket is already connected, the anonymous login message is sent immediately.\nIf not connected, the socket connection process is started, and the anonymous login \nmessage is sent once the connection is established.\n\n- Parameters:\n  - targetId: The target ID for the AI assistant\n  - targetType: The target type (defaults to \"ai_assistant\")\n  - targetVersionId: Optional target version ID\n  - userVariables: Optional user variables to include in the login\n  - reconnection: Whether this is a reconnection attempt (defaults to false)\n  - serverConfiguration: Server configuration to use for connection (defaults to TxServerConfiguration())\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| targetId | The target ID for the AI assistant |\n| targetType | The target type (defaults to “ai_assistant”) |\n| targetVersionId | Optional target version ID |\n| userVariables | Optional user variables to include in the login |\n| reconnection | Whether this is a reconnection attempt (defaults to false) |\n| serverConfiguration | Server configuration to use for connection (defaults to TxServerConfiguration()) |\n\n### `sendRingingAck(callId:)`\n\n```swift\npublic func sendRingingAck(callId: String)\n```\n\nSend a ringing acknowledgment message for a specific call\n- Parameter callId: The call ID to acknowledge\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| callId | The call ID to acknowledge |\n\n### `sendAIAssistantMessage(_:)`\n\n```swift\npublic func sendAIAssistantMessage(_ message: String) -> Bool\n```\n\nSend a text message to AI Assistant during active call (mixed-mode communication)\n- Parameter message: The text message to send to AI assistant\n- Returns: True if message was sent successfully, false otherwise\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| message | The text message to send to AI assistant |\n\n### `sendAIAssistantMessage(_:base64Images:imageFormat:)`\n\n```swift\npublic func sendAIAssistantMessage(_ message: String, base64Images: [String]?, imageFormat: String = \"jpeg\") -> Bool\n```\n\nSend a text message with multiple Base64 encoded images to AI Assistant during active call\n- Parameters:\n  - message: The text message to send to AI assistant\n  - base64Images: Optional array of Base64 encoded image data (without data URL prefix)\n  - imageFormat: Image format (jpeg, png, etc.). Defaults to \"jpeg\"\n- Returns: True if message was sent successfully, false otherwise\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| message | The text message to send to AI assistant |\n| base64Images | Optional array of Base64 encoded image data (without data URL prefix) |\n| imageFormat | Image format (jpeg, png, etc.). Defaults to “jpeg” |\n\n\n### Call\n\n**CLASS**\n\n# `Call`\n\n```swift\npublic class Call\n```\n\nA Call represents an audio or video communication session between two endpoints: WebRTC Clients, SIP clients, or phone numbers.\nThe Call object manages the entire lifecycle of a call, from initiation to termination, handling both outbound and inbound calls.\n\nA Call object is created in two scenarios:\n1. When you initiate a new outbound call using TxClient's newCall method\n2. When you receive an inbound call through the TxClientDelegate's onIncomingCall callback\n\n### Key Features\n- Audio and video call support\n- Call state management (NEW, CONNECTING, RINGING, ACTIVE, HELD, DONE)\n- Mute/unmute functionality\n- DTMF tone sending\n- Custom headers support for both INVITE and ANSWER messages\n- Call statistics reporting when debug mode is enabled\n\n### Examples\n### Creating an Outbound Call:\n\n// Initialize the client\n### Handling an Incoming Call:\n\nclass CallHandler: TxClientDelegate {\n### Examples\n```swift\n// Access local audio tracks for visualization\nif let localStream = call.localStream {\n    let audioTracks = localStream.audioTracks\n    // Use audio tracks for waveform visualization\n}\n```\n\n### `remoteStream`\n\n```swift\npublic var remoteStream: RTCMediaStream?\n```\n\nThe remote media stream containing audio and/or video tracks received from the remote party.\nThis stream represents the media being received from the other participant in the call.\nCan be used for audio visualization, remote video display, or other media processing.\n\n### Examples\n```swift\n// Access remote audio tracks for visualization\nif let remoteStream = call.remoteStream {\n    let audioTracks = remoteStream.audioTracks\n    // Use audio tracks for waveform visualization\n}\n```\n\n\n### TxConfig\n\n**STRUCT**\n\n# `TxConfig`\n\n```swift\npublic struct TxConfig\n```\n\nThis structure is intended to used for Telnyx SDK configurations.\n\n### Methods\n### `init(sipUser:password:pushDeviceToken:ringtone:ringBackTone:pushEnvironment:logLevel:customLogger:reconnectClient:debug:forceRelayCandidate:enableQualityMetrics:sendWebRTCStatsViaSocket:reconnectTimeOut:useTrickleIce:enableCallReports:callReportInterval:callReportLogLevel:callReportMaxLogEntries:)`\n\n```swift\npublic init(sipUser: String, password: String,\n            pushDeviceToken: String? = nil,\n            ringtone: String? = nil,\n            ringBackTone: String? = nil,\n            pushEnvironment: PushEnvironment? = nil,\n            logLevel: LogLevel = .none,\n            customLogger: TxLogger? = nil,\n            reconnectClient: Bool = true,\n            debug: Bool = false,\n            forceRelayCandidate: Bool = false,\n            enableQualityMetrics: Bool = false,\n            sendWebRTCStatsViaSocket: Bool = false,\n            reconnectTimeOut: Double = DEFAULT_TIMEOUT,\n            useTrickleIce: Bool = false,\n            enableCallReports: Bool = true,\n            callReportInterval: TimeInterval = 5.0,\n            callReportLogLevel: String = \"debug\",\n            callReportMaxLogEntries: Int = 1000\n)\n```\n\nConstructor for the Telnyx SDK configuration using SIP credentials.\n- Parameters:\n  - sipUser: The SIP username for authentication\n  - password: The password associated with the SIP user\n  - pushDeviceToken: (Optional) The device's push notification token, required for receiving inbound call notifications\n  - ringtone: (Optional) The audio file name to play for incoming calls (e.g., \"my-ringtone.mp3\")\n  - ringBackTone: (Optional) The audio file name to play while making outbound calls (e.g., \"my-ringbacktone.mp3\")\n  - pushEnvironment: (Optional) The push notification environment (production or debug)\n  - logLevel: (Optional) The verbosity level for SDK logs (defaults to `.none`)\n  - customLogger: (Optional) Custom logger implementation for handling SDK logs. If not provided, the default logger will be used\n  - reconnectClient: (Optional) Whether the client should attempt to reconnect automatically. Default is true.\n  - debug: (Optional) Enables WebRTC communication statistics reporting to Telnyx servers. Default is false.\n  - forceRelayCandidate: (Optional) Controls whether the SDK should force TURN relay for peer connections. Default is false.\n  - enableQualityMetrics: (Optional) Controls whether the SDK should deliver call quality metrics. Default is false.\n  - sendWebRTCStatsViaSocket: (Optional) Whether to send WebRTC statistics via socket to Telnyx servers. Default is false.\n  - reconnectTimeOut: (Optional) Maximum time in seconds the SDK will attempt to reconnect a call after network disruption. Default is 60 seconds.\n  - useTrickleIce: (Optional) Controls whether the SDK should use trickle ICE for WebRTC signaling. Default is false.\n  - enableCallReports: (Optional) Enable automatic call quality reporting to voice-sdk-proxy. Default is true.\n  - callReportInterval: (Optional) Interval in seconds for collecting call statistics. Default is 5.0.\n  - callReportLogLevel: (Optional) Minimum log level to capture for call reports. Default is \"debug\".\n  - callReportMaxLogEntries: (Optional) Maximum number of log entries to buffer per call. Default is 1000.\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| sipUser | The SIP username for authentication |\n| password | The password associated with the SIP user |\n| pushDeviceToken | (Optional) The device’s push notification token, required for receiving inbound call notifications |\n| ringtone | (Optional) The audio file name to play for incoming calls (e.g., “my-ringtone.mp3”) |\n| ringBackTone | (Optional) The audio file name to play while making outbound calls (e.g., “my-ringbacktone.mp3”) |\n| pushEnvironment | (Optional) The push notification environment (production or debug) |\n| logLevel | (Optional) The verbosity level for SDK logs (defaults to `.none`) |\n| customLogger | (Optional) Custom logger implementation for handling SDK logs. If not provided, the default logger will be used |\n| reconnectClient | (Optional) Whether the client should attempt to reconnect automatically. Default is true. |\n| debug | (Optional) Enables WebRTC communication statistics reporting to Telnyx servers. Default is false. |\n| forceRelayCandidate | (Optional) Controls whether the SDK should force TURN relay for peer connections. Default is false. |\n| enableQualityMetrics | (Optional) Controls whether the SDK should deliver call quality metrics. Default is false. |\n| sendWebRTCStatsViaSocket | (Optional) Whether to send WebRTC statistics via socket to Telnyx servers. Default is false. |\n| reconnectTimeOut | (Optional) Maximum time in seconds the SDK will attempt to reconnect a call after network disruption. Default is 60 seconds. |\n| useTrickleIce | (Optional) Controls whether the SDK should use trickle ICE for WebRTC signaling. Default is false. |\n| enableCallReports | (Optional) Enable automatic call quality reporting to voice-sdk-proxy. Default is true. |\n| callReportInterval | (Optional) Interval in seconds for collecting call statistics. Default is 5.0. |\n| callReportLogLevel | (Optional) Minimum log level to capture for call reports. Default is “debug”. |\n| callReportMaxLogEntries | (Optional) Maximum number of log entries to buffer per call. Default is 1000. |\n\n### `init(token:pushDeviceToken:ringtone:ringBackTone:pushEnvironment:logLevel:customLogger:reconnectClient:debug:forceRelayCandidate:enableQualityMetrics:sendWebRTCStatsViaSocket:reconnectTimeOut:useTrickleIce:enableCallReports:callReportInterval:callReportLogLevel:callReportMaxLogEntries:)`\n\n```swift\npublic init(token: String,\n            pushDeviceToken: String? = nil,\n            ringtone: String? = nil,\n            ringBackTone: String? = nil,\n            pushEnvironment: PushEnvironment? = nil,\n            logLevel: LogLevel = .none,\n            customLogger: TxLogger? = nil,\n            reconnectClient: Bool = true,\n            debug: Bool = false,\n            forceRelayCandidate: Bool = false,\n            enableQualityMetrics: Bool = false,\n            sendWebRTCStatsViaSocket: Bool = false,\n            reconnectTimeOut: Double = DEFAULT_TIMEOUT,\n            useTrickleIce: Bool = false,\n            enableCallReports: Bool = true,\n            callReportInterval: TimeInterval = 5.0,\n            callReportLogLevel: String = \"debug\",\n            callReportMaxLogEntries: Int = 1000\n)\n```\n\nConstructor for the Telnyx SDK configuration using JWT token authentication.\n- Parameters:\n  - token: JWT token generated from https://developers.telnyx.com/docs/v2/webrtc/quickstart\n  - pushDeviceToken: (Optional) The device's push notification token, required for receiving inbound call notifications\n  - ringtone: (Optional) The audio file name to play for incoming calls (e.g., \"my-ringtone.mp3\")\n  - ringBackTone: (Optional) The audio file name to play while making outbound calls (e.g., \"my-ringbacktone.mp3\")\n  - pushEnvironment: (Optional) The push notification environment (production or debug)\n  - logLevel: (Optional) The verbosity level for SDK logs (defaults to `.none`)\n  - customLogger: (Optional) Custom logger implementation for handling SDK logs. If not provided, the default logger will be used\n  - reconnectClient: (Optional) Whether the client should attempt to reconnect automatically. Default is true.\n  - debug: (Optional) Enables WebRTC communication statistics reporting to Telnyx servers. Default is false.\n  - forceRelayCandidate: (Optional) Controls whether the SDK should force TURN relay for peer connections. Default is false.\n  - enableQualityMetrics: (Optional) Controls whether the SDK should deliver call quality metrics. Default is false.\n  - sendWebRTCStatsViaSocket: (Optional) Whether to send WebRTC statistics via socket to Telnyx servers. Default is false.\n  - reconnectTimeOut: (Optional) Maximum time in seconds the SDK will attempt to reconnect a call after network disruption. Default is 60 seconds.\n  - useTrickleIce: (Optional) Controls whether the SDK should use trickle ICE for WebRTC signaling. Default is false.\n  - enableCallReports: (Optional) Enable automatic call quality reporting to voice-sdk-proxy. Default is true.\n  - callReportInterval: (Optional) Interval in seconds for collecting call statistics. Default is 5.0.\n  - callReportLogLevel: (Optional) Minimum log level to capture for call reports. Default is \"debug\".\n  - callReportMaxLogEntries: (Optional) Maximum number of log entries to buffer per call. Default is 1000.\n\n**Parameters**\n\n| Name | Description |\n| ---- | ----------- |\n| token | JWT token generated from https://developers.telnyx.com/docs/v2/webrtc/quickstart |\n| pushDeviceToken | (Optional) The device’s push notification token, required for receiving inbound call notifications |\n| ringtone | (Optional) The audio file name to play for incoming calls (e.g., “my-ringtone.mp3”) |\n| ringBackTone | (Optional) The audio file name to play while making outbound calls (e.g., “my-ringbacktone.mp3”) |\n| pushEnvironment | (Optional) The push notification environment (production or debug) |\n| logLevel | (Optional) The verbosity level for SDK logs (defaults to `.none`) |\n| customLogger | (Optional) Custom logger implementation for handling SDK logs. If not provided, the default logger will be used |\n| reconnectClient | (Optional) Whether the client should attempt to reconnect automatically. Default is true. |\n| debug | (Optional) Enables WebRTC communication statistics reporting to Telnyx servers. Default is false. |\n| forceRelayCandidate | (Optional) Controls whether the SDK should force TURN relay for peer connections. Default is false. |\n| enableQualityMetrics | (Optional) Controls whether the SDK should deliver call quality metrics. Default is false. |\n| sendWebRTCStatsViaSocket | (Optional) Whether to send WebRTC statistics via socket to Telnyx servers. Default is false. |\n| reconnectTimeOut | (Optional) Maximum time in seconds the SDK will attempt to reconnect a call after network disruption. Default is 60 seconds. |\n| useTrickleIce | (Optional) Controls whether the SDK should use trickle ICE for WebRTC signaling. Default is false. |\n| enableCallReports | (Optional) Enable automatic call quality reporting to voice-sdk-proxy. Default is true. |\n| callReportInterval | (Optional) Interval in seconds for collecting call statistics. Default is 5.0. |\n| callReportLogLevel | (Optional) Minimum log level to capture for call reports. Default is “debug”. |\n| callReportMaxLogEntries | (Optional) Maximum number of log entries to buffer per call. Default is 1000. |\n\n### `validateParams()`\n\n```swift\npublic func validateParams() throws\n```\n\nValidate if TxConfig parameters are valid\n- Throws: Throws TxConfig parameters errors\n\n<!-- END AUTO-GENERATED API REFERENCE -->","tags":["telnyx","webrtc","client","ios","team-telnyx","agent-skills","ai-coding-agent","claude-code","cpaas","cursor","iot","llm"],"capabilities":["skill","source-team-telnyx","skill-telnyx-webrtc-client-ios","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-ios","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 (31,300 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:55.282Z","embedding":null,"createdAt":"2026-04-18T22:08:43.962Z","updatedAt":"2026-04-22T00:54:55.282Z","lastSeenAt":"2026-04-22T00:54:55.282Z","tsv":"'+15551234567':409,733 '+18004377950':411,735 '/docs/v2/webrtc/quickstart':2791,3049 '/team-telnyx/telnyx-webrtc-ios.git':83 '0':381,503 '0.1.0':61 '02x':502 '1':73,91,134,449,461,653,655,799,1897 '1000':749,754,2161,2432,2696,2772,3038,3296 '13':601,925 '2':79,100,181,531,813,1910 '3':84,118,633,837 '3.0':795 '3.1':792 '3.6':793 '3.7':789 '4':857 '4.0':790 '4.1':786 '4.2':784,787 '5.0':2155,2405,2669,2766,3011,3269 '60':2361,2625,2967,3225 'access':127,1978,2046 'acknowledg':1665,1677,1686 'action':696,707,713,717 'activ':360,1046,1364,1413,1703,1768,1936 'add':77,121 'agent':797 'ai':796,807,811,832,1480,1503,1511,1557,1565,1610,1618,1700,1717,1738,1765,1778,1820 'ai-assist':831 'airplay':113 'allow':1508 'alreadi':1520 'and/or':2009 'anonym':800,818,1496,1523,1540 'anonymouslogin':1465,1475 'answer':429,1355,1389,1951 'answeract':706,1340,1347 'answerfromcallkit':1339,1346 'apac':280 'api':956,976 'apn':160,197,231,916 'app':328 'appdeleg':468,640 'applic':18,990 'appropri':1042 'array':1782,1824 'assist':808,812,833,856,1481,1504,1512,1558,1566,1611,1619,1701,1718,1739,1766,1779,1821 'associ':2181,2445 'attempt':1587,1640,2277,2351,2541,2615,2883,2957,3141,3215 'audio':112,237,244,665,907,932,1032,1043,1072,1115,1122,1152,1854,1925,1980,1992,2008,2035,2048,2060,2203,2219,2467,2483,2809,2825,3067,3083 'audiosess':676,679,680,686,689,690,1021,1026,1050,1086,1092,1093,1097,1104,1109,1129,1166,1172,1173,1177 'audiotrack':1989,2057 'auth':228 'authent':132,972,1515,2177,2441,2782 'auto':274 'automat':2280,2382,2544,2646,2886,2988,3144,3246 'avaudiosess':453,677,687,1027,1052,1087,1099,1110,1131,1167,1179 'avoid':261 'backend':303,308,994,1444,1501 'background':102,107 'bad':772,794 'base':137,184 'base64':1761,1784,1826 'base64images':1741,1749,1780,1822 'bash':64 'bitcod':93,96 'bool':257,1320,1353,1489,1694,1754,2129,2132,2135,2138,2141,2148,2151,2740,2743,2746,2749,2752,2759,2762 'branch':88 'break':355,358,361,364 'buffer':2427,2691,3033,3291 'build':10,94 'cacentr':278 'call':131,241,314,323,324,326,333,336,337,339,344,374,399,402,416,420,421,428,431,433,437,553,631,718,727,824,929,1000,1004,1036,1061,1140,1358,1365,1371,1391,1670,1674,1683,1704,1769,1843,1845,1849,1851,1870,1878,1888,1890,1904,1916,1928,1930,1953,1965,1972,2030,2198,2210,2227,2321,2355,2383,2401,2414,2429,2462,2474,2491,2585,2619,2647,2665,2678,2693,2804,2816,2833,2927,2961,2989,3007,3020,3035,3062,3074,3091,3185,3219,3247,3265,3278,3293 'call.answer':432 'call.dtmf':447 'call.hangup':438 'call.hold':443 'call.localstream':1987 'call.mp3':164,201 'call.muteaudio':440 'call.oncallqualitychange':740 'call.remotestream':2055 'call.unhold':444 'call.unmuteaudio':441 'callback':1068,1147,1922 'caller':562,569 'callernam':405,560,730,826 'callernumb':408,567,608,732,828 'callhandl':603,613,1974 'callid':349,412,551,618,736,835,1398,1405,1655,1660,1672,1681 'callkit':460,597,634,638,670,921,931,1076,1156,1360,1387,1412 'callkitprovid':643,656,1394 'callkitprovider.setdelegate':660 'callreportinterv':2100,2153,2394,2658,2713,2764,3000,3258 'callreportloglevel':2101,2156,2406,2670,2714,2767,3012,3270 'callreportmaxlogentri':2102,2159,2419,2683,2715,2770,3025,3283 'callstat':347,348,352 'callupd':610,620 'callupdate.remotehandle':612 'cancel':842 'capabl':105,106 'captur':2412,2676,3018,3276 'case':353,356,359,362,365,383,390,761,766,770,775 'catch':175 'certif':917 'check':1322 'class':467,639,873,979,983,1844,1848,1973 'clean':1119,1150 'cleanup':1199 'client':4,287,574,987,1011,1458,1863,1865,1968,2275,2539,2881,3139 'client.aiassistantmanager.subscribetotranscriptupdates':843 'client.anonymouslogin':803 'client.newinvite':825 'client.sendaiassistantmessage':864 'cloglient':1219 'cocoapod':57 'collect':2400,2664,3006,3264 'communic':15,1708,1857,2288,2552,2894,3152 'complet':521,530 'condit':704 'config':648,659 'config.maximumcallgroups':652 'config.maximumcallspercallgroup':654 'configur':90,211,462,658,1030,1044,1055,1102,1123,1593,1646,2081,2167,2778 'conn':1415 'connect':177,300,354,988,1006,1202,1208,1215,1326,1336,1461,1505,1509,1521,1531,1534,1547,1597,1650,1934,2309,2573,2915,3173 'constructor':2162,2773 'contain':2007 'control':434,1411,2299,2315,2365,2563,2579,2629,2905,2921,2971,3163,3179,3229 'convers':815 'crash':922 'creat':23,1893,1962 'creation':958 'credenti':25,136,219,227,489,944,957,1229,1235,1274,2170 'credential-bas':135 'credentials.token.map':499 'critic':664 'current':1427,1437,1447 'custom':870,1247,1286,1944,2255,2519,2861,3119 'customhead':1341,1349 'customlogg':901,2091,2125,2253,2517,2704,2736,2859,3117 'cxanswercallact':697,1348,1382 'cxcallupd':611 'cxendcallact':714,1404 'cxhandl':604 'cxprovid':644,657,674,684,694,711,1084,1164 'cxproviderconfigur':649 'cxproviderdeleg':641,939,1064,1143,1374,1395 'data':1787,1789,1829,1831 'deactiv':1125 'debug':253,723,738,1342,1352,1957,2093,2131,2158,2241,2284,2418,2505,2548,2682,2706,2742,2769,2847,2890,3024,3105,3148,3282 'default':1255,1294,1563,1588,1598,1616,1641,1651,1798,1840,2145,2250,2266,2281,2294,2310,2324,2339,2359,2376,2391,2403,2416,2430,2514,2530,2545,2558,2574,2588,2603,2623,2640,2655,2667,2680,2694,2756,2856,2872,2887,2900,2916,2930,2945,2965,2982,2997,3009,3022,3036,3114,3130,3145,3158,3174,3188,3203,3223,3240,3255,3267,3280,3294 'defin':1245,1284 'deinit':1192,1194 'deiniti':1195 'deleg':288,1014 'deliv':2320,2584,2926,3184 'descript':215,1096,1176,1269,1603,1680,1730,1812,2435,3041 'desir':1227,1233,1272 'destin':820 'destinationnumb':410,734,830 'developers.telnyx.com':2790,3048 'developers.telnyx.com/docs/v2/webrtc/quickstart':2789,3047 'devic':159,196,2189,2453,2795,3053 'didactiv':675,1067,1085 'didactivateaudiosess':1090 'diddeactiv':685,1146,1165 'diddeactivateaudiosess':1170 'didreceiveincomingpushwith':515 'didupd':488 'digit':448 'disabl':92,1111,1422 'disableaudiosess':937,1103,1108 'disablepushnotif':1416,1420 'disconnect':306,1302,1306,1307 'display':2039 'disrupt':2358,2622,2964,3222 'documment':1238,1277 'doe':407 'done':366,1938 'doubl':2144,2755 'drop':391,395 'dtmf':446,1941 'e.g':49,2211,2228,2475,2492,2817,2834,3075,3092 'els':548 'empti':1453 'enabl':101,721,995,1028,1960,2286,2381,2550,2645,2892,2987,3150,3245 'enableaudiosess':936,1020,1025 'enablecallreport':2099,2150,2379,2643,2712,2761,2985,3243 'enablequalitymetr':2095,2137,2313,2577,2708,2748,2919,3177 'encod':1762,1785,1827 'end':375,436,1409 'endact':716,1397,1403 'endcallfromcallkit':1396,1402 'endpoint':1861 'ensur':908,1197 'enter':80 'entir':1874 'entri':2425,2689,3031,3289 'environ':270,2238,2502,2844,3102 'error':178,179,251,317,318,320,621,625,626,632,1266,3313 'escap':522 'establish':1549 'etc':1797,1839 'eu':279 'event':293,1015 'exampl':1005,1078,1158,1392,1961,1976,2044 'excel':762,783 'extens':295,1016,1393 'fail':628,941 'fair':767,788 'fals':1337,1354,1490,1590,1643,1726,1808,2133,2136,2139,2142,2149,2296,2312,2326,2341,2378,2560,2576,2590,2605,2642,2744,2747,2750,2753,2760,2902,2918,2932,2947,2984,3160,3176,3190,3205,3242 'featur':1924 'file':76,238,245,2204,2220,2468,2484,2810,2826,3068,3084 'flow':1366 'forc':258,2304,2568,2910,3168 'forcerelaycandid':256,2094,2134,2297,2561,2707,2745,2903,3161 'foreground':331 'format':501,1794,1836 'func':298,304,309,315,321,334,345,418,478,484,511,536,645,671,681,691,708,876,1024,1081,1107,1161,1207,1305,1318,1345,1401,1419,1432,1474,1658,1690,1745,3300 'function':1940 'generat':27,960,2787,3045 'generic':606 'get':1435 'getsessionid':1429,1433 'github.com':82 'github.com/team-telnyx/telnyx-webrtc-ios.git':81 'good':763,785 'grant':911 'gray':777 'green':764 'guard':540 'handl':319,532,667,702,1002,1071,1380,1883,1969,2259,2523,2865,3123 'handlevoippush':527,537 'header':1945 'held':363,1937 'hello':865 'help':868 'hold/unhold':442 'ice':2372,2636,2978,3236 'id':554,809,1439,1554,1571,1607,1624,1675,1684 'ignor':822,834 'imag':1763,1786,1793,1828,1835 'imageformat':1742,1751,1792,1834 'immedi':1528 'implement':289,2257,2521,2863,3121 'import':140,465,637,1056,1135 'inbound':415,1887,1915,2197,2461,2803,3061 'includ':1577,1630 'incom':163,200,240,325,338,928,1003,1357,1390,1971,2209,2473,2815,3073 'indic':765,769,774,778 'info':254 'info.plist':123 'inform':1241,1280 'init':1183,1186,2083,2105,2697,2718 'initcallkit':646 'initi':1009,1880,1900,1966 'initpushkit':479 'instal':56,66 'instanc':1053,1100,1132,1180 'instanti':1191 'int':2160,2771 'integr':635,798,1077,1157,1385 'intend':2075 'intern':1259,1298 'interv':2396,2660,3002,3260 'invit':1949 'io':5,8,17,600,924,1218 'ip':111 'isconnect':1315,1319 'issu':904,934 'item':847 'item.content':852 'item.role':851 'javascript':55 'jitter':747 'john':406,731 'join':504 'jpeg':1753,1795,1800,1837,1842 'jwt':186,193,2780,2785,3043 'key':1923 'languag':47 'let':142,147,188,267,367,370,385,392,401,497,541,550,559,566,578,602,609,624,647,726,823,841,862,892,1985,1988,2053,2056 'level':780,878,888,889,2246,2410,2510,2674,2852,3016,3110,3274 'lifecycl':1875 'listen':1012 'local':262,1979 'localizednam':650 'localstream':1986 'localstream.audiotracks':1990 'log':871,877,885,1441,2249,2261,2409,2424,2513,2525,2673,2688,2855,2867,3015,3030,3113,3125,3273,3288 'logger':2256,2267,2520,2531,2862,2873,3120,3131 'login':29,138,185,801,819,940,1008,1228,1234,1273,1497,1524,1541,1580,1633 'loglevel':168,205,248,249,879,899,2090,2122,2123,2242,2506,2703,2733,2734,2848,3106 'main':87,477 'make':397,998,2225,2489,2831,3089 'make/receive':313 'manag':72,1362,1872,1932 'maximum':2344,2421,2608,2685,2950,3027,3208,3285 'mean':1455 'media':2005,2021,2042 'messag':860,880,890,891,1525,1542,1666,1692,1698,1710,1713,1722,1731,1734,1747,1758,1771,1774,1804,1813,1816,1952 'metadata':542,544,552,561,568,594 'method':700,1019,1038,1058,1118,1137,1368,1378,1507,1909,2082 'metric':720,741,2323,2587,2929,3187 'metrics.jitter':748 'metrics.mos':745 'metrics.quality':760 'metrics.quality.rawvalue':758 'metrics.rtt':753 'microphon':119,126,909 'minimum':2408,2672,3014,3272 'mix':1706 'mixed-mod':1705 'mode':103,108,1707,1958 'mos':744,781 'mp3':2215,2232,2479,2496,2821,2838,3079,3096 'ms':750,755 'multipl':1760 'must':926,966,1059,1138 'mute/unmute':439,1939 'my-ringbackton':2229,2493,2835,3093 'my-rington':2212,2476,2818,3076 'myanalytics.log':887 'mylogg':874,902 'name':563,1095,1175,1268,1602,1679,1729,1811,2205,2221,2434,2469,2485,2811,2827,3040,3069,3085 'network':263,2357,2621,2963,3221 'new':1902,1933 'newcal':1908 'nil':663,1407,1484,2112,2115,2118,2121,2127,2723,2726,2729,2732,2738 'none':250,2124,2252,2516,2735,2858,3116 'notif':342,458,963,975,1424,2192,2199,2237,2456,2463,2501,2798,2805,2843,3056,3063,3101 'nsmicrophoneusagedescript':125 'number':570,1868,2422,2686,3028,3286 'object':1871,1891 'oncallstateupd':346 'onclienterror':316 'onclientreadi':310 'onincomingcal':322,419,1921 'onpushcal':335 'onsocketconnect':299 'onsocketdisconnect':305 'option':133,180,212,1243,1282,1568,1573,1621,1626,1781,1823,2187,2201,2217,2234,2243,2254,2272,2285,2298,2314,2328,2343,2364,2380,2395,2407,2420,2451,2465,2481,2498,2507,2518,2536,2549,2562,2578,2592,2607,2628,2644,2659,2671,2684,2793,2807,2823,2840,2849,2860,2878,2891,2904,2920,2934,2949,2970,2986,3001,3013,3026,3051,3065,3081,3098,3107,3118,3136,3149,3162,3178,3192,3207,3228,3244,3259,3271,3284 'otherwis':1338,1727,1809 'outbound':398,1885,1903,1964,2226,2490,2832,3090 'outgo':999 'packag':71,78 'paramet':213,1049,1094,1128,1174,1230,1265,1267,1550,1601,1671,1678,1709,1728,1770,1810,2171,2433,2783,3039,3306,3312 'parti':2016 'particip':2027 'password':154,157,223,226,583,584,897,898,2085,2108,2178,2180,2442,2444 'payload':516,528,529,538 'payload.dictionarypayload':543 'peer':2308,2572,2914,3172 'per':2428,2692,3034,3292 'perform':695,712,1377,1494 'permiss':120,910 'phone':1867 'pictur':115,117 'pkpushcredenti':490 'pkpushpayload':517,539 'pkpushregistri':475,487,514 'pkpushregistrydeleg':471 'pkpushtyp':493,520 'play':2207,2223,2471,2487,2813,2829,3071,3087 'plugin':48 'png':1796,1838 'pod':59,65 'poor':771,791 'portal':222,920,947 'prefix':1791,1833 'prerequisit':22 'print':176,373,378,387,394,627,743,746,751,756,850,1088,1168 'privat':472 'process':576,1535,2043 'product':271,1261,1300,2239,2503,2845,3103 'project':89 'proper':1070,1149,1198,1384 'provid':672,673,682,683,692,693,709,710,1066,1082,1083,1089,1145,1162,1163,1169,1376,2264,2528,2870,3128 'provider.reportnewincomingcall':614 'proxi':2390,2654,2996,3254 'public':982,1023,1106,1185,1206,1304,1317,1344,1400,1418,1431,1473,1657,1689,1744,1847,1999,2069,2104,2717,3299 'push':233,341,457,534,577,912,962,974,1423,2191,2236,2455,2500,2797,2842,3055,3100 'pushdevicetoken':158,195,229,585,2086,2110,2186,2450,2699,2721,2792,3050 'pushenviron':2089,2119,2120,2233,2497,2702,2730,2731,2839,3097 'pushkit':459,463,466 'pushmetadata':593 'pushregistri':474,485,512 'pushregistry.delegate':480 'pushregistry.desiredpushtypes':482 'python':52 'qualiti':719,757,779,2322,2384,2586,2648,2928,2990,3186,3248 'queue':476,662 'race':703 'rang':782 'read':967 'readi':311 'real':12 'real-tim':11 'reason':368,371,372,386,393 'reason.cause':376 'reason.rawvalue':389,396 'reason.sipcode':380 'reason.sipreason':382 'receiv':292,414,838,1913,2012,2023,2196,2460,2802,3060 'reconnect':384,388,573,1470,1488,1581,1586,1634,1639,2279,2353,2543,2617,2885,2959,3143,3217 'reconnectcli':2092,2128,2271,2535,2705,2739,2877,3135 'reconnecttimeout':2097,2143,2342,2606,2710,2754,2948,3206 'red':773 'refer':423,977 'references/webrtc-server-api.md':948,949 'region':264,272 'registri':486,513 'relay':260,2306,2570,2912,3170 'remot':2004,2015,2037,2047 'remotestream':1997,2001,2054 'remotestream.audiotracks':2058 'repo':68 'repo-upd':67 'report':595,630,927,1955,2290,2385,2415,2554,2649,2679,2896,2991,3021,3154,3249,3279 'repres':1852,2019 'requir':128,598,2194,2458,2800,3058 'reset':1113,1134,1182 'resourc':1153,1201 'return':549,1330,1445,1719,1801 'ring':357,1664,1935 'ringback':166,203,247 'ringbackton':165,202,242,2088,2116,2216,2231,2480,2495,2701,2727,2822,2837,3080,3095 'rington':162,199,235,2087,2113,2200,2214,2464,2478,2700,2724,2806,2820,3064,3078 'role':853 'rout':456,933,1073 'rtcmediastream':2002 'rtt':752 'rubi':58 'run':63 'save':505 'scenario':1896 'sdk':9,37,699,2080,2166,2248,2260,2302,2318,2349,2368,2389,2512,2524,2566,2582,2613,2632,2653,2777,2854,2866,2908,2924,2955,2974,2995,3112,3124,3166,3182,3213,3232,3253 'second':2347,2362,2398,2611,2626,2662,2953,2968,3004,3211,3226,3262 'see':38,1236,1275 'select':85,265 'self':146,481,661 'self.currentcall':332,343,427 'self.telnyxclient.disableaudiosession':1171 'self.telnyxclient.enableaudiosession':1091 'send':445,858,882,1662,1695,1715,1736,1755,1776,1818,1943,2331,2595,2937,3195 'sendaiassistantmessag':1687,1691,1740,1746 'sendringingack':1654,1659 'sendwebrtcstatsviasocket':2096,2140,2327,2591,2709,2751,2933,3191 'sent':1527,1544,1724,1806 'server':35,46,953,1224,1249,1253,1262,1288,1292,1301,1314,1329,1464,1592,1645,2293,2338,2557,2602,2899,2944,3157,3202 'server-sid':34,952 'serverconfig':268,286,592 'serverconfigur':285,591,1204,1211,1242,1281,1471,1491,1591,1644 'servic':886 'session':666,1033,1048,1116,1127,1438,1858 'sessionid':1448 'set':95,97,970,1039 'setup':964 'show':425 'side':36,954 'sign':104 'signal':1223,1248,1287,1313,2375,2639,2981,3239 'sip':152,156,225,379,943,1864,2169,2174,2184,2438,2448 'sipus':150,216,581,582,895,896,2084,2106,2172,2436 'skill':43 'skill-telnyx-webrtc-client-ios' 'socket':1334,1518,1533,2335,2599,2941,3199 'solut':905 'source-team-telnyx' 'speaker':451,455 'specif':1669 'start':814,1537 'state':1931 'statist':1954,2289,2333,2402,2553,2597,2666,2895,2939,3008,3153,3197,3266 'store':422 'stream':2006,2018 'string':218,224,230,236,243,500,546,556,565,572,881,1350,1351,1434,1477,1479,1483,1486,1661,1693,1748,1750,1752,2107,2109,2111,2114,2117,2157,2720,2722,2725,2728,2768 'struct':2066,2070 'structur':2073 'stun':1252,1291 'success':863,1725,1807 'support':1929,1946 'swift':70,139,187,266,294,400,417,435,464,535,636,725,802,816,840,861,872,981,1022,1080,1105,1160,1184,1193,1205,1303,1316,1343,1399,1417,1430,1472,1656,1688,1743,1846,1977,1998,2045,2068,2103,2716,3298 'switch':351,759 'target':1553,1561,1569,1606,1614,1622 'targetid':804,1466,1476,1551,1604 'targettyp':810,1467,1478,1559,1612 'targetversionid':1468,1482,1567,1620 'telnyx':2,6,20,33,41,51,54,221,302,919,946,993,1222,1260,1299,1312,1328,1443,1463,1500,2079,2165,2292,2337,2556,2601,2776,2898,2943,3156,3201 'telnyx-javascript':53 'telnyx-python':50 'telnyx-webrtc':40 'telnyx-webrtc-client-io':1 'telnyxcli':143 'telnyxclient.answerfromcallkit':705 'telnyxclient.connect':172,208,282 'telnyxclient.delegate':145 'telnyxclient.disableaudiosession':688 'telnyxclient.enableaudiosession':678 'telnyxclient.endcallfromcallkit':715 'telnyxclient.newcall':404,729 'telnyxclient.processvoipnotification':588 'telnyxrtc':60,141,651,986 'termin':1882 'text':859,1697,1712,1733,1757,1773,1815 'throw':1214,1263,3302,3309,3310 'time':13,2345,2609,2951,3209 'timeinterv':2154,2765 'timeout':2146,2757 'toggl':450 'token':30,161,183,191,194,198,217,234,498,506,586,959,2193,2457,2698,2719,2781,2784,2786,2799,3042,3044,3057 'token-bas':182 'tone':1942 'tone.mp3':167,204 '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' 'track':1981,1993,2011,2049,2061 'tradit':1514 'transcript':839,844,849 'tri':171,207,281,403,587,728 'trickl':2371,2635,2977,3235 'troubleshoot':903 'true':724,739,1331,1720,1802,2130,2152,2283,2393,2547,2657,2741,2763,2889,2999,3147,3257 'turn':259,1251,1290,2305,2569,2911,3169 'two':1860,1895 'txclient':144,978,980,984,1013,1187,1309,1324,1333,1906 'txclientdeleg':290,297,1018,1919,1975 'txconfig':148,149,173,174,189,190,209,210,283,284,510,579,580,589,590,893,894,1203,1209,1210,1231,1237,1264,1270,1276,2065,2067,2071,3305,3311 'txlogger':875,2126,2737 'txserverconfigur':269,1212,1213,1492,1493,1600,1653 'type':214,492,495,519,525,605,1562,1615 'ui':426 'uiapplicationdeleg':470 'uirespond':469 'unknown':377,776 'updat':69,619 'url':1790,1832 'usag':1079,1159 'uscentr':276 'use':19,31,452,508,698,935,1075,1155,1225,1257,1296,1595,1648,1905,1991,2033,2059,2077,2168,2270,2370,2534,2634,2779,2876,2976,3134,3234 'useast':273,275 'user':827,829,854,1428,1574,1627,2185,2449 'usernam':153,2175,2439 'uservari':1469,1485,1572,1625 'usetrickleic':2098,2147,2363,2627,2711,2758,2969,3227 'uswest':277 'uuid':350,413,557,616,737,836,1406 'uuidstr':558,617 'valid':3303,3308 'validateparam':3297,3301 'valu':607,1451 'var':473,642,2000 'variabl':1575,1628 'verbos':2245,2509,2851,3109 'verifi':915,942 'version':1570,1623 'via':2334,2598,2940,3198 'video':1856,1927,2010,2038 'viewcontrol':296,1017 'visual':1983,1996,2036,2051,2064 'voic':14,109,2388,2652,2994,3252 'voice-sdk-proxi':2387,2651,2993,3251 'void':523 'voip':130,232,483,496,526,533 'warn':252 'waveform':1995,2063 'webrtc':3,7,21,24,42,669,955,1862,2287,2332,2374,2551,2596,2638,2893,2938,2980,3151,3196,3238 'whether':1582,1635,2273,2300,2316,2329,2366,2537,2564,2580,2593,2630,2879,2906,2922,2935,2972,3137,3164,3180,3193,3230 'without':1513,1788,1830 'work':914 'xcode':75 'xml':124 'yellow':768 'your-ai-assistant-id':805","prices":[{"id":"6d4c28b7-6e5d-4bcd-8dc0-ec4e586a1952","listingId":"ade15a50-e79d-44e3-bd24-bfe677645944","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:43.962Z"}],"sources":[{"listingId":"ade15a50-e79d-44e3-bd24-bfe677645944","source":"github","sourceId":"team-telnyx/ai/telnyx-webrtc-client-ios","sourceUrl":"https://github.com/team-telnyx/ai/tree/main/skills/telnyx-webrtc-client-ios","isPrimary":false,"firstSeenAt":"2026-04-18T22:08:43.962Z","lastSeenAt":"2026-04-22T00:54:55.282Z"}],"details":{"listingId":"ade15a50-e79d-44e3-bd24-bfe677645944","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"team-telnyx","slug":"telnyx-webrtc-client-ios","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":"82d35648e962ba98cd619c4f9378fd6dea23ef7e","skill_md_path":"skills/telnyx-webrtc-client-ios/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/team-telnyx/ai/tree/main/skills/telnyx-webrtc-client-ios"},"layout":"multi","source":"github","category":"ai","frontmatter":{"name":"telnyx-webrtc-client-ios","description":">-"},"skills_sh_url":"https://skills.sh/team-telnyx/ai/telnyx-webrtc-client-ios"},"updatedAt":"2026-04-22T00:54:55.282Z"}}