Using the Zoom Clip API to upload Videos via Google Drive

I have been trying to get this API call to work for a while now. This call is designed to upload new MP4 files from Google Drive to Zoom Clips. However, if you run it, you will get a 300 error. Can someone please tell me what’s going on and how to make it work?

// Function to get MP4 files from Google Drive and upload to Zoom
function getMP4FilesAndUploadToZoom() {
  var folderId = "GOOGLE-DRIVE-FOLDER-ID"; // Replace with your Google Drive folder ID
  var folder = DriveApp.getFolderById(folderId);
  
  // Fetch only MP4 files using the correct MIME type for MP4 video files
  var files = folder.getFilesByType("video/mp4");  
  
  var zoomAccessToken = getZoomAccessToken(); // Obtain Zoom Access Token
  
  if (!zoomAccessToken) {
    Logger.log("Failed to obtain Zoom Access Token");
    return;
  }
  
  while (files.hasNext()) {
    var file = files.next();
    Logger.log('MP4 file found: ' + file.getName());
    
    // Fetch the Blob (binary content) of the file
    var fileBlob = file.getBlob();
    
    // Now upload the file to Zoom Clips API
    uploadToZoomClip(fileBlob, file.getName(), zoomAccessToken);
  }
}

// Function to obtain Zoom Access Token (automatically refresh if needed)
function getZoomAccessToken() {
  var clientId = 'CLIENT-ID';  // Replace with your Zoom app Client ID
  var clientSecret = 'CLIENT-SECRET'; // Replace with your Zoom app Client Secret
  var redirectUri = 'REDIRECT-URI'; // Replace with your app redirect URI
  var tokenUrl = '-';
  
  // Retrieve tokens from Script Properties
  var accessToken = PropertiesService.getScriptProperties().getProperty('zoomAccessToken');
  var refreshToken = PropertiesService.getScriptProperties().getProperty('zoomRefreshToken');
  
  if (!accessToken || !refreshToken) {
    // No tokens, let's get authorization code
    var authorizationUrl = "/oauth/authorize?response_type=code&client_id=" + clientId + "&redirect_uri=" + redirectUri;
    Logger.log("Go to the following URL and authorize the app: " + authorizationUrl);
    // After authorization, Zoom will redirect with ?code=AUTHORIZATION_CODE, then use the authorization code to get the tokens
    return null;
  }

  // Try to refresh the access token using the refresh token
  var options = {
    method: 'post',
    headers: {
      'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret),
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    payload: {
      grant_type: 'refresh_token',
      refresh_token: refreshToken
    },
    muteHttpExceptions: true
  };
  
  var response = UrlFetchApp.fetch(tokenUrl, options);
  var responseData = JSON.parse(response.getContentText());
  
  if (response.getResponseCode() === 200) {
    // Extract and store the new access token and refresh token
    var newAccessToken = responseData.access_token;
    var newRefreshToken = responseData.refresh_token;
    
    PropertiesService.getScriptProperties().setProperty('zoomAccessToken', newAccessToken);
    PropertiesService.getScriptProperties().setProperty('zoomRefreshToken', newRefreshToken);
    
    Logger.log('Zoom Access Token refreshed: ' + newAccessToken);
    return newAccessToken;
  } else {
    Logger.log('Failed to refresh Zoom Access Token. Response: ' + response.getContentText());
    return null;
  }
}

// Function to exchange the authorization code for tokens
function exchangeAuthorizationCodeForTokens(authorizationCode) {
  var clientId = 'YOUR_CLIENT_ID';  // Replace with your Zoom app Client ID
  var clientSecret = 'YOUR_CLIENT_SECRET'; // Replace with your Zoom app Client Secret
  var redirectUri = 'YOUR_REDIRECT_URI';  // Replace with your redirect URI
  var tokenUrl = '/oauth/token';

  var options = {
    method: 'post',
    headers: {
      'Authorization': 'Basic ' + Utilities.base64Encode(clientId + ':' + clientSecret),
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    payload: {
      grant_type: 'authorization_code',
      code: authorizationCode,
      redirect_uri: redirectUri
    },
    muteHttpExceptions: true
  };
  
  var response = UrlFetchApp.fetch(tokenUrl, options);
  var responseData = JSON.parse(response.getContentText());

  if (response.getResponseCode() === 200) {
    var accessToken = responseData.access_token;
    var refreshToken = responseData.refresh_token;
    
    // Store the tokens for future use
    PropertiesService.getScriptProperties().setProperty('zoomAccessToken', accessToken);
    PropertiesService.getScriptProperties().setProperty('zoomRefreshToken', refreshToken);
    
    Logger.log('Access Token: ' + accessToken);
    Logger.log('Refresh Token: ' + refreshToken);
  } else {
    Logger.log('Failed to exchange authorization code for tokens. Response: ' + response.getContentText());
  }
}

// Function to upload MP4 file to Zoom Clips API
function uploadToZoomClip(fileBlob, fileName, accessToken) {
  var url = "/v2/clips";  // Zoom Clips API endpoint for uploading clips
  
  var boundary = "Boundary-" + new Date().getTime();
  var headers = {
    "Authorization": "Bearer " + accessToken,
    "Content-Type": "multipart/form-data; boundary=" + boundary
  };
  
  // Prepare the multipart payload
  var payload =
    "--" + boundary + "\r\n" +
    "Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\"\r\n" +
    "Content-Type: " + fileBlob.getContentType() + "\r\n\r\n" +
    fileBlob.getBytes() + "\r\n" +  // Directly use binary data from the Blob
    "--" + boundary + "--";

  var options = {
    method: 'post',
    headers: headers,
    payload: payload,  // Use the binary data directly for the payload
    muteHttpExceptions: true
  };
  
  // Make the POST request to the Zoom Clips API
  var response = UrlFetchApp.fetch(url, options);
  
  // Log the response for debugging
  Logger.log(response.getResponseCode());
  Logger.log(response.getContentText());
  
  if (response.getResponseCode() === 201) {
    Logger.log('File uploaded successfully to Zoom Clips: ' + fileName);
  } else {
    Logger.log('Failed to upload file to Zoom Clips. Response: ' + response.getContentText());
  }
}

// Manually set initial Zoom tokens after authorization
function setInitialZoomTokens() {
  var accessToken = 'YOUR_ACCESS_TOKEN'; // Replace with the access token you got from Zoom
  var refreshToken = 'YOUR_REFRESH_TOKEN'; // Replace with the refresh token you got from Zoom
  
  PropertiesService.getScriptProperties().setProperty('zoomAccessToken', accessToken);
  PropertiesService.getScriptProperties().setProperty('zoomRefreshToken', refreshToken);
  
  Logger.log('Tokens stored successfully.');
}

// Function to check if the tokens were stored correctly
function checkStoredTokens() {
  var accessToken = PropertiesService.getScriptProperties().getProperty('zoomAccessToken');
  var refreshToken = PropertiesService.getScriptProperties().getProperty('zoomRefreshToken');
  
  Logger.log('Stored Access Token: ' + accessToken);
  Logger.log('Stored Refresh Token: ' + refreshToken);
}

@Davea,

Thanks for posting in the Zoom Developer Forum! First, can you share if you have been able to successfully upload one video?

Hello Donte,

No, I have not.

@Davea ,

Can you share the link to the endpoint you are using ?

“/v2/clips/files/multipart”

@Davea,

I’ve successfully upload a Zoom Clip with the following endpoint :

https://fileapi.zoom.us/v2/clips/files

Can you test uploading one file on your end and let me know the results ?

Hello Donte,

Thank you very much. That works very well.

Your help is much appreciated.

All the best.