Rate this page:


Incoming Calls and Registration for Push Notifications

How can my App receive calls?

To be able to receive incoming calls, you need to setup your push certificate in the Console and your app needs to register for incoming calls using [TwilioVoiceSDK registerWithAccessToken:deviceToken:completion]. Twilio will send you a push notification through APNS (Apple Push Notification Service). Check our quickstart implementation for how it is done.

Can the App receive incoming VoIP calls while it's not running or in the background?

Yes! See the FAQ “How can my App receive calls” to register for incoming calls. Once the app is registered, it will receive calls even when it’s in the background or not running.

How often should I register my device token?

The device token must be registered every time it changes or if the device has not received a push notification for 1 year. We recommend you implement the following logic:

  1. Invoke [TwilioVoiceSDK registerWithAccessToken:deviceToken:completion] the first time the App is launched
  2. Store the device token and the current time. This is necessary to determine if the device token has changed
  3. Implement PKPushRegistryDelegate and check when the token is updated or invalidated. Compare it to the token value stored in 2. If it has changed, invoke[TwilioVoiceSDK registerWithAccessToken:deviceToken:completion] and store the new device token value.
  4. Check when [TwilioVoiceSDK registerWithAccessToken:deviceToken:completion] was last invoked. If it has been over 6 months, register the token again to not let it expire.

What's the difference between an Access Token and a Device Token?

An Access Token is required to access Twilio's services. A device token is a token that identifies the device so it can receive a push notification.

  • I’ve created an Access Token vending endpoint but why do I keep failing to register for incoming call push notifications?
    • Please make sure that you have added VoiceGrant to the access token for the Programmable Voice SDK.
    • You might have used the incorrect API key/secret pair to create the Access Token. Please make sure you keep the API secret safely once the API key is created, and use both the API key and secret to create the Access Token.
    • The maximum expiry duration allowed by Twilio is 24 hours, which equals 86,400 seconds. Please make sure that you have expiry <= (86400 -1).
    • The API keys/secrets are not sharable between main account and sub-accounts.
    • Please make sure the Access Token has non-empty identity.
  • Why am I not receiving any incoming call push notification although the SDK says it registered successfully?
    • The Twilio Programmable Voice iOS SDK requires Apple’s VoIP Service certificate so that Twilio can send VoIP Push Notifications to your mobile client on your behalf. Twilio will fail to send the push notifications to Apple's VoIP Service endpoint since it is using the regular APNS certificate.
    • Please use a fresh CSR (certificate signing request) for creating VoIP Service certificate. Using a CSR which was previously used for creating an APNS certificate will cause service type confusion in the VoIP Service certificate.
    • During the development phase, please create Push Credentials with the “sandbox” option checked and make sure the app’s plist has “APS Environment: development”. Once the app is ready for store submission, update the plist with “APS Environment: production” and create another Push Credential without checking the sandbox option.
    • Please make sure you have not modified the device token requested using the PKPushRegistry and use it in the registration methods. Also be sure to notice that the description method on the NSData class has changed in iOS 13, and your code may need to be updated. To properly convert an NSData object to a String, use one of these examples:
    /* Obj-C */
    - (void)pushRegistry:(PKPushRegistry *)registry
            didUpdatePushCredentials:(PKPushCredentials *)credentials
                 forType:(NSString *)type {
                const unsigned *tokenBytes = [credentials.token bytes];
                self.deviceTokenString = [NSString stringWithFormat:@"<%08x %08x %08x %08x %08x %08x %08x %08x>",
                                  ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                                  ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                                  ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
        // Call registerWithAccessToken:deviceToken:completion: ...
    /* Swift */
    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {
        let deviceToken = { String(format: "%02x", $0) }.joined()
        self.deviceTokenString = deviceToken
        // Call registerWithAccessToken:deviceToken:completion: ...
  • I was using the iOS TwilioClient 1.2 SDK and just migrated to the Programmable Voice SDK. Why am I getting “Authentication error” using my Capability Token?
    • The Programmable Voice SDK requires a new token format called the Twilio Access Token for authentication. Please update your token vending endpoint to generate Access Tokens for your application.


  • I ran pod install, but it couldn’t find the latest version of the SDK
    • If you are using Cocoapods v1.x, please run pod repo update before trying to install new versions of the Voice SDK.
  • I am getting “Include of non-modular header inside framework module 'TwilioVoiceClient’” and unable to install SDK with my Swift app using Cocoapods
    • Please update your Cocoapods library to the latest version.


  • I got a "@"" - code: 1" error when sending the CXStartCallAction request to the CXCallController and was not able to make outgoing calls using the SDK
    • Please go to the Capabilities page of the app's project settings and make sure you have the Voice over IP option enabled.


  • Why am I getting errors saying “failed to create audio device”?
    • The Programmable Voice iOS SDK requires the category of the AVAudioSession to be .playAndRecord so that it can access the system audio device properly. In case you need to do special manipulation to the AVAudioSession, please make sure the category is restored to .playAndRecord before making/accepting calls.
  • Can my application record the audio during the call?
    • You can use the AVAudioRecorder to record the audio during active calls, but the limitation is that it can only record the audio of the device’s input source.

Push notifications

My phone has been off for a while and when I turn it back on, I get calls that were already hung up

Note: The following does not apply to sdk versions 2.1 onwards and 5.0 onwards. The push notification sent to those versions will only be delivered if the device is reachable when the call is placed.

Twilio Voice SDKs use push notifications (APNS, FCM/GCM) as a mechanism to notify the callee of an incoming call. However, a problem presents itself when the callee‘s device is offline or not reachable: the push notification services cache the notification and re-attempt delivery at a much later time. The delayed notification may arrive after the call has already been terminated. The callee‘s device will briefly alert the user of a call that has already terminated leading to a poor user experience. Twilio plans to address this issue in a later release. However, in the meantime the following is a proposal for a work around you can implement to avoid the poor user experience.

The following 4 step proposal allows an app developer to use time information as an additional criteria to determine whether or not to display a call notification to the user.

Step 1 Generate a UTC based timestamp on the TwiML Application Server based on the Unix epoch in milliseconds.

var timeInMS =

Step 2 Add this generated timestamp to the used to reach the callee

Parameters can be sent to a callee by initiating a TwiML . Use the attribute to specify your key/value parameters as shown below. The value shown below is an example of a timestamp obtained in step 1. You must pass the value as string. Pass the time as custom parameters in TwiML

<?xml version="1.0" encoding="UTF-8"?>
        <Dial answerOnBridge="false" callerId="client:alice">
                <Parameter name="timestamp" value="1555825985"  />

When the call invite push message arrives to the callee it will have the specified parameters.

Step 3 Get the timestamp from the bundle or message provided by APNS when it arrives to the iOS application.

2.X SDKs

When receiving the push notification from Twilio, you can obtain the parameter from the bundle or message. The parameters are provided by APNS payload as the key: twi_params. The following shows how you can parse the contents of the data to get a map of the parameters you passed into the Dial. The “data” variable is the map provided by APNS

NSString *customParams = notificationPayload[@”twi_params”];
NSMutableDictionary *customParamsDict = [NSMutableDictionary dictionary];
If ([customParams length] > 0) {
    NSArray *paramPairs = [customParams componentsSeparatedByString:@"&"];
    for (NSString *param in paramPairs) {
        NSArray *keyValue = [param componentsSeparatedByString:@"="];
        if ([keyValue count] == 2) {
            NSString *decodedValue = [keyValue[1] stringByReplacingOccurrencesOfString:@"+" withString:@"%20"];
            customParamsDict[keyValue[0]] = [decodedValue stringByRemovingPercentEncoding];
3.X+ SDKs

When receiving the push notification from Twilio, you can obtain the parameter from TVOCallInvite with the following:

NSDictionary *customParameters = callInvite.customParameters;
NSString *timestamp = customParameters[@”timestamp”];

Step 4 Compare the UTC based timestamp to the UTC time on the device and discard the notification if the device time is significantly later than the timestamp generated by the server.

// Get the timestamp from the customParameters map and the current device time
NSUInteger timestampValue = [customParameters[@”timestamp”] unsignedIntegerValue];
NSDate now = [NSDate date];

// Compare the time difference
if ([now timeIntervalSince1970] > timestampValue + 60000) {
  // discard notification...
} else {
  // display notification...

Great tools and links

How to create a GitHub issue or file a support ticket

We love feedback and questions especially those with helpful debugging information so we can diagnose and respond in time.

We prefer that you create GitHub issues - that way the entire community benefits.

Please make sure you do not include any sensitive information in your GitHub issue, e.g. auth tokens, signing key secrets, Account SIDs, Calls SIDs and the like.

If you ever need to share any sensitive information with us to troubleshoot a specific issue, please contact Twilio Support through the Console or Help Center and reference your GitHub issue.

When submitting Github issues or support tickets, it would be awesome if you could also add the following:

  • Description - what are you trying to achieve, what steps did you take, and what issue did you have.
  • SDK version
  • SDK debug log - SDK logs are always the best for the team to debug.

Configure the SDK log level:

/* Obj-C */
[VoiceClient setLogLevel:TVOLogLevelDebug];
/* Swift */ 
VoiceClient.logLevel = .debug

When you have gathered all this helpful info, you can open a Github issue in the applicable Github repository:

If you are opening a support ticket through Console or Help Center, you should also submit your Twilio Account SID and Twilio Call SID, if applicable.

Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.

Loading Code Sample...

        Thank you for your feedback!

        Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

        Sending your feedback...
        🎉 Thank you for your feedback!
        Something went wrong. Please try again.

        Thanks for your feedback!