<!--
{
  "documentType" : "article",
  "framework" : "CallKit",
  "identifier" : "/documentation/CallKit/making-and-receiving-voip-calls",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Making and receiving VoIP calls"
}
-->

# Making and receiving VoIP calls

Initiate outgoing calls with VoIP and configure your app to receive incoming calls.

## Overview

You can handle outgoing calls in your app by providing information about the recipient
and initiating the call. To receive incoming calls, you can configure your app to
respond to an external VoIP notification.

### Make outgoing calls

Initiate an outgoing call with a VoIP app in any of the following ways:

- Perform an interaction within the app.
- Open a link with a supported custom URL scheme.
- Begin a VoIP call using Siri.

For more information about registering and handling URLs, see <doc://com.apple.documentation/documentation/Xcode/defining-a-custom-url-scheme-for-your-app>. For more information about initiating a call using Siri, see the <doc://com.apple.documentation/documentation/Intents/INStartCallIntentHandling> protocol.

To make an outgoing call, request a [`CXStartCallAction`](/documentation/CallKit/CXStartCallAction) object. The action consists of a UUID to uniquely identify the call and a [`CXHandle`](/documentation/CallKit/CXHandle) object to specify the recipient, as shown in the following example from the [VoIP calling with CallKit](/documentation/CallKit/voip-calling-with-callkit) sample code project:

```swift
func startCall(handle: String, video: Bool = false) {
    let handle = CXHandle(type: .phoneNumber, value: handle)
    let startCallAction = CXStartCallAction(call: UUID(), handle: handle)

    startCallAction.isVideo = video

    let transaction = CXTransaction()
    transaction.addAction(startCallAction)

    requestTransaction(transaction)
}

private func requestTransaction(_ transaction: CXTransaction) {
    callController.request(transaction) { error in
        if let error = error {
            print("Error requesting transaction:", error.localizedDescription)
        } else {
            print("Requested transaction successfully")
        }
    }
}
```

The `CXHandle` object specifies metadata for the outgoing call. In the example above, it uses an email address. The system on the receiving device uses this metadata to display caller information in the CallKit interface by looking for a matching contact in the recipient’s Contacts app. If the system finds a match, the CallKit interface uses the matching contact data to display the name and a photo as a poster or contact icon. If the recipient’s system can’t find a matching contact in the Contacts app and you provide a Call Directory app extension, it uses the extension to identify the caller and display rich contact information in the CallKit interface. For more information about creating a Call Directory app extension, see [Identifying and blocking calls](/documentation/CallKit/identifying-and-blocking-calls).

After you request to start a VoIP call using a [`CXTransaction`](/documentation/CallKit/CXTransaction), you need to wait for confirmation from the system to actually start the call. After you requested a call as shown in the example above, the system calls your provider delegate’s [`provider(_:perform:)`](/documentation/CallKit/CXProviderDelegate/provider(_:perform:)-2lem5) method to let you know that it accepted the [`CXTransaction`](/documentation/CallKit/CXTransaction) you created and that you can start your VoIP session. In your implementation of the [`provider(_:perform:)`](/documentation/CallKit/CXProviderDelegate/provider(_:perform:)-2lem5) callback method, configure an <doc://com.apple.documentation/documentation/AVFAudio/AVAudioSession> and call the [`fulfill()`](/documentation/CallKit/CXAction/fulfill()) method on the action object when it finishes, as shown in the following code snippet from the [VoIP calling with CallKit](/documentation/CallKit/voip-calling-with-callkit) sample code project:

```swift
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
    // Create and configure an instance of SpeakerboxCall to represent the new outgoing call.
    let call = SpeakerboxCall(uuid: action.callUUID, isOutgoing: true)
    call.handle = action.handle.value

    /*
     Configure the audio session but do not start call audio here.
     Call audio should not be started until the audio session is activated by the system,
     after having its priority elevated.
     */
    configureAudioSession()

    /*
     Set callbacks for significant events in the call's lifecycle,
     so that the CXProvider can be updated to reflect the updated state.
     */
    call.hasStartedConnectingDidChange = { [weak self] in
        self?.provider.reportOutgoingCall(with: call.uuid, startedConnectingAt: call.connectingDate)
    }
    call.hasConnectedDidChange = {[weak self] in
        self?.provider.reportOutgoingCall(with: call.uuid, connectedAt: call.connectDate)
    }

    // Trigger the call to be started via the underlying network service.
    call.startSpeakerboxCall { success in
        if success {
            // Signal to the system that the action was successfully performed.
            action.fulfill()

            // Add the new outgoing call to the app's list of calls.
            self.callManager.addCall(call)
        } else {
            // Signal to the system that the action was unable to be performed.
            action.fail()
        }
    }
}
```

### Receive incoming calls

To configure your app to receive incoming calls, first create a [`CXProvider`](/documentation/CallKit/CXProvider) object and store it for global access. An app reports an incoming call to the provider in response to an external notification, such as a VoIP push notification from <doc://com.apple.documentation/documentation/PushKit>, with the <doc://com.apple.documentation/documentation/PushKit/PKPushRegistryDelegate/pushRegistry(_:didReceiveIncomingPushWith:for:completion:)> and <doc://com.apple.documentation/documentation/PushKit/PKPushRegistryDelegate/pushRegistry(_:didReceiveIncomingPushWith:for:completion:)> callbacks.

> Note:
> For more information about VoIP push notifications and PushKit, see <doc://com.apple.documentation/documentation/PushKit/supporting-pushkit-notifications-in-your-app>.

Using the information from the external notification in the callback, the app creates a UUID and a [`CXCallUpdate`](/documentation/CallKit/CXCallUpdate) object to uniquely identify the call and the caller. Then it passes them both to the provider using the [`reportNewIncomingCall(with:update:completion:)`](/documentation/CallKit/CXProvider/reportNewIncomingCall(with:update:completion:)) method to report the incoming call.

```swift
if let uuidString = payload.dictionaryPayload["UUID"] as? String,
    let identifier = payload.dictionaryPayload["identifier"] as? String,
    let uuid = UUID(uuidString: uuidString)
{
    let update = CXCallUpdate()    
    update.callerIdentifier = identifier
    
    provider.reportNewIncomingCall(with: uuid, update: update) { error in
        // Add your implementation to report the call.
        // ...
    }
}
```

After the call connects, the system calls the [`provider(_:perform:)`](/documentation/CallKit/CXProviderDelegate/provider(_:perform:)-2lem5) method of the provider delegate. In your implementation, the delegate is responsible for configuring an <doc://com.apple.documentation/documentation/AVFAudio/AVAudioSession> to initiate the audio for a call and calling [`fulfill()`](/documentation/CallKit/CXAction/fulfill()) on the action when it finishes.

```swift
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
    // Configure the audio session.
    // ...
    action.fulfill()
}
```

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)