Search code examples
c#azuremicrosoft-graph-apimicrosoft-graph-teams

Bad Request Error when create Team meeting with Application token


I'm trying to use Application token to create a meeting on behalf of a user and is getting error Bad Request - Request payload cannot be null. I'm able to generate access token, get User object.

Do I need to Configure application access policy (I have no idea where to find this on Azure portal)? Your help is greatly appreciated.

Json payload

"{\"subject\":\"Test Online Meeting for Team 2025 a\",\"startDateTime\":\"2025-03-01T10:00:00Z\",\"endDateTime\":\"2025-03-01T11:00:00Z\",\"accessLevel\":\"everyone\",\"entryExitAnnouncement\":true,\"body\":{\"content\":\"\"},\"participants\":{\"organizier\":{\"identity\":{\"user\":{\"id\":\"aa005b40-9bcb-465d-ac17-56b50ee99e3e\"}}}}}"

Create meeting code

try
{
  string user_id = "aa005b40-9bcb-465d-ac17-56b50ee99e3e";
  using (HttpClient httpClient = new HttpClient())
  {
    httpClient.BaseAddress = new Uri("https://graph.microsoft.com/v1.0/me/onlineMeetings");
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    httpClient.DefaultRequestHeaders.Add("authorization", "Bearer " + accessToken + "");

    var content = new StringContent(jsonobj.ToString(), System.Text.Encoding.UTF8, "application/json");

    servicerequest = httpClient.PostAsync("https://graph.microsoft.com/v1.0/{user_id}/onlineMeetings", content).Result;
    string response = servicerequest.Content.ReadAsStringAsync().Result;
    meetingObj = JsonConvert.DeserializeObject<TeamsMeetingEventObj>(response);

    String OnlineTeamLink = meetingObj.join_WebUrl.ToString();

  }
}
catch (Exception ex)
{
  meetingObj.general_message = "Exception: " + ex.Message;
}

This is the API Permission enter image description here

Decode of Access Token

{
  "typ": "JWT",
  "nonce": "O0k-Dr8iF7CHKVRRoieNaaWlzrR1H3Q7MkJ6_zDjsYw",
  "alg": "RS256",
  "x5t": "imi0Y2z0dYKxBttAqK_Tt5hYBTk",
  "kid": "imi0Y2z0dYKxBttAqK_Tt5hYBTk"
}.{
  "aud": "https://graph.microsoft.com",
  "iss": "https://sts.windows.net/d411855d-e0b2-480b-93d7-cba60cxxxxxx",
  "iat": 1739862965,
  "nbf": 1739862965,
  "exp": 1739866865,
  "aio": "k2RgYCj+P1Fzzf+uAvP7rUvv/Nxxxxxx",
  "app_displayname": "Teams Integration",
  "appid": "2faa523d-93e5-4f0e-b129-247ae7xxxxxx",
  "appidacr": "1",
  "idp": "https://sts.windows.net/d411855d-e0b2-480b-93d7-cba60cxxxxxx/",
  "idtyp": "app",
  "oid": "11dc9a92-88a3-46c3-82c6-58369axxxxxx",
  "rh": "1.AWEBXYUR1LLgC0iT18umDPso7gMAAAAAAAAAwAAAAAAAAABiAQBhAQ.",
  "roles": [
    "OnlineMeetings.ReadWrite.All",
    "Application.ReadWrite.OwnedBy",
    "Calendars.ReadWrite",
    "Application.Read.All",
    "Reports.Read.All"
  ],
  "sub": "11dc9a92-88a3-46c3-82c6-58369axxxxxx",
  "tenant_region_scope": "NA",
  "tid": "d411855d-e0b2-480b-93d7-cba60cxxxxxx",
  "uti": "yz1WwGN5XEqtGSUbtvXZAA",
  "ver": "1.0",
  "wids": [
    "0997a1d0-0d1d-4acb-b408-d5ca73xxxxxx"
  ],
  "xms_idrel": "6 7",
  "xms_tcdt": 1738391941
}.[Signature]

This is the error

{BadRequest} - {\"error\":{\"code\":\"General\",\"message\":\"Request payload cannot be null.\",\"innerError\":{\"request-id\":\"d2bd44f7-46c2-47b3-8239-a159264b0a0f\",\"date\":\"2025-02-19T09:09:26\",\"client-request-id\":\"d2bd44f7-46c2-47b3-8239-a159264b0a0f\"}}}"


{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Transfer-Encoding: chunked
  Strict-Transport-Security: max-age=31536000
  request-id: f3af64c0-dddf-4055-962f-53a9a821b990
  client-request-id: f3af64c0-dddf-4055-962f-53a9a821b990
  x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"West US 3","Slice":"E","Ring":"2","ScaleUnit":"000","RoleInstance":"PH1PEPF00011630"}}
  scenario-id: 67df8b07-7824-4a72-8c02-68a59e482571
  Date: Wed, 19 Feb 2025 16:25:25 GMT
  Content-Type: application/json
}}

Solution

  • I got the same error message, When I tried the code provided by you:

    enter image description here

    Initially, Registered Single Tenant Microsoft Entra ID Application, Added Application type OnlineMeetings.ReadWrite.All API Permission and Granted Admin Consent like below:

    enter image description here

    Note: For using Application type API permission needs to use client-credentials flow only.

    Ensure to create and configure application access policy for your application.Refer this MsDoc,

    Use below Powershell Script for creating and configuring application access Policy:

    Install-Module -Name MicrosoftTeams -Force -AllowClobber #If not installed
    Import-Module MicrosoftTeams
    
    Connect-MicrosoftTeams
    
    New-CsApplicationAccessPolicy -Identity Demo-policy -AppIds "<app-id>" -Description "Allow access to Teams App"
    
    Grant-CsApplicationAccessPolicy -PolicyName Demo-policy -Global
    

    enter image description here

    Use below modified C# script:

    using System;
    using System.Net.Http;
    using System.Text;
    using System.Text.Json;
    using System.Threading.Tasks;
    using Azure.Identity;
    using Microsoft.Graph;
    
    class Program
    {
        private static readonly string tenantId = "<tenant-id>";
        private static readonly string clientId = "<client-id>";
        private static readonly string clientSecret = "<client-secret>";
        private static readonly string organizerUserId = "<user-id>"; // Organizer's Azure ID
    
        static async Task Main()
        {
            try
            {
                var graphClient = GetGraphClient();
                string meetingLink = await CreateTeamsMeeting(graphClient);
    
                if (!string.IsNullOrEmpty(meetingLink))
                {
                    Console.WriteLine("Meeting Created Successfully!");
                    Console.WriteLine("Join URL: " + meetingLink);
                }
                else
                {
                    Console.WriteLine("Failed to create meeting.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception: {ex.Message}");
            }
        }
    
        /// <summary>
        /// Authenticates and returns a GraphServiceClient using client credentials flow.
        /// </summary>
        private static GraphServiceClient GetGraphClient()
        {
            var options = new ClientSecretCredential(tenantId, clientId, clientSecret);
            return new GraphServiceClient(options);
        }
    
        /// <summary>
        /// Creates a Microsoft Teams online meeting with Access Level "everyone".
        /// </summary>
        private static async Task<string> CreateTeamsMeeting(GraphServiceClient graphClient)
        {
            var httpClient = new HttpClient();
            string token = await GetAccessToken();
    
            var requestBody = new
            {
                subject = "Test Online Meeting for Team 2025 a",
                startDateTime = "2025-03-01T10:00:00Z",
                endDateTime = "2025-03-01T11:00:00Z",
                accessLevel = "everyone",  
                participants = new
                {
                    attendees = new[]
                    {
                        new
                        {
                            identity = new
                            {
                                user = new { id = "<object-id>" }
                            },
                            upn = "<upn>"
                        }
                    }
                }
            };
    
            // Use System.Text.Json instead of Newtonsoft.Json
            var jsonContent = JsonSerializer.Serialize(requestBody, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
            var httpRequest = new HttpRequestMessage(HttpMethod.Post, $"https://graph.microsoft.com/v1.0/users/{organizerUserId}/onlineMeetings")
            {
                Content = new StringContent(jsonContent, Encoding.UTF8, "application/json")
            };
    
            httpRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
            var response = await httpClient.SendAsync(httpRequest);
    
            if (response.IsSuccessStatusCode)
            {
                var responseContent = await response.Content.ReadAsStringAsync();
                using var doc = JsonDocument.Parse(responseContent);
                return doc.RootElement.GetProperty("joinWebUrl").GetString();
            }
            else
            {
                Console.WriteLine($"Error: {response.StatusCode} - {await response.Content.ReadAsStringAsync()}");
                return null;
            }
        }
    
        /// <summary>
        /// Gets an access token using Client Credentials Flow.
        /// </summary>
        private static async Task<string> GetAccessToken()
        {
            var options = new ClientSecretCredential(tenantId, clientId, clientSecret);
            var tokenRequestContext = new Azure.Core.TokenRequestContext(new[] { "https://graph.microsoft.com/.default" });
            var accessToken = await options.GetTokenAsync(tokenRequestContext);
            return accessToken.Token;
        }
    }
    
    

    Response:

    enter image description here

    Reference:

    Create and Configure Application Access Policy

    Create Online Meetings