Integrating with Microsoft Teams

OpenFin’s Microsoft 365 integration API includes functionality specifically for integrating your OpenFin app with Microsoft Teams. This functionality includes:

  • Navigating users to existing Teams conversations (both group conversations and Teams Channel conversations)

  • Starting chats and calls

  • Enabling OpenFin Interop to work "out of the box" with Teams

  • Sharing Instrument context to a Teams conversation

📘

Note

The rest of this article assumes that your app is connected to Microsoft 365 as described in Microsoft 365 integration.

Create a Teams connection

Before you are able to call Teams-specific functionality provided by the API, you must create a new TeamsConnection object:

import { TeamsConnection } from '@openfin/microsoft365';

const useMsTeamsProtocol = true; // Optional, if true will use msteams: protocol to open links directly in the Teams app instead of via web browser (defaults to true)
const teamsConnection = new TeamsConnection(ms365Connection, useMsTeamsProtocol);

Teams deep linking

The API makes use of Teams deep linking to automate some features of the native Teams desktop application. Deep linking is provided with the msteams protocol (the integration default) or alternatively via a standard https link.

The msteams protocol is preferred if you can assume that the native Teams app is installed on users' desktops, because it provides a seamless Teams user experience. Otherwise, a web page opens in the user's default browser, interrupting the user flow, which in addition to directing the Teams app to the correct function (if installed), also provides the user with options to use the Teams web app or download the native app.

Usage

Make a call or start a chat in Teams from OpenFin

The API provides the option of making a call or starting a chat in Teams in either of the following ways:

  • Calling the API functions (startChat or startCall) directly

  • Calling registerIntentHandlers and then raising StartChat or StartCall intents via the OpenFin Interop APIs or FDC3 APIs

Start a group chat by calling startChat with a list of email addresses of users who should be included in the group chat:

import { GroupChatOptions } from '@openfin/microsoft365';

const startGroupChatOptions: GroupChatOptions = {
  emailAddresses: ['[email protected]', '[email protected]'],
  topicName: 'New Topic', // Optional, name for the group chat
  message: 'Let’s chat about something...', // Optional, text to prefill message field with
};
const resolvedUsersForChat = await teamsConnection.startChat(startGroupChatOptions);

📘

Note

Add emails to Azure AD user accounts.

The Microsoft 365 integration API attempts to resolve any provided email addresses to Azure Active Directory users. We recommend that all Azure AD users have email addresses defined for best user experience. If an email address can't be resolved to an AD user, no error is thrown.

Start a channel chat by calling startChat with the IDs of the team and (optionally) the channel to start the chat with. These ID values would typically be known ahead of time, or alternatively can be discovered by executing a request to the Graph API:

import { ChannelChatOptions } from '@openfin/microsoft365';

const startChannelChatOptions: ChannelChatOptions = {
  teamId: 'fbe2bf47-16c8-47cf-b4a5-4b9b187c508b',
  channelId: '19:[email protected]', // Optional, if not provided will use team’s primary channel
};
const resolvedTeamChannelForChat = await teamsConnection.startChat(startChannelChatOptions);
);

Start a Teams call by calling startCall with a list of email addresses of users who should be included in the call. By default, the caller’s camera is turned off when making the call, but can be turned on by default by setting the second parameter to true:

const emailAddresses = ['[email protected]', '[email protected]'];

// Make a video call
const withVideo = true;
const resolvedUsersForVideoCall = await teamsConnection.startCall(
  emailAddresses,
  withVideo // Optional, defaults to false
);

// Make an audio call
const resolvedUsersForAudioCall = await teamsConnection.startCall(emailAddresses);

All of the above functions return a Promise that resolves to one of the following:

  • For group chats or calls, to an array of UserResult objects in the same order as their matching email addresses were provided. If a user is not found for an email address, undefined is returned instead of the object.

  • For channel chats, to a TeamChannelResult object for the resolved Team and Channel.

If no email addresses can be resolved to UserResult objects, or if no team or channel can be resolved to a TeamChannelResult object, no error is thrown and the function resolves with an empty value.

Work with Interop/FDC3

Register intent handlers for StartChat or StartCall intents:

  // Register StartChat and StartCall intent handlers for Teams
  const teamsIntentHandlerRegistration = await teamsConnection.registerIntentHandlers();

  // Trigger the handler by firing a StartChat or StartCall intent
  await fin.me.interop.fireIntent({
    name: 'StartChat', // or StartCall
    context: {
      type: 'fdc3.contactList',
      contacts: [
        {
          type: 'fdc3.contact',
          id: {
            email: '[email protected]',
          },
        },
        {
          type: 'fdc3.contact',
          id: {
            email: '[email protected]',
          },
        },
      ],
    },
  });  

  // Unsubscribe intent handlers if intent handling is no longer required
  await teamsIntentHandlerRegistration.unsubscribe();

Share instrument contexts to Teams conversations

The API provides the ability to share financial context data to a Teams chat, resulting in a formatted card being posted that allows users in the chat to send the message data to other OpenFin apps via Interop.

The share function is able to share either an instrument with price info:

Teams message showing instrument price information

or a chart image:

Teams message showing instrument chart

When the share function is called, here's what happens:

  • Sharer’s desktop: Sharer posts a message to the recipient(s) providing the (hidden) context data in the message payload. The shared information is displayed with a button that fires an intent when clicked. If a chart image was shared, the button fires a ViewChart FDC3 intent, otherwise a ViewInstrument FDC3 intent is fired.

  • Recipient’s desktop: Receives message from sharer. When the button is clicked in the message card, a URL is opened in the user's default web browser that attempts to run (or if already running, focus on) the app that originally shared the message. At this point, the app fires the relevant intent via Interop.

📘

Note

Allow OpenFin "start" links.

For the button action from the Teams message to work, your client must allow internal access to https://start.openfin.co from the default web browser. When the user clicks the button, the app that calls the share function must already be running, or have access to the URL for the app's manifest so the app can be launched.

The process:

  1. Specify the target to share to.
    To share to a group chat, provide an array of email addresses, or to share to a Team Channel, provide an object of type TeamChannelTarget:

    import { TeamChannelTarget } from '@openfin/microsoft365';
    
    let target: string[] | TeamChannelTarget;
    
    // Share to group chat
    target = ['[email protected]', '[email protected]'];
    
    // Or, share to team channel
    target = {
      teamId: 'fbe2bf47-16c8-47cf-b4a5-4b9b187c508b',
      channelId: '19:[email protected]', // Optional, if not provided will share to the team’s primary channel
    } as TeamChannelTarget;
    
  2. Specify the financial instrument context data to share in the form of an FDC3 instrument context which must include the id.ticker value at a minimum and ideally name also (if required, you may also include additional properties and metadata to support your workflows):

    import * as fdc3 from '@finos/fdc3';
    
    const context: fdc3.Instrument = {
      type: 'fdc3.instrument',
      name: 'Microsoft Corporation',
      id: {
        ticker: 'MSFT',
      },
    };
    
  3. Create the options object to provide to the share function. To share a chart, specify an object of type ShareChartOptions. To share instrument price information, specify an object of type ShareInstrumentOptions:

    import { ShareChartOptions, ShareInstrumentOptions } from '@openfin/microsoft365';
    
    let shareOptions: ShareChartOptions | ShareInstrumentOptions;
    
    // When sharing a chart
    shareOptions = {
      chartImage: new Blob([blobArrayBuffer], { type: 'image/png' }), // blobArrayBuffer would be a byte array containing the chart image data. Supported image types: gif, jpg, png
      context,
      target,
      showMessage: true, // Optional, show message in Teams app after posting, defaults to true
      timestamp: Date.now(), // Optional, date will not be displayed with chart if not provided
    } as ShareChartOptions;
    
    // Or, when sharing instrument price info
    shareOptions = {
      context,
      priceCurrent: 128.95, // Instrument current price
      priceHigh: 129.43, // Optional, instrument session high price
      priceLow: 127.25, // Optional, instrument session low price
      priceOpen: 127.42, // Optional, instrument session open price
      showMessage: true, // Optional, show message in Teams app after posting, defaults to true
      target,
      timestamp: Date.now(), // Optional, defaults to now
    } as ShareInstrumentOptions;
    
  4. Call the share function with the specified options:

    const resolvedShareTarget = await teamsConnection.share(shareOptions);
    

The share function returns a Promise that resolves to either a TeamChannelResult object or an array of UserResult objects, depending on the target specified. As with startChat and startCall, no error is thrown if the specified target cannot be resolved.