Custom API knowledge base

This guide shows you how to use Zoom Marketplace APIs to build a custom application to sync knowledge base content. Admins can create and manage a knowledge base with a custom API connector that enables you to write scripts, pull content from external systems, and then push it to the Zoom knowledge base.

This method is an alternative for those who cannot use Zoom Virtual Agent's native knowledge base solutions. Although this method offers great flexibility, it requires technical expertise to initiate API requests and access customer knowledge base content.

1. Create custom API knowledge base

To create a new knowledge base in AI Management, choose 3rd party integration, then Custom API to enable integration with external systems through a tailored API connector.

  1. Go to AI Management, then Knowledge Base.

  2. Choose Add Knowledge Base.

  3. Select Custom API Connection from the connection dropdown.

  4. Name your knowledge base and choose Add.

  5. Copy the knowledge base ID. Save the ID to use in API calls.

2. Create Zoom Marketplace app

Developing a custom app on the Zoom App Marketplace provides a seamless way to integrate and extend Zoom's capabilities with your knowledge base system.

  1. Go to Zoom Marketplace.

  2. In the top right, choose Develop, then Build Server-to-Server App.

  3. To manage knowledge bases and articles, add the Zoom Virtual Agent scopes to your name.

3. Make API calls to manage the knowledge base

You can now make API calls to manage a custom API knowledge base. You could write a script to pull content from any external system and push that content into your Zoom Knowledge Base.

In this section, we provide an example script that pulls content from the Atlassian Confluence system, and pushes it into a created knowledge base. This example script has no dependencies and only requires node 18+.

Note: You must install Node to run this script.

  1. Update email on line 1.

  2. Replace line 3 CONFLUENCE_API_TOKEN to generate the Atlassian API Token.

    To create an API token for your Atlassian account, see Manage API tokens for your Atlassian account | Atlassian Support.

  3. Update lines 5 and 6 for your Confluence Page ID and Space Name.

  4. Replace lines 8-12 for your knowledge base specific settings.

ReplaceWith
KM_BASE_URLhttps://marketplace.zoom.us/
KM_KB_IDPreviously created Knowledge Base ID
KM_TOKENPreviously generated OAuth token (See the Postman example in Step 3.)
KM_ARTICLE_CATEGORYThe category you want your articles under.

Example Node JS Script

In this example script, you can pull content from any external system and push the content into your Zoom Knowledge Base.

// Custom API Demo
const EMAIL = "example.user@zoom.us";
const CONFLUENCE_API_TOKEN = "CONFLUENCE_API_TOKEN";
const CONFLUENCE_BASE_URL = "https://zoomvideo.atlassian.net";
const CONFLUENCE_PAGE_ID = 2727675125;
const CONFLUENCE_SPACE_NAME = "ZVA";
const KM_BASE_URL = "https://openapi-gateway.zoomdev.us";
const KM_KB_ID = "KM_KB_ID";
const KM_ARTICLES_URL = `${KM_BASE_URL}/v2/km/kbs/${KM_KB_ID}/articles`;
const KM_TOKEN = "KM_TOKEN";
const KM_ARTICLE_CATEGORY = "Sports";
async function main() {
    // 1. get existing kb articles
    console.log("fetching existing kb articles...");
    const kmHeaders = { Authorization: `Bearer ${KM_TOKEN}` };
    let kmUrl = KM_ARTICLES_URL;
    let existingArticles = [];
    while (kmUrl) {
        const articlesResponse = await fetch(kmUrl, { headers: kmHeaders });
        const articlesJson = await articlesResponse.json();
        const { next_page_token, articles } = articlesJson || {};
        existingArticles = existingArticles.concat(articles);
        kmUrl = next_page_token
            ? `${KM_ARTICLES_URL}?next_page_token=${next_page_token}`
            : null;
    }
    // 2. get external articles (i.e. Atlassian Confleunce)
    console.log("fetching latest external articles...");
    const authorization = btoa(`${EMAIL}:${CONFLUENCE_API_TOKEN}`);
    const externalHeaders = { Authorization: `Basic ${authorization}` };
    let externalArticles = [];
    let confluenceUrl = `/wiki/api/v2/pages/${CONFLUENCE_PAGE_ID}/children`;
    while (confluenceUrl) {
        const childrenResponse = await fetch(
            `${CONFLUENCE_BASE_URL}${confluenceUrl}`,
            {
                headers: externalHeaders,
            },
        );
        const pages = await childrenResponse.json();
        const { results, _links } = pages || {};
        const { next } = _links || {};
        externalArticles = externalArticles.concat(results);
        confluenceUrl = next;
    }
    // 3. create/update/delete articles
    console.log("processing articles...");
    externalArticles.forEach(async (externalArticle) => {
        const { id } = externalArticle;
        // get external article page contents
        const externalArticleDetailResponse = await fetch(
            `${CONFLUENCE_BASE_URL}/wiki/api/v2/pages/${id}/?body-format=view`,
            {
                headers: externalHeaders,
            },
        );
        const externalArticleDetail =
            await externalArticleDetailResponse.json();
        const { title, body } = externalArticleDetail || {};
        const { value: content } = body.view || {};
        const existingArticle = existingArticles.find(
            (article) => article.external_id === String(externalArticle.id),
        );
        const newArticle = {
            title,
            content,
            url: `${CONFLUENCE_BASE_URL}/wiki/spaces/${CONFLUENCE_SPACE_NAME}/pages/${id}`,
            exclude: false,
            category: KM_ARTICLE_CATEGORY,
            external_id: id,
            language: "en",
        };
        if (!existingArticle) {
            // create new article
            console.log("creating new article", id);
            const createResponse = await fetch(KM_ARTICLES_URL, {
                method: "POST",
                body: JSON.stringify(newArticle),
                headers: {
                    "Content-Type": "application/json",
                    ...kmHeaders,
                },
            });
        } else {
            // update article
            console.log("updating article", id);
            await fetch(`${KM_ARTICLES_URL}/${existingArticle.id}`, {
                method: "PUT",
                body: JSON.stringify(newArticle),
                headers: {
                    "Content-Type": "application/json",
                    ...kmHeaders,
                },
            });
        }
    });
    const externalArticleIds = externalArticles.map((externalArticle) =>
        String(externalArticle.id),
    );
    const articlesToRemove = existingArticles.filter(
        (existingArticle) =>
            !externalArticleIds.includes(existingArticle.external_id),
    );
    articlesToRemove.forEach(async (article) => {
        // delete article
        console.log("deleting article", article.id);
        await fetch(`${KM_ARTICLES_URL}/${article.id}`, {
            method: "DELETE",
            headers: kmHeaders,
        });
    });
}
main().catch(console.error);