# Voice Calling

## Voice Calling Setup

{% tabs %}
{% tab title="iOS" %}
**Configure your Xcode project for outgoing calls.**

* In your app's `info.plist`, add the `NSMicrophoneUsageDescription` key. This is required to request microphone permission.
* In your app's capabilities, add the background mode capabilities `Audio, Airplay, and Picture in Picture` ,`Voice over IP`, and `Remote Notifications`
* Add the `Push Notifications` capability.
  {% endtab %}

{% tab title="Android" %}
**Configure your Android application for outgoing calls.**

* Add the following to your `Android Manifest` file:

  ```
  <uses-permission android:name="android.permission.RECORD_AUDIO"/>
  ```
* Request microphone permissions from within your application code:

  ```
  ActivityCompat.requestPermissions(
      this, 
      arrayOf(Manifest.permission.RECORD_AUDIO), 
      MIC_PERMISSION_REQUEST_CODE // a code you set for monitoring whether or not the permission was granted
  )
  ```

**Configure your Android application for incoming calls.**

* Incoming calls take advantage of Firebase Cloud Messaging. To get started with Cloud Messaging follow the Firebase set up instructions [here](https://firebase.google.com/docs/cloud-messaging/android/client). *\*\**
  {% endtab %}
  {% endtabs %}

## Making an Outgoing Call

Outgoing calls are made using the `createVoiceCall` method. The active call is returned to the provided delegate or listener once it connects. During this time the call can be considered to be in progress.

{% tabs %}
{% tab title="Swift" %}

```swift
let localNumber: PhoneNumber!
let remoteNumber = "2024561414"
let delegate: ActiveCallDelegate
try! telephonyClient.createVoiceCall(localNumber: localNumber, remoteNumber: remoteNumber, delegate: delegate)
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val localNumber: PhoneNumber
val remoteNumber = "2024561414"
val listener: ActiveCallListener
telephonyClient.createVoiceCall(localNumber, remoteNumber, listener)
```

{% endtab %}
{% endtabs %}

## Interacting With an Active Call

The active call delegate (iOS) / listener (Android) must be provided when a call is created and is used to communicate active call events such as when the call connects, disconnects, or fails.

If a call connects successfully, the delegate/listener will be provided with an`ActiveVoiceCall` object via the `activeVoiceCallDidConnect` method. This object provides functions for manipulating the call such as muting the microphone, ending the call, or switching the audio to speakerphone.

{% tabs %}
{% tab title="Swift" %}

```swift
public protocol ActiveCallDelegate {

    /// Notifies the delegate that the call has connected
    /// - Parameters:
    ///     - call: The `ActiveVoiceCall`
    func activeVoiceCallDidConnect(_ call: ActiveVoiceCall)

    /// Notifies the delegate that the call failed to connect
    /// - Parameters:
    ///     - error: `CallingError` that occurred.
    func activeVoiceCallDidFailToConnect(withError error: CallingError)

    /// Notifies the delegate that the call has been disconnected
    /// - Parameters:
    ///     - call: The `ActiveVoiceCall`
    ///     - error: Error that caused the call to disconnect if one occurred.
    func activeVoiceCall(_ call: ActiveVoiceCall, didDisconnectWithError error: Error?)

    /// Notifies the delegate that the call has been muted or un-muted
    /// - Parameters:
    ///     - call: The `ActiveVoiceCall`
    ///     - isMuted: Whether outgoing call audio is muted
    func activeVoiceCall(_ call: ActiveVoiceCall, didChangeMuteState isMuted: Bool)

    /// Called when the system audio route has changed.
    /// Use `call.isOnSpeaker` to determine if the call is on speaker.
    ///
    /// - Parameter call: The `ActiveVoiceCall`
    func activeVoiceCallAudioRouteDidChange(_ call: ActiveVoiceCall)
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
interface ActiveCallListener : TelephonySubscriber {
    /**
     * Notifies the listener that the call has connected
     * @param call The `ActiveVoiceCall`
     */
    fun activeVoiceCallDidConnect(call: ActiveVoiceCall)

    /**
     * Notifies the listener that the call failed to connect
     * @param exception An Exception that occurred
     */
    fun activeVoiceCallDidFailToConnect(exception: Exception)

    /**
     * Notifies the listener that the call has been disconnected
     * @param call The `ActiveVoiceCall`
     * @param exception An optional Exception that is the cause of the disconnect if not null
     */
    fun activeVoiceCallDidDisconnect(call: ActiveVoiceCall, exception: Exception?)

    /**
     * Notifies the listener that the call has been muted or un-muted
     * @param call The `ActiveVoiceCall`
     * @param isMuted Whether outgoing call audio is muted
     */
    fun activeVoiceCallDidChangeMuteState(call: ActiveVoiceCall, isMuted: Boolean)

    /**
     * Notifies the listener that the call audio routing has changed
     * @param call The `ActiveVoiceCall`
     * @param audioDevice The `AudioDevice` that the audio has been routed to
     */
    fun activeVoiceCallDidChangeAudioDevice(call: ActiveVoiceCall, audioDevice: VoiceCallAudioDevice)
}
```

{% endtab %}
{% endtabs %}

## Receiving an Incoming Call

{% hint style="info" %}
**VOIP Notifications for Incoming Calls.** Receiving incoming voice calls on iOS and Android devices requires APNS and FCM push credentials, respectively. If you would like to enable this functionality, contact your solutions engineer to assist you in configuring your environments with push credentials for your applications.
{% endhint %}

{% tabs %}
{% tab title="iOS" %}
**Subscribe to Incoming Calls**

In AppDelegate.swift import PushKit and add a `PKPushRegistry` variable:

{% code title="AppDelegate.swift" %}

```swift
import PushKit
class AppDelegate: UIResponder, UIApplicationDelegate {
    // ...
    var pushRegistry: PKPushRegistry!
}
```

{% endcode %}

In the `applicationDidFinishLaunchingWithOptions` function set up the push registry:

```swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // ...
    self.pushRegistry = PKPushRegistry(queue: nil)
    pushRegistry.delegate = self
    pushRegistry.desiredPushTypes = [.voIP]
    return true
}
```

Implement the `PKPushRegistryDelegate` methods to register, deregister and handle incoming call push notifications. When a push notification comes in, pass the payload from the voip push on to the `SudoTelephonyClient` along with an `IncomingCallNotificationDelegate`:

```swift
extension AppDelegate: PKPushRegistryDelegate {

    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        try! self.telephonyClient.registerForIncomingCalls(with: pushCredentials.token, useSandbox: true, completion: { (error) in
            if let error = error {
                // Error updating push credentials
            }
            else {
                // Push credentials updated with telephony SDK
            }
        })
    }

    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        let _ = try! self.telephonyClient.handleIncomingPushNotificationPayload(payload.dictionaryPayload, notificationDelegate: self)
        completion()
    }

    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
        try! self.telephonyClient.deregisterForIncomingCalls(completion: { (error) in
            if let error = error {
                // Error de-registering for push notifications
            } else {
                // Successfully de-registered
            }
        })
    }
}
```

**IncomingCallNotificationDelegate:**

```swift
public protocol IncomingCallNotificationDelegate: class {
    /// Called when an incoming call is recieved.  This is called before reporting the call to callkit so the correct
    /// display name can be provided.
    ///
    /// In your implementation of this function you should set the `displayName` property of the incoming call that should be displayed by callKit.
    ///
    /// You should also set the `delegate` of the incoming call.  When the call is answered, `incomingCallAnswered(_ call: ActiveVoiceCall)` will be called
    /// and future call related events will be delivered to the delegate.
    func incomingCallReceived(_ call: IncomingCall) -> Void

    /// Indicates that the incoming call was canceled.
    /// - Parameter call: The incoming call
    /// - Parameter error: The error that occured, if any.
    func incomingCall(_ call: IncomingCall, cancelledWithError error: Error?)


    /// An incoming call was answered through CallKit and is in the process of connecting.
    /// No more updates on the incoming call will be provided to this delegate.
    /// Future call updates will be provided to the delegate set on the incoming call delegate in `incomingCallReceived(_ call: IncomingCall) -> Void`.
    func incomingCallAnswered(_ call: ActiveVoiceCall)
}
```

When the `incomingCallReceived()` function is called, you can take the `IncomingCall` object and either accept the call or decline the call. The accept method takes an `ActiveCallDelegate` instance which will then handle all of the call events.

**IncomingCall:**

```swift
public protocol IncomingCall: class {

    /// When an incoming call is recieved the delegate should be set in order to get updates on call related events.
    /// For example if the call is answered through CallKit the delegate will recieve the call connected event with
    /// a handle to the call.
    var delegate: ActiveCallDelegate? { get set }

    /// The phone number receiving the call
    var localNumber: String { get }

    /// The phone number the call is coming from
    var remoteNumber: String { get }

    /// The UUID for the call
    var uuid: UUID { get }

    /// Accepts the call with a delegate to provide call lifecycle updates.
    func accept(with delegate: ActiveCallDelegate)

    /// Decline the call.
    func decline()

    /// The display name used by callKit when reporting a call
    var displayName: CallKitDisplayName? { get set }
}
```

{% endtab %}

{% tab title="Android" %}
**Subscribe to Incoming Calls**

In order to receive calls you must first set up Firebase Cloud Messaging in your app. Instructions can be found [here](https://firebase.google.com/docs/cloud-messaging/android/client).

\_\*\*\_Subscribe to incoming calls using the `fcmToken` provided by Firebase. The following code will fail if Google Play Services are not enabled on the device.

```kotlin
FirebaseInstanceId.getInstance().instanceId
    .addOnCompleteListener(OnCompleteListener { task ->
        if (!task.isSuccessful) {
            // failed to get instance id
            return@OnCompleteListener
        }

        val fcmToken = task.result!!.token
        sudoTelephonyClient.calling.registerForIncomingCalls(fcmToken) { result ->
            when (result) {
                is Result.Success -> {
                    // successfully registered
                }
                is Result.Error -> {
                    // failed to register
                }
            }
        }
    })
```

**Handle Incoming Firebase Messages**

When a message comes in from Firebase, pass it on to the `SudoTelephonyClient` along with an `IncomingCallNotificationListener`:

```kotlin
override fun onMessageReceived(message: RemoteMessage) {
    super.onMessageReceived(message)
    // listener: IncomingCallNotificationListener
    sudoTelephonyClient.calling.handleIncomingPushNotification(message.data, listener)
}
```

**IncomingCallNotificationListener:**

```kotlin
/**
 * Listener for receiving notifications about new `IncomingCall` events.
 */
interface IncomingCallNotificationListener: TelephonySubscriber {
    /**
     * Notifies the listener that the call was received
     * @param call The `IncomingCall`
     */
    fun incomingCallReceived(call: IncomingCall)

    /**
     * Notifies the listener that the call was canceled
     * @param call The `IncomingCall`
     */
    fun incomingCallCanceled(call: IncomingCall, error: Throwable?)
}
```

When the `incomingCallReceived()` function is called, you can take the `IncomingCall` object and either accept the call or decline the call. The accept method takes an `ActiveCallListener` instance which will then handle all of the call events.

**IncomingCall:**

```kotlin
class IncomingCall() {
    /**
     * The phone number receiving the call
     */
    var localNumber: String

    /**
     * The phone number the call is coming from
     */
    var remoteNumber: String

    /**
     * The unique string identifying the call invite
     */
    var callSid: String

    /**
     * Accepts the call
     * @param listener A listener to receive call lifecycle updates
     */
    fun acceptWithListener(listener: ActiveCallListener)

    /**
     * Declines the call
     */
    fun decline()
}
```

{% endtab %}
{% endtabs %}

## Manage Call Records

A call record is generated for each call made and received. The SDK provides three methods for accessing that data and one for deleting call records.

### Get Call Records

Retrieve a list of call records with a provided `PhoneNumber` and an optional limit and token for pagination:

{% tabs %}
{% tab title="Swift" %}

```swift
// localNumber: PhoneNumber
// nextToken: String
try telephonyClient.getCallRecords(localNumber: localNumber, limit: nil, nextToken: nextToken) { (result) in
    switch result {
    case .success(let listToken):
        // listToken: TelephonyListToken<CallRecord>
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// localNumber: PhoneNumber
// nextToken: String
sudoTelephonyClient.calling.getCallRecords(localNumber, null, nextToken) { result ->
    when (result) {
        is Result.Success -> {
            // result.value.items: List<CallRecord>
        }
        is Result.Absent -> {
            // no call records
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### **Get Call Record**

Get a `CallRecord` providing its ID.

{% tabs %}
{% tab title="Swift" %}

```swift
// callRecordId: String
try telephonyClient.getCallRecord(callRecordId: callRecordId) { (result) in
    switch result {
    case .success(let callRecord):
        // callRecord: CallRecord
    case .failure(let error):
        // error: SudoTelephonyClientError
    }  
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// callRecordId: String
sudoTelephonyClient.calling.getCallRecord(callRecordId) { result ->
    when (result) {
        is Result.Success -> {
            // result.value: CallRecord
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### **Subscribe To Call Records**

Subscribe to `CallRecord` events and state changes. On iOS the `subscribeToCallRecords()` function returns a `SubscriptionToken` which can be used to cancel the subscription. On Android the function takes a `CallRecordSubscriber` instance which will receive `CallRecord` updates.

{% tabs %}
{% tab title="Swift" %}

```swift
let subscriptionToken = try telephonyClient.subscribeToCallRecords(resultHandler: { (result) in
    switch result {
    case .success(let callRecord):
        // callRecord: CallRecord
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
})
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
sudoTelephonyClient.calling.subscribeToCallRecords(this, "CallRecordSubscriberId")



/**
 * Subscriber for receiving notifications about new `CallRecord` objects.
 */
interface CallRecordSubscriber : TelephonySubscriber {

    /**
     * Notifies the subscriber of a new 'CallRecord'.
     */
    fun callRecordReceived(callRecord: CallRecord)
}
```

{% endtab %}
{% endtabs %}

### **Delete Call Record**

Delete a `CallRecord` providing its ID.

{% tabs %}
{% tab title="Swift" %}

```swift
// callRecordId: String
try telephonyClient.deleteCallRecord(id: callRecordId) { (result) in
    switch result {
    case .success(let deletedId):
        // deletedId: String
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// callRecordId: String
sudoTelephonyClient.calling.deleteCallRecord(callRecordId) { result ->
    when (result) {
        is Result.Success -> {
            // result.value: String (callRecordId)
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Call Record

A `CallRecord` contains all the information for a voice call made or received.

{% tabs %}
{% tab title="Swift" %}

```swift
public struct CallRecord {
    /// Enum that represents the direction of the call.
    public enum Direction {
        case inbound
        case outbound
        case unknown

        init(internalDirection: SudoTelephony.Direction) {
            switch internalDirection {
            case .inbound:
                self = .inbound
            case .outbound:
                self = .outbound
            case .unknown:
                self = .unknown
            }
        }
    }

    // Enum that represents the state of the call record.
    public enum State {
        case authorized
        case queued
        case ringing
        case answered
        case completed
        case unanswered
        case unknown

        init(internalState: CallState) {
            switch internalState {
            case .authorized:
                self = .authorized
            case .queued:
                self = .queued
            case .ringing:
                self = .ringing
            case .answered:
                self = .answered
            case .completed:
                self = .completed
            case .unanswered:
                self = .unanswered
            case .unknown:
                self = .unknown
            }
        }
    }

    /// Voicemail data belonging to a call record
    public struct VoicemailData {
        /// Unique ID of the voicemail record
        public let id: String
        /// The duration of the voicemail recording in seconds
        public let durationSeconds: UInt
        /// The media object that can be used to download the voicemail recording
        public let media: MediaObject
    }

    /// Unique ID of the call record
    public let id: String
    /// The user ID of the owner of the call record (also referred to as the subject)
    public let owner: String
    /// The ID of the sudo that made or received the call
    public let sudoOwner: String
    /// The ID of the `PhoneNumber` associated with the call record
    public let phoneNumberId: String
    /// The direction of the call. Either `outbound` or `inbound`
    public let direction: Direction
    /// The state of the call record
    public let state: State
    /// Timestamp that represents the last time the call was updated
    public let updated: Date
    /// Timestamp that represents the time the call was created
    public let created: Date
    /// The E164Number of the local phone number
    public let localPhoneNumber: String
    /// The E164Number of the remote phone number
    public let remotePhoneNumber: String
    /// The duration of the call in seconds
    public let durationSeconds: UInt32
    /// The voicemail data of this call record if it exists
    public let voicemail: VoicemailData?
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
data class CallRecord(
     /**
      * Unique ID of the call record
      */
     val id: String,
     /**
      * The user ID of the owner of the call record (also referred to as the subject)
      */
     val owner: String,
     /**
      * The ID of the sudo that made or received the call
      */
     val sudoOwner: String,
     /**
      * The ID of the `PhoneNumber` associated with the call record
      */
     val phoneNumberId: String,
     /**
      * The direction of the call. Either `outbound` or `inbound`
      */
     val direction: Direction,
     /**
      * The state of the call record
      */
     val state: CallRecordState,
     /**
      * Timestamp that represents the last time the call was updated
      */
     val updated: Instant,
     /**
      * Timestamp that represents the time the call was created
      */
     val created: Instant,
     /**
      * The E164Number of the local phone number
      */
     val localPhoneNumber: String,
     /**
      * The E164Number of the remote phone number
      */
     val remotePhoneNumber: String,
     /**
      * The duration of the call in seconds
      */
     val durationSeconds: Int,
     /**
      * The voicemail data of this call record if it exists
      */
     val voicemail: VoicemailData?

) : Parcelable

/**
 * Enum that represents the state of the call record.
 */
enum class CallRecordState() {
     AUTHORIZED,
     QUEUED,
     RINGING,
     ANSWERED,
     COMPLETED,
     UNANSWERED,
     UNKNOWN;
}

/**
 * Voicemail data belonging to a call record
 */
data class VoicemailData (
     /**
      * Unique ID of the voicemail record
      */
     val id: String,
     /**
      * The duration of the voicemail recording in seconds
      */
     val durationSeconds: Int,
     /**
      * The media object that can be used to download the voicemail recording
      */
     val media: MediaObject
) : Parcelable
```

{% endtab %}
{% endtabs %}

## Voicemail

When an incoming call is rejected or times out, the caller is sent to voicemail. After hearing a recorded greeting, the caller can record up to 3 minutes of voicemail. The SDK provides several methods to manage these stored voicemail recordings.

### Get Voicemails

Retrieve a list of voicemail records with a provided `PhoneNumber` and an optional limit and token for pagination:

{% tabs %}
{% tab title="Swift" %}

```swift
// localNumber: PhoneNumber
// nextToken: String
telephonyClient.getVoicemails(localNumber: localNumber, limit: nil, nextToken: nextToken) { (result) in
    switch result {
    case .success(let listToken):
        // listToken: TelephonyListToken<Voicemail>
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// localNumber: PhoneNumber
// nextToken: String
sudoTelephonyClient.calling.getVoicemails(localNumber, null, nextToken) { result ->
    when (result) {
        is Result.Success -> {
            // result.value.items: List<Voicemail>
        }
        is Result.Absent -> {
            // no voicemails
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### **Get Voicemail**

Get a `Voicemail` by its ID.

{% tabs %}
{% tab title="Swift" %}

```swift
// voicemailId: String
telephonyClient.getVoicemail(id: voicemailId) { (result) in
    switch result {
    case .success(let voicemail):
        // voicemail: Voicemail
    case .failure(let error):
        // error: SudoTelephonyClientError
    }  
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// voicemailId: String
sudoTelephonyClient.calling.getVoicemail(voicemailId) { result ->
    when (result) {
        is Result.Success -> {
            // result.value: Voicemail
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### **Subscribe To Voicemails**

Subscribe to `Voicemail` create events, update events, and delete events. On iOS the `subscribeToVoicemails()` function returns a `SubscriptionToken` which can be used to cancel the subscription. On Android the function takes a `VoicemailSubscriber` instance which will receive Voicemail events.

{% tabs %}
{% tab title="Swift" %}

```swift
let subscriptionToken = try telephonyClient.subscribeToVoicemails(resultHandler: { (result) in
    switch result {
    case .success(let voicemail):
        // voicemail: Voicemail
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
})
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
sudoTelephonyClient.calling.subscribeToVoicemails(this, "VoicemailSubscriberId")



/**
 * Subscriber for receiving notifications about new `Voicemail` updates.
 */
interface VoicemailSubscriber : TelephonySubscriber {

    /**
     * Notifies the subscriber of a new 'Voicemail' update.
     */
    fun voicemailUpdated(voicemail: Voicemail)
}
```

{% endtab %}
{% endtabs %}

**Delete Voicemail**

Delete a `Voicemail` by its ID.

{% tabs %}
{% tab title="Swift" %}

```swift
// voicemailId: String
try telephonyClient.deleteVoicemail(id: voicemailId) { (result) in
    switch result {
    case .success:
        // successfully deleted
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// voicemailId: String
sudoTelephonyClient.calling.deleteVoicemail(voicemailId) { result ->
    when (result) {
        is Result.Success -> {
            // result.value: String (voicemailId)
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Voicemail Record

A `Voicemail` contains all the information for a voicemail recording.

{% tabs %}
{% tab title="Swift" %}

```swift
public struct Voicemail {
    /// Unique ID of the voicemail record
    public let id: String
    /// The ID of the Sudo Platform user that owns the voicemail.
    public let owner: String
    /// The ID of the Sudo that received the voicemail.
    public let sudoOwner: String
    /// The ID of the `PhoneNumber` associated with the voicemail.
    public let phoneNumberId: String
    /// The ID of the `CallRecord` associated with the voicemail.
    public let callRecordId: String?
    /// Timestamp that represents the time the voicemail was created.
    public let created: Date
    /// Timestamp that represents the last time the voicemail was updated.
    public let updated: Date
    /// The E.164 formatted phone number that received the voicemail.
    public let localPhoneNumber: String
    /// The E.164 formatted phone number of the caller.
    public let remotePhoneNumber: String
    /// The duration of the voicemail recording in seconds.
    public let durationSeconds: UInt
    /// The media object that can be used to download the voicemail recording.
    public let media: MediaObject
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
data class Voicemail(
    /**
     * Unique ID of the voicemail record
     */
    val id: String,
    /**
     * The ID of the Sudo Platform user that owns the voicemail.
     */
    val owner: String,
    /**
     * The ID of the Sudo that received the voicemail.
     */
    val sudoOwner: String,
    /**
     * The ID of the `PhoneNumber` associated with the voicemail.
     */
    var phoneNumberId: String,
    /**
     * The ID of the `CallRecord` associated with the voicemail.
     */
    var callRecordId: String?,
    /**
     * Timestamp that represents the time the voicemail was created.
     */
    val created: Instant,
    /**
     * Timestamp that represents the last time the voicemail was updated.
     */
    val updated: Instant,
    /**
     * The E.164 formatted phone number that received the voicemail.
     */
    val localPhoneNumber: String,
    /**
     * The E.164 formatted phone number of the caller.
     */
    val remotePhoneNumber: String,
    /**
     * The duration of the voicemail recording in seconds.
     */
    val durationSeconds: Int,
    /**
     * The media object that can be used to download the voicemail recording.
     */
    val media: MediaObject
) : Parcelable
```

{% endtab %}
{% endtabs %}

### Downloading Voicemail Audio Data

When voicemail records are retrieved, the audio data is not automatically downloaded. This gives you control over performance when retrieving voicemail records, particularly those with long recordings.

To download voicemail audio data, use the `downloadData()` method, passing it a `MediaObject` which can be retrieved from the `media` property of a `Voicemail`. This method returns the raw audio data.

{% tabs %}
{% tab title="Swift" %}

```swift
import AVFoundation

let voicemail: Voicemail
telephonyClient.downloadData(for: voicemail.media) { (result) in
    switch result {
    case .success(let data):
        let audioPlayer = try! AVAudioPlayer(data: data)
        audioPlayer.play()
    case .failure(let error):
        // error: SudoTelephonyClientError
    }
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// voicemail: Voicemail
sudoTelephonyClient.downloadData(voicemail.media) { result ->
    when (result) {
        is Result.Success -> {
            // result.value: ByteArray                   
        }
        is Result.Error -> {
            // result.throwable: Throwable
        }
    }
}
```

{% endtab %}
{% endtabs %}

### Associating Voicemail Records with Call Records

Voicemail records and call records can be managed independently. However, a call record that has a corresponding voicemail has an additional `voicemail` property containing a copy of the voicemail data. When said voicemail is deleted this copy is also deleted.

{% tabs %}
{% tab title="Swift" %}

```swift
let callRecord: CallRecord
if let voicemailData: CallRecord.VoicemailData = callRecord.voicemail {
    // The call record has associated voicemail data.
    // voicemailData.id: String
    // voicemailData.durationSeconds: UInt
    // voicemailData.media: MediaObject
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val callRecord: CallRecord
callRecord.voicemail?.let { voicemailData ->
    // The call record has associated voicemail data.
    // voicemailData.id: String
    // voicemailData.durationSeconds: Int
    // voicemailData.media: MediaObject
}
```

{% endtab %}
{% endtabs %}

The `Voicemail`record also has an optional `callRecordId` property which relates the voicemail to its call record if the call record has not been deleted.
