Siri can’t place calls while device is locked

Hello,

I’m developing a third-party VoIP app called Heyno and trying to support Siri-initiated calls so they behave like WhatsApp / FaceTime, especially from the lock screen.

Target behavior

From the locked device, the user says:

“Hey Siri, call <contact> using Heyno”

Expected result:

• System CallKit audio-call UI appears.
• No “continue in <app>” sheet, no forced unlock or foregrounding.
• Our app handles the VoIP leg in the background via CXProviderDelegate.

WhatsApp already does this with:

“Hey Siri, call <contact> on WhatsApp”

I’m trying to reproduce that behavior for Heyno using public APIs.

I have followed the SiriKit + CallKit VoIP docs but cannot get a clean Siri → CallKit → app flow from the lock screen without either:

  1. Being forced into .continueInApp (unlock + foreground), or
  2. Hitting CallKit transaction errors when starting the call from the app in response to the intent.

Current implementation

  1. Intents extension (INStartCallIntentHandling)

• resolveContacts(for:with:) normalizes to E.164 and returns INPersonResolutionResult.success.
• resolveDestinationType → .success(.normal).
• resolveCallCapability → .success(.audioCall).

Confirm / handle currently:

func confirm(intent: INStartCallIntent,
             completion: @escaping (INStartCallIntentResponse) -> Void) {
    completion(INStartCallIntentResponse(code: .ready, userActivity: nil))
}

func handle(intent: INStartCallIntent,
            completion: @escaping (INStartCallIntentResponse) -> Void) {
    completion(INStartCallIntentResponse(code: .ready, userActivity: nil))
}

Earlier, I used .continueInApp with an NSUserActivity carrying the normalized number and metadata, but that always produced a “Continue in Heyno” sheet that requires unlock and foreground, which breaks the lock-screen Siri flow.

  1. App target – CallKit provider

In the app I have CXProvider + CXProviderDelegate, which work correctly when calls are initiated from inside the app:

func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
    let handle = action.handle.value
    // Start VoIP / WebRTC / LiveKit / Asterisk call here
    provider.reportOutgoingCall(with: action.callUUID,
                                startedConnectingAt: Date())
    provider.reportOutgoingCall(with: action.callUUID,
                                connectedAt: Date())
    action.fulfill()
}

If I construct a CXStartCallAction and submit it via CXCallController.request(...) from the app, CallKit UI appears and our pipeline runs correctly.

What I tried and what fails

  1. Starting CallKit from the Intents extension

Calling CXCallController.request(...) directly from handle(intent:completion:) in the extension always yields:

com.apple.CallKit.error.requesttransaction error 1 (unentitled)

The extension does not have the CallKit entitlement, and the docs say not to initiate calls from the extension, so this path seems unsupported.

  1. Using .continueInApp + NSUserActivity

Pattern:

• handle(intent:) builds NSUserActivity (activityType = NSStringFromClass(INStartCallIntent.self), title = "Heyno Start Call", userInfo with E.164 handle, etc.).
• Returns INStartCallIntentResponse(code: .continueInApp, userActivity: activity).
• App receives the activity, then starts CallKit + VoIP.

Functionally this works, but iOS always requires unlock + foreground (“Continue in Heyno”), which is not acceptable for a Siri lock-screen call.

  1. App group + Darwin notification (extension → app → CallKit)

Experiment:

• Extension writes the normalized number into an app-group UserDefaults.
• Extension posts a Darwin notification.
• App (if running) listens, reads the number, and initiates CXStartCallAction + VoIP.

Observed:

• Works only when the app is already running in the background; a killed app is not woken.
• In some states I see CXErrorCodeRequestTransactionError.invalidAction (error 6) if I try to issue a CXStartCallAction while CallKit is already doing something as part of the Siri flow.
• Siri sometimes replies “There was a problem with the app,” likely because CallKit rejects the transaction or sees duplicate/conflicting actions.

My understanding so far

• The Intents extension should resolve/confirm the intent but not start the call.
• The source of truth for starting a call should be:

Siri → CallKit → app’s CXProviderDelegate.provider(_:perform: CXStartCallAction)

• The app then starts the VoIP leg, reports started/connected, and fulfills.

Where I am stuck

What is not clear is how Siri is supposed to route an INStartCallIntent into CallKit for a third-party VoIP app on a locked device without using .continueInApp.

If my extension simply:

• resolves the contact,
• confirm → .ready,
• handle → .ready (no NSUserActivity, no CallKit),

I do not see a documented mechanism that causes:

“Hey Siri, call <contact> using Heyno”

on the lock screen to:

• Present a CallKit audio call bound to Heyno, and
• Deliver CXStartCallAction to my CXProviderDelegate while the app stays in the background.

Questions

  1. For third-party VoIP apps today, is it recommended to implement INStartCallIntentHandling at all, or should we rely only on CallKit registration and Siri’s built-in support for “Call <contact> with <app>” (no SiriKit extension)?

  2. If an INStartCallIntentHandling extension is still the intended pattern: • Should confirm/handle simply return .ready and never start CallKit or set NSUserActivity?
    • In that case, is Siri expected to invoke CallKit on our behalf and create a CXStartCallAction targeting our provider, even when the device is locked and the app is not foreground?

  3. Is there any supported way for a Siri-triggered third-party VoIP call to start from the lock screen via CallKit without: • using .continueInApp (unlock + foreground), and
    • starting CallKit directly from the Intents extension (unentitled)?

  4. Is there any additional configuration, entitlement, provisioning profile flag, or Info.plist key required so that Siri can map “Call <contact> using Heyno” directly to our CallKit provider and background VoIP implementation?

Current options:

• .continueInApp + NSUserActivity → works, but always requires unlock + app UI.
• Start CallKit from the extension → fails with “unentitled” and appears unsupported.
• Extension → app-group + notification → app → CallKit → VoIP → fragile, with intermittent CXErrorCodeRequestTransactionError.invalidAction.
• Remove the extension and hope Siri/CallKit auto-routes to our provider → unclear if this is supported for third-party VoIP apps or reserved for privileged apps.

I would appreciate guidance on the intended architecture for this scenario, and whether the “Siri from lock screen → CallKit UI → background VoIP call” flow is achievable for an App Store VoIP app like Heyno using public APIs only.

Siri can’t place calls while device is locked
 
 
Q