Zoom Signed-in Bots

Zoom Web bots will emit a fatalerror indicating "Sign In Required" if they attempt to join a Zoom meeting where the host has enabled "Only Authenticated Users can Join":

Enabling this setting means that only users who are logged in to a Zoom account are permitted to join the meeting. Because the bot is not logged in by default, it will not be able to join the meeting.

To enable the bot to join these meetings, you must provide it with a Zoom ZAK token.

📘

ZAK tokens also allow bots to start meetings on behalf of the user. This can be useful if you want the bot to be able to join or start a meeting on behalf of a user, before they've joined.

Bots that are provided a ZAK token will also take on the profile photo of the underlying account of the Zoom user that generated the token. The bot's name can still be customized using the bot_name field in the Create Bot config.

To set a custom profile photo for the bot when using ZAK tokens, see Output an Image.

What is a Zoom ZAK Token and how do I get one?

A ZAK token is a access token that can be generated through the Zoom API. These tokens are short-lived, and must be regenerated frequently.

Note:any ZAK token from any Zoom user can be used to authenticate the bot in any meeting.

We recommend creating a dedicated Zoom account for the purpose of generating ZAK tokens, and using that account's ZAK tokens to authenticate all your bots. This means you don't need to retrieve the ZAK token from your user's Zoom account.

How do I provide a ZAK token to the bot?

To provide the ZAK token to the bot, you must specify the zoom.zak_url when creating the bot. The zoom.zak_url is a URL on your server which returns the ZAK token as a plain text HTTP response. Some example pseudocode:

def validate_request(request) -> bool:
  """Validate the authenticity of the request. Read about how to do this in our docs here:
  https://docs.recall.ai/docs/verify-events#/
  """
  pass

def retrieve_zak_token() -> str:
  """This function retrieves a Zoom ZAK token. Note that the response returned
  by calling Zoom's API is a JSON object, and we must extract the token to return a string.
  (https://developers.zoom.us/docs/api/rest/reference/zoom-api/methods/#operation/userZak)
  """
  zoom_api_response = call_zoom_api_userZak()
  return zoom_api_response['token']

# HTTP handler for https://example.com/recall/callbacks/zak
def http_handler(request):
  if not validate_request(request):
    return HttpResponse(code=401)
  
  zak_token = retrieve_zak_token()
  return HttpResponse(body=zak_token)
  
  

How to create a ZAK token?

After creating a Zoom OAuth app, you can have the user (or your service account) authorize your app so that you can generate ZAK tokens on their behalf. The following steps require you to have a Zoom OAuth app

Step 1: Generate the Zoom OAuth URL

The Zoom OAuth URL will allow users to authorize your app. These are one-time urls that you can put directly in your app and will redirect to the following page:

This is how to generate an authorization URL

/**
 * Requires the following scopes:
 * - user:read:zak (default)
 * - user:read:token
 */

const generateAuthorizationUrl = (args: { ZoomClientId: string, ZoomRedirectUri: string }): string => {
    const { ZoomClientId, ZoomRedirectUri } = args;

    const url = new URL("https://zoom.us/oauth/authorize");
    url.searchParams.set("response_type", "code"); // Telling Zoom to return a code which you can exchange for the user's Zoom OAuth access/refresh tokens
    url.searchParams.set("client_id", ZoomClientId); // Your Zoom app's client id
    url.searchParams.set("redirect_uri", ZoomRedirectUri); // Your Zoom app's OAuth Redirect URL

    return url.toString();
}

Step 2: Trade the code for the user's Zoom OAuth access/refresh token

After the user grants OAuth access, you will need to get the user's access/refresh token where:

  • The refresh token is a long-lived token used to generate access tokens
  • The access token is a short-lived token used to interact with the Zoom API

You must save the refresh token so that you can generate new access tokens when the access token expires.

Now when the user authorizes your app, Zoom will send a request to {redirect_uri}?code=.... You need the code in the redirect URI to generate the access/refresh tokensThis is how to trade the code from the redirect URI for the user's access/refresh tokens:

const generateOAuthAccessToken = async (args: { code: string, ZoomClientId: string, ZoomClientSecret: string, ZoomRedirectUri: string }): Promise<{ accessToken: string, refreshToken: string }> => {
    const { code, ZoomClientId, ZoomClientSecret, ZoomRedirectUri } = args;
    if (!code) {
        throw new Error("Code is missing in generateOAuthAccessToken");
    }

    const url = new URL("https://zoom.us/oauth/token");
    url.searchParams.set("grant_type", "authorization_code");
    url.searchParams.set("code", code);
    url.searchParams.set("redirect_uri", ZoomRedirectUri);

    const response = await fetch(url.toString(), {
        method: "POST",
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": `Basic ${Buffer.from(`${ZoomClientId}:${ZoomClientSecret}`, 'utf8').toString('base64')}`,
        },
    });
    if (!response.ok) {
        throw new Error(`Failed to generate OAuth access token: ${await response.text()}`);
    }
    const data = await response.json();
    return {
        accessToken: data.access_token,
        refreshToken: data.refresh_token,
    };
}

Step 3: Retrieve the ZAK Token for the user

The last step is to use the Zoom user's access token to generate a new ZAK token:

const generateZakToken = async (args: { accessToken: string }): Promise<string> => {
    const { accessToken } = args;
    if (!accessToken) {
        throw new Error("Access token is missing in generateZakToken");
    }
    const response = await fetch(`https://api.zoom.us/v2/users/me/token?type=zak`, {
        headers: {
            "Authorization": `Bearer ${accessToken}`,
        },
    });
    if (!response.ok) {
        throw new Error(`Failed to generate ZAK token: ${await response.text()}`);
    }
    const data = await response.json();
    return data.token;
}

FAQs

Can I use a bot with a ZAK token (signed-in Zoom bot) to skip the waiting room?

No you can't use a ZAK token to have a bot skip the waiting room. The ZAK token doesn't have the full permissions/identity that a signed-in participant has.

This means that the bot won't be able to take on the signed-in participant's email associated with the user who created the ZAK token, which is required to use the skip waiting room allowlist.