Using UI Toolkit for LTT (Live Transcription) Callback Without Direct Video SDK Implementation

Hello Zoom Team,

I am currently using the Zoom Video UI Toolkit to manage UI components and session handling in my web application. I prefer to use the UI Toolkit instead of implementing everything manually via the Zoom Video SDK.

Now, I want to enable the Live Transcription (LTT) feature and receive its status updates through a callback. However, I do not want to use the Video SDK directly for implementing LTT—I just want the UI Toolkit to handle the feature and provide me with the necessary callback.

What I Have Done So Far:
• Integrated the Zoom Video UI Toolkit successfully.
• Added JavaScript files for handling session join/leave.
• Configured the features array in my config object to include ‘ltt’:

var config = {
videoSDKJWT: ‘’,
sessionName: ‘test’,
userName: ‘JavaScript’,
sessionPasscode: ‘123’,
features: [‘video’, ‘audio’, ‘settings’, ‘users’, ‘chat’, ‘ltt’], // Included LTT
options: { init: {}, audio: {}, video: {}, share: {} }
};

•	Successfully joined a session using uitoolkit.joinSession(sessionContainer, config).

What I Need Help With:
1. How can I receive Live Transcription (LTT) status updates as a callback?
2. Does the UI Toolkit automatically handle LTT, or do I need to call any additional functions to enable it?
3. Is there an event like client.on(‘caption-status’, callback) that works within the UI Toolkit?

What I Want to Avoid:
• I do not want to use the Zoom Video SDK directly to manage Live Transcription.
• I want the UI Toolkit to handle everything related to LTT except for providing a callback when status changes.

Would appreciate any guidance on how to properly listen for LTT events using the UI Toolkit.

Do you have any API call which i can made and get all transcript from particular Session ??

Thanks in advance! :rocket:

// Import UI toolkit
import uitoolkit from './@zoom/videosdk-ui-toolkit/index.js'

// Get ZoomVideo from CDN-loaded global object
const ZoomVideo = window.WebVideoSDK.default;

// Initialize variables
var sessionContainer = document.getElementById('sessionContainer');
var onMeetingEndedCallback = null;

// Initialize the Video SDK client as a singleton
const client = ZoomVideo.createClient();

var config = {
    videoSDKJWT: '',
    sessionName: 'test',
    userName: 'JavaScript',
    sessionPasscode: '123',
    features: ['video', 'audio', 'settings', 'users', 'chat', 'ltt','liveTranscription'],
    options: { 
        init: {
            liveTranscription: true, 
            language: 'en-US', 
        }, 
        audio: {}, 
        video: {}, 
        share: {}
    },
    virtualBackground: {
        allowVirtualBackground: false,
        allowVirtualBackgroundUpload: false,
    },
    // Add disclaimer for live transcription
    ltt: 'Live transcription is now active'
};

// Initialize global functions
const zoomManager = {
    joinSession: function(signature, sessionName, userName, password)  {
        console.log('Joining Session:', { sessionName, userName });
        config.videoSDKJWT = signature;
        config.sessionName = sessionName;
        config.userName = userName;
        config.sessionPasscode = password;

        try {
            client.init('en-US', `CDN`)
            // Set up live transcription listener before joining
            this.setupLiveTranscription();
            
            uitoolkit.joinSession(sessionContainer, config);
            console.log('Joined meeting successfully');
            uitoolkit.onSessionClosed(this.handleSessionClosed);
            return true;
        } catch (error) {
            console.error('Failed to join session:', error);
            return false;
        }
    },

    setupLiveTranscription: function() {
        // Listen for caption events
        client.on('caption-message', (payload) => {
            
            console.log('Live Caption:', JSON.stringify(payload));
        });

        // Listen for caption status changes
        client.on('caption-status', (status) => {
            console.log('Caption Status Changed:', JSON.stringify(status));
        });
    },

    closeSession: function() {
        console.log('Closing session');
        try {
            uitoolkit.closeSession(sessionContainer);
            return true;
        } catch (error) {
            console.error('Failed to close session:', error);
            return false;
        }
    },

    handleSessionClosed: function() {
        console.log('Session closed callback');
        uitoolkit.closeSession(sessionContainer);
        
        if (typeof onMeetingEndedCallback === 'function') {
            onMeetingEndedCallback();
        }
    },

    setMeetingEndCallback: function(callback) {
        console.log('Setting meeting end callback');
        onMeetingEndedCallback = callback;
    }
};

// Expose functions to window object
window.joinZoomSession = zoomManager.joinSession.bind(zoomManager);
window.closeSession = zoomManager.closeSession.bind(zoomManager);
window.setMeetingEndCallback = zoomManager.setMeetingEndCallback;
<!DOCTYPE html><html><head>
  <!--
    If you are serving your web app in a path other than the root, change the
    href value below to reflect the base path you are serving from.

    The path provided below has to start and end with a slash "/" in order for
    it to work correctly.

    For more details:
    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base

    This is used to configure paths for PWA assets and html tags.
    Topdoc is served from the subpath /onboarding/ instead of the root of a subdomain.
  -->
  <base href="$FLUTTER_BASE_HREF">

  <meta charset="UTF-8">
  <meta content="IE=Edge" http-equiv="X-UA-Compatible">
  <meta name="description" content="A new Flutter project.">

  <!-- iOS meta tags & icons -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="apple-mobile-web-app-title" content="topdoc">
  <link rel="apple-touch-icon" href="icons/Icon-192.png">

  <!-- Favicon -->
  <link rel="icon" type="image/x-icon" href="favicon.ico">

  <title>Topdoc - Doctor Dashboard</title>

  <link rel="manifest" href="manifest.json">

  <script>
    // The value below is injected by flutter build, do not touch.
    const serviceWorkerVersion = null;
    
  </script>
  <script src="https://source.zoom.us/videosdk/zoom-video-1.9.8.min.js"></script>
  <!-- This script adds the flutter initialization JS code -->
  <script src="flutter.js" defer=""></script>
  <style id="splash-screen-style">

  html, body {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  body {
    margin: 0;
    min-height: 100%;
    background-color: #ffffff;
    background-size: 100% 100%;
  }

  .center {
    margin: 0;
    position: absolute;
    top: 45%;
    left: 50%;
    transform: translate(-50%, -50%);
  }

  .contain {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
  }

  .stretch {
    display: block;
    width: 100%;
    height: 100%;
  }

  .cover {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .bottom {
    position: absolute;
    bottom: 0;
    left: 50%;
    -ms-transform: translate(-50%, 0);
    transform: translate(-50%, 0);
  }

  .bottomLeft {
    position: absolute;
    bottom: 0;
    left: 0;
  }

  .bottomRight {
    position: absolute;
    bottom: 0;
    right: 0;
  }

  /* Loader styles */
  .loader {
    margin-top: 10px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #4F50F6;
    border-radius: 50%;
    width: 36px;
    height: 36px;
    display: block;
    z-index: 10;
    animation: spin 1s linear infinite;
  }

  @keyframes spin {
    0% {
      transform: rotate(0deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
  </style>
  <script id="splash-screen-script">
    function removeSplashFromWeb() {
      document.getElementById("splash")?.remove();
      document.getElementById("splash-branding")?.remove();
      document.getElementById("loader")?.remove();
      document.body.style.background = "transparent";
    }
  </script>
  <link rel="stylesheet" href="@zoom/videosdk-ui-toolkit/dist/videosdk-ui-toolkit.css">


</head>
<body>
 <picture id="splash">
      <source srcset="splash/img/light-1x.png 1x, splash/img/light-2x.png 2x, splash/img/light-3x.png 3x, splash/img/light-4x.png 4x" media="(prefers-color-scheme: light)">
      <source srcset="splash/img/dark-1x.png 1x, splash/img/dark-2x.png 2x, splash/img/dark-3x.png 3x, splash/img/dark-4x.png 4x" media="(prefers-color-scheme: dark)">
      <img class="center" aria-hidden="true" src="splash/img/light-1x.png" alt="">
  </picture>


  <script>
    window.addEventListener('load', function(ev) {
      // Download main.dart.js
      _flutter.loader.loadEntrypoint({
        serviceWorker: {
          serviceWorkerVersion: serviceWorkerVersion,
        },
        onEntrypointLoaded: function(engineInitializer) {
          engineInitializer.initializeEngine().then(function(appRunner) {
            appRunner.runApp();
          });
        }
      });
    });
    self.addEventListener('install', (event) => {
        event.waitUntil(
          caches.open('flutter-app-cache').then((cache) => {
            return cache.addAll([
              '/index.html',
              '/main.dart.js',
              // Critical assets
            ]);
          })
        );
      });
  </script>
  <script src="scripts.js" type="module"></script>
    <script type="text/javascript">
      window.flutterWebRenderer = "html";
    </script>
  <script type="text/javascript" id="hs-script-loader" async="" defer="" src="//js.hs-scripts.com/40823668.js"></script>
 <script>
 function joinZoomSession(signature, sessionName, userName, password) {
 console.log('Joining')
 joinSession(signature, sessionName, userName, password)
 }
  </script>
 <div style="position: relative;">
     <div id="sessionContainer" aria-hidden="false" style="background-color: white; width: 60%; margin: 0 auto;"></div>
     <div class="loader" id="loader"></div>
 </div>

 <script src="@zoom/videosdk-ui-toolkit/index.js" type="module"></script>
 
</body></html>

Hi @Shubham3 The client retrieval for webhooks feature has been revised in UItoolkit version 2.1.10-1 where the on and off functions are exposed so you can subscribe to the sdk events. At the moment, accessing the VideoClient directly is no longer officially supported and I do not have a roadmap on that feature currently.

Regarding transcriptions, the uitoolkit will automatically capture and display the Transcription for you. You can also retrieve transcriptions via API with this endpoint. You can also subscribe to the transcription completed webhook which will include a download url and token