---
sourceDocument: Australia API Reference
sourceDocumentLink: https://servicenow-prod.fluidtopics.net/r/api-reference

 Release :

    - australia

ft:locale :

    - en-US

ft:publication_title :

    - Australia API Reference

ft:clusterId :

    - crapiref

bundleId :

    - crapiref

workflow :

    - Creator


---

# Embed an AI voice agent with NowVoice

# Embed an AI voice agent with NowVoice {#ariaid-title1}

Release version: Australia  
Updated July 14, 2026  
![](https://www.servicenow.com/docs/portal-asset/ico-clock) 5 minutes to read  
Use the NowVoice module to embed a real-time AI voice agent session into your iOS application.

## NowVoice features

NowVoice gives your app a complete, ready-to-present voice agent UI. When a user starts a voice agent session, the SDK:

* Authenticates against your ServiceNow instance using the token your app provides.
* Opens a WebSocket connection to the configured voice agent endpoint.
* Renders a full-screen native SwiftUI view with microphone controls and a live transcript.
* Delivers real-time transcript messages and mute-state changes to your app through callbacks.
* Shows an optional post-call transcript summary screen on session end.

## Theming {#mobsdk-ios-nowvoice__section_qsn_p12_zjc}

NowVoice integrates with the NowUI theming system. Pass any type conforming to [`NowVoiceThemeable`](https://servicenow-prod.fluidtopics.net/t_MbAcjx8gsbDauJxoWyuA "Sets the colors to apply to NowVoice UI elements.") to apply custom colors. [`NowVoiceDefaultTheme`](https://servicenow-prod.fluidtopics.net/03n1e_DysVRF5a0ai~ZzQQ#NVoiceDefThemeiOSStruct "The default implementation of NowVoiceThemeable.") is provided as a default.

## NowChat integration {#mobsdk-ios-nowvoice__section_rsn_p12_zjc}

If your app uses NowChat, you can enable NowVoice in the chat interface. Set [`NowChatConfiguration`](https://servicenow-prod.fluidtopics.net/6O7zLRurtue2bQk8MVsndw#NowChatOptionsiOS "Configures options on a chat session.") properties to configure voice within the NowChat UI and add a voice button. Tapping it launches the voice agent UI as a full-screen overlay within the chat
flow.

## Mobile SDK instance settings {#mobsdk-ios-nowvoice__section_ys3_4k2_zjc}

The NowVoice module reads the following keys from your ServiceNow instance's Mobile SDK settings response.

    {
      "sdk": {
        "voice": {
          "enabled": true,
          "configurations": [
            {
              "voiceServiceId": "String",
              "voiceWebhookURL": "String",
              "mobileChannelType": "String",
              "mobileChannelId": "String"
            }
          ]
        }
      }
    }

## Configure NowVoice {#ariaid-title2}

Configure NowVoice to add a real-time voice agent to your iOS application.

### Before you begin

Role required: admin

* Mobile SDK 2.22.0 or later must be added to your Xcode project via Swift Package Manager.
* Your ServiceNow instance must have voice agent services enabled. Confirm that `sdk.voice.enabled` is `true` in the Mobile SDK settings.

### Procedure

1. Add microphone usage description.  
   Add the `NSMicrophoneUsageDescription` key to your app's Info.plist:  

       <key>NSMicrophoneUsageDescription</key>
       <string>This app uses your microphone to communicate with the ServiceNow voice agent.</string>

   Note:  
   Omitting this key can cause iOS to crash the app when the voice session begins.
2. Configure NowSDK.  
   If you have not already done so, configure NowSDK once at app launch. NowVoice requires NowSDK to be configured before any voice operations.
   1. Call [`NowSDK.configure(with:)`](https://servicenow-prod.fluidtopics.net/FghfNKN7lTh4ix~ZDUFv7Q#NSDK-configure_O "Configures NowSDK for use. You must call this function before calling any of the feature services within the Mobile SDK.").  

          import NowSDK

          try NowSDK.configure(with: NowSDKConfiguration(
              authorizationProvider: self,
              permissionDelegate: self,
              logLevel: .error
          ))

   2. Implement `NowSDKAuthorizationProviding` to supply OAuth tokens for your instance.  

          func requestAuthorization(for instanceUrl: URL, completion: @escaping ([AuthorizationToken]?) -> Void) {
              // Retrieve and return your OAuth tokens here.
              completion(yourTokens)
          }

   3. Implement `DevicePermissionDelegate` to allow microphone access.  

          func canRequestPermission(_ permission: DevicePermission) -> Bool {
              return true
          }

3. Create a NowVoiceService.  
   Import NowVoice and call [`makeVoiceService(instanceUrl:)`](https://servicenow-prod.fluidtopics.net/417Kga_bz7s4G4N2FyGC9Q#NVoice-makeVoiceService_S "Creates an instance of NowVoiceService for the specified ServiceNow instance.") with the URL of your ServiceNow instance. This is an async function; call it from an async context or a Task.  

       import NowVoice

       let instanceUrl = URL(string: "https://your-instance.service-now.com")!

       do {
           let voiceService = try await NowVoice.makeVoiceService(instanceUrl: instanceUrl)
       } catch {
           // Handle NowServiceError
           print("Failed to create voice service: \(error)")
       }

4. Select a voice endpoint.  
   Use [`NowVoiceService.configurations`](https://servicenow-prod.fluidtopics.net/s_OvVdDJDVew6swvY7zHBA#NowVoiceServiceiOSAPI "The NowVoiceService class manages voice agent sessions for a single ServiceNow instance.") to retrieve the available voice endpoints. In most apps, there is a single endpoint.  

       guard let endpoint = voiceService.configurations.first else {
           // No voice endpoints configured on this instance.
           return
       }

5. Launch the voice UI.  
   Call [`startVoice(endpoint:uiConfiguration:callbacks:theme:)`](https://servicenow-prod.fluidtopics.net/s_OvVdDJDVew6swvY7zHBA#NVoiceServ-startVoice_O_O_O_O "Creates a UIViewController containing the voice agent UI, ready to be presented in a modal.") to obtain a `UIViewController` with the voice agent UI. Present it full-screen.  

       let vc = try await voiceService.startVoice(
           endpoint: endpoint,
           uiConfiguration: NowVoiceUIConfiguration(
               hidesPostCallTranscript: false,
               shouldBlockTranscriptSharing: false
           ),
           callbacks: NowVoiceCallbacks(
               onMuteStateChanged: { isMuted in
                   // Update your UI to reflect the current mute state.
                   print("Microphone muted: \(isMuted)")
               },
               onMessageReceived: { message in
                   // Receive real-time transcript messages during the session.
                   print("[\(message.role)]: \(message.content)")
               },
               onCallEnded: { conversationId, error, endedFromCallKitUI in
                   // Called when the voice session ends.
                   if let error {
                       print("Session ended with error: \(error)")
                   } else {
                       print("Session complete. Conversation ID: \(conversationId ?? "unknown")")
                   }
               },
               onCallMinimized: {
                   // Called when the voice UI is minimized.
                   print("Voice chat is minimized")
               }
           ),
           theme: NowVoiceDefaultTheme()
       )

       vc.modalPresentationStyle = .fullScreen
       present(vc, animated: true)

   Note:  
   `startVoice()` fetches an OAuth access token automatically before launching the voice UI.
6. **Optional:** Apply a custom theme.  
   To match the voice UI to your app's visual identity, create a type conforming to [`NowVoiceThemeable`](https://servicenow-prod.fluidtopics.net/t_MbAcjx8gsbDauJxoWyuA "Sets the colors to apply to NowVoice UI elements.") and pass it to [`startVoice(endpoint:uiConfiguration:callbacks:theme:)`](https://servicenow-prod.fluidtopics.net/s_OvVdDJDVew6swvY7zHBA#NVoiceServ-startVoice_O_O_O_O "Creates a UIViewController containing the voice agent UI, ready to be presented in a modal.") or [`updateTheme(theme:)`](https://servicenow-prod.fluidtopics.net/s_OvVdDJDVew6swvY7zHBA#NVoiceServ-updateTheme_O "Updates the visual theme of the currently active voice UI.").  

       struct MyVoiceTheme: NowVoiceThemeable {
           var color: NowUIColoring = MyAppColors()
       }

       // Apply at voice session launch
       let vc = try await voiceService.startVoice(endpoint: endpoint, theme: MyVoiceTheme())

       // Or update while a voice session is active
       voiceService.updateTheme(theme: MyVoiceTheme())

7. **Optional:** Embed voice inside NowChat.  
   If your app already uses NowChat, you can enable NowVoice in the chat interface. Pass the voice configuration directly to [`NowChatConfiguration`](https://servicenow-prod.fluidtopics.net/6O7zLRurtue2bQk8MVsndw#NowChatOptionsiOS "Configures options on a chat session."). A voice button appears in the chat UI automatically when a valid voice endpoint is provided.  
   Note:  
   When using NowChat with voice, don't call [`startVoice(endpoint:uiConfiguration:callbacks:theme:)`](https://servicenow-prod.fluidtopics.net/s_OvVdDJDVew6swvY7zHBA#NVoiceServ-startVoice_O_O_O_O "Creates a UIViewController containing the voice agent UI, ready to be presented in a modal.") to launch the voice UI. NowChat manages the voice session lifecycle internally.  

       import NowChat
       import NowVoice

       // voiceConfiguration is optional --- omit it to have the SDK use instance settings automatically
       let chatConfig = NowChatConfiguration(
           closePrompt: NowChatConfiguration.ClosePrompt(
               header: "Leave chat?",
               message: "Your conversation will end.",
               acceptButtonTitle: "Leave",
               declineButtonTitle: "Stay"
           ),
           disabledFeatures: [],
           conversationOptions: [.forceNewConversation],
           uiConfiguration: NowChatConfiguration.UIConfiguration(
               closeButton: .text("Close"),
               attachmentUploadButton: .init(isVisible: true),
               hideBranding: false
           ),
           voiceConfiguration: voiceService.configurations.first, // optional endpoint override
           voiceUIConfiguration: NowVoiceUIConfiguration(
               hidesPostCallTranscript: false,
               shouldBlockTranscriptSharing: false
           ),
           voiceCallbacks: NowVoiceCallbacks(
               onMuteStateChanged: { isMuted in
                   print("Muted: \(isMuted)")
               },
               onMessageReceived: { message in
                   print("Transcript: \(message)")
               },
               onCallEnded: { conversationId, error, endedFromCallKitUI in
                   // Called when the embedded voice session ends
               },
               onCallMinimized: {
                   // Called when the voice UI is minimized.
                   print("Voice chat is minimized")
               }
           )
       )

       let chatVC = try await chatService.startChat(configuration: chatConfig)
       present(chatVC, animated: true)

### Result

The voice agent UI is presented full-screen. The user can begin a real-time voice session with transcript and mute-state updates delivered to your app through the configured callbacks.

### Complete example

    import NowSDK
    import NowVoice

    // --- App launch ---
    try NowSDK.configure(with: NowSDKConfiguration(
        authorizationProvider: self,
        permissionDelegate: self
    ))

    // --- When the user taps "Start Voice" ---
    func startVoiceSession() async {
        let instanceUrl = URL(string: "https://your-instance.service-now.com")!

        do {
            let voiceService = try await NowVoice.makeVoiceService(instanceUrl: instanceUrl)

            guard let endpoint = voiceService.configurations.first else { return }

            let vc = try await voiceService.startVoice(
                endpoint: endpoint,
                callbacks: NowVoiceCallbacks(
                    onMuteStateChanged: { [weak self] isMuted in
                        self?.updateMuteIndicator(isMuted: isMuted)
                    },
                    onMessageReceived: { [weak self] message in
                        self?.appendTranscript(message)
                    },
                    onCallEnded: { [weak self] _, error, endedFromCallKitUI in
                        self?.dismiss(animated: true)
                    },
                    onCallMinimized: {
                        // Called when the voice UI is minimized.
                        print("Voice chat is minimized")
                    }
                )
            )
            vc.modalPresentationStyle = .fullScreen
            present(vc, animated: true)

        } catch let error as NowServiceError {
            handleServiceError(error)
        }
    }


