Back to Articles

Reverse-Engineering Microsoft Teams: Building Go Clients with Unofficial APIs

[ View on GitHub ]

Reverse-Engineering Microsoft Teams: Building Go Clients with Unofficial APIs

Hook

Microsoft Teams processes over 280 million messages daily, yet the official Graph API won't let you build a native desktop client or read messages in real-time. One open-source project is changing that by reverse-engineering the APIs Teams itself uses.

Context

Microsoft Teams dominates enterprise collaboration, but developers face a fundamental constraint: the official Microsoft Graph API only exposes what Microsoft wants you to access. You can send webhook notifications, create channels, and manage memberships, but you can't build an alternative client that reads messages in real-time, handles typing indicators, or provides a custom UI experience. This isn't an oversight—it's by design. Microsoft wants you using their Electron-wrapped client, not building competitors.

The fossteams/teams-api library emerged from this friction. Built in Go, it reverse-engineers the internal APIs that Microsoft's own Teams clients use—the same endpoints the Electron app hits when you send a message or update your status. This isn't about scraping web pages; it's about speaking the same protocol as official clients, using the same authentication tokens and API contracts. The result is a foundation for building alternative Teams clients entirely outside Microsoft's intended ecosystem, whether that's a lightweight terminal client, a privacy-focused desktop app, or integration tooling that needs deeper access than Graph API provides.

Technical Insight

The library's architecture centers on JWT token management and typed HTTP client patterns. Unlike OAuth flows you might implement with Graph API, Teams' internal APIs use three separate JWT tokens: one for the chat service aggregator (chatsvcagg), one for Skype infrastructure (the legacy backbone Teams still runs on), and one for Teams-specific endpoints. Here's how you initialize an authenticated client:

package main

import (
    "github.com/fossteams/teams-api"
    "log"
)

func main() {
    // Load tokens from standard config directory
    // (obtained separately via teams-token tool)
    client, err := teamsapi.NewClient()
    if err != nil {
        log.Fatalf("Failed to initialize client: %v", err)
    }
    
    // Fetch recent conversations
    conversations, err := client.GetConversations()
    if err != nil {
        log.Fatalf("Failed to fetch conversations: %v", err)
    }
    
    for _, conv := range conversations.Conversations {
        log.Printf("Chat: %s (ID: %s)\n", conv.ThreadProperties.Topic, conv.ID)
    }
}

Under the hood, the library maintains separate HTTP clients for different Teams service endpoints. This design mirrors how Microsoft's backend is actually structured—Teams isn't a monolithic service but a federation of legacy Skype infrastructure, newer Teams-native services, and shared Microsoft 365 components. The library's Client struct wraps these concerns:

type Client struct {
    ChatSvcAggClient *http.Client
    SkypeClient      *http.Client
    TeamsClient      *http.Client
    tokens           *TokenSet
}

Each client automatically injects the appropriate JWT token in request headers and handles the specific endpoint patterns for that service domain. For example, fetching messages from a conversation hits the chatsvcagg endpoint with a specific URL structure: https://teams.microsoft.com/api/csa-{region}/api/v1/teams/users/ME/conversations/{threadId}/messages. The library abstracts this into a simple method call while preserving the underlying service architecture.

The token acquisition process deserves special attention because it reveals how Teams actually authenticates. The companion teams-token tool (separate repository) automates extracting tokens from an authenticated Teams session—either by intercepting browser traffic or extracting from local storage. These tokens are then cached in your system's standard config directory (~/.config/teams-api/ on Linux, ~/Library/Application Support/teams-api/ on macOS). The library includes a token refresher that monitors expiration and can trigger re-authentication workflows, though this is still a work-in-progress area.

What makes this library particularly valuable is its typed response structs. Rather than parsing JSON manually, you get idiomatic Go types:

type Message struct {
    ID               string    `json:"id"`
    Content          string    `json:"content"`
    MessageType      string    `json:"messagetype"`
    OriginArrivalTime time.Time `json:"originalarrivaltime"`
    From             string    `json:"from"`
    ConversationID   string    `json:"conversationid"`
}

This typing is the result of careful reverse-engineering—capturing actual API responses, identifying stable fields, and documenting their types and semantics. It's ongoing detective work, which is why the library explicitly marks itself as work-in-progress. Not all endpoints are mapped yet, and some response fields remain unexplored or poorly understood.

The library also handles Teams' event streaming mechanism, which uses long-polling HTTP connections to deliver real-time updates. When you see typing indicators or receive messages instantly in Teams, that's this polling mechanism at work. The library exposes this through Go channels, letting you build event-driven applications:

events := client.SubscribeToEvents()
for event := range events {
    switch e := event.(type) {
    case *MessageEvent:
        log.Printf("New message: %s\n", e.Content)
    case *TypingEvent:
        log.Printf("%s is typing...\n", e.UserID)
    }
}

This architecture decision—using Go channels for events—aligns with idiomatic concurrent Go patterns, making it natural to build reactive Teams clients that handle multiple simultaneous conversations or monitor organization-wide activity.

Gotcha

The elephant in the room: this library relies entirely on undocumented APIs that Microsoft never intended for external use. When Microsoft refactors their backend—and they do, frequently—endpoints change, authentication flows evolve, and previously working code breaks. There's no deprecation notice, no migration guide, no support channel. You discover breakage when your production integration suddenly returns 401s or malformed responses. The fossteams community is responsive, but you're fundamentally on your own for reliability.

API coverage is incomplete by necessity. As of current versions, you can read conversations, fetch messages, and send basic text, but advanced features like meeting management, file uploads, or Teams app interactions remain unmapped or partially implemented. The reverse-engineering process is time-intensive—it requires capturing real Teams traffic, deducing API contracts, and testing edge cases—so features arrive gradually. If your use case requires comprehensive Teams functionality, you'll likely need to contribute your own endpoint mappings or wait for community contributions. Additionally, the token acquisition workflow adds operational complexity. Unlike OAuth where you redirect users to Microsoft's login and receive tokens programmatically, here you need the separate teams-token tool, which requires users to authenticate in a browser and extract credentials. This works fine for developer tooling or personal automation, but becomes awkward for distributing applications to non-technical users.

Verdict

Use if: You're building alternative Teams clients, internal automation that needs real-time message access, or privacy-focused tools where running Microsoft's Electron client isn't acceptable. This library is ideal for developers comfortable with maintenance risk and willing to debug API changes in exchange for access beyond official Graph API limits. It's particularly valuable for open-source projects that want Teams integration without vendor lock-in, or for organizations with specific compliance requirements that prevent using official clients. Skip if: You need production stability with SLAs, can accomplish your goals with the official Microsoft Graph API (webhooks, channel management, user administration), or are building commercial products where unofficial API usage creates legal uncertainty. Also skip if you're not prepared to actively monitor for breaking changes or lack the reverse-engineering skills to debug opaque API failures when Microsoft changes their backend.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/cybersecurity/fossteams-teams-api.svg)](https://starlog.is/api/badge-click/cybersecurity/fossteams-teams-api)