Want to create a meeting via /users/{userId}/meetings - works on Postman but not Visual Studio

Hi, I’m new to working with the Zoom API. We’re trying to write a plugin to create a Zoom meeting at the click of a button.

I’ve managed to get POST /users/{userId}/meetings working on Postman using both the userId and ‘me’. This is the body:
{
“topic”: “Name of Course.”,
“userId”: “EMAIL ADDRESS”,
“type”: 2,
“start_time”: “2023-12-12”,
“duration”: 30
}

We now want to get that working in our C# app. Here’s the code I have in Visual Studio. I’ve edited a couple of fields here to PRIVATE to protect data but I believe everything is as it should be, but maybe I’m missing something obvious:

public async Task SetMeeting()
{
string tokenUrl = “https://zoom.us/oauth/token”;
// Your Zoom API credentials
string apiKey = “PRIVATE”;
string apiSecret = “PRIVATE”;
string accessToken = await GetAccessToken(tokenUrl, apiKey, apiSecret);

        // API base URL
        string baseUrl = "https://api.zoom.us/v2";

        // Create the HttpClient
        var httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri(baseUrl);
        httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
        httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

        // Create the request body
        var requestBody = new
        {
            topic = "Name of Course.",
            userId = "PRIVATE",
            type = 2,
            start_time = "2023-12-12",
            duration = 30
        };

        // Convert the body to JSON
        string jsonBody = JsonConvert.SerializeObject(requestBody);

        // Create the HttpContent
        var httpContent = new StringContent(jsonBody, Encoding.UTF8, "application/json");

        // Send the POST request
        HttpResponseMessage response = await httpClient.PostAsync("/users/me/meetings", httpContent);

        // Check if the request was successful
        if (response.IsSuccessStatusCode)
        {
            string jsonResponse = await response.Content.ReadAsStringAsync();
            dynamic responseData = JsonConvert.DeserializeObject<dynamic>(jsonResponse);
            Console.WriteLine("Meeting created successfully!");
            Console.WriteLine("Meeting ID: " + responseData.id);
        }
        else
        {
            Console.WriteLine("Failed to create a meeting. Error: " + response.ReasonPhrase);
        }

        Console.ReadLine();
    }

Each time I run the method it gets to the if and errors:
StatusCode: 404, ReasonPhrase: ‘Not Found’, Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:

It seems it’s not even finding the endpoint.

I’ve also tried with the userId instead of ‘me’ but no luck.

I believe I’ve enabled the settings specified here too:

But I may have missed something. Any pointers would be great. Thanks.

I’m no expert but your PostAsync uses “/users/me/meetings” my guess is “/me” is wrong and it should be your actual userId.

Hi @p2282
thanks for reaching out to us!
Are you trying to use a Server to Server Oauth app to generate the access token?
If so here is a code snippet from a Sample App that might help you:

I noticed that you are using apiKey and apiSecret in your code, those are not the right credentials, you should be using clientID, clientSecret and accountID

Please let me know if this helps!
Elisa

Thanks for the responses both. I’ve got it working, but by hard coding in the access token I obtained via Postman as a string…

So ‘me’ did actually work in the endpoint URL. Also yeah, I’d just chosen suboptimal names for my variables but they were the client ID and client secret all along. Yes, I’m trying to use a Server to Server Oauth app to generate the access token

So here’s how the method looks right now. PRIVATE used again in place of certain things.

public async Task SetMeeting()
{
string clientID = “PRIVATE”;
string clientSecret = “PRIVATE”;
string tokenUrl = “https://zoom.us/oauth/token”;

        string accessToken = "PRIVATE";

        // API base URL
        string baseUrl = "https://api.zoom.us/v2";

        // Create the HttpClient
        var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        // Create the request body
        var requestBody = new
        {
            topic = "Name of Course.",
            userId = "me",
            type = 2,
            start_time = "2023-12-12",
            duration = 30
        };

        // Convert the body to JSON
        string json = JsonConvert.SerializeObject(requestBody);
        var content = new StringContent(json);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        var createMeetingUrl = $"{baseUrl}/users/me/meetings";

        // Send the POST request
        var response = await httpClient.PostAsync(createMeetingUrl, content);

        // Check if the request was successful
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadAsStringAsync();
            // Parse the JSON response to get the meeting details
            // You can retrieve the join URL, meeting ID, and other information from the response
            return result;
        }

        // Handle error when unable to create the meeting
        return "Failed to create meeting";
    }

And I followed these instructions to get my access token in Postman (Guide: Making a Zoom API Call with OAuth Credentials in Postman), which would make it of grant type: authorization code, right?

I have been able to get an access token from my own method, but of grant type: client credentials:

async Task GetAccessToken(string tokenUrl, string clientID, string clientSecret)
{

        using (var httpClient = new HttpClient())
        {
            try
            {
                // Set the base address for the Zoom API
                httpClient.BaseAddress = new Uri(tokenUrl);
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{clientID}:{clientSecret}")));

                // Request the access token
                var requestContent = new FormUrlEncodedContent(new[]
                {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            //new KeyValuePair<string, string>("account_id", accountId)
        });

                var response = await httpClient.PostAsync("", requestContent);
                var responseContent = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                {
                    // Deserialize the response to get the access token
                    var tokenResponse = System.Text.Json.JsonSerializer.Deserialize<TokenResponse>(responseContent);
                    Console.WriteLine("Got access token: " + tokenResponse.access_token);
                    return tokenResponse?.access_token;
                }
                else
                {
                    Console.WriteLine("Error getting access token!!");
                    Console.WriteLine("Response: " + responseContent);
                    throw new Exception("Error obtaining the access token. Status Code: " + response.StatusCode);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occurred while getting access token: " + ex.ToString());
                throw;
            }

        }
    }

Note, I commented out the line where I add the account_id key value pair to get it to work.

This client credentials token does not work in my SetMeeting method.

And all this seems to fly in the face of the Zoom documentation (Server-to-Server OAuth) which recommends using grant type: account credentials and including the account id.

My next step is going to be seeing if I can generate an access token of grant type: authorization code in my method as SetMeeting seems to work with this.

I hope you can see my conundrum. Maybe I’m missing something obvious. Thanks again.

OK, we solved it. We created a new app and realised we’d selected Meeting SDK on the old one. With the new one as a Server-To-Server OAuth app and the below code we can create meetings :slight_smile:

public async Task SetMeeting()
{
string clientID = “PRIVATE”;
string clientSecret = “PRIVATE”;

        string accessToken = await GetAccessToken(clientID, clientSecret);

        // API base URL
        string baseUrl = "https://api.zoom.us/v2";

        // Create the HttpClient
        var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

        // Create the request body
        var requestBody = new
        {
            topic = "Name of Course.",
            userId = "me",
            type = 2,
            start_time = "2023-12-12",
            duration = 30
        };

        // Convert the body to JSON
        string json = JsonConvert.SerializeObject(requestBody);
        var content = new StringContent(json);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
        var createMeetingUrl = $"{baseUrl}/users/me/meetings";

        // Send the POST request
        var response = await httpClient.PostAsync(createMeetingUrl, content);

        // Check if the request was successful
        if (response.IsSuccessStatusCode)
        {
            var result = await response.Content.ReadAsStringAsync();
            // Parse the JSON response to get the meeting details
            // You can retrieve the join URL, meeting ID, and other information from the response
            return result;
        }

        // Handle error when unable to create the meeting
        return "Failed to create meeting";
    }


    async Task<string> GetAccessToken(string clientID, string clientSecret)
    {

        using (var httpClient = new HttpClient())
        {
            try
            {
                // Set the base address for the Zoom API
                httpClient.BaseAddress = new Uri("https://zoom.us/oauth/token");
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{clientID}:{clientSecret}")));

                // Request the access token
                var requestContent = new FormUrlEncodedContent(new[]
                {
            new KeyValuePair<string, string>("grant_type", "account_credentials"),
            new KeyValuePair<string, string>("account_id", "PRIVATE")
        });

                var response = await httpClient.PostAsync("", requestContent);
                var responseContent = await response.Content.ReadAsStringAsync();

                if (response.IsSuccessStatusCode)
                {
                    // Deserialize the response to get the access token
                    var tokenResponse = System.Text.Json.JsonSerializer.Deserialize<TokenResponse>(responseContent);
                    Console.WriteLine("Got access token: " + tokenResponse.access_token);
                    return tokenResponse?.access_token;
                }
                else
                {
                    Console.WriteLine("Error getting access token!!");
                    Console.WriteLine("Response: " + responseContent);
                    throw new Exception("Error obtaining the access token. Status Code: " + response.StatusCode);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception occurred while getting access token: " + ex.ToString());
                throw;
            }
        }
    }
1 Like

@p2282
Happy to hear you were able to troubleshoot this on your end and thanks for sharing your solution with the community!
Cheers,
Elisa