ODC with Bots for Teams - Introduction

Stefan WeberStefan Weber
10 min read

This is the start of a new series of articles about developing bots for Microsoft Teams using OutSystems Developer Cloud.

Bots for Microsoft Teams offer many benefits. They can answer user questions and boost productivity by automating tasks directly within a conversation, among other things.

💡
Please note that the podcast episode above is automatically generated from this article and may not include a proper level of detail.

The Microsoft Bot for Teams ecosystem is extensive, and Microsoft provides several tools for developing bot solutions: the Bot Framework SDKs, the Teams Toolkit, Bot Composer, and the newest addition, Copilot Studio. Unfortunately, the documentation mainly assumes you are using one of the provided toolkits, and it takes some time to figure out how to create a bot—or multiple bots—with an alternative technology stack like ODC.

This is what I aim to cover in this article series because, in the end, all the above toolkits just use configurations and APIs you can leverage in ODC directly without needing to create custom code or adapt the Microsoft Bot Framework SDK for .NET.

So I hope you enjoy this journey on how to build Bots for Microsoft Teams with OutSystems Developer Cloud.

Why you should care

Nearly every organization is now working or at least experimenting with GenAI chatbots and AI agents. OutSystems has introduced its own AI Agent Builder, a user-friendly Forge component for creating conversational GenAI solutions. It also includes a UI widget library to design your own chatbot interface. This is ideal if you want to add an AI conversation feature to another ODC application, like a CRM you built. However, if you want to build a conversational AI experience that isn't directly linked to another application in ODC, it makes sense to integrate with an application users are familiar with. Since Microsoft Teams is the most popular messaging platform, it's a great choice.

Microsoft Teams is a great choice for providing users with a single point of contact to access multiple conversational AI solutions, like Bots. This applies not only to the AI Agent Builder but also in general.

Components of a custom Bot Environment

Although the documentation might seem overwhelming at first, setting up a bot environment for ODC is quite simple. Here are the core components:

  • A configured Azure Bot resource for each bot you want to build, with an attached Microsoft Entra application registration.

  • A single REST endpoint in ODC to receive conversation messages from channels like Microsoft Teams.

  • Action flows in ODC to handle incoming messages and respond (with many structures for deserializing and serializing messages 😒).

  • A Teams app publishing profile to add a Bot to a user's Teams client.

Without further ado, let's take a brief look at all of these components.

Azure Bot

Azure Bot is a resource you can set up from the Azure Marketplace in your tenant. Once configured, the Azure Bot resource acts as a gateway or router between one or more channels and a messaging endpoint.

Channels

Channels are integrations that allow your bot to interact with users on various communication platforms, such as Microsoft Teams, Facebook Messenger, Slack, Telegram, and many others.

💡
In this article series, we are focusing only on Microsoft Teams.

Messaging Endpoint

The Messaging Endpoint is a specific URL that handles incoming HTTP requests from messaging channels. This is where the bot receives messages from users and processes them to create appropriate responses. For ODC, this is a single REST POST endpoint. A single messaging endpoint doesn't necessarily represent just one bot; it can be used for multiple different Azure Bot resources.

Bot Framework REST APIs

The Microsoft documentation for developing bots mainly focuses on using one of the official Bot Framework SDKs, such as the .NET Framework. These SDKs work with the Bot Framework REST APIs, which are Microsoft-managed APIs for sending messages (Connector API), receiving messages (Direct Line API), and managing user authentication (Token API) to and from a channel.

In OutSystems Developer Cloud applications, we need to use the Bot Framework REST APIs directly instead of the SDK. The Bot Framework offers the following REST APIs.

💡
To use the Bot Framework REST APIs, your bot first needs to acquire an access token. Then, use this access token in the Authorization header of a request.

Connector API

Communication from a channel to a messaging endpoint is one-way. The messaging endpoint only receives requests and cannot directly reply to the channel. Instead, for a bot to send a message to a channel, it must use the Bot Framework Service Connector API. This API converts messages from the general Azure Bot message format to the specific format needed by each channel, ensuring messages are delivered correctly across different channels. For more details, see the Connector API OpenAPI specification.

💡
It's important to note that the Connector API does not have a standard base URI. Instead, when the messaging endpoint receives a request, the incoming request includes a serviceUrl property that specifies the endpoint where your bot should send its response. To access the Bot Framework Connector service, you must use the serviceUrl value as the base URI for API requests. You can find more details in the Connector API documentation.

Token API

In addition to the Connector API, the Bot Framework Service offers the Token API, which issues and manages access tokens to ensure secure and authenticated communication between a bot and a channel. A key feature of the Token API is that it simplifies the authentication process and token retrieval if your bot needs a user to sign in directly within a conversation. See the Token API OpenAPI specification for details.

Direct Line API

The Direct Line API is another API provided by the Bot Framework Service. It allows you to build your own client application to interact with a bot if none of the supported channels meet your needs. See the Direct Line API reference and the OpenAPI specification for details.

Bot Messages

Channels send requests to the Messaging Endpoint, and bots send requests to a channel using the Connector API. Request payloads are defined in the Azure AI Bot Activity Schema, which is a standardized JSON format used across all supported channels. Each request, called an Activity, is identified by a type attribute in the payload. Below is a list of all possible activities, along with simplified payload examples.

💡
Keep in mind that the actual payload for some activity types is much larger. See full Activity Schema.
  • message - Standard communication between bot and user. Includes the main content the bot wants to convey, such as text, attachments, or interactive cards.
{
  "type": "message",
  "text": "Hello! How can I assist you today?"
}
  • conversationUpdate - Sent when a conversation's state changes. For example, when a user joins or leaves a chat, or when the conversation is created or deleted.
{
  "type": "conversationUpdate",
  "membersAdded": [
    {
      "id": "user1",
      "name": "User One"
    }
  ],
  "membersRemoved": [],
  "timestamp": "2025-01-10T07:10:00Z"
}
  • contactRelationUpdate - Triggers when the bot's contact list changes. Includes adding or removing a user as a contact.
{
  "type": "contactRelationUpdate",
  "action": "add",
  "user": {
    "id": "user1",
    "name": "User One"
  }
}
  • typing - Indicates that the bot or user is currently typing a response. Helps manage user expectations and improves interaction flow.
{
  "type": "typing"
}
  • ping - A lightweight activity used to check connectivity between bot and endpoint. Ensures the bot is active and responsive.
{
  "type": "ping"
}
  • event - Represents a named event sent from the bot or channel. Often used for channel-specific events or to trigger specific bot actions.
{
  "type": "event",
  "name": "customEventName",
  "value": {
    "key": "value"
  }
}
  • endOfConversation - Indicates that the conversation has ended. Useful for closing sessions or indicating that no further interaction is expected.
{
  "type": "endOfConversation",
  "code": "completedSuccessfully",
  "text": "Thank you for chatting! Goodbye."
}
  • handoff - Used to transfer the conversation to a human agent or another bot. It can include context and state information to facilitate the handover.
{
  "type": "handoff",
  "handoffMetadata": {
    "conversationId": "12345",
    "context": "Customer needs support from a human agent."
  }
}
  • invoke - Represents a request to perform a specific operation. Commonly used for actions like triggering a dialog or processing specific input.
{
  "type": "invoke",
  "name": "triggerDialog",
  "value": {
    "dialogId": "greetingDialog"
  }
}
  • installationUpdate - Sent when the bot is added or removed from a channel or a group. Helps the bot manage its presence and react to changes.
{
  "type": "installationUpdate",
  "action": "add",
  "timestamp": "2025-01-10T07:10:00Z"
}

Microsoft Teams Channel Integration

A channel is set up in the Azure Bot resource, but this alone is not enough to make a bot available in Microsoft Teams. To let users chat with a bot, it must be added as an app to Microsoft Teams.

An app for Microsoft Teams is a JSON document that follows the Microsoft App Manifest schema and includes configuration details about your bot. The manifest, along with the referenced app icon resources, is then compressed into a ZIP archive and published either as a custom app to a single Teams client (suitable for testing), to the organization's app catalog, or to the Teams Store.

If you aim to make your bot available to all Teams users by publishing it to the Teams Store, you must submit it to the Teams Store and ensure it meets all the necessary compliance and security requirements.

💡
You need to configure your tenant to allow custom apps and custom app uploads.

Other Channel Integrations

In this article series, we will focus only on Microsoft Teams. For information on how to integrate a bot with other supported channels, like Slack, please see Configure an Azure AI Bot Service bot to run on one or more channels - Bot Service | Microsoft Learn.

Conversation Scenario

Lets look at a simple conversation example scenario.

Mike has already a custom bot app OutSystems Bot published to his Teams client. In the Teams client he selects the OutSystems Bot App and Microsoft Teams display a conversation window with the Bot.

Mike enters “Hello” into the chat field and sends the message.

Message Receive

Our messaging endpoint will now receive two requests with two different type values

  • conversationUpdate - This request indicates that Mike joined a conversation with the bot. The payload for this request looks like this.
{
  "type": "conversationUpdate",
  "membersAdded": [
    {
      "id": "29:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx",
      "aadObjectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    },
    {
      "id": "28:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
    }
  ],
  "id": "f:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "serviceUrl": "https://smba.trafficmanager.net/emea/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
  "from": {
    "id": "29:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx",
    "aadObjectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }
  "recipient": {
    "id": "28:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "name": "OutSystems Bot"
  },
  .... Additional payload data
}

Note the membersAdded array. The first item is the Id of the user Mike, the second one is the Id of the bot. Both have been added to the conversation.

💡
conversationUpdate is only sent once for a user joining a conversation.
  • message - The second request of type message is the chat message Mike entered in the chat window. The payload looks like this.
{
  "text": "Hello",
  "textFormat": "plain",
  "type": "message",
  "id": "xxxxxxxxxxx",
  "serviceUrl": "https://smba.trafficmanager.net/emea/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",
  "from": {
    "id": "29:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx",
    "name": "Mike",
    "aadObjectId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  },
  "conversation": {
    "conversationType": "personal",
    "tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "id": "a:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx"
  },
  "recipient": {
    "id": "28:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx",
    "name": "OutSystems Bot"
  },
  ... Additional payload data
}

Message Respond

Responding to an incoming message is straightforward. We can either send a response to the conversation itself or reply to an incoming activity (represented by the id attribute of the incoming message payload).

We choose to send a message directly to the conversation.

  • First, we acquire an access token for interacting with the Connector API.

  • We extract the serviceUrl, recipient.id (the Bot Id), from.id (Mike's Id), and conversation.id values from the "Hello" message sent by Mike.

  • We make a request to

URL: https://<serviceUrl>/v3/conversations/<conversation.id>/activities
METHOD: POST
AUTHORIZATION: BEARER <Akquired Access Token>
JSON PAYLOAD:
{
  "type": "message",
  "from": {
    "id": "<recipient.id>" // Bot User Identifier
  },
  "recipient": {
    "id": "<from.id>" // Mikes user identifier
  },
  "text": "Welcome Mike, Iam happy to chat with you"
}
💡
We will cover the details of each step throughout the article series, including user authentication.

Summary

This concludes the introduction to Bots for Microsoft Teams with OutSystems Developer Cloud. Please take a moment to review the provided links, as they contain important additional information.

Setting up a basic environment for Microsoft Teams Bots with ODC is straightforward. It involves creating an Entra application registration and an Azure Bot resource in your Azure tenant, configuring the Bot resource to send messages to your exposed REST API endpoint, setting up one or more channels, and finally creating a publishing profile for a Teams app. We will cover how to perform these steps in the next part of the series.

0
Subscribe to my newsletter

Read articles from Stefan Weber directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Stefan Weber
Stefan Weber

As a seasoned Senior Director at Telelink Business Services EAD, a leading IT full-service provider headquartered in Sofia, Bulgaria, I lead the charge in our Application Services Practice. In this role, I spearhead the development of tailored software solutions using no-code/low-code platforms and cutting-edge cloud-ready/cloud-native solutions based on the Microsoft .NET stack. Throughout my diverse career, I've accumulated a wealth of experience in various capacities, both technically and personally. The constant desire to create innovative software solutions led me to the world of Low-Code and the OutSystems platform. I remain captivated by how closely OutSystems aligns with traditional software development, offering a seamless experience devoid of limitations. While my managerial responsibilities primarily revolve around leading and inspiring my teams, my passion for solution development with OutSystems remains unwavering. My personal focus extends to integrating our solutions with leading technologies such as Amazon Web Services, Microsoft 365, Azure, and more. In 2023, I earned recognition as an OutSystems Most Valuable Professional, one of only 80 worldwide, and concurrently became an AWS Community Builder.