Creating Meeting in Microsoft Teams


I wonder does anyone know how I would go about creating a meeting in Microsoft Teams from Xojo DeskTop.

I have tried using oAuth with Christian’s CURL but missing authentication bits. I think the AccessToken is the Azure Application (client) ID, the ConsumerKey is the Directory (tenant) ID and the AccessTokenSecret is the Client secrets, but I have idea how to find the ConsumerSecret…

And maybe I’m doing it completely wrong?

Hopefully someone can give me some light :slight_smile:
Thanks in advance

Did you create an Azure App first? By doing so, you will get all the necessary credentials.

Thanks very much Jeannot. No (well I don’t think I did! :grinning:). I’ll go investigate on how to do that now!

Hi again Jeannot.

I’ve subscribed to the free Azure trial and set up an App (I picked PHP for the runtime stack - I’m not sure if that was correct or not). I still can’t find a reference to any secrets, either in the dashboard or the exported deployment json files.

Would you have any idea where I can find these? Also, I’m trying to do this for one of my customers. Do you know if they need to subscribe to Azure and I use their details or can I access their Teams using my subscription?

Thanks again for any help to someone clearly lost!


I can’t remember how I exactly did it. It looks like I went to, then under Azure AD you will find the option to register an app. Choosing a programming language (xojo is missing) doesn’t matter. That’s only for generating some sample code. All you need are the credentials, to populate the Curl Plugin, as you already did.

I think it is per customer only, unless you are a CSP partner of Microsoft. If you have an admin account from your customer you can grant the nequired permission to your app. Pretty much like the Microsoft Graph “simulator”, where you need an account with necessary privileges to execute the examples:

My routine to get the access_token, I hope it might help you. This is from the early days, it works but it is not ideal:

while d.response = ""

I meanwhile changed this for other APIs but not yet for Office365 stuff :wink:

Public Sub getMSAccessToken (domain as string) as String

Var d as new processCURL
var strDomain as string
Var status as integer

strDomain = ""
strDomain = strDomain + domain
strDomain = strDomain + "/oauth2/V2.0/token"
d.OptionURL = strDomain
d.OptionUseSSL = 3
d.CollectOutputData = true

d.FormAddField("grant_type",   "client_credentials")

select case domain
case "customer1"
  // Customer1
  d.FormAddField("client_id",     "99999-88888888-a11111-2222222222_222")
  d.FormAddField("client_secret", "99999-88888888-a11111-2222222222_222")
case ""
  // TECcompanion
  d.FormAddField("client_id",     "99999-88888888-a11111-2222222222_222")
  d.FormAddField("client_secret", "99999-88888888-a11111-2222222222_222")
case "customer2"
  d.FormAddField("client_id",     "99999-88888888-a11111-2222222222_222")
  d.FormAddField("client_secret", "99999-88888888-a11111-2222222222_222")
end select

d.FormAddField("scope",     "")


status = d.Perform

while d.response = ""

Var j as new JSONMBS(d.OutputData)
Var child as jsonMBS = j.childnode

while child <> Nil 
  if = "access_token" then
    return child.ValueString
  end if
  child = child.NextNode

return "Error"

End Sub

Jeannot, I can’t thank you enough! I’ll dive into this and let you know how I get on :slight_smile:

Chris, you are welcome.

In the next two weeks, I’m busy on another project, but afterward, I will dive into the Office365 Microsoft Graph API again. I’m happy to share my findings and to collaborate ;-).

The good news: it’s feasible ( if you are using CurlMBS) , the ugly news: Microsoft documentation is a mess, the bad news: it is a bit of trial and error sometimes, and hence I never documented it well enough, as I was just happy that it finally worked.

2 tools which I think are very helpful:


Good luck!

:+1:t2: :slight_smile:

@Chris_O_Brien note that if setting the CollectOutputData option to ‘True’ the CURLMBS is working synchronously …

From the MBS documentation:

If you set this property to true, you can grab the data from the transfer in the OutputData Property instead of collecting the pieces yourself in the write event. Of course this is optional and you can still process data in write event.
Due to memory limitation, collecting data will not work right if your app is running low on memory.
(Read and Write property)

From my experience this I working generally speaking very well with the few data from Microsoft Graph, as you have to use anyhow “pagination” to retrieve more data.

Here is a working code snippet for LiveAgent (a ticket system in the cloud):

Var cURL as new CURLSMBS
var strURL as string
Var status as integer
Var header() as string

strURL = "https://**YOURDOMAIN**.com/api/v3/tickets?_perPage=100&_filters=**YOUR FILTER**"

cURL.OptionURL = strURL
cURL.OptionUseSSL = 3
cURL.OptionSSLVersion = CURLSMBS.kSSLVersionTLSv12
cURL.OptionGet = true
cURL.OptionVerbose = true
cURL.OptionTimeOut = 30
cURL.CollectOutputData = true

header.AddRow( "apikey: " + kAPIKeyLiveAgent ) 
cURL.SetOptionHTTPHeader( header) 

status = cURL.Perform
var response as string = ReplaceLineEndings( cURL.OutputData, EndOfLine)

// New Tickets
var j as new JSONMBS( response ) 
var size as integer = j.arraysize -1
redim output( size, 5 )

for i as integer = 0 to j.ArraySize - 1
  var ticket as JSONMBS = j.ArrayItem(i)
  output(i, 0) = ticket.lookupValue("status","").Lowercase
  output(i, 1) = ticket.LookupValue("date_created", "")
  output(i, 2) = ticket.lookupValue("owner_name","")
  output(i, 3) = ticket.lookupValue("subject","")
  output(i, 4) = "[" + ticket.lookupValue("code","").Uppercase + "]"
next I

With this you can avoid the “silly”

while d.response = ""

which I only implemented in the above MS example, as I was testing the functionality and didn’t want to handle with the asynchronous collection of data via event handlers … if you have a lot of data to retrieve then apparently the is the recommended way. Though so far I never run into any such issue with simple APIs, synchronous mode seems to be good enough.

1 Like

If you don’t provide a file to stream output to and you don’t implement write event, we will turn on CollectOutputData for you automatically to avoid a CURL write error.

1 Like

If you don’t provide a file to stream output to and you don’t implement write event, we will turn on CollectOutputData for you automatically to avoid a CURL write error.

Cool, I never noticed! Your plugin is anyhow a lifesaver, thank you for it.

Thanks very much Jeannot - Hopefully this will make more sense to me as I work through it :slight_smile:

1 Like

Never give up ;-). The biggest challenge is really the “interesting” documentation of MS … and MS changing the interfaces sometimes out of the blue. Probably less an issue for you, if you only want to create a Teams meeting, but for more complex stuff it is sometimes frustrating …

@Chris_O_Brien Don’t forget to safely url encode your requests for web. For instance I am using an API where the filters have to look like this:

["status", "IN", "I,N,P,A,C,W"]]

You have to concert these special characters. So you either do this by using one of the URL en- / decoders on the web. The above filters will then look like:


This will be hard to edit in future. Hence I’m using the following conversion, which allows me to easily edit the request in my code and converting it to the url safe encoding:

var filters  as string = "[['status', 'IN', 'I,N,P,A,C,W']]"
filters = EncodeURLComponent( ReplaceAll(filters, "'", chr(34) ) )

For performance reasons, you can still push the URL safe version directly to a variable, once you are fine with your results :wink:

Brilliant! Thanks Jeannot

1 Like