Agent MessengerAgent Messenger
TypeScript SDK

Teams

TypeScript SDK reference for Microsoft Teams — client, credential management, and types.

Installation

npm install agent-messenger
import { TeamsClient, TeamsCredentialManager, TeamsError } from 'agent-messenger/teams'

TeamsClient

The main client for interacting with Microsoft Teams' API programmatically. All API methods include automatic rate-limit handling with per-bucket tracking and exponential backoff on server errors.

Teams tokens expire in 60-90 minutes. The client checks expiry before each request and throws a TeamsError with code token_expired when the token is stale. Re-run agent-teams auth extract to refresh.

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login({ token })
// With expiry tracking: await new TeamsClient().login({ token, tokenExpiresAt: '2025-03-23T12:00:00Z' })

Or use automatic credential extraction — credentials are extracted from the desktop app, no manual token needed:

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login()

Authentication

// Verify credentials and get identity
const user = await client.testAuth()
// → TeamsUser { id, displayName }

Teams

// List all teams the authenticated user belongs to
const teams = await client.listTeams()
// → TeamsTeam[]

// Get a specific team by ID
const team = await client.getTeam(teamId)
// → TeamsTeam { id, name, description? }

Channels

// List all channels in a team
const channels = await client.listChannels(teamId)
// → TeamsChannel[]

// Get a specific channel by ID
const channel = await client.getChannel(teamId, channelId)
// → TeamsChannel { id, team_id, name, type }

Channel IDs use a Teams thread ID format (19:xxx@thread.tacv2), not numeric snowflakes. Always pass the parent teamId alongside the channelId.

Messages

// Send a message to a channel
const msg = await client.sendMessage(teamId, channelId, 'Hello world')
// → TeamsMessage { id, channel_id, author, content, timestamp }

// Get recent messages from a channel (default limit: 50)
const messages = await client.getMessages(teamId, channelId)
const limited = await client.getMessages(teamId, channelId, 25)
// → TeamsMessage[]

// Get a single message by ID
const message = await client.getMessage(teamId, channelId, messageId)
// → TeamsMessage

// Delete a message
await client.deleteMessage(teamId, channelId, messageId)

Reactions

// Add a reaction to a message
await client.addReaction(teamId, channelId, messageId, 'like')

// Remove your reaction
await client.removeReaction(teamId, channelId, messageId, 'like')

Users

// List all members in a team
const users = await client.listUsers(teamId)
// → TeamsUser[]

// Get a specific user by ID
const user = await client.getUser(userId)
// → TeamsUser { id, displayName, email?, userPrincipalName? }

Files

// Upload a file to a channel (by file path)
const file = await client.uploadFile(teamId, channelId, '/path/to/report.pdf')
// → TeamsFile { id, name, size, url, contentType? }

// List files in a channel
const files = await client.listFiles(teamId, channelId)
// → TeamsFile[]

TeamsCredentialManager

Manages Teams credentials stored at ~/.config/agent-messenger/teams-credentials.json. Files are written with 0o600 permissions. Supports multiple accounts (work and personal) with automatic migration from the legacy single-account format.

The static property TeamsCredentialManager.accountOverride lets you force a specific account type ('work' or 'personal') for all operations, overriding the stored current_account setting.

import { TeamsCredentialManager } from 'agent-messenger/teams'

const manager = new TeamsCredentialManager()
// Custom path: new TeamsCredentialManager('/custom/config/dir')
// Load full config from disk (returns null if file doesn't exist)
const config = await manager.loadConfig()
// → TeamsConfig | null

// Save full config to disk
await manager.saveConfig(config)

// Get stored token for the current account
const token = await manager.getToken()
// → string | null

// Get token with expiry info
const tokenInfo = await manager.getTokenWithExpiry()
// → { token: string; tokenExpiresAt?: string } | null

// Store a token for a specific account type
await manager.setToken('my-teams-token', 'work', '2025-03-23T12:00:00Z')

// Check if the current account's token has expired
const expired = await manager.isTokenExpired()
// → boolean

// Check if a specific account's token has expired
const workExpired = await manager.isAccountTokenExpired('work')
// → boolean

// Clear all stored credentials (deletes the file)
await manager.clearCredentials()

// Get current default team
const team = await manager.getCurrentTeam()
// → { team_id: string; team_name: string } | null

// Set the current default team
await manager.setCurrentTeam('team-uuid', 'Engineering')

// Get the active account type
const accountType = await manager.getCurrentAccountType()
// → 'work' | 'personal' | null

// Switch the active account
await manager.setCurrentAccount('personal')

// Get the full account object for the current account
const account = await manager.getCurrentAccount()
// → TeamsAccount | null

// Get all saved accounts
const accounts = await manager.getAccounts()
// → Record<string, TeamsAccount>

// Override account selection globally (static property)
TeamsCredentialManager.accountOverride = 'personal'

Types

import type {
  TeamsTeam,
  TeamsChannel,
  TeamsMessage,
  TeamsUser,
  TeamsReaction,
  TeamsFile,
  TeamsCredentials,
  TeamsAccountType,
  TeamsAccount,
  TeamsConfig,
  TeamsConfigLegacy,
} from 'agent-messenger/teams'

Zod Schemas

Runtime-validated schemas are also exported for parsing API responses:

import {
  TeamsTeamSchema,
  TeamsChannelSchema,
  TeamsMessageSchema,
  TeamsUserSchema,
  TeamsReactionSchema,
  TeamsFileSchema,
  TeamsCredentialsSchema,
  TeamsAccountTypeSchema,
  TeamsAccountSchema,
  TeamsConfigSchema,
  TeamsConfigLegacySchema,
} from 'agent-messenger/teams'

Examples

Deploy Notifier

Post a deployment message, stream progress, and mark it complete with a reaction.

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login({ token, tokenExpiresAt })

// Post parent message to deploys channel
const msg = await client.sendMessage(teamId, channelId, '🚀 Deploying v2.1.0 to production...')
await client.addReaction(teamId, channelId, msg.id, 'like')

// Stream progress with follow-up messages
await client.sendMessage(teamId, channelId, '• Building application...')
await client.sendMessage(teamId, channelId, '• Running tests (142 passed)...')
await client.sendMessage(teamId, channelId, '• Rolling out to 3 regions...')

// Finalize
await client.removeReaction(teamId, channelId, msg.id, 'like')
await client.addReaction(teamId, channelId, msg.id, 'heart')
await client.deleteMessage(teamId, channelId, msg.id)
await client.sendMessage(teamId, channelId, '✅ v2.1.0 deployed to production')

Channel Monitor

List channels in a team and pull recent messages from each one.

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login({ token, tokenExpiresAt })

// List all channels in the team
const channels = await client.listChannels(teamId)

for (const channel of channels) {
  const messages = await client.getMessages(teamId, channel.id, 5)
  console.log(`#${channel.name} (${messages.length} recent messages)`)

  for (const msg of messages) {
    console.log(`  [${msg.author.displayName}] ${msg.content.slice(0, 80)}`)
  }
}

Team Summary

Build a snapshot-style overview of a team: members, channels, and latest activity.

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login({ token, tokenExpiresAt })

// Gather team info
const team = await client.getTeam(teamId)
const channels = await client.listChannels(teamId)
const members = await client.listUsers(teamId)

console.log(`Team: ${team.name}`)
console.log(`Members: ${members.length}`)
console.log(`Channels: ${channels.length}`)
console.log()

// Show latest message per channel
for (const channel of channels.slice(0, 5)) {
  const messages = await client.getMessages(teamId, channel.id, 1)
  const latest = messages[0]
  if (latest) {
    console.log(`#${channel.name}: "${latest.content.slice(0, 60)}" — ${latest.author.displayName}`)
  } else {
    console.log(`#${channel.name}: (no messages)`)
  }
}

File Pipeline

Collect files from one channel and re-upload them to an archive channel with a summary.

import { TeamsClient } from 'agent-messenger/teams'

const client = await new TeamsClient().login({ token, tokenExpiresAt })

// Get files from the source channel
const files = await client.listFiles(teamId, sourceChannelId)

for (const file of files.slice(0, 5)) {
  // Fetch the file content
  const response = await fetch(file.url)
  const buffer = Buffer.from(await response.arrayBuffer())

  // Write to a temp file and re-upload to archive channel
  const tmpPath = `/tmp/${file.name}`
  const fs = await import('node:fs/promises')
  await fs.writeFile(tmpPath, buffer)
  const uploaded = await client.uploadFile(teamId, archiveChannelId, tmpPath)

  await client.sendMessage(
    teamId,
    archiveChannelId,
    [
      `📎 Archived **${file.name}**`,
      `Size: ${(file.size / 1024).toFixed(0)} KB · Type: ${file.contentType ?? 'unknown'}`,
    ].join('\n'),
  )
}

On this page