Skip to content

roshank8s/teams-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 

Repository files navigation

Microsoft Teams Internal API - Python Client

Overview

This Python client interfaces directly with Microsoft Teams' internal (unofficial) APIs — the same APIs used by the Teams web client at teams.live.com. It bypasses the official Microsoft Graph API entirely.

WARNING: These APIs are undocumented and may change without notice. This is for educational/research purposes.


Prerequisites

pip install requests websocket-client rich questionary
Package Purpose
requests HTTP API calls
websocket-client Trouter real-time WebSocket connection
rich Tables, panels, colored output, loading spinners
questionary Arrow-key menus, checkbox multi-select, file path autocomplete

Optional (for automated browser login):

pip install playwright
playwright install chromium

Quick Start

Interactive CLI

python teams_internal_api.py

This launches an interactive terminal with arrow-key navigation:

╔══════════════════════════════════╗
║ Microsoft Teams CLI Client       ║
╚══════════════════════════════════╝

? What would you like to do?
> Chat (select user)
  List conversations
  Read messages
  Send a message
  Check user presence
  Set my status
  Search messages
  Create a new chat
  Start real-time listener
  Upload a file
  Exit

CLI Features

  • Arrow-key navigation — select options with Up/Down, confirm with Enter
  • "< Back" on every screen — return to the previous menu from anywhere
  • Ctrl+C safe — cancels current operation, returns to main menu
  • Rich tables — formatted output with colors, icons, and borders
  • Loading spinners — animated feedback during API calls
  • Breadcrumb headers — always shows where you are (e.g., Main Menu > Chat > John)

Chat View

Selecting any conversation opens an interactive chat view:

─── Main Menu > Chat > John Doe ───

  05/03 14:30  John Doe      Hey, how's it going?
  05/03 14:32  You           Good, working on the project
  05/03 14:35  John Doe      Great, let me know if you need help

Type a message and press Enter to send. Commands: /file  /presence  /refresh  /back
Auto-refresh every 5s. New messages appear automatically.

>

Chat commands:

Command Action
/back Return to previous menu
/refresh Manually reload messages
/file Upload a file (with tab-autocomplete for paths)
/presence Check online status of chat members
Any text Send as a message

Authentication

First-Time Setup

On first run, the CLI auto-detects there's no saved session and starts authentication:

Option A — Automated browser extraction (recommended, requires Playwright):

client = TeamsClient()
client.authenticate_via_browser()
# Opens Chromium, user logs in, refresh token extracted automatically

Option B — Interactive OAuth2 + PKCE:

client = TeamsClient()
client.authenticate()
# Opens browser for login, user pastes redirect URL

Option C — Manual refresh token:

client = TeamsClient()
client.authenticate_with_refresh_token("0.AX4A...")

Subsequent Sessions

Tokens are saved to ~/.teams_tokens.json and reloaded automatically:

client = TeamsClient()
if client.load_session():
    print("Ready!")

Tenant Auto-Detection

The client auto-detects whether the account is consumer (personal Microsoft) or enterprise (organization) by extracting the tid claim from the JWT id_token. The token endpoint adjusts automatically:

  • Consumer: login.microsoftonline.com/consumers/oauth2/v2.0/token
  • Enterprise: login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token

Architecture

Teams internally uses 7 different OAuth2 tokens for different microservices:

Service Endpoint Token Scope
Chat (Messaging) msgapi.teams.live.com service::api.fl.spaces.skype.com::MBI_SSL
Middle Tier teams.live.com/api/mt https://mtsvc.fl.teams.microsoft.com/teams.mt.readwrite
Groups teams.live.com/api/groups https://groupssvc.fl.teams.microsoft.com/teams.readwrite
Presence presence.teams.live.com (uses Skype token)
Search teams.live.com/api/search https://searchsvc.fl.teams.microsoft.com/teams.search.readwrite
Media/Files us-api.asm.skype.com (uses Skype token)
Profile teams.live.com/api/mt (uses MT token)

The client ID 4b3e8f46-56d3-427f-b1e2-d239b2ea6bca is Microsoft's own Teams web client registration (SPA type).


API Reference

Conversations / Chats

# List all conversations
conversations = client.get_conversations(count=25)
for conv in conversations:
    print(f"{conv.title} - {conv.type} - {conv.id}")

# Get conversation details
details = client.chat.get_conversation_details("19:xxx@thread.v2")

Messages

# Read messages
messages = client.get_messages("19:xxx@thread.v2", count=50)
for msg in messages:
    print(f"[{msg.display_name}]: {msg.content}")

# Send a text message
client.send_message("19:xxx@thread.v2", "Hello from Python!")

# Send HTML-formatted message
client.chat.send_message(
    "19:xxx@thread.v2",
    "<p><b>Bold text</b> and <i>italic</i></p>",
    message_type="RichText/Html"
)

# Reply to a message
client.chat.send_reply("19:xxx@thread.v2", "1769692433796", "This is a reply")

# Edit a message
client.chat.edit_message("19:xxx@thread.v2", "1769692433796", "Updated content")

# Delete a message
client.chat.delete_message("19:xxx@thread.v2", "1769692433796")

# Send typing indicator
client.chat.send_typing_indicator("19:xxx@thread.v2")

# Mark messages as read
client.chat.set_consumption_horizon("19:xxx@thread.v2", "1769692433796")

# Add reaction
client.chat.add_reaction("19:xxx@thread.v2", "1769692433796", "like")

Create Chats

# Create 1:1 chat
thread_id = client.chat.create_chat(
    member_mris=["8:live:.cid.abc123"],
    message="Hey! Starting a new chat."
)

# Create group chat with topic
thread_id = client.chat.create_chat(
    member_mris=["8:live:.cid.abc123", "8:live:.cid.def456"],
    topic="Project Discussion",
    message="Welcome everyone!"
)

# Add/remove members
client.chat.add_member("19:xxx@thread.v2", "8:live:.cid.newuser")
client.chat.remove_member("19:xxx@thread.v2", "8:live:.cid.olduser")

# Set chat topic
client.chat.set_topic("19:xxx@thread.v2", "New Topic Name")

Contacts

# Get all contacts (API + conversation-extracted)
contacts = client.contacts.get_all_contacts(client.chat)
for c in contacts:
    print(f"{c['displayName']} - {c['mri']}")

# Get contacts from API only
api_contacts = client.contacts.fetch_contacts()

# Get contacts from recent conversations
conv_contacts = client.contacts.get_contacts_from_conversations(client.chat)

Presence / Status

# Check user presence
presences = client.get_user_presence([
    "8:live:.cid.abc123",
    "8:live:.cid.def456"
])
for p in presences:
    print(f"{p.mri}: {p.availability} on {p.device_type}")

# Set your status
client.set_status("Available")    # Available, Away, Busy, DoNotDisturb, Offline

# Set custom status message
client.presence.set_status_message("In a meeting", expiry_minutes=60)

# Subscribe to presence changes
client.presence.subscribe_to_presence(["8:live:.cid.abc123"])

Search

# Search messages across recent conversations
results = client.search_messages("project update")
for r in results:
    print(f"[{r['sender']}] in {r['conversation']}: {r['content']}")

File Attachments

# Upload a file
result = client.media.upload_file(
    conversation_id="19:xxx@thread.v2",
    file_path="/path/to/document.pdf"
)
print(f"Object ID: {result['object_id']}")

# Download a file
client.media.download_file("object_id_here", "/path/to/save/file.pdf")

Profile & Teams

# Get tenant info
tenants = client.middle_tier.get_tenants()

# Get user profile
profile = client.middle_tier.get_user_profile()

Real-Time Events

# Define callback
def on_new_message(url, body, headers):
    event = parse_trouter_event(url, body, headers)
    if event and event["type"] == "message":
        print(f"[NEW] {event['from']}: {event['content'][:80]}")

# Start listening (uses Trouter WebSocket push notifications)
client.start_realtime(on_message=on_new_message)

# ... your app runs ...

# Stop listening
client.stop_realtime()

Internal API Endpoints

Discovered from live Teams session:

Service URL
Chat Service https://msgapi.teams.live.com
Chat Service (AFD) https://teams.live.com/api/chatsvc/consumer
Chat Aggregator https://chatsvcagg.teams.microsoft.com
Middle Tier https://teams.live.com/api/mt
Groups V2 https://teams.live.com/api/groups
Presence https://presence.teams.live.com
Presence UPS https://teams.live.com/ups/global
Search https://teams.live.com/api/search
Search (Legacy) https://msgsearch.skype.com
Media (AMS) https://us-api.asm.skype.com
User Intelligence https://teams.live.com/api/nss
User Profile https://teams.microsoft.com/api/userprofilesvc/amer
Vault/GraphQL https://teams.live.com/api/datalayer/vault/graphql
OpenAI https://teams.live.com/api/openai
Events https://teams.live.com/api/events
URL Preview https://urlp.asm.skype.com
Auth Bootstrap https://teams.live.com/api/auth/v1.0/authz/consumer

User ID (MRI) Formats:

  • Personal/Consumer: 8:live:.cid.{hex_cid}
  • MSS accounts: 8:mss.{username}
  • Org/AAD accounts: 8:orgid:{guid}

Thread ID Formats:

  • Legacy Skype: 19:{hex}@thread.skype
  • New V2: 19:uni01_{base32}@thread.v2

Message Types:

  • Text — Plain text
  • RichText/Html — HTML formatted
  • RichText/Media_GenericCard — Cards/attachments
  • Event/Call — Call events
  • ThreadActivity/MemberJoined — Member join notifications
  • ThreadActivity/TopicUpdate — Topic change events
  • Control/Typing — Typing indicators

How Authentication Works Internally

  1. User opens OAuth2 URL with Teams client ID (4b3e8f46...) and PKCE challenge
  2. Microsoft login returns an authorization code
  3. Code is exchanged for access_token + refresh_token at /oauth2/v2.0/token (with PKCE verifier)
  4. Tenant ID is auto-detected from the id_token JWT tid claim
  5. The refresh token acquires service-specific tokens for each backend
  6. For Chat/Messaging: an additional Skype token is obtained from /api/auth/v1.0/authz/consumer
  7. Tokens are stored locally (~/.teams_tokens.json) and refreshed automatically

Real-Time Push Notifications (Trouter)

The client uses Microsoft's Trouter protocol for real-time push notifications:

  1. AllocatePOST /v4/a to get a Trouter session (socketio URL, surl, registrar URL)
  2. WebSocket Connect — Connect to wss://<host>/v4/c using socket.io v0.9 protocol
  3. Authenticate — Send user.authenticate event with Skype token and connect params
  4. Register — After trouter.connected event, register with the PNH registrar using three template keys:
    • TFLSkypeSpacesWeb_2.0 (primary)
    • TFLSkypeSpacesWeb_1.0 (fallback)
    • SkypeSpacesWeb_2.3 (catch-all)
  5. Receive — Incoming messages arrive as type-3 socket.io packets, must be ACKed immediately

Data Storage (IndexedDB in Web Client)

The Teams web client uses 82+ IndexedDB databases including:

  • conversation-manager (1101 conversations cached)
  • replychain-manager (5696 message chains cached)
  • presence-manager (30 user presence records)
  • syncstate-manager (sync tokens and cursors)
  • messaging-slice-manager (threads, mentions, drafts, saved items)
  • profiles (user profile cache)

Service Classes

Class Purpose
TokenManager OAuth2 token lifecycle, PKCE, tenant auto-detection
ChatService Conversations, messages, reactions, typing indicators
MiddleTierService Tenant info, user profiles
PresenceService Online status, status messages
SearchService Message search across conversations
ContactsService Contact list from API and conversation extraction
MediaService File upload/download via AMS
TrouterService Real-time WebSocket push notifications
TeamsClient Unified client combining all services

Disclaimer

This project reverse-engineers Microsoft Teams' internal APIs for educational purposes. These APIs are undocumented, subject to change, and not covered by any SLA. Use of these APIs may violate Microsoft's Terms of Service. Use at your own risk.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages