Store meeting transcript on SharePoint

Microsoft Teams has great features like automatic recordings and transcripts. It is even possible to download the transcript after the meeting. In this blog I will show you how you can retrieve and store a meeting transcript on SharePoint Online via a Power Automate flow.

transcript_meeting_manual_download

Inspiration

I was inspired by the tweet below from Lee Holmes. He showed that it was possible to retrieve the transcript content via PowerShell. And I thought, why not try it via Power Automate as well?

YouTube video

Before you continue reading. I also have a YouTube video about the setup.

Meeting Policies

Transcription is a feature which will be enabled or disabled in one of your Meeting Policies. Double check this before you start with your Power Automate setup.

enable_transcription

GET callTranscript content

After reading some several Graph API docs I figured out this would be possible.

The onlineMeeting id can be retrieved via the Get onlineMeeting method and a $filter query parameter and the JoinWebUrl.

After that it is possible to retrieve all transcripts of our meeting with a List transcripts method.

And finally the transcript content can be retrieved in application/vnd.openxmlformats-officedocument.wordprocessingml.document format via the Get callTranscript method.

Lets try and turn this into a working flow!

Flow setup

Checklist before you start:
– This example uses HTTP connector actions which are part of a premium feature, make sure you have a license which covers this.
– The HTTP actions also use an app (with the correct Graph API permissions) in Azure Active Directory for authentication.

storetranscriptasfile

1. Add a When an upcoming event is starting soon (V3) action.

whenanupcomingeventisstartingsoon

a. Select your Calendar Id, in this case Calendar

2. Add a Get my profile (v2) action.
To retrieve my own email for the Author field of the SharePoint page.

getmyprofile_02

3. Add a Initialize variable action.

onlinemeetingvariable

a. Provide a Name, in this example onlineMeetingId
b. Select a Type, String
c. Leave Value empty

4. Add a second Initialize variable action.

initializevariable_transcriptid

a. Provide a Name, in this example transcriptId
b. Select a Type, String
c. Leave Value empty

5. Add a Condition action.
In this condition the body field within the body is checked for the meetup-join hyperlink. A indexOf function is used. If this is found it will output 1. If it is not found (equal to -1), it will output 0.

condition_checkifteamsmeeting

a. Use the expression below for the where statement

6. Add a HTTP action (in the If Yes section).
A Get onlineMeeting method from the Graph API is used. In this case specifically with a $filter query parameter and the JoinWebUrl. This is information we can retrieve from the body/body of the event message.

onlinemeetingid_graph

a. Select the GET method
b. Use the URI from the code snippet below

c. Select Active Directory OAuth for the Authentication, provide the details of your Azure AD App.

7. Add a Set Variable action (in the If Yes section).

setvariable_onlinemeetingid

a. In the Name field select the onlineMeetingId variable
b. In the Value field use the expression from the code snippet below

8. Add a Delay until action (in the If Yes section).

delayuntil_30minutesafterscheduledmeetingend

a. In the Timestamp field use the expression from the code snippet below

9. Add a second HTTP action (in the If Yes section).
A List transcripts method is used to retrieve the transcripts of the specific online meeting.

listtranscripts

a. Select the GET method
b. Use the URI from the code snippet below

c. Select Active Directory OAuth for the Authentication, provide the details of your Azure AD App.

10. Add a second Set Variable action (in the If Yes section).

setvariable_transcriptid

a. In the Name field select the transcriptId variable
b. In the Value field use the expression from the code snippet below

11. Add a third HTTP action (in the If Yes section).
A Get callTranscript method is used to retrieve the content of an individual transcripts in the application/vnd.openxmlformats-officedocument.wordprocessingml.document format.

getcalltranscriptcontent

a. Select the GET method
b. Use the URI from the code snippet below

c. Select Active Directory OAuth for the Authentication, provide the details of your Azure AD App.

12. Add a Create File action.

createfile_spo

a. Select the Site Address from the dropdown, or use an environment variable (parameter) like me
b. Select a Folder Path, in this example /Shared Documents/Meeting Transcripts
c. Use the expression from the code snippet below for the File Name

d. Use the Body field of the HTTP – Transcript action

That is it for the setup of this example.

Testing Transcription

You can start a transcript from within the More menu option.

starttranscript

When the transcript is started you would see it in the side panel during the meeting.

starttranscript02

After the meeting you will see it in the Recordings & Transcripts tab

transcript_meeting_manual_download

But also as a file in our specified Document Library

doclib_meetingtranscripts

When you open the file you should see something like below

transcript_meeting

You can now e-mail/share a link to the document. Or potentially process it with an AI model. More on that in a future blog…

For now, Happy testing!

You may also like...

24 Responses

  1. S. Sprague says:

    This is great, Dennis! Thanks so much for documenting!

    Do you think that the same process could be used to obtain the Attendance reports for meetings? If so, do you know how the process would differ?

  2. Dennis says:

    @S. Sprague, yes the process would be pretty much the same. I actually have written another blog about this as well: https://www.expiscornovus.com/2022/10/24/share-attendance-directly-after-meeting/

  3. sarah says:

    Hi,

    Thank you for you tutorial. I seem to be getting this error on the first HTTP

    ActionBranchingConditionNotSatisfied. The execution of template action ‘HTTP’ skipped: the branching condition for this action is not satisfied.

  4. samer says:

    Great work Dennis, i think this is the only post online to do this

    I am nevertheless struggling with the authentication part keep getting 403 forbidden maybe as ai am trying to access then from zapier (if you know the tool); I will try through power automate and see

  5. Samer says:

    i followed you instructions step by step but I keep getting below error at there first API call where all stops

    can you tell where I might getting this wrong? thanks for the great effort in your video and blog!

    {
    “statusCode”: 403,
    “headers”: {
    “Transfer-Encoding”: “chunked”,
    “Vary”: “Accept-Encoding”,
    “Strict-Transport-Security”: “removed”,
    “request-id”: “removed”,
    “client-request-id”: “removed”,
    “x-ms-ags-diagnostic”: “removed”,
    “scenario-id”: “removed”,
    “Date”: “Tue, 26 Mar 2024 18:34:31 GMT”,
    “Content-Type”: “application/json”,
    “Content-Length”: “198”
    },
    “body”: {
    “error”: {
    “code”: “Forbidden”,
    “message”: “”,
    “innerError”: {
    “request-id”: “removed”,
    “date”: “2024-03-26T18:34:32”,
    “client-request-id”: “removed”
    }
    }
    }
    }

  6. Dennis says:

    Hi Samer,

    That first call uses the Get OnlineMeeting method of the Graph API. Have you made sure that the app you registered in Entra Id has the correct permissions?
    https://learn.microsoft.com/en-us/graph/api/onlinemeeting-get?view=graph-rest-1.0&tabs=http#permissions

  7. Samer says:

    Thanks Dennis
    I figured it out, for some reason the formula to pick the teams url was generating a faulty link (including more text); i replaced it by two compose steps removing text before the link and then after the first “ and now it works 🙂

    Now stuck with creating the file as the $format part which i reconfirmed from ms docs is generating an error, i tried it in header with the ‘accept’ header and same as value and in the uri as you did and it is giving an error everytime

    Looking for some third party to convert transcript data body to .docx

    By the way what you did is super! Might be the only comprehensive guide on this on the internet

  8. Nutz says:

    @Dennis – nice one.
    I’m struggling to get Step 6b working – seems to be an issue with:

    https://graph.microsoft.com/v1.0/users/@{outputs(‘Get_my_profile_(V2)’)?[‘body/id’]}/onlineMeetings?$filter=JoinWebUrl eq ‘@{if(equals(indexOf(triggerOutputs()?[‘body/body’], ‘https://teams.microsoft.com/l/meetup-join/’), -1), ‘No Teams Meeting’, concat(‘https://teams.microsoft.com/l/meetup-join/’, slice(split(split(triggerOutputs()?[‘body/body’], ‘https://teams.microsoft.com/l/meetup-join/’)[1], ‘class’)[0], 0, -2)))}’

    Has Microsoft changed something so this is broken now OR is it me? Anyone else having issues extracting the MeetingID ?

    Thanks

  9. TG says:

    @Dennis – can you share the full string URI being used in HTTP? Mine is kicking back that HTML is invalid and HTML tags and formatting need to be removed. URLs should only contain URL data not HTML coding.

  10. Ali Muayad says:

    Do you need to be invited to the meeting for you to be able to get the transcript? I need meeting transcripts to be downloaded so I can use Co-Pilot to generate meeting notes for a wide range of meetings, I wouldn’t necessarily be invited to these meetings but would need the transcripts to be stored somewhere for Co-Pilot to reference.

    I am thinking that creating a service account that can be invited to all required meetings would allow me to create the flow under the service account and access the transcripts, would this be the correct approach?

  11. Dennis says:

    Hi Ali,

    Good question. I have only tested it with meetings where I was either the organizer or an attendee.

    But yes, I would say that service account approach should work. Let me know about your progress or if you have any other challenges with this setup.

  12. Ali Muayad says:

    Hi Dennis, yes unfortunately I’m getting the following error when I try to test – No application access policy found for this app.

    I created an App in azure with the following permissions granted
    OnlineMeetings.Read
    OnlineMeetings.Read.All

    I have now ran the following command in Power shell

    New-ApplicationAccessPolicy -AppId ‘xxxx’ -PolicyScopeGroupId ‘Ali.Muayad@xxx.xxx.uk’ -AccessRight RestrictAccess -Description “Restrict app to Ali ”

    But I still get the same error, am I missing something? If you have documentation on how you set up the app in Azure that would be really helpful. To add. the Tenant, Client ID and Secret are all values I got from the Azure App overview

  13. Dennis says:

    Hi Ali,

    Have you also granted the policy to the user? The steps for this policy setup can be found in this Microsoft document:
    https://learn.microsoft.com/en-us/graph/cloud-communication-online-meeting-application-access-policy#configure-application-access-policy

  14. Ali Muayad says:

    Hi Dennis, Yes I’m just having trouble creating the file at the moment, the 3rd HTTP request has issues with the format
    {
    “error”: {
    “code”: “BadRequest”,
    “message”: “Invalid format ‘application/vnd.openxmlformats-officedocument.wordprocessingml.document’ specified.”,

  15. Dennis says:

    Can you try the ?$format=text/vtt instead?

    Looks like Microsoft have deprecated that docx format in this specific method:
    https://learn.microsoft.com/en-us/graph/api/calltranscript-get?view=graph-rest-beta&tabs=http#examples

  16. Ali Muayad says:

    Hi Dennis, not sure how I didn’t see that but yes /vtt worked, I had to create a Word template, compose the vtt output, populate the word template with the compose output and then save in sharepoint as a new file. Thanks very much for your assistance with this I learned a lot, if you think that it can be achieved in less steps than I mentioned it would be great to know how.

  17. Dennis says:

    Hi Ali,

    Great to hear that it worked and thanks for your nice feedback, appreciate it 🙂

  18. Gerard Ulto says:

    Thanks Dennis for pulling this together, we have been able to set up the flow to do this and the results are great !! We have created a record in SharePoint where we are storing the transcript text and then we are also running it through Google Gemini to get a summary and also storing that as well. We tried to get the summary using Copilot but it could not handle the transcript from a 1 hour meeting. Hopefully the following will be going GA soon so we can really start using this

    https://graph.microsoft.com/beta/users/@{outputs(‘Get_my_profile_(V2)’)?[‘body/id’]}/onlineMeetings/@{variables(‘onlineMeetingId’)}/transcripts/@{variables(‘transcriptId’)}/content?$format=application/vnd.openxmlformats-officedocument.wordprocessingml.document

    Thanks again !!

  19. Dennis says:

    Hi Gerard,

    Great to hear this was useful for you, happy to share it.

  20. Gerard Ulto says:

    I have been doing some testing with this and it has been working great. Today I just received the message below. I was wondering what kind of billing plan some other folks have implemented in order to be able to download transcripts for their organizations:

    Evaluation capacity for the month has exceeded. To continue beyond the evaluation limits complete billing onboarding. Visit https://docs.microsoft.com/en-us/graph/teams-licenses for API specific requirements.

    Thanks !!

  21. Bassel says:

    Hello Dennis,

    Thank you for this informative blog! I am actually getting a bad request at the first GET Action. The output coming from the initial workflow trigger is coming in an HTML format and the HTTP trigger is apparently using a wrong URI such as the following:

    https://graph.microsoft.com/v1.0/users/12345-678-9012-a15a-25428b58/onlineMeetings?$filter=JoinWebUrl eq ‘https://teams.microsoft.com/l/meetup-join/19%3ameeting_ZmMwLTk1ZjgtNmI37wr4ZTJkZDQz%40thread.v2/0?context=%7b%22Tid%22%3a%222da697b3-7qtr-4a85-8act-e6bafd2a7ate%22%2c%22Oid%22%3a%221234567r-f678-4567-8910-12345678912345%22%7d” id=”meet_invite_block.action.join_link” title=”Meeting join link’

    I guess the problem is with the URL encoding where the id is not getting populated. id=”meet_invite_block.action.join_link” title=”Meeting join link’

    I appreciate your help. Cheers!

  22. Dennis says:

    Hi Bassel,

    I see you filter contains a link which has some html attributes at the end of the string (id & title). My suggestion would be to trim that part. You can use a split function for that.

    first(split(variables('YourJoinWebUrlLink'), '" id='))

  23. Bassel says:

    Thank you so much, Dennis, worked just as expected! I am at the end of the flow now and getting another error related to the .docs file format. When the flow reaches the HTTP Transcript step, I am getting a Bad request. The output of the action is: {
    “error”: {
    “code”: “BadRequest”,
    “message”: “Invalid format ‘application/vnd.openxmlformats-officedocument.wordprocessingml.document’ specified.”,
    “innerError”: {
    “date”: “2024-06-02T20:08:13”
    }
    }
    }

    It seems there’s a problem with the file format. Based on the Microsoft documentation (https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/meeting-transcripts/api-transcripts), adding an ‘Accept’ Header with “application/vnd.openxmlformats-officedocument.wordprocessingml.document” Value should get the callTranscript content in .docs format but unfortunately, this didn’t work either. Any suggestions, please?

    Thanks!

  24. Dennis says:

    Hi Bassel,

    Yes, this error was mentioned by Ali, earlier in the comments. I will also update my post shortly to highlight this deprecation.

    Microsoft have deprecated that docx format in the Get CallTranscript method:
    https://learn.microsoft.com/en-us/graph/api/calltranscript-get?view=graph-rest-beta&tabs=http#examples.

    Can you try the ?$format=text/vtt instead?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.