Microsoft Teams Incoming Webhook Integration With ASP.NET Core

Introduction

Incoming Webhook is one of the features that come with Teams. It allows you to communicate between your application and Teams Channel. Incoming Webhook exposes HTTPs endpoint. So, we can say it’s an HTTPS based callback function. It accepts correctly formatted JSON. Cards are used to maintain user interface. JSON format can be different for each card type. More detail about card can be found here.

Let’s take a real scenario of article submission and approval flow in C# corner. When an author submits an article, all editors need to get a notification. And when editor finishes his/her work then the admin needs to get notification to approve the article. Incoming webhook can be handy for this scenario. We can create a Teams channel of editors. When user submits an article, notification goes to Teams channel of editors. Then editor select action button to proceed further. Once editorial part is done with article correction then editor can send it for admin approval. And admin group can have separate Teams channel where they receive notifications.

Based on this scenario I will be discussing how to setup Teams Incoming Webhook and integrate that webhook with ASP.NET Core application

Pre-requisites

  • Microsoft Teams with work/school account
  • Microsoft Visual Studio
  • Familiar with ASP.NET Core MVC

Setup in Teams

In this section, I will be discussing about creating channel and enabling Incoming Webhook for the channel

Create Channel

To use Incoming Webhook, first you need to have a channel. You can use your existing channel also, but you need to configure your connector. To add a new channel in your team, follow these steps:

  1. Select Teams menu
  2. Go to the team where you want to add a new channel and select from the upper right corner.
  3. Select Add Channel from the dropdown menu.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

  4. Now add Name and Description of your channel.
  5. Select Privacy level that fits your requirement. You will see Standard, Private and Shared option to select.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

  6. If you select Private or Shared option in Privacy level, then you have to add team members to your channel. You can decide to skip and add team members later

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

Create Incoming Webhook

To add Incoming Webhook in your Teams channel, follow these steps:

  1. Select the channel in which you want to add incoming webhook and select from upper right corner.
  2. Select Connectors from the dropdown menu.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

  3. Search for Incoming Webhook and select Add.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

  4. Select Configure to add details for the webhook.
  5. Add name, and upload image if necessary for your webhook and select Create.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

  6. Now you will see the screen with a unique webhook URL. Copy the webhook URL and select Done.

    Microsoft Teams Incoming Webhook Integration with ASP.NET Core

Integration in ASP.NET Core

To integrate Incoming Webhook in ASP.NET Core, first you need to have a project. For this article, I created a new ASP.NET Core MVC project. If you are not familiar with creating project in Visual Studio then you can follow this tutorial. You can also download source code attached to this article. All the code example I have included in this section is based on Visual Studio 2022 and .NET 7.0.

In my project I added ArticleModel.cs which have properties needed to build a form. I also added a view Index.cshtml under Article folder which have my form HTML and ArticleController.cs to handle form submission and communicate with channel. TeamsWebHook.cs holds all the properties needed to create JSON to post into channel using webhook.

Microsoft Teams Incoming Webhook Integration with ASP.NET Core

ArticleModel.cs

public class ArticleModel {
    public ArticleModel() {
        Message = new MessageModel();
    }
    public string Title {
        get;
        set;
    }
    public string Description {
        get;
        set;
    }
    public string Author {
        get;
        set;
    }
    public MessageModel Message {
        get;
        set;
    }
}

TeamWebHook.cs

This class holds all the structure and property that is required to form a correct JSON format as per Teams card type.

public class Root {
    public Root() {
            PotentialAction = new List < PotentialAction > ();
        }
        [JsonProperty("@context")]
    public string Context {
        get;
        set;
    }
    [JsonProperty("@type")]
    public string Type {
        get;
        set;
    }
    [JsonProperty("themeColor")]
    public string ThemeColor {
        get;
        set;
    }
    [JsonProperty("summary")]
    public string Summary {
        get;
        set;
    }
    [JsonProperty("title")]
    public string Title {
        get;
        set;
    }
    [JsonProperty("text")]
    public string Text {
        get;
        set;
    }
    [JsonProperty("potentialAction")]
    public List < PotentialAction > PotentialAction {
        get;
        set;
    }
}
public class PotentialAction {
    public PotentialAction() {
            Targets = new List < Target > ();
        }
        [JsonProperty("@type")]
    public string Type {
        get;
        set;
    }
    [JsonProperty("name")]
    public string Name {
        get;
        set;
    }
    [JsonProperty("targets")]
    public List < Target > Targets {
        get;
        set;
    }
}
public class Target {
    [JsonProperty("os")]
    public string Os {
        get;
        set;
    }
    [JsonProperty("uri")]
    public string Uri {
        get;
        set;
    }
}

Index.cshtml

This is the HTML that I have in my Views>Article>Index.cshtml

@model TeamsWebHook.Models.ArticleModel
@{
    ViewData["Title"] = "Create Article";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h4>Create new article</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        @if (Model.Message != null)
        {
            <div class="@(Model.Message.IsSuccess?"text-success":"text-error")">@Model.Message.Message</div>
        }
        <form asp-action="Index">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Title" class="control-label"></label>
                <input asp-for="Title" class="form-control"/>
                <span asp-validation-for="Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Description" class="control-label"></label>
                <textarea asp-for="Description" class="form-control" rows="10"></textarea>
                <span asp-validation-for="Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Author" class="control-label"></label>
                <input asp-for="Author" class="form-control"/>
                <span asp-validation-for="Author" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary"/>
            </div>
        </form>
    </div>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Below screenshot shows the output of this HTML.

Microsoft Teams Incoming Webhook Integration with ASP.NET Core

ArticleController.cs

This code holds all the basic things needed to display in the channel. I have used MessageCard type. Summary, title and text is the part where you have to add all your custom messages.

var teamsMessage = new Root(){
    Context = "https://schema.org/extensions",
    Type = "MessageCard",
    ThemeColor = "0072C6",
    Summary = $"{model.Author} has submitted new article {model.Title}",
    Title = "New Article Alert!!!",
    Text = $"**{model.Author}** has submitted new article **{model.Title}**. Please click below action buttons to proceed."
};

This code is responsible for adding action button in the channel. We can add action button multiple times.

//Adds approve button and its target
var approveAction = new PotentialAction {
    Type = "OpenUri",
        Name = "Approve",
};
var approveTarget = new Target() {
    Os = "default",
        Uri = "https://www.c-sharpcorner.com/articles/review.aspx"
};
approveAction.Targets.Add(approveTarget);
teamsMessage.PotentialAction.Add(approveAction);

And here I have converted teamsMessage object into JSON and sends the JSON content to webhook.

var teamsPayload = JsonConvert.SerializeObject(teamsMessage);
request.Content = new StringContent(teamsPayload, Encoding.UTF8, "application/json");
var response = await httpClient.SendAsync(request);

JSON content posting to webhook is done by using HttpRequestMessage

using (var request = new HttpRequestMessage(new HttpMethod("POST"), webHookUrl))

Full code of ArticleController.cs

public class ArticleController: Controller {
    private readonly ILogger < ArticleController > _logger;
    public ArticleController(ILogger < ArticleController > logger) {
            _logger = logger;
        }
        [HttpGet]
    public IActionResult Index() {
            var model = new ArticleModel();
            return View(model);
        }
        [HttpPost]
    public async Task < ActionResult > Index(ArticleModel model) {
        using(var httpClient = new HttpClient()) {
            var webHookUrl = "<your webhook URL>";
            using(var request = new HttpRequestMessage(new HttpMethod("POST"), webHookUrl)) {
                var teamsMessage = new Root() {
                    Context = "https://schema.org/extensions",
                        Type = "MessageCard",
                        ThemeColor = "0072C6",
                        Summary = $ "{model.Author} has submitted new article {model.Title}",
                        Title = "New Article Alert!!!",
                        Text = $ "**{model.Author}** has submitted new article **{model.Title}**. Please click below action buttons to proceed."
                };
                //Adds approve button and its target
                var approveAction = new PotentialAction {
                    Type = "OpenUri",
                        Name = "Approve",
                };
                var approveTarget = new Target() {
                    Os = "default",
                        Uri = "https://www.c-sharpcorner.com/articles/review.aspx"
                };
                approveAction.Targets.Add(approveTarget);
                teamsMessage.PotentialAction.Add(approveAction);
                //Adds review button and its target
                var reviewAction = new PotentialAction {
                    Type = "OpenUri",
                        Name = "Review"
                };
                var reviewTarget = new Target() {
                    Os = "default",
                        Uri = "https://www.c-sharpcorner.com/authors/dashboard.aspx"
                };
                reviewAction.Targets.Add(reviewTarget);
                teamsMessage.PotentialAction.Add(reviewAction);
                var teamsPayload = JsonConvert.SerializeObject(teamsMessage);
                request.Content = new StringContent(teamsPayload, Encoding.UTF8, "application/json");
                var response = await httpClient.SendAsync(request);
                var messageModel = new MessageModel();
                if (response.StatusCode == HttpStatusCode.OK) {
                    messageModel.IsSuccess = true;
                    messageModel.Message = "Your article has been submitted successfully.";
                } else {
                    messageModel.IsSuccess = false;
                    messageModel.Message = "Unable to submit your article.";
                }
                model.Message = messageModel;
            }
        }
        return View(model);
    }
}

Final Output

This is the message received in Teams channel. You can see 2 different buttons, one for approval and one for review. Both of the button holds link to the system where auto approve can happen or shows detail to review.

Microsoft Teams Incoming Webhook Integration with ASP.NET Core

Summary

This article has described about adding channel, Incoming Webhook and how to configure Incoming Webhook in Microsoft Teams. This also described integrating Incoming Webhook with ASP.NET Core application.

I hope you will find this article helpful. If you have any suggestions, then please feel free to ask into the comment section.

Thank you for reading the article