Skip to content

Start typing to search the documentation.

Microsoft Teams Channel API

AI-generated, awaiting review View as Markdown

Import from @flue/teams.

createTeamsChannel()

function createTeamsChannel<E extends Env = Env>(options: TeamsChannelOptions<E>): TeamsChannel<E>;

Creates one stateless, fixed-application, fixed-tenant Teams activity channel. The callback runs only after Bot Connector token, channel, service URL, and host-tenant verification.

TeamsChannelOptions

interface TeamsChannelOptions<E extends Env = Env> {
  appId: string;
  tenantId: string;
  openIdMetadataUrl?: string;
  tokenIssuer?: string;
  fetch?: typeof globalThis.fetch;
  bodyLimit?: number;
  activities(input: { c: Context<E>; activity: Activity }): TeamsHandlerResult;
}
FieldDescription
appIdExpected Bot Connector JWT audience.
tenantIdExpected tenant in the authenticated activity.
openIdMetadataUrlOpenID metadata URL. Defaults to Microsoft’s public cloud.
tokenIssuerExpected issuer. Defaults to https://api.botframework.com.
fetchFetch used for OpenID metadata and JWKS discovery.
bodyLimitMaximum request body. Default: 1 MiB.
activitiesReceives every authenticated and structurally valid activity.

The activity is the provider-native Bot Framework Activity, re-exported from botframework-schema. It is verified but otherwise unmodified.

type TeamsHandlerResult = void | JsonValue | Response | Promise<void | JsonValue | Response>;

Returning nothing produces an empty 200. A JSON-compatible value is serialized with Response.json and returned as a JSON response. An ordinary Hono or Fetch Response passes through unchanged.

invoke activities expect a JSON acknowledgement body in the response (for example, adaptiveCard/action responses), so return the appropriate value or Response. The Bot Connector retries on any non-2xx status, so reserve error statuses for deliveries you want redelivered.

TeamsChannel

interface TeamsChannel<E extends Env = Env> {
  readonly routes: readonly ChannelRoute<E>[];
  destination(activity: Activity): TeamsConversationRef;
  conversationKey(ref: TeamsConversationRef): string;
  parseConversationKey(id: string): TeamsConversationRef;
}

routes contains one POST /activities declaration. A file named channels/teams.ts is served at /channels/teams/activities relative to the flue() mount.

destination(activity) derives the canonical routing identity from a verified activity. Activities delivered to the activities callback always derive a destination; it throws InvalidTeamsInputError for an activity that lacks the minimal structure needed to address a reply.

Conversation keys are canonical identifiers, not authorization capabilities.

Activities

The callback receives the provider-native Bot Framework Activity, re-exported from the official botframework-schema package:

import type {
  Activity,
  ChannelAccount,
  ConversationAccount,
  Entity,
  Attachment,
  Mention,
  MessageReaction,
} from '@flue/teams';

The activity keeps Microsoft’s documented type values and field names. Switch on the native activity.type (message, conversationUpdate, invoke, messageReaction, installationUpdate, and others) and read native fields such as activity.text, activity.from, activity.recipient, activity.id, activity.entities, activity.reactionsAdded, and activity.membersAdded. See Microsoft’s Activity schema for the full field inventory.

The channel does not reshape, rename, or normalize the payload; it derives only the routing identity below.

Identity

interface TeamsConversationRef {
  tenantId: string;
  serviceUrl: string;
  conversationId: string;
  scope: 'personal' | 'groupChat' | 'channel' | 'unknown';
  botId: string;
  threadId?: string;
  teamId?: string;
  channelId?: string;
}

For channel activities, threadId is the provider replyToId or the current activity id for a root message. The verified serviceUrl is retained so stateless applications can address the correct Bot Connector endpoint.

Derive a TeamsConversationRef from a verified activity with channel.destination(activity). It is not a callback argument; call the helper inside the activities callback when you need to address a reply.

Verification

The package retrieves the configured OpenID metadata and endorsed JWKS keys, caches them with bounded response cache metadata, and refreshes once when a token references an unknown key id.

Requests fail before the callback when the bearer token, signature, issuer, audience, expiration, msteams endorsement, exact service URL, channel id, or tenant identity is invalid. Discovery failures return 503.

Errors

  • InvalidTeamsConversationKeyError
  • InvalidTeamsInputError, with structured field

See Microsoft Teams setup for the project-owned Fetch client and application tool composition.